pax_global_header00006660000000000000000000000064121664127740014524gustar00rootroot0000000000000052 comment=d92c23f5710c41eb9d04fd0206404e65a4c12b08 ninja-ide-2.3/000077500000000000000000000000001216641277400132265ustar00rootroot00000000000000ninja-ide-2.3/.gitignore000066400000000000000000000002211216641277400152110ustar00rootroot00000000000000# Ignore .pyc files *.pyc *.swp tests/tools/build/ _trial_temp .DS_Store ninja_ide/addins/plugins/* NINJA_IDE.egg-info/* build/* dist/* *.pyo ninja-ide-2.3/COPYING000066400000000000000000001045141216641277400142660ustar00rootroot00000000000000 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 . ninja-ide-2.3/README.md000066400000000000000000000022711216641277400145070ustar00rootroot00000000000000# **Ninja-ide** | Ninja-ide Is Not Just Another IDE. Ninja-IDE is a cross-platform integrated development environment (IDE) that runs on Linux/X11, Mac OS X and Windows desktop operating systems. Ninja-IDE allows developers to create applications for any purpose making the task of writing software easier and more enjoyable. ## Contact - [Homepage](http://ninja-ide.org) at ninja-ide.org - [Mailing List](http://groups.google.com/group/ninja-ide/topics) at Google Groups - [@ninja\_ide](https://twitter.com/ninja_ide) at Twitter - [#ninja-ide](irc://freenode.net/ninja-ide) at Freenode.net ## Dependencies Both of these: - [Python](http://python.org/) >= 2.7 (or Python3) - [PyQt](http://www.riverbankcomputing.com/software/pyqt/intro) >= 4.8 And one of these: - [pyinotify](https://github.com/seb-m/pyinotify) (on Linux) - [pywin32](http://sourceforge.net/projects/pywin32/files/pywin32/) (on Windows) - [macfsevents](http://pypi.python.org/pypi/MacFSEvents) (on Mac OS) ## Cloning and Running You can clone this repo and simply execute: git clone git://github.com/ninja-ide/ninja-ide.git cd ninja-ide python ninja-ide.py Piece of cake, uh? ## License - GPL v3 ninja-ide-2.3/build_files/000077500000000000000000000000001216641277400155075ustar00rootroot00000000000000ninja-ide-2.3/build_files/PKGBUILD000066400000000000000000000025271216641277400166410ustar00rootroot00000000000000# Submitted: Federico Cinelli # Maintainer: Federico Cinelli pkgname=ninja-ide-git pkgver=20130206 pkgrel=1 pkgdesc="Ninja Is Not Just Another Intergrated Development Environment - Latest git pull" arch=('any') url="http://ninja-ide.org" license=('GPL') depends=('python2' 'python2-distribute' 'python2-pyqt' 'git' 'python2-pyinotify') install=$pkgname.install provides=('ninja-ide-git') conflicts=('ninja-ide') replaces=('ninja-ide') source=(ninja-ide-git.desktop) md5sums=('384b13743f204b17ed3b87cc7d8df456') _gitroot="git://github.com/ninja-ide/ninja-ide.git" _gitname="ninja-ide" build() { msg "Connecting to GIT server...." if [ -d $_gitname ] ; then cd $_gitname && git pull origin msg "The local files are updated." else git clone $_gitroot $_gitname fi msg "GIT checkout done or server timeout" rm -rf "$srcdir/$_gitname-build" cp -r "$srcdir/$_gitname" "$srcdir/$_gitname-build" cd "$srcdir/$_gitname-build" } package() { cd "$srcdir/$_gitname-build" install -Dm644 "$srcdir/$pkgname.desktop" "$pkgdir/usr/share/applications/$pkgname.desktop" install -Dm644 "ninja_ide/img/icon.png" "$pkgdir/usr/share/pixmaps/$pkgname.png" sed -i 's|#!/usr/bin/env python|#!/usr/bin/env python2|' ninja-ide.py python2 setup.py install --root=$pkgdir/ --optimize=1 } ninja-ide-2.3/build_files/installer/000077500000000000000000000000001216641277400175045ustar00rootroot00000000000000ninja-ide-2.3/build_files/installer/windows/000077500000000000000000000000001216641277400211765ustar00rootroot00000000000000ninja-ide-2.3/build_files/installer/windows/installer.nsi000066400000000000000000000065141216641277400237140ustar00rootroot00000000000000;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Auto-Generated With py2Nsis ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; HM NIS Edit Wizard helper defines !define PRODUCT_NAME Ninja !define PRODUCT_VERSION 2.1 !define PRODUCT_PUBLISHER !define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\Ninja.exe" !define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" !define PRODUCT_UNINST_ROOT_KEY "HKLM" ; MUI 1.67 compatible ------ !include "MUI.nsh" ; MUI Settings !define MUI_ABORTWARNING !define MUI_ICON ninja.ico !define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico" ; Welcome page !define MUI_WELCOMEPAGE_TITLE_3LINES !insertmacro MUI_PAGE_WELCOME ; License page ; Directory page !insertmacro MUI_PAGE_DIRECTORY ; Instfiles page !insertmacro MUI_PAGE_INSTFILES ; Finish page !define MUI_FINISHPAGE_TITLE_3LINES !insertmacro MUI_PAGE_FINISH ; Uninstaller pages !insertmacro MUI_UNPAGE_INSTFILES ; Language files !insertmacro MUI_LANGUAGE "Spanish" ; MUI end ------ Name "${PRODUCT_NAME} ${PRODUCT_VERSION}" OutFile "installer.exe" InstallDir "$PROGRAMFILES\Ninja" InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" "" ShowInstDetails hide ShowUnInstDetails hide Section "Principal" SEC01 SetOutPath "$INSTDIR" File C:\\NINJA\\ninja-ide-2.1\\dist\\Ninja.exe File C:\\NINJA\\ninja-ide-2.1\\dist\\w9xpopen.exe SetOutPath $INSTDIR\\doc File /r C:\\NINJA\\ninja-ide-2.1\\dist\\doc\\* SetOutPath $INSTDIR\\img File /r C:\\NINJA\\ninja-ide-2.1\\dist\\img\\* SetOutPath $INSTDIR\\addins File /r C:\\NINJA\\ninja-ide-2.1\\dist\\addins\\* SetOutPath "$INSTDIR" SectionEnd Section -AdditionalIcons CreateShortCut "$SMPROGRAMS\Ninja.lnk" "$INSTDIR\Ninja.exe" CreateShortCut "$DESKTOP\Ninja.lnk" "$INSTDIR\Ninja.exe" CreateShortCut "$SMPROGRAMS\Ninja\Desinstalar.lnk" "$INSTDIR\uninst.exe" SectionEnd Section -Post WriteUninstaller "$INSTDIR\uninst.exe" WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\Ninja.exe" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\Ninja.exe" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}" SectionEnd Function un.onUninstSuccess HideWindow MessageBox MB_ICONINFORMATION|MB_OK "La desinstalacion de $(^Name) finalizo satisfactoriamente." FunctionEnd Function un.onInit MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Esta completamente seguro que desea desinstalar $(^Name) junto con todos sus componentes?" IDYES +2 Abort FunctionEnd Section Uninstall Delete "$INSTDIR\*" Delete "$SMPROGRAMS\Ninja\Desinstalar.lnk" Delete "$DESKTOP\Ninja.lnk" Delete "$SMPROGRAMS\Ninja.lnk" Delete "$INSTDIR\*" RMDir "$SMPROGRAMS\Ninja" RMDir "$INSTDIR" DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY}" SetAutoClose true SectionEnd ninja-ide-2.3/build_files/installer/windows/ninja.ico000066400000000000000000001072371216641277400230030ustar00rootroot00000000000000 JV00 %_J  p  h7PNG  IHDR\rf IDATxwUnfRI (%Q AiRJ0H!1eS )fs޹;ٙ{Ι3Ξ9ob@js߀澁MzSMM7j]VTR RR3G&$ak׮m[V`eVX`…̙3?ٳ[n# BSBC*T881Gܚ^"7裎: ` 0l{ѳ@**Xt /3x≧O~jI (!'6W^. N;S:uec趄{^{9s|hT6)h/ C10l_K|ٮ7nz>} h"fO][[ Xh/ZeKXgYUVn]#{JfsF?D^_)˶q+R;t@Mt޽{0p@vi'vyg 8 kV\`„ [RCۋ cE 4wy?0a,wOr464E0C0vXNNy)M7t?#F o߾su]/to։bf6_ȵEEE+^{PWϘ9oy f'W}\O>{t ;تcE ’\M=3_d `/3k֬{fWju&$TVlkw^PVVV 0cƌvm/9j-o:e+]@* `&#Qכ͹z9+f\.p^a*3:\Nê+opwPc`{gfs 1nܸ'M|ڣ0i.-8812Pp@Xx۟5>b]i۪Q2'91<&h@f6$>/L3PxmLfv3&]ظN:O=m?8nԨQ_xǟ2]6OC*cEbb6,GqH?繎g> @h ୲;^r{k_^ -Bb?vz1_<N&uothZE_Nۛ?u888l(&jvߒ.QlRlHQj/pO:,x]<{_ަ[R}Wfx V- TI?ޚl?8.8[ >`AǮ4YUSm2>_~?xXl+)آCMoc :t0K6J4k j.(rPfP,I3 sS{{x{ V ]mUتoaP @#_`Ȑ!;DrH!kfOW]}:9ƶz '0 F=3A&LѶߪqp(b#20h 2^AJp_'`< 0iҤM|u [pp(b&5]*6kn# W6L$h  yg.T׵JTT܂mRMy8lZk&T8+:?]D Y62+ t9Y+xjuz[&flwmNV"_W5k֬6CV__rf881E`Ǎkll5x=19lRէq)a9$ϩ۹Kť5eO&Z/\`ܹ+vVO{}.[o9T,W8)H[#H:i[誫kʼW6TY!lB;~ɺ28/z H) ax3MTe%*^ pI'w;ON=_ u B%881dtnm:tk<RgYn)HRĿ#JvwayW(+#R-ڇ L6\ /I[0{WjRT[oi53BCCr %08IןtVff#.\( lj,[VFW~ /㏽)W|; ;ʦ[n@O.~#S _lY1%.=?C:w\ PQQPT3@CCCiW˖-[P[[[ py/hnnA}I[U8lFCcs0B%mHv5JUtԩkW/G=tѣ@yyy @III @SSS1cֆO>UW]C&j$emM!1:mJklp[cĈZ:/1c[SLy~`CmUQ6+B1"F;mqkw{H0dȐХWB9qg1:S0: l}뀜8!%=Jg|`.+9?8\Ϻ)nݺ:Op :uV(F=p 1u$t?SN9c۷ojCpa^Œ_Ȉp`@ϕ) qe(FAj+F`b2rJzCjdrJSt09E'ңػ7UUIJ`Glz.jkkCĜd(8816H*uf#&?N^{UEi/5Yl&3Uĭ]@$P ^SP@SF$őy[o WU֙\^!т [7Ai}?l~ 5s曽I{gr } }^+D%8} @MMM`e˖=Pf͚evD핡(pr"D{28 0bĈO?$ݻ:}/Of0UqnT[=|$sa˅|֣B~-+ M_VЧOopY^a!H1.#kcߣ}&}=$jN)V7zS9{[' lrϩK:&+~_mMь`}= 1ʄC9D(ve8816$ٳԩS;/k~s= P+/r&K*"mdq T4[& ֭$0^tA\|x?y[no .D[gVy /Nl٢A*my 2 <ýLHfUA4iR>GLWd ڞp ?B^{/ ,e-|QG吏̭gpey ֮hgsmTy/Ҏc?_?n ]s5ʫ1ϓQo`V_#;9ۃZd*?|N}M:\uƒ$wDI.NwqxI1]6Io≘cE$~ /u|xڕ6!ȯQvhf^ANI/`[+Z֎׫GPvmg yvaOe}+ˑ)GDL_mV:p|hMcޏ7N[G ٸ_9;6AڭC *(D5l0;}w{*4rq3f[A pp(bimL p知Կ %/[dkV7-Ek В^AI(͖Fnty{oa@9=eq|O~r ']ڧt W_}5/45"i1,/mĴZ_zP_QbtA:#D}:z4yq-o ?=AS8 @GCVcE 1 EzQVZ k5Ykf yr%̬5lLyO`M{Ί<*Id/V+ AϳiӼue8!_1QmoxZ>.ټy0;=DG"HAOUjXd\N3 Y;cy 8+Mg<چ$ Pk@ԩ%V?o6mT 5vM n`:ɠ\z\`;e0LVG1Ն03fO`7‹.NcwO<TGJyl_C/L'zwml Ossɷ ӒspEO?v~?O/K2>_Ohq CюVP_@wgvluIJ, @;o+L5]888M cwз_@Y|W,x%(?c=c3aVkն }sxZt"(A+0ٟR~6qowaK.7tG}4?0߿? =ɓx QZ+&A`ٲemn+At`9+póMƠA?`pppF[vaQ_`Vkx+#HgP-m^I=°D}ݭ@y6jmv7:z-ldW`}̝;7\q@ uu56.hѢիSm蘀8app(b! 4f {lS{%K`KPh_u1)oB~*0T'Yb:sks9-V2Ϙ89Ɵ*_f>fN4u$qIdF ~ $V:sԺ'뀲3>,ݺuh ZX'CWޑEZ(gh3!HO՛JumBQ\z%πi1hD6 =~[ϔ)Sm*HVc(kj|gp7zy@bLk Zʪ(:6%ڌ/r6FvVhD1U]@MN+W lzキoy}奒|`kmE DNJ~b@{&j6IRZ9 Cx v}hb$#džD☧H!۹"W]]]PQQQy C=:M  IDATwR:o*Kz5N~l[@HkP?%Uޫ@4W) |f6O1mmmm8);a.Ll"1. -߇2Jb"~y @Hyyy)@8Zo/DG"~坄%*n>A׈318u~S ]^d@JjIZ߇rh݅x:X!.sNIz{oz5тRQ (չsgL^R l͟V_5BaCmF6h0w ў+4}rђu;U5Hx݊D^k$~~^ O?ÇЭ[45zDi&#y´..v%^9`|V˗/7Quq~;pSNu,d=5^{ keՅ6+Q3;,'j1"F[< }.#TnakP=h~I%H\uXf* 4ߛ{vZpS_p @>}ȟ9 fߋH4|#qM={Чwoŗ~WSQ c)aa,$s=g$WnW/~ hqOj j{kc|E?>c.}8bkc Z!MbɫRAif1 Q@!p V[|Tc=!?Vzk݌ `LN2k~H*u'/M贄!W8O J~/I8ؾ:k(2Jdk;1"Fg 6D}O?_}]?n hЬsK/h_po/س%Ns]'ʐ'mmX$xH %J~|6Q#d6H'C=@"g( g%5R c@YQk,kL J _?OHZq'n(V;4yz2ZZD׌ n[EYu*QxwX;ﭩYh!9۬jcڳ՚RWWрLT7oD%f&k\\tq BPưmȚ?GPe e =RPsL&dwQWW _.Æ=@3@CCCSNYT֜/3QV:G (unPM>e ЄCՎߜ1?r 7[ $Ό7_Qr:^t&!AZITA%m0O"l$&D(ƀ?v۴l~}M2^2i])_Z&݀cE a3ЪU^QFgdiPnC+@JK| Ȳ݋s=G=6;#w& <SO=3 |@ɖS_ڜ٬hɣס5jWmlo.ȧk<buujI 44-_YUfCY.jXfL*Ͽ?ޜficL+DKɩWer >_:zw2ۈ$bGhE' ;WM~- 9i<5/F{sLѕ[ieI~  T!c`RYtO: MFŋ0fUkC"cE$ v1 dQ;͘q3k{yx-+3-ZPto];o'}@>uef/pկ~He ",d _>u~m< 3H]CMl->>(NmY˧l_NcX͆WeEN7gΜr jYpp(b `qˣ8p` hIh}.S}~] XhMF;3r!|n͗ 9'U:0PtR1G+3'0kmW Y 6Ϲ"Y-Wkv9>د%wagnDTI3e裏.ahO?*]QQwZjh(V)][B`*pp(bG,@iٟ~(j`ݔ5س%J6Cj M 64kmٮ~^ROx=sBlJT&z'|f#a%FKvo-mэi#sQE _gL pV?8 3%crLfz*b#G:ٳ{'K m! hkӐW.]t%@^ 4rE;y;yǓh5 .3}I⋁vs9z)@o뮻~u<a~^$dyIdK<V.ү< -:"XrJ%}0$M :0:?>Z+"К?Ji ߿?~x|t_p #mM/{_hSrj>'GJ) M$}-JH@A|A^K+i5|%tH2.a&W_}57|Sp0xf5gbe4xGpjS5DUJ쁭F/(1YY_˫( s`CaW[`βI/!OR7!L2m%oæ !rcE֦IJU[nZ Vm/yoW_{톖']v-Æ `ԘA~e%Yt,]*7;yd<@8{9s]zuJKD~@8t ;+Ƽygہ|t_aB阊>ڇc mߕd>|8/WϿ`M7mDҋ5N Vu8810iU[aZjL[ JL6#Gnywߏ REXf!r1=ZZmdg}3f SN$]W2Qת GouN3G_ }5$SY @3q,5ml4 ؘ@\fCRgno=)Sc^7 3-]<|>#:KpZ3h x|1YǢVd/0&/w28vث}ٗ.Y$8BTV3jʊnu{1,n~?蠃D@Hc$ڭXd6m_kM@eE}5ݳMuWNk-uf걯6n<ǙFo={I]w_T$3^ğ@~P_L?ᅭ:ϱ *lLϿ`M7=`zpp(b$a8m :1O? f qyfVg $ xn%Z[,3O`ʨ/3ty g١橦ktPMMs^ug1 k<ܹsW~W\Tt+Br`L n=|O>u_qu>zK~~gDri7H/p dPki]tѷ~u'F]l9k|]ɪ+mdZek;gndnX^}ͫө&py?a$-~ V >ך5 9x"!o/͜9Z/NԐߣsg4SaIT3TtX6 e#{K]^zɣPigOCLT'm$t_ 0~?лwo*D|d9s|f֙> 0A(-{:g.iQJZu[vuy}y a;g |-n?d^ie/p 9QfR-3L:ՐׂN:cKqq(aZm} מatti?DTtsM&*d^ÒK=,ŋW|^yչSLgkh? %|/T881 qJ=u ȶW зos:jO#F ر,8 E<Uv^4Lz3iŞ>˞}ҤIt6ZmY{&y!MM5Z`,Yv% V.Z `ݺu:68AkcEP٬IV-׷= 6lX?C4p`={Vtԩ, Pb\~}ޓk6N]**+cu Gڲ+@]/ @5  ˡCjg:G{g(Ih\(5VW~K/}`A-ȇ>8-ugOo/زU Bva$.Zв}ꩧ"RT9@yCE02t:`֮]rizL/k7RYmSߚ6䂣|Հ'0o#Wu kA]MHjеKWjēnKd`AįD+y} @3&m}٢$C5)h~MMM-a'AOGN ΥfKd$9%} o;^q9}ZUEWiz  2-Z{n݈`2mǵI]C=v22ڬرc7ۮ7@UeefI2k$iKŷDj(fON ,9=e4~ hBs`[aFNӞqq=۶ J p VBuI6 @;Î?q~~ƎFXi09 T%VlSq[K*RǗSm?4d(A3PӶu ^(#Էcq3m 53o8r-ԩSdqg#ˌ}D%F$HR0Ҕjqrzc i9dՕY+~yy6.l?T*lxF}II*5pp(b{W͈jۖH3:cn ZzݻKKgNd<Mx[F$HHYqVp [bҟ'L桜GkRC7L VޝvY߷u^hK ;.V@~-4Y k=s7o񼨛cg|ѫ\l$|ʷ`W] O=VU >D7eGJv"ډYxæy5i4 y/ʉMMzMl`m{1"Ʀ $eEA?= * 腅_~7XЩ4Vjl'zmvʌC^m'r=A)+()1?kSo{G$B,:E'^zdy5m,_CcSy'l@nGC=tt?Ftj. 8 ~j"+(3*i}*BKtN˗KuݤZW=Zۺv߬5;816gBgD66l.R`o𖩈SIfѷݷLyK粃 ֢0d?e9;m` YjN:t#top 52|yN;Zo_e˩kJIG`0/^.]=c0%9]x>R'hfP%!cǀEe2'.aiS/mU>/uPtYQpMtɑM0 .%Y}m_uئCckfqL@3Otԩ,^?IQr|D 0~/w}(//T}Ynp#Sxnm}vCv?No*D88OY4(rC=_N8L&#_^h?i%m881& 18ncf} 0 Xy^Ɯ_xW_}3= <3^RۺkhjjbKTL&_B/Ƣ߯v:F@7O㶿e*9}ՂC1"IuCr䏻.={p~ui> Irg`&]C`d\װ4 Y2 @| r3N;*ٮ _(ϟ'\OwXg?OK^ pp(bl )_uD2!Cpgp{ 2$]bY7gXp]wM{_XjR3T$H8g/ʵ1"meZB*M8yyy Q>| 3ۯ_ @]:VWI) v&WjZ|y=… W̙ wy`is>={!@..Y$^$^uԟ [i53%*㘆M{ݿH Ν;wԩCEE9@UUU)@eee)@:KKKܜ|uݬ1#4555khT="v#@od]Z[n>-uOCqbkdL &1Bu[ۤmZ"Kkc 6`IE)[Ў dU_U'bcZbq{Ilk$~;1"֬Јq =_G禂MTx`6ok]M8Pؖеq$X߁MoڿP^Cc[d6:18?ķy nnv[m\?8CQ@RU'󰭱q( $x=?$+p @P[:!γIR>"6mܛJJB%rҵ{ C1džz"n*B=ۻߡlFcI'Tho]A{!a31-:; QC/\LrIENDB`(0` %    ,4:>@BBBB@>:6."  .@NZ`f jjjjj hfbZPB.  &06>Nd׺ǔ++,nL0,DVZbwwwddeMMMCCC??@??????AABGGGPPQddeH( BùUUV[[[kll GGI6(uuvr]djpw ! "! " "!"!" nn---J *ȯ SsrHHpy!#!%"&#(%*&+',)+(RP-*-*.+,)x& 3332퀀V_gpy!#!%"&#(%*&+',))&RP-*.++((&llU]fpy!#!%"&#(%*&+',),)52-*-*ȹ. $0xpz!#!%"&#)&*'+'-*-+,)><:8-*.+.+<(  &6DNXSSSݺ07; > A!!C!!F! H J MO S U U U U U UTF<4*   ,FXlRRRwwwϹ!!!##"##"""" ! ;<;nnnvvvvvvvvvvvvvvvvvvvvvnf^ZP@0*Heee ! !!! =>>۷ŀ(()pP6 <;;; """ 111___KKLFFFHHHSSSiii񟟠 P0(OOP`WWW    GGGddd   NNNB" 0  ##"##"   EEEUUU ""!$$#$$#$$###"!! )))xxyx,6\\\ ''&**)*))&&% PPPNNN $$#''&))(++*,,+,,+++*))(%%%  2,,,F444&&%,,+//./..**)!!  tttEEE !! %%$(('++*-.-///000110000...++*&&%EEE>XXYT!!!**)00/433332/..%%$%%%  ###&&%))),,+///111333444443222///***##"&&&OOORooo^""!-,,433776766322*))aaalll::: !! $$#''&**)..-110443555555444221--,&&%rrr`wwwb$$#/..666::::::766//.!! ???  $$#))(..-221444555555333//.(('hwwwb%%$00/888=<<===;;;554++*uuukkk )))*** (('..-221554555333///))( jxxxb%%$110999?>>@@@???;::322))(FFF~~~kkkfffiiiooosssnnnddd[[[XXX[[\mnnYYY(('...222443332//.))( lzzz`%%$110::9@??BBBBAB?>>:99221)))  SSS""!**)///221111...))( jxxxZ$$#100999@??CBBDCCBAA>>>:99333--,(('##" mmmLLL%&%,,+//////,,,''&fjjjN##"//.877>>>BAACBCCBB@@@=<<988433//.++*''&$$#!!  RRRrrr!!!))(,,+--,**)%%$^DDD<---,++443;;;?>>A@@A@@@??===::9666221..-**)&&%""! <<>>###(('+*),++,+++**)((&%%!! ooo Ε viii...|||ppp   jjj{{{0  ^nnncccbaafff{{{/// ,,,###QQQR FFGF|$%$ !! ! ܕ.,Ɉ997997997997997997997342 !! ! DDDKKKtBBBV""".*ss%$%$%$%$%$%$%$$#$"y#"s#"m"!h" c! ]XRMDݍ F$&yw-*-+/,ihgf/,-*,)+'*&(%&#%"#!!xnmmvuqp( .+.+86,),)+'*&(%&#%"#!!xof^V]] ( ͣ,).+-*B?)&,)+'*&(%&#%"#!!xof_W"d-*-+-*-*C@ii-*,)+'*&(%&#%"#!!x)(y TxOOO"||"! ~wpjd]XRLJݺ񅅅0  Vopm ddd.0ۡ\ Xwwwrrrqqqqqqqqruuu~~~彙2 001J|֓ޣߥՑp.??( @   0@HJJJJF@2  *4JNNNzøöcccJ& 445PPPPthǤVVV667,,,*****+...889QQQH .}}DIOW_hqwwwii& qqqN^KJr"$"'$)&+(+(:8.+,). ^Xdr"$"'$)&+(,)1.-*%%%@ (<gt!#!&$(%*'+)-*75+),):::\,  "B ^FFF~dddaaa !"" !" ::8VWSVWTVWTVWTVWT!!"x\RD,J 222ݭ:::j6 ,ʽjjj   hhh%%%NNNzzzz&999H %%$#"! kllOOO##"&&%&&%$%$  8tyyx**)..-+** FFF ##"((',,+//.00///.++*##"h\\\#""00/544110""!*** !! %%$**)...221444444110)))```TTS&&%443:99766++*BBACCD  ##"))(00/444666443--, ! QQQRRQ(('766>===<<443!! bbaQQQ&&&>>>NNNWWWXXX---(('000444443..-"""NNMśSSR(('887@@@BAA=<<221##"$$#OOOQQQFFF222 BBBiii**)110222--,""!NNMƛVVV''&776@@@CCCAAA<;;433++*$$# >>>HHH""!,,,//.++* OOOƗ xggg##"332=<>>998322-,+''&!!  :;:(('++*''&YYYJ,++554:99;:::99655000**)##"WWWUUU"""%%$!!!wwwh $$#''&-,+..---,***%%$```>??   VVV*yyy++*;;;>>>332444QQQ<<<666<<<:::???DDDCCC..-444111$%$xxxlllD```:{{{++*=<>>221##" UUT##"/////.###xxxnnnBGGH,''&999AAAA@@;;;332**)##"^^^qqq))(**)}}|aaa6/..77698866500/''&)))iii[[[ghh""!##" niih $$#""!666  ___###̫Rǯٟﷷ ! hii}}}ͫ |WV/.a/.a/.`%%PE! B =7pp}VVVjtVT0.HF+()&%#" }.-yCB|Lzx-*a_ba+()&%#" }8653q ///33iJ D = 7 0 +77S׹֫bڏãӣ٣٣յ|sss??(  @  ###Dwwwzwwwz---H\yy,,I C!!M--^tsqqqL$"iy# (%+(.+ʗ8wwxD"!T"!_98DCA@>$uuuT{{{WWWrrroonjjj񇇇@@@: (('PPO*++WWWGGG&&%++*'(' zzz++*655DDCjjj000  ))(222333%%${{{ yyy0//???110>>>...++++++///==<''&332(('{{{ }}}--,@@@???322&%%555^^^CCB+++$$#||{ ""!100443---HHGIJJSSS rrrwvv~~~]]\,/-<;*("!j! [Jhڑ.,+((%# y0.p*<>0aaaninja-ide-2.3/build_files/installer/windows/setup.py000066400000000000000000000025341216641277400227140ustar00rootroot00000000000000#********************************************* # Auto-Generated With py2Nsis #********************************************* from setuptools import find_packages packages = find_packages(exclude=["tests"]) import warnings #ignore the sets DeprecationWarning warnings.simplefilter('ignore', DeprecationWarning) import py2exe warnings.resetwarnings() from distutils.core import setup target = { 'script' : "ninja-ide.py", 'version' : "2.1", 'company_name' : "", 'copyright' : "GPL", 'name' : "Ninja", 'dest_base' : "Ninja", 'icon_resources': [(1, "ninja.ico")] } setup( data_files = [], zipfile = None, options = { "py2exe": { "compressed": 0, "optimize": 0, "includes": ['sip', 'PyQt4.QtNetwork', 'win32com'], "excludes": ['_gtkagg', '_tkagg', 'bsddb', 'curses', 'email', 'pywin.debugger', 'pywin.debugger.dbgcon', 'pywin.dialogs', 'tcl', 'Tkconstants', 'Tkinter'], "packages": packages, "bundle_files": 1, "dist_dir": "dist", "xref": False, "skip_archive": False, "ascii": False, "custom_boot_script": '', } }, console = [], windows = [target], service = [], com_server = [], ctypes_com_server = [] ) ninja-ide-2.3/build_files/ninja-ide-git.desktop000066400000000000000000000003241216641277400215200ustar00rootroot00000000000000[Desktop Entry] Name=Ninja IDE Comment=A Python IDE Exec=ninja-ide Icon=ninja-ide Terminal=false Type=Application Categories=Development; StartupNotify=true MimeType=application/x-python;application/x-ninja-ide; ninja-ide-2.3/build_files/ninja-ide-git.install000066400000000000000000000001731216641277400215170ustar00rootroot00000000000000post_install() { update-desktop-database -q } post_upgrade() { post_install } post_remove() { post_install } ninja-ide-2.3/clean_pycs.bat000066400000000000000000000000161216641277400160330ustar00rootroot00000000000000DEL /S .\*.pycninja-ide-2.3/clean_pycs.sh000077500000000000000000000000361216641277400157040ustar00rootroot00000000000000find . -iname '*.pyc' -delete ninja-ide-2.3/icon.png000066400000000000000000001216361216641277400146750ustar00rootroot00000000000000PNG  IHDR\rftEXtSoftwareAdobe ImageReadyqe<"iTXtXML:com.adobe.xmp IDATx]E~3]X(A*fsg<;=1'#6M_CUuUwϲ 3;=]{ Bm۰fpOvv6̛7YYY0}t5khݺ5ٓVWWi&8cࢋ.qw/,,R/H$テW_}rsspWVVԩSI*q+WnJJJ߽M6tw俧QFp[ZZʷQaX~= 4wy1k9s@MM_4i7oΟ_wuH$G=! aڴi믿ŋ|mp2iT˃K.뮰>>փA=!SL#GQv6^zDtg@׮]:td C !Ge˖յ0?uqLV[npGSb9AG=l_ @YYٖ~:/L.z7scm6֭[s$GD#s] 9ʔlѴiS87Xv-<#g^&O.Lus Zz饗zQu6Ǝ r c fom@^CӐil4h_=Ea'n=l@M뭷A}Os]lٲH:R6<5&.0@1%k-Zz Lv< M2h{~6ӣO>1j}w/(hdLa3f;/GݸqS֮]a9!{FA܁6zwy'ef0i&hٲ%1hpws)˶H&xu%^:9=ٲ";+/k;ȑ#R? ?Ҿ. B?9!Ϩ|ÿ_|9g^3b5s{]u@@!@}dذa<*T Gbh~U]/pN1/ގ- ,eyy%5/ a9d [R1ق?Kyt׈fT<#<묋@fRYQjJ^S`Svj([_Wܩi&+rv|E3Ttc8OrMܸqc^at#.EyfMA"qoR4){2іiӦ}w0f̘&4j8!d?t"Jpr.]gD1XÇ駟<5Hy^˃۷o9!_d … aŊlX 2 sEe2Cof'ԩtؑo-Zɮc.3 lGi\`h֬vm1#띀 5}׈m;OawMZ˴2pe˖iIJ,"M^|pd¢d&m=İdYAs}]GGIٵm|4|EEE-@sLk CwQ̆l|s*L^{Gٮ K B~xɗ/>Q⊨?ћnv܉ |.Mf%i^~mPԀABZXX@ {477dgle B-RwOB6wvb`/:{֬X _`&GkG:@,AsP AӀ  дiS?}g~  6gVMVF^C[hAs7j܈o%JhÆŴ AA>ˎGFg e}Kr\Pn9NӧMѣG`Ls/ @&3V%%%-X΁|C1;$СAL=lۢE`:/Rϡyuaޢ+ͥ4MKcƍiFhÒPQ3V _*'!B%LD ?flܰ!!G+t3A=S5+Wz+W3bĈO8>\duMŸg&;nϛmUܾ-dBݐi͚qoR~QߤQUO@3umγמ{ҩSm{^{AaC XYBhW4D6 >1ۏѾ8v/Pl~N5Q s92-^ڬ)m<-Mxڟ ?.]gڟD[SCaC'%޽uvr`G_d[o|“աf&|x d e /Ä{0J}Y) gmK?P0m)yssrG?4 ~ÅXdW L|7^$ .8ř nb I{lRiL~7WIF;/_Wkh6&L3oĄ ƴ 0/dŸτ? .&Y`Ÿd`o  qkȘĉ;[p@{&V5 s[nֹ%Q di7:uD0>p;wKa$&_s }U_?_Vj/_S۹v1RB2YH̥paQY2Q3FIYZ0ꫯA ùs}{=쬳>ø1UVB駟/z^CJI> K<$5`Z:9j*0+f[L4#F&SfuV/tT[Yg'g* Kg2f7_ %Q3f~Jxs~Sٟ9]O֭npפ,?? ޟ3Gx;`SOAV]vq.],\pvJThwzoO_d~*.Sus.] r?{]drj u35|Th)Gu@Ii좆0`SNE~68L7g4hbVwO J ֐/>;w./x,'O_~T؀,=OԝfʏP`pofGm iAu0k5O x\5l_Ljؾ}~"eޤq]w\U!{t!Apj<Zn3]@7Unl+ +~Ox2^rg**׵]jؠ<@E^~矓7@ YgWj@,l<\\U9lx3OcN=Ot ^ rVT90k8lҴYfMJK !oI=l1c aP.ZxA>@1ʗ{oSPiqH$|<?0<8xcaw&MYi|{nJذzmDn-5&,{/_6L4?JZڰa&AdoBUL,(dŸ0{3;Зb$W\Yz,π`3O?GxSa\"^ khӶ]V5β`# @'~Xj`W^y%JGzzhO9Me&|쬭+YνZ6fAH rna!/_s|pt)+P>Ky6>hO?pݠxw`[ ,=f̘=q׳Z[+jŸه-ߴ;@֡~4ˇ7o VlwnJAQFРxvNN3c{; nuN4_ƍM @zb]۶-wef?b+?Y}s? V@֕q')ot{Eœz!yKİ%0'xňO|m*I;Gs\y@&!#7n6ig?JX`E3dgE~ftڭ.6c/k>6Æ бc]uHںs?7{e=**v /k-dk׮q߸q㶻p._@b%ҥ2`4!w?r Ld%\Y?n=ЧxapS`Է͎;64K.),'^[iN|40wܱ̩/ :6֟-T_/͵ĂMl֭KK`ܼAo+l.Д621zzؚ矏kPԠA֭[>^Sg9dV3{-2? ӷ_EP\X{_Ŝr5 h9DU]Κ5+ukRPX A0>{2 D 2?oFZːMd`wr3׿$4m-Z| ] (*,no+TrJ:u*4u&ݰI&آ |*QzT Ͻ+ه*hҪ%ucƎ3 ͷ[3c~3''7@{'7ܲ @Z]w^2?^hN_̛o;of/,_ۈcڈA |aJJJzn-́q쬳f8*T"l}!r'| &r38Ù~G}3 qV]U-kcP[?s@kЖ <*xw} 6sۀz|SO>ξôիW| ;iYY60& n?t)UJj 3BPQ}҆ ةvd;o=q~3gZQifpL9Cǎ!++ `ĉؤY&djYe:}4(3,t1"2d@Z,)( (`UP驧͟]w٥aqqqNeeee6 (l(Q8Qdp/9-#µIGVOf3 >`aD;K.RzGoK/ VMR["a%&`ڗؼY +UXcQ8BŅ@Y 'C0ǟI؊'@}~5[Kru|{0>:ʮVnQ#dj[fkذ!Ѳ`cFH&۹oTqO&<<@/'$5@ rMN&&{jC2a1h3 OܸZytW S*p7L`ݺup7{\W_ 4B{; 1} >Pm$bʪ%FӠ5j \= 5 .Aa G]pyGQ,f*i~*?ϱMD:2QXPH[V;fK6LXMP ,b0FW)Vaf{bkX^2tqLH2s뭷SCIII)/E1$$1hl|wٲ;Qܒ7.3g3b7P͗ pN-=)T.&M'M+V@nn.6c:[iҐfi_'`dp sgc ҶSv8Â~[[YUb`7 1?$@"28M7n,_t醍62rY]f̙3 S.hK0٢E Sl™gI0i';>ΒݸAsQ-G~LAm;kw?Xh޻Oï Mwhi| Dv@OEsm' q#knR6 1nܸߎ;-hoFMo?#]nロİsȣk 6u4{^f KkءC $IWܮm₂"F`2j&555t}ٹY O8V1F6`0AQZ-G9H3psQ<m{3/=`4`_|{V1v8/G~>(++[(@@3" 0>[u #!y!m9Y-c6 pӦk:ۮ֩SvM5kӪU [{bfXfX(,,'Qbx{,&9B 2ݘ#>s @fQ#GmKyw{ff̘1˙9 Dekd_>hc~'1whm٢E˞={v} nδzSk8WcdwrB\?YGGlk $Wm` 0/[5*O}=Ə?ӌm L2xڭX'opܹs3 |-Nmڴ߿w_aaaz*z~g"H8@@II:{(|H IA*U ݺ# ,өۘa%I0A2+ü]x׆6pC׀ :@m۶żZC[ h\}$7P~Yda?u>O#׋lݏ4(tS0&=5]e4wv1S&O+&!T:"6 9rssxh7++rsyȷ hެ9mvh ڵoBZ֬^믿qȐ!;Ng$8oviX#ca"{ף?f&}W^u%;wޯ^D;;eopfgB+-t.=DC^#xp_{^{mY=NJ+1cBPZZ  ް_Ϟy8FϿ]uշW;H@)(:|z 5k=ضa) mqE X\^|1Ygy:1֭] ֯˖-UyeV\iK*+yLG 1/s1[PO:$MbP.4#<̧1UV: g̘ٟٳ]T.B#{+&=m˖-NmavN;nv/(`96WV@6( /  :=\'H4$] < ?Q_R!$;͈{0p=sƌqGs ,X%@*?d&3gΤM4z&<1n:NzGôiӋ?=c!Zp2ʖ`7!˙ Lm#'9Zl :tt.{qo~)cHerL8Z6ϝHAZC%;zrT)3"$ E,'/?1F,@HZMkq.og[n0'8H=ةS{#8,6:u8Ѕ3noF[u]Ɋ+~V'xGwQ!//W75;,A-i`.I suq\ٮ&{,R=:wƪ0p'F&pÓO?ͨһ6R?qD$:@Tq%QqKoésQ =k! @!g}oxw<Q˕I!I&(ۗ1pVI?/7bZ|1}k` akyG-W>L/\ )ȧAV m lͤpAd8p opCIFu.ܼfAXtW^6V+DaK/À/;ʿ a&`{{ϋo Gu5՛o hk<ü@wqG7㥭~:3/; s- ~#$dқxs+^.A+>x [@aeg=8+80~Dłv\IyA1kXur-"HH!HԜݥ`"~E5{6ah`Ӊ79*UBHN8-˙Go5ڵ'Kl#?t( cZw_qDh!m*AtիBM2G͌+Q4 1L_궁k߾ wqxɒ; >vI/ t5BȰ-}ϰOhR-)@~M!7O<; GMM>3^4 a4i$Y\vBpMٵKm?|Kjy~oZV |'Y#RM!W5,!d .ZDB,yJ8xǸ/> a bJ &MrхѝM: ~[O􄓴 uUD=\ pB {Plsdc%$SB6 hoZI \0p ~N>s_naHq>C赟~y-/JAZnscR"H)vT>z@"D?_ X'p*.h(W"N\P5i&DZ<3UUg?f>;x~pvs47 e>,a'1l7p<[n1mƽZW4+.=s贲 _S'6xHxZ>%Έ_:=9@1ae&L/1[QMW>_?Pf>rN ~@Hi'W;Rs;y_ 8ohH߹硶,obVvk݀ {B,^XMIi X"lΗs 'ޠ0c6#|^x!n.ϵ`X9F 2AiJI|hH%aK !HI8 KvеkWxaдL9x Ca}5g@H ӂN`h|o^T+s;p߳ %ɔT  ktQQSAI,Se ϲ;w}G+h]ګs=hrX_|~].˴fĈa=-7oy'j&0M<߫;MoPxND-O Yz@Pϝx~ռ)&e,@4$".2 x` Z 83v, CؿW/)D7\0v{T+Mu Q=.-((4T ,8Xy[0NH _bfp]1`zVXulԈ8i0 !po~5)-m?^Z+>~ 9^<׶QS{7xp!> @[+/"S7e@q{z6|zIP/^,b9l@ZwPw `a) AaBYJ G*5w=bCǘ1cH,[tEHL@qGieDt"!'ħ(|A6_%A< ,BY,NLJi?ޏRޙv_` 8b xy6!D򹣠 ?СIlHEҧ kSՒHA,oݻu뜛S W:FU[`A8̟?I$noɨ'v*ʚGc FQ8q#$s*@с$ @tR[Sp 7K&\cgKt= vbıLltOI(0,"ENoiZ.h в 35?~={ٰT|,gyOE($/;#`:}Gj=x&]$ >|qBWi ѱH9i<5k]ipsO7t?P)iFA4ѼE&)DJv2:D"f\APJ69h`80бc{&bD,!BpZp!h?-^'HFP^CUnj̡څ/)A7>He$-,?%1o!@I6aДdI+g  6l"AO@mT4ZjطqtU?Aa W:7g"%1nS-w&>n63g$nT0lam[M\;'Smzj-1-Sܲ2TLKNnJAAXMGI:]rR؎&\7Иk 曰?Gu|XWĉy1GC6薬p]-|)\(FBVRӧi7cmU& K"|bnL6-=Эw?eSf[KkW?DS[5y굽%.ОMzlK#?5 Ih!(}f': IAE @gTÅG=^yenC9< ÿ[ Ծjù\z[ZD7}\~Zpc I@G)w̚7ӎ! 0 .-V @)=ԨٔRq (RAq)x 2YP-[>رc+#'DbTf xKZt"]snU.nL1ZZޜIP*؂a\rqSVq况tW$!%m@αH㎃ ئz)Gq%i6K~1̘(`Ԑ,K/6nʩ> )qI~M)Pz (F<}71B(0nQ AKtF\~mڬ򋷄Rt34ξв~ dov` JMWwynK)nT;UrZ<@-&j9s\Ϩ>;<^T{,<3Y>c7y!8av '';<ײ%wシYFC[Sh2UhtaHD#bx͖1 4 ő[><4_u5mZԲeˀK-0g,9@u2K\R/sXMos bzwwOؓz6p境wwN[cٸڋ78SuǕ|X\s 'bᢅM,)NmF*JMx$u6qh݇j@sxںi2.dh+ 6e_eT_өSPo2҉ ? [w٧7%pQu7ɗC_c ;Nh3iDhM Q1|Hy﷢P@@/l'b| 1x=ͤɓvÑy|A; aÆIt[', @9j.:{p XZohѢ&ׂ%4@1bVDB4)--fhU;V5~hL5"aLc8S% ڰn_(PJnC64Hng`)ru ",~&Ki0{kn._|nዑ#=5?;tnBdU|"]]D3͞ SL 5Ocr}& @=w\!J3fi uv :@j b< k҆b >$#oz%XT~o< /Av߂nu#$1bM~L ` K/[Z%I'¹ ݺvl[|\}'*"*O']\6mB0ƬYzΩFaaa\$g^HZ_zi׶ }E=ON^(l ZRA P q:b 0)\(ЮT% ?Ѵp?R٧>7/ynVת݆yCzn>` Z{|U9C={˱]HpG%YsOP! ̈V.MrMy; + #*hn^<]&hKNQҐST׀*pk)p!jj+~d4 ^^-BB:E0k@ ?W?ldѶNz|&a/u=QhXB;D5b2!M_ε1T%QG5aGdt UM=Bo? l6VMzժ 4yY5mb.}L"F _|؃=mOMmSI6jKq^v:et'ZOr$DuMM5 _s_2G 7}XJ*>~@ĤT5,6~%_Jo'*ީ)\IO怖v?/ H C-*"-8䓽 qEQjBDUQT<6h>D@s9KLA^'svvV"H2HsCy5t3Δ}$OAӱS2"#Hq/;ߕ)mmI)Sw}XC&y!T;*IU0Іi wMf#nmuMN]{7M;9n z)/,@o3mv z K&lUZD7O?(tQW`|Kl؂R<:@#&RBIM ǣ\pԁi\qsG$՗—!;N`GS?Ga:V!B2֓L,](#$ȷ\%u\2K tUVӐP5eC=0mt|@ (zFDm4AH QA0hA | 1LtчD4Hd)ݐ Mz Ѫ+aYnE88zW`k虀&L_Vр?|[|l*͟@cDZ&lE54"g^`baCwzs=$rc,s#7z0M+ޡA(K)%L5 7//_w`yFY_Djk~~-®;X0McQCHAHJɥRwsdu]k{LLJfP=kuoFxtmbz)Bc!ʝ\N:`{2m^㋀wk+02euEwhΏc.E&3h?}^r \ve{XvȐ!~WiK ^¦s|Z Nb*%2" W45a Oc!ު:1?w?}$XХVx^[k4Ÿ`NQˆk-83u йN<xb σ+ ę%4a;\L> /sBM0T6H2±,C]fSvGpI^#L:}u!ZqI@"XAtd ,b w}~c8EMMJg+U  '5/hh,"HQ /$({R"l,yRG_& '|h'%| :PmT1ί0 ,R{嵸s|oa[h\6iW5f[aa\a3yX\a,MH(2qҏÁ:8쳥~)\|i/h~op ~`A$UoS'οL@\@ qQdZ!]& TBOBJ{)yZ+rnݗi} .#:Ji >cx,Btz!&b% m[x2 aAn| Y l-l)=TKYx~PB|*>[@rB]A6FQclX(~:oն]П38phVu1(,q  n8څ|>c k9@,NĪU}9p^gjєȬ ۊa]!pTz!H=c+8CoX\~Kdd0 `f]GNE2Qd@2 Z ңj~uX\Oew5? }uCUl L >|p6~`g͚ S&O~絯\ Ҫt srШQ ֏s4n ;ic#&xhf1M7[/c t_B6*Lw0GE˖д[]sab)'/ o,c@B _ؒՊTgD8j:_A~aX `(>(V,TлzQӜ[nWV?'N0j32lb:miӦ0lՊ hfz/ jbv\c[eo1}:1~~ Q'zy|_ ^$P_Զ[Kuo(@Э NAq F*w/{T|ACaР'C~7_(cܡHȖE[#6Ѓ5j&PQK#khѲc-9(6nG!4lXp(  2=`>KSϘ1t{p}('B} Hh5 0"GA:00Xm h:xiAOHOQS>,x'H_-o&&J6iyv똰V5 f`4Grs͡Ml5 A-Obt8T--DeRM{;&mR?!ҧϫOA:>L?,CI0;x3p+wYnvxᇵyQLs X(trn4 DʎPUC[6g9G돮-JqXVL5 -a*6 p$}ѧⰥZ~<RnhM~;n8;zձ7N0*虚4['RlXiS5˲&&OL[ªByD6ߪw2vfx_ UP9E f?<Ï :Pc}Ϛ5 ׏V¤uBlʚke1AL% ]VBn(LN)lU0.@-V\Ŵ]C(Qh{ j~)U}ety~~}ACy/^ݼ304q `>ا:~@,MO6UXj.mWhb(59Dm0`S+T[z)ԉF/>@ &7tl'q8 yYZ7 W'1A¤v-`HB B? O4wHؿi%Rl<omV'8Ku] R@{b80k:w7t?>Rpb K|dhGQ{Q՞DiB4 !~`[B;m,$kH%!'1#&v$6Uj=*.K`sCB^ PoM^Wcf; W^yw#ǟxNtacPa.a`*+<ѧ?*QMe T Ok[oWl\QSë n{;(<)dJ|EE7raR|cSˆ%%w;2ڮ:9&Y}-[gNHWϩc $ʤQXcj>曠o 8946ZcQ EE!qNLv%#1"FRS9%I)V+ـi/UjIw(hKta4dfM4_w0Fz]-I<B>oOF75L@h(1}[:P;1QZ&PLϚ07n`Bp3\!m%H!-fK{|3cwԓOf,a,(bbc fa 58_/ht%RwxE0?{CR.!G]cmNZF'Svmh?Qvd@BR,PG~C>7NN1aWHϲ0m@&h1+B0 Yu>hŬe`TR:n5fb4  `ժUrYSKG5BMІ$55"삭/f[vXS;i=W&NiDE>%WzۆaW2SR'/=:XgZ!<$%t*M2}j bROC|esjb6mڬDxa24`+mի4(*򼵟|1/rsaдiS*\l),\M &M3fuq{} tߵ4L@OGja@>GGh}XޗuAak#6m3ǴU">Jqz(/߸%NP ۤ6nܤlR,!j=^u}@nQW׵Wy?{,4y2|饗zqk&Q/FԩӴ`CKH#`ΣTК 2i15x![5IHL |DFM_F+a ($0h޼uoUԹp6CXRESs+k9W&B5⁀!,&eت/P"58q U}F'~+{c qߨ}СCaܸq! s&HK$n:|9 V["B+W˫E]3xeM/k 3 ŎTǂ~[Nl'o 92 >>H5ċ#'w 'j%P@92[ڷ;ĺuIDMةsgkoSw{uQw$ۍSkk7"GڻO f΄P đ'[  >(Q1\_bэk֬s p]!-)ĵMl28]_MәDl,tJ?!JPsN)"eQFlEwqбS:aN:vn~q_B|1*,P( I NFQ&G8O4ɞϴrcm> RMۗTk&X?X#h(ta[+++Kp͆(P'#NjDMڒv FbABWZtj|"S7>}:/w/sy̸]O?/|0/]0 NU;S]zfZX ui5;*6ʐ|A*~&0Dyf:ΟVRNF\G!ŋh޼9_t9[8P#PV p&՝זkh۶qb-g~ ^xE^KyXYC6;ž{ pzk ,';497pz-XL/=wlRۉID4Ew1Qk7=qѭ[iv s K]wXOUW[]\N8Z1/sGek׭[(m[!rx (b߱\%h\H;Bj҅PPeTM4veW텘;gv0 zd֒Kݸ0 ;+ :2ߧO۷/Y.m] {q;߆_f.]'we+()`WSy C[ XW7=ml# ayש)%~~/QBx>6oPMwdC/H >&q,ZxeDrF zTڰa2MSeDtI9"@!!;MW"kbkK±;w6ET(\(@ZA*ii87L-] w_';8 !&;7|i`#n7gC̄=o%C]OL:zwaʔp5$oٲpۖ`X 51@ SQJ*;duqvrXuu1dGU;_ʋ9ןX#{ӦO5?1NG ;`I~cWN\3ZT?[v΍& 6i7ިSgfN{}ЭkW`4y=뮻zfΘ0V. Alx"|T ]2 ]VZ R" ؀*PU:EJC! 'k!4m p>/ž-MqǸ4B9Qٳ퐣. s=DZg i [']ѐ;5yذa𢋠cꫮV?yN zitIsP_ZTu!! "- p0֖`l p hr*K7^04#hX1n#FO:3σl0?a?; f-_e*Ŷi: غ*o[Ɗji@Xm$pأla ;Az@^QJTF\W+?rIF쵎D J̝;w @J[CvfBo]7wQ~0b%6tXreZl)cΜpsxUW?z<~W`I0yHqPL, :eFFj f:65'ȎTL>PӲ! WYf7cz޻#GN4-h,l|gИ,r.Z_]Kg}|mڝ@gS:m@,5T_vmdK,0m׬H駝Ǎ w zIޑom] A@d)M@~MM/k+`!s5- ̀8lM5`F Loܸ/{pB:uF=^ l@d4iҤ죃L³k57lx7[p}YYƂ&؄հ-(`[oW'O? ߁ f͜ w}_V=rnc[0" Utd']༴#TC0{ dsdV9{&#Ԙ1cf͛? s@#@c}~r?N*JThib@CHP@`%6'ȺV0-M8 Zਣz{[fڼ馛 !b# |qޯ 4x5vqMp"$VipY(}5$i B^GۿÌ}5 AwQS!~lf&;YmPt*Km,*w=`@N%WbuwY0ckYNk,Khw4+聀j΋cI' ݻu^}/T2tq4xǠ}vRD < F]P$lBV@Rn~P(mP iV2{iN]KJX TĬٳ͞5K`߁7p3=-4^WƢ_ #eǦ͛B #i!#yS؀t\35L 2u}9<蕗_w`_,5BB7Ԝ 籷}Z.i`j*@(c ʇJD&hSOO9^ F7y睱 VK3]:$3*B$UWהgt eD_(A&TC bHFrZ*6@aM8{p'2ldd"ldoK?S o{K/Ĕ\x!e!<ڀנ1<3 (ds"=LIu ;C~ ˱퀙S1u׿7oxgFېvۍ EÆ ^;#'X"i|蘫[0٣T ~MCl'n_{6cVsrr>s_ ה3g͂?k/x wEH4#'7;qD:uJ3&ajA )Y`R`TCjRμBu *&/6 JD҆OE{Q`L3fsprB$׺uk`瞛(6L˩+c/6Ҵ1 \@m kg O!bٳ|^}P&p3:F)`2Rn%;eZaԅԔgas黴/۲gONTg@+K.^knx~=Jy 1"v_<7x݇a>T&EM{}J#oޣ qVa~Ws M\J. ll͝Y W'6P$g :c H8)0xёݺo5VOZo&>i@`oPQcR5*-TM0 ׿?1O |b TP   J8ryR׿ߞeWNOܦާ7X f̈́.S=P^ m! 2qA^қg#C)N8~+B[n/&M7VN%>r?hTM9s@MmXAG Խ{E܀-í)a*'jL+Ṍ8wazi}X5c(Q}|QBt@؋`hWjv]z ܢR9g-7aF#~] B b#:6T_%AK&QM0E<Դ53f?;z0''SLy 2&2 .n:7X h1c~O,z!Xv4/{LCEɍo'm b K2F|v~mwjSPÂg5Tw˃{̾LP%\Lx+_Tb6}Z";Zi lRhԨtIToFc\ƌgvVȺnX(K-ŬklR݌)Ƭve F&q$4vz{pb;|G>cƝ&24n-yjѣ7[]񮙍O4xzkDIa<2ujLd|B]0\j p{n>/( Ȥ["lݷxh\t !"FST i.wXWO3}Z5ïi {qE{~fvԂs FҎ;o4*j HL˻vLi@'-3 (NdFaCAMOL31/9E,+NsB0+iwۻ\qmyg" ,v*MԐ_Z@i.˂[]8w(kFeSM'}tm(P%} f0-ӄʣhy5T8 /QG"Vz`РAh"]2.v!P$}e,*[RT2ٝߪP{Ψ`7})צg? `Dbz7o.iӦ)dAjuºNX֯_O,]J͚5cҞP?#ý4x`nU@3QPEт;  +%Pٜ;mлHPeU5l[bF7CY %TW uBgU2FZZ!8> u5sHnn5rٞ"L>}xe¬b%((.?}m l :7 u$(<:2(ZV  Dc޿_LyRH?K w ]A tm6*/@#-mlS.JXm%ZfD`Btk3ÍT4@ D tgGz$8;f+|XXW& 'mxƢDyek1ؿo߱>}gw߭5?~<1y0@h /mܸyoֽ?A?fLÇ5.4@tX'/QX Ph hUZ3(UAh-߁  9[ԍ7hL<ՐW^~][l}Aaa!oS3! 6o׭SPkr36wٲU+]8t.gI8D~FT'cl̃c`pv%:(;U7- 5b]O>t蝲;;(FWoFQFcbo#$mOAOq/G4&YZp!!jYEtќ <= O7TݻwÎ;`>СCpAc-CIq4J M pJ53JvlhذΎ[5 Ǯ]2dΤɓWZ2crCgԄWp$T% f @U3tذ'N=>#49m N?` ˲ :q͙70'70`5dW­T[p(:!Ot@B piZ0? ?(ah3iws# =y ux7OƘ1c;v~ RLE@ j`|WG6h{7 oW p{_oۺ57݅Ds=o,0~5i.E2qABs:oE O!afmQс-J˥XęrS`ceQo԰7 /LN/06mx?\g:zXŀtn@b,5!,? 5PfI5j7> @ ,vҥ;ttev r(XKw4 lv48h@, a  -RU.ܨS.~v57>lЃNOF@dS+~wI`eR*@bU->τ1~Å۷o?aV޻f͚}۶m+6o;a~tĪ]"7СClY G-͙I{$5ux<_wۥ{mtjRC~O>6O5*_` p80;qǏiFGv0/syׅ`aPWEEEe'N(-9 عk={۵kW)ekJD;N 1܀r`O?#l%믿ިU]2ծi"K) _4Zz.첦:;9VCT ;} f;KAϴ*f`a@a 4@xRg@fI!D'ee>()-^ׯZ:ЫȚCl"\1g  Kq/~)@/b,YaNO3G.B3Wl׾}.^ڤSN.l׮^&MjըQ#Epc@N|<W#2>6[M-bhDUM6@iYx?RհY>̉}"uEEmxT˂ Ё46ݺuN$2ڐ!C4bZ (-!BL=wQr,_<_5~Ԯ]+I&55jT3;;FugeeU[N:ԑНwGE_1 zu*zx%0XgY(J*~ZaHl0XeW͜0SoC |:?:]ͨw ?Kd`DE0@1 g[ހ-{Sof7owQ䙶xxM 9VtΗwLGY0v JMMH%E%$e޲JJK9ϟwKsc%D@$!D 0 [рJR`=AQQ1̞3fΜ));:N=IzUa[wЯ֭5\ 5Ӈ<ÎIy"XFc; b# cVQ!~_A"x]\aӐsUch'^(&0d 9]*b DU5,(--bط8%5׬U+#@vu` bN\~ y4B ڹ>DM\0 ޘ-' NU-JV~M$5…ň|D'b/L BAJ[ (cd>J@ϯh\F 8L@a t~cІh6nw^2U$R}O 60(<` 81'"P9`{T(.mZ6: (wx7'tfҨ$dBODXP:" 3,UAЂU~'ZFFte_3k=ۚ?o_ 4d+8x LP@/W_}Ue G6 T H~h@eKz0'% %YuXBbBDI)ԧcgg(uEEB1HKMSB@j TF x1&q'jgU\c6 @c`2kתUYߴij0hh^^͛'%3pOk-k6%ND`CT#D@؝b 6 }kT0_}Xw;1(h2Б8usZ_z%r=(a C5FK$%A+P0i ?33zםwvKzSID'JSyB^;"~X)߭ժYOIMM6_tm:߉͉5 DH_ӦM#TqM7_] Rb<Ry;\gyYYY䤤$-@4r+D(b)Fvv YNJ.>TFyN&yk*k uaTU J}Fn!ٓ Io-TQmj5(sdSVy'@+* 8|{tFVE:d:ES }i_!JDX-8 ~p1{.{a Juߧ4uˊ `TYȭ^|2@UeG9zTp)L|8p ̜98`g PB;= *Ȅ yWOYzXsQ]_qyP.&5X ZSs\ἧ1߷r@ga)$\δp82{۽;RG7߄ 먪zj_hUk)p`z6 OU~úE}_|񂂝:+hdԮT41N/K *v^/.^س8 1M* m[УG8 Лl/^f08[n?9;/˅}[6CoAT=Qcjb ÉÇ}] sD[bsE4ዌtNwmc!;|Ds;C6 @]ͩ8lT[]v?ٔTaN_ܱc%K07&#l]i=C톍f <^'[4|> D:5#P/-p W';v90?d- ,`yvY8lSֱ֮]VQG}}dT<fP >,4 :^/ 99% bẕS,k<s܀QV%U{7`Uy}w/LH}*Sߕ(=;t;9d)[KJ k c۲e΋.)[:|^/gu< ÇǸ#[ZQ?ԱcG~D`e Z˒+}rrz a! :'ge~CmC)_@=JXxMA[#!ck"w@ܷ/b ^h(2Pdt)D ouVү_J "iҤI)Ky ސJ0[ovBX=]+qqO%!QS @m2~FN"@ѣ(/62;{Ax`߿`=>?C`͚50vXo9-*1.b \vم /_IO`]'H{yP6C BUV:o =l=k,m6n;-?''oNK8T3 *_ށwN]6DϟO'~ůœtl}V>{߾, DkQ(Xj {p~dӦMmy}h.|ǰa,SD `Y6u(Q5w.-pk8IGQ!o= d=ٓ%j}~];wDcu^=cU+V_S΍E&?#عs'5jL:UίaIA#Ɉ-[Tr@ԬbPt8PuS5 k.c႞={*>kvQN9֍@c_R~@9G0 @R>8jJmntٶ}; 8vR{O*NF]vvяe˖ЦM*} O[BV^x1jϗ QRpup') 탉/ R_(X.JzuG?9o6 AɈG:xU3: )5\3۹sg !Ggk$@09}tl^H]x@ iӦn7mtykB㐚[Pe {…b!\ٳ7`碳3]05iy3Go1cb 篴G`ф(4ۍ.n Sw+OͻnڸSUˠYw2>ƻhgU?|hܸq\L9k˖.]-ͫWA%t -O_窾]ܻ;DsIGbUB*'R~fСC\ޞղw/-()oju#GC~`W_~ptNU, =|.!FR*Z2}+Ν;w5DW$,6Q>͛ԢcGV|~p܆wӡf,1Xj=F9}ME&*zjA*OH{Uxՠ>,] /^|5gDj /`(oCgil*%()Ęa G #R4BW:}ެ^yI9Ɛ!Cӿ>_LJJz+fϏ~g4qyܤZfuRF&ӫW#i$5-dI9ET(F_Ͻ(d{5en3 ϮvĖpH1 uTj#;F֭[TԬ26L ɱ+,zMV oC,:t"sA}>ُ?nꟓ3r-~O>$8y(.,4BS0}w)XȊೢ){ϟi1GzE/4trRS {0VH7 yCIiiq߇w'2xC "G1ɫV /|'˯C_ X,mA}"LL!īѭ΄`F7w`B~)[di/[iӦgQJLr5p_~k^F?~\ԯsxcI[FX2k֬xn rW.Aiү^RN/ ÔֺdϬ^1'1b{‹ەm!UKڤ4it֭I&<; z ⧟.0"JYQkʥ]t~ W_mݦPxQv 7 `˲cȑhѢIb8|`dqI|>?5(Nht,5ٹq=.HOKz sᑖ#Xe֬i.IIuǩ3gffVH Y?9ӉR3w6ƓO>I}NСa'@'L9v]7ժedp8[:ue9;?_߿ DJ|A龏'g<"aK/4z\3!CWk1}oyCԽ0BP};?+䟙3go=kş={X͝Jp @%=-zNw[W^ .U:wjAh^^Ν;e˖X|ݻw!!*o 9={L.(iqq{Z&4M6a5q) T#/M_7d yJ !y[ju^6m6ӏo4~kNͨV-9===IQ~'S,++󗔔Ǐ,E(طq]:TgϞEB~|~@~xg=Wiq*'O[^j#v*a ,D|ͭFffJjR32R;|%&;'#=r]qpDj^oM/1JYiȧℯ4++ x^>׊KK|'Nxrl.L  Kb@@""x {FNð' Vy߳9@#(_< N2Ev\9z#wD~%GqNbUp}E였"yTG\UlO$k0(v~^&gAI6^k.Ue޽#ÇcbY]^>]'b< {pg,ʫI@,g]|Do 9Q .ݻz]VUwe 蒀@< U6(+BHe/ R=zy8\0Atr/#61Y@\ KfkP2 g=PUTHTQE' ?>}SF1;v6D@^.R`NTs2^;@eq\n[a25@f: O&N .6l'4*{N< N NDr |y?^ (`*?xPoӦ & W4TXdE,  fWEicH%+iG'''IN\g$Xt)Fk)'`:-J_ٝfq*mNJ;+I7:X~xaɒ%l)aPN)C x ;@@# cƌ! sy` zW0Ly:5Bfװ  ȷon _~ePEwށ3f ' "ʭ]>_6dQ avِ^MJ@IV+:YXf L8QyLKEY]*;$2T0;c kI^w;?L:5!g+@Ν;⚟}̙3GA0:y'sǪCcayZ)6jd" 4k >o`f]Ea=͛7uWX`ƒBnnnED"* pȎ5Q炥Ƒ?Ƒ#Gc(5QFUlׯ_o&@('R^ @ x|hժyqOH{,n^{ E]v900;,T6(u 0X; o&4l0PL= _|񅂪D6']^?%*vwb1wȑoL,T8pX_\ʞ={eN2  N cԬYhq}P+Νꁂqę N#'- yH޽yS3m2_~W(!&"u+@8ܵEuy裏Bz 2F  װ<N5 9ǟMKKۓkws>7Ulq㰰$F%*kpsJS ݽN:Ю];W^yeb%d3g4`޽N<\l7mM7_|qbA%PrJؾ};lٲE~!qa ǜ| †PnI9Ѐo># `Dv &4a}z_pX%Pk 3MOt]t1x m!sz`bYY!)X-qDc@a@&ށy͚5k-."㏇P4h`U ϩF\`A0%8pIENDB`ninja-ide-2.3/ninja-ide.nja000066400000000000000000000011661216641277400155620ustar00rootroot00000000000000{ "mainFile": "ninja-ide.py", "use-tabs": false, "venv": "", "relatedProjects": [], "name": "NINJA-IDE", "license": "GNU General Public License v3", "url": "http://ninja-ide.org", "pythonPath": "python", "preExecScript": "", "additional_builtins": [], "programParams": "", "indentation": 4, "PYTHONPATH": "", "supported-extensions": [ ".py", ".html", ".jpg", ".png", ".ui", ".css", ".json", ".ini", ".js", ".qss", ".qml" ], "project-type": "python", "postExecScript": "", "description": "Ninja-IDE Is Not Just Another IDE" }ninja-ide-2.3/ninja-ide.py000077500000000000000000000022231216641277400154400ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . ############################################################################### # IMPORTS ############################################################################### from __future__ import absolute_import import ninja_ide ############################################################################### # MAIN ############################################################################### if __name__ == "__main__": ninja_ide.setup_and_run() ninja-ide-2.3/ninja_ide/000077500000000000000000000000001216641277400151465ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/__init__.py000066400000000000000000000046541216641277400172700ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import ############################################################################### # METADATA ############################################################################### __prj__ = "NINJA-IDE" __author__ = "The NINJA-IDE Team" __mail__ = "ninja-ide at googlegroups dot com" __url__ = "http://www.ninja-ide.org" __source__ = "https://github.com/ninja-ide/ninja-ide" __version__ = "2.3" __license__ = "GPL3" ############################################################################### # DOC ############################################################################### """NINJA-IDE is a cross-platform integrated development environment (IDE). NINJA-IDE runs on Linux/X11, Mac OS X and Windows desktop operating systems, and allows developers to create applications for several purposes using all the tools and utilities of NINJA-IDE, making the task of writing software easier and more enjoyable. """ ############################################################################### # SET PYQT API 2 ############################################################################### import sip API_NAMES = ["QDate", "QDateTime", "QString", "QTime", "QUrl", "QTextStream", "QVariant"] API_VERSION = 2 for name in API_NAMES: sip.setapi(name, API_VERSION) ############################################################################### # START ############################################################################### def setup_and_run(): # import only on run # Dont import always this, setup.py will fail from ninja_ide import core from multiprocessing import freeze_support # Used to support multiprocessing on windows packages freeze_support() # Run NINJA-IDE core.run_ninja() ninja-ide-2.3/ninja_ide/addins/000077500000000000000000000000001216641277400164105ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/addins/lang/000077500000000000000000000000001216641277400173315ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/addins/lang/Chinese_simplified.qm000066400000000000000000001330321216641277400234550ustar00rootroot00000000000000е"Tz"F##C}&d$4">CL]j[,] 3b%v+dY,ivjn{ ~ U)jC2bNFSwR "ZM )iA!l2aw،ahDeo<;"i$<' ( w;X< uw=&_IPPS9U!}YBZ%H_t_grp܂yhm X@  E 06ACI(I1tI`Oρ fz9Jw{bhv%52f"Mk%s8&m2&md. <4|NKP9RSld Nk7 ?{z HCk HC HC HC. O6J P( ats bOz b$ bH dʪ^ ee ef e g:v kb4 zsJ $J (p VR O  dh  L* ȗX "v Ӊ$ d $ R\ p c O @ U_ ! FM Xi hJEr kPf l m ^ O 8iIh ȵ oG "l 2  F5 >t mYd  t Đ: !Uia "q Ek )N ِ > YBB E' ! )$ F />> RS R` .` -@M 8: @e/ Gf@Z ry t tEc za4m : q  e +  :S Z 1< j ^ BX {: *i = > (33 mj u Eq z[ =}U Hpc H& MNO M#, Wy W$ ]p)H2 bI~ c p5n C0 K 9@ R Ĝ < <28 <W @*S @1 ǽ2 + ւ7 +@ uV 뷀uL=~4q}4/y7N IJ|\]J\a#GkKn Fl+r,o3{  *b",!S}NUhS%HzKucؑ>RtFtTb%GS?-4/?$7=lmH5Mm_ zKiTw,i|3Ef19΢kxz^ L\`a`M'8uxfZj( , ѝn\n&_2_dS)%1</a> Source Code: %1 AboutNinja rHg,%1 Version: %1 AboutNinja,zp<a href='%1'>%1</a>Website: %1 AboutNinjamReNR0yvAdd File to Project AddToProject mRkd Add here! AddToProjectSmCancel AddToProjectYcNExternal PluginsAvailableWidget"NINJA T/NOe9RueH07NINJA needs to be restarted for changes to take effect.AvailableWidget:\cNv URL...URL from Plugin missing...AvailableWidgetnzz~z Clean Console ConsoleWidgetY R6Copy ConsoleWidget Y R6~zQ[Copy Console Content ConsoleWidgetY R6SS Copy History ConsoleWidgetRjRCut ConsoleWidget|^Paste ConsoleWidgetf>y:/ϖShow/Hide (F4) ConsoleWidgetT aAcceptDependenciesHelpDialogFOy: h<_hg !Find and Show Check Style errors.EditorConfigurationgb~Tf>y:Find and Show Errors.EditorConfigurationNT PEP8 vetL-Highlight the whole line for Errors and PEP8.EditorConfiguration )ە^Indentation Length:EditorConfigurationL Margin Line:EditorConfigurationRR dL\>zzv}^vmRgTNL7Remove Trailing Spaces and add Last Line automatically.EditorConfiguration f>y:LShow Margin LineEditorConfigurationf>y:R6h{&Tzzv}Show Tabs and Spaces.EditorConfiguration"f>y:QsN PEP8 vcy:O`o0Show Tool tip information about the PEP8 errors.EditorConfigurationf>y:QsNvcy:O`o+Show Tool tip information about the errors.EditorConfiguration Ou(R6h{& Use Tabs.EditorConfiguration Vh[WOS Editor Font: EditorGeneralHT/u(/yu( MiniMapT/  MAC OS N e/cf^ LEnable/Disable MiniMap (Requires restart): (opacity not supported in MAC OS) EditorGeneral N Tlv[WOS Invalid Font EditorGeneral gY'f^ Max Opacity: EditorGeneral g\f^ Min Opacity: EditorGeneralMiniMapMiniMap: EditorGeneral MrehH Scheme Color: EditorGeneralv[NVhvS:WY'\!Size Area relative to the Editor: EditorGeneralN*[WOSN u(NVh0(This font can not be used in the Editor. EditorGeneralcrH Typography: EditorGeneral <b>evMrehHT y</b>New Theme Name:EditorSchemeDesignerWbSforBrace Background:EditorSchemeDesignerWbSRMforBrace Foreground:EditorSchemeDesignerWbSBraces:EditorSchemeDesigner b阜rN:Choose Color for: EditorSchemeDesignerlComment:EditorSchemeDesigner_SRML Current Line:EditorSchemeDesigner[NI Definition:EditorSchemeDesignerVhforEditor Background:EditorSchemeDesignerVh bvSUEditor Selected Word:EditorSchemeDesignerVh bforEditor Selection Background:EditorSchemeDesignerVh b阜rEditor Selection Color:EditorSchemeDesigner Vheg, Editor Text:EditorSchemeDesigner N R~Error Underline:EditorSchemeDesignerYExtras:EditorSchemeDesigner bSS:W Fold Area:EditorSchemeDesigner bS{Y4 Fold Arrow:EditorSchemeDesignerQs.[WKeyword:EditorSchemeDesigner c[*Link Navigate:EditorSchemeDesignerep[WNumbers:EditorSchemeDesignerdO\{& Operator:EditorSchemeDesignerPEP8 N R~PEP8 Underline:EditorSchemeDesigner b阜r Pick ColorEditorSchemeDesigner _Sv[aProper Object:EditorSchemeDesignerO[XMrehH Save Scheme!EditorSchemeDesigner glg O[XScheme Not SavedEditorSchemeDesigner g]O[X Scheme SavedEditorSchemeDesignerMrehH]~[XW(Scheme already existsEditorSchemeDesignerOhforSidebar Background:EditorSchemeDesignerOhRMforSidebar Foreground:EditorSchemeDesignerzzv}Spaces:EditorSchemeDesigner [W{&N22String2:EditorSchemeDesigner[W{&N2String:EditorSchemeDesignerT y_SN Tl0The name probably is invalid.EditorSchemeDesignereQh Completion EditorTabMn Configuration EditorTabVhMrehHEditor Scheme Designer EditorTabu(General EditorTab_Path:FileSystemOpenerY'\QeOa&A C&ase sensitiveFindInFilesDialogSmCancelFindInFilesDialogv_U Directory: FindInFilesDialognVhFilter: FindInFilesDialoggb~Find!FindInFilesDialogN;MainFindInFilesDialogbS_OpenFindInFilesDialogbS_v_UOpen DirectoryFindInFilesDialog yOptionsFindInFilesDialogkcRh_&E R&egular ExpressionFindInFilesDialog_Rv&U  Rec&ursiveFindInFilesDialogfcb Replace: FindInFilesDialogOu(wd}"|xnS9M Search by Phrase (Exact Match).FindInFilesDialog,gb~b@g SUeNvNOUW0e N W(Nw BSearch for all the words (anywhere in the document, not together).FindInFilesDialogeg,Text: FindInFilesDialogeNFileFindInFilesResultLLineFindInFilesResult,O`xn[fcbNeNvQ[Tfe9N S`bY ZAre you sure you want to replace the content in this files? (The change is not reversible)FindInFilesWidgetndClear!FindInFilesWidgetgb~Find!FindInFilesWidgetlg ~g No ResultsFindInFilesWidgetfcbReplaceFindInFilesWidget fcbeNQ[Replace Files ContentsFindInFilesWidget fcb~gN:Replace results with:FindInFilesWidgetP\kbStopFindInFilesWidget bSQhyvFold all projectsFoldingContextMenubSyvFold the projectFoldingContextMenu \U_QhyvUnfold all projectsFoldingContextMenu\U_yvUnfold the projectFoldingContextMenumRAddFromImportDialogom;cNActivate PluginsGeneralConfigurationf/T&xn[͋Mn0Are you sure you want to reset your preferences?GeneralConfiguration Qexn Confirm Exit.GeneralConfigurationNN k!OR}eNLoad files from last sessionGeneralConfigurationg feSu(ewb$Nofity me for new available updates.GeneralConfigurationQse On Close:GeneralConfigurationT/Re On Start:GeneralConfiguration ͋ NINJA-IDE MnReset NINJA-IDE Preferences:GeneralConfiguration͋MnReset preferencesGeneralConfiguration ͋MnReset preferences?GeneralConfiguration b Python _Select Python PathGeneralConfiguration b]O\S:Select WorkspaceGeneralConfiguration f>y:T/RubShow Start PageGeneralConfiguration e/cvbi\UT Supported Extensions:GeneralConfiguration]O\S: WorkspaceGeneralConfiguration]O\S:TyvWorkspace and Project:GeneralConfiguration^-3fTJ Python 2.x R0 3.x N Y{SUOY v Python 3.x N Q|[`'J-3: warn about Python 3.x incompatibilities that 2to3 cannot trivially fixGeneralExecution.-BN W([QeeNu *.py[co] eN'-B: don't write .py[co] files on importGeneralExecutionB-E_ue PYTHON* sXSؑkY PYTHONPATH =-E: ignore PYTHON* environment variables (such as PYTHONPATH)GeneralExecution-O{_W0OSubv[Wx(-O: optimize generated bytecode slightlyGeneralExecution<-OOO\N: -O OSvDR yd doc-strings;-OO: remove doc-strings in addition to the -O optimizationsGeneralExecution-QdlՐ y-Q: division options:GeneralExecution4-SW(RYSeN T+ 'import site'/-S: don't imply 'import site' on initializationGeneralExecution-WfTJcR6-W: warning control:GeneralExecution-dVhvՏQ-d: debug output from parserGeneralExecution.-sN mRu(b7zpv_UR0 sys.path--s: don't add user site directory to sys.pathGeneralExecution&-tN:N Nv TAB Ou(NufTJ/-t: issue warnings about inconsistent tab usageGeneralExecution(-ttN:N Nv TAB Ou(Nu.-tt: issue errors about inconsistent tab usageGeneralExecution-v~*[QeXf %-v: verbose (trace import statements)GeneralExecution-xnNx{,NL-x: skip first line of sourceGeneralExecutionPython _ Python Path:GeneralExecution b Python _Select Python PathGeneralExecution]O\S:TyvWorkspace and Project:GeneralExecutionbgL Execution GeneralTabu(General GeneralTab_cw. Shortcuts GeneralTabeeHOMnIncorrect LocationImportFromSourcesProjectHandler yvel^zThe project couldn't be createImportFromSourcesProjectHandlerR d UninstallInstalledWidget&Central OrientationCentral Orientation InterfaceTab؋gav Default Items InterfaceTab mOVhbgExplorer Panel: InterfaceTabGUI uLb[R6GUI Customization: InterfaceTab Language: InterfaceTabT/NOe9RueHRequires restart... InterfaceTabRotate CentralRotate Central InterfaceTabRotate LateralRotate Lateral InterfaceTab bSelect Language: InterfaceTab f>y:eNShow File Errors. InterfaceTabf>y:yvmOVhShow Project Explorer. InterfaceTab f>y:{&SRhShow Symbols List. InterfaceTab f>y: Web InspectorShow Web Inspector. InterfaceTabevgav\OW( bTcQe6The New Item will be inserted after the item selected. InterfaceTab]Qwh[NITool Bar Customization: InterfaceTab ]QwhgavToolbar Items: InterfaceTab {tVhLanguage ManagerLanguagesManagerWidget LanguagesLanguagesManagerWidgete}QeReloadLanguagesManagerWidgetTN|^SSRh bgav.O`SNOu( %1 Y R6gavQeRh,bOu( %2 ۈL|^qSelect the item from the Paste Historial list. You can Copy items into this list with: %1 or Paste them using: %2 LateralPanel%2 v %1%1 of %2 LineEditCountnNx[b|{WN:LQ[b)Set completion type to: Inline CompletionLineEditTabCompleternNx[b|{WN:_9Q[b(Set completion type to: Popup CompletionLineEditTabCompleter & &R} "%1" ۈLN- LOADING: "%1" LoadingItem [NIg*b~R0Definition Not FoundLocatorN*[NIN \^Nyv00This Definition does not belong to this Project.LocatorQsN NINJA-IDEAbout NINJA-IDE MenuAbout QsN QTAbout Qt MenuAbout"YOU_S NINJA-IDE cNHow to Write NINJA-IDE plugins MenuAboutcNehcPlugins Documentation MenuAboutPython ^.R (%1)Python Help (%1) MenuAboutbTJ Bugs! Report Bugs! MenuAbout f>y:T/RuShow Start Page MenuAboutY R6(&C) (%1+C) &Copy (%1+C)MenuEditRjR(&C) (%1+X) &Cut (%1+X)MenuEdit|^(&P) (%1+V) &Paste (%1+V)MenuEditNx[OM]Qw (%1)Code Locator (%1)MenuEditlcb bveg,N::[W{&Y'Q$Convert selected Text to: Title WordMenuEditlcb bveg,N::Qh[W{&Y'QConvert selected Text to: UPPERMenuEditlcb bveg,N::Qh[W{&\QConvert selected Text to: lowerMenuEditgb~ (%1) Find (%1)MenuEditW(eNN-gb~ (%1)Find in Files (%1)MenuEdit_SRMSUgb~ (%1)!Find using word under cursor (%1)MenuEditgb~/fcb (%1)Find/Replace (%1)MenuEditlc[L (%1)Jump to Line (%1)MenuEdit y(&S) Preference&sMenuEditPZ (%1) Redo (%1)MenuEditd (%1+Z) Undo (%1+Z)MenuEditQsb@g yv(&C)&Close All ProjectsMenuFileQsh{~u(&C) (%1)&Close Tab (%1)MenuFile Q(&E)&ExitMenuFilee^eN(&N) (%1)&New File (%1)MenuFilebS_(&O) (%1) &Open (%1)MenuFileO[X(&S) (%1) &Save (%1)MenuFile om;MneNActivate ProfileMenuFile dMneNDeactivate ProfileMenuFilee^yv(&J) (%1)New Pro&ject (%1)MenuFilebS_yv(&P) (%1)Open &Project (%1)MenuFilebSSpeN(&I) (%1)Pr&int File (%1)MenuFile͏}eN (%1)Reload File (%1)MenuFileO[XN:(&A)Save &AsMenuFileQhO[XSave AllMenuFileO[Xyv(&J) (%1)Save Pro&ject (%1)MenuFile Vhh7_Editor Schemes MenuPlugins {tVhLanguages Manager MenuPlugins{tcNManage Plugins MenuPlugins bS_yv\^`'Open Project Properties MenuProjectW(؋mOVh WebPreview Web in Default Browser MenuProjectЈLeN (%1) Run File (%1) MenuProjectЈLyv (%1)Run Project (%1) MenuProjectP\kbЈL (%1) Stop (%1) MenuProjectR d_SRML(&R) (%1)&Remove Line (%1) MenuSourceR dL\>zzv}(&R)&Remove Trailing Spaces MenuSourcel (%1) Comment (%1) MenuSource ~ߋNxLepCount Code Lines MenuSourceb]Debugging Tricks MenuSourceY _SRML(&T) (%1)Duplica&te (%1) MenuSource*l[NI (%1 b %2+Click)!Go To Definition (%1 or %2+Click) MenuSourceQ\) (%1)Indent Less (%1) MenuSourceXY') (%1)Indent More (%1) MenuSource&cQe Import (&I) (%1)Insert &Import (%1) MenuSourcecQel4^s~l (%1)Insert Horizontal Line (%1) MenuSource&W(b@ NKLvkψLcQe "print". Insert Prints per selected line. MenuSourcecQehS:WWl (%1)Insert Title Comment (%1) MenuSourceTN yR(&D) (%1)Move &Down (%1) MenuSourceTN yR(&U) (%1) Move &Up (%1) MenuSourcefcb TAB N:zzh<(&S)Replace Tabs With &Spaces MenuSourceSml (%1)Uncomment (%1) MenuSourceSmR~h{~uDeactivate Group TabsMenuView"mQe (Alt+Wheel-Up)Fade In (Alt+Wheel-Up)MenuView&mQ (Alt+Wheel-Down)Fade Out (Alt+Wheel-Down)MenuViewT kenRj!_ (%1)Follow Mode (%1)MenuViewQh\Oj!_(&M) (%1)Full Screen &Mode (%1)MenuViewc yvR~h{~uGroup Tabs by ProjectMenuView"f>y:/ Qh(&A) (%1)Show/Hide &All (%1)MenuView"f>y:/ ~z(&C) (%1)Show/Hide &Console (%1)MenuView$f>y:/ Vh(&E) (%1)Show/Hide &Editor (%1)MenuView$f>y:/ mOVh(&E) (%1)Show/Hide &Explorer (%1)MenuViewf>y:/ ]Qwh(&T)Show/Hide &ToolbarMenuViewl4^sRRrh{~u(%1)Split Tabs Horizontally (%1)MenuViewWvRRrh{~u (%1)Split Tabs Vertically (%1)MenuView.)\(&I) (Shift+Wheel-Up)Zoom &In (Shift+Wheel-Up)MenuView2e>Y'(&O) (Shift+Wheel-Down)Zoom &Out (Shift+Wheel-Down)MenuViewndClean OutputWidget pQf>y:NxClick to show the source OutputWidgetQOutput OutputWidget[bYN [Wkge^z˘yv~g0=Complete the following fields to create the Project StructurePageProjectProperties hg000 Examine...PageProjectProperties e^yvepcnNew Project DataPageProjectProperties e^yvv_UNew Project FolderPageProjectPropertiese^yvT y(*)New Project Name (*):PageProjectProperties yvcϏProject Description:PageProjectPropertiesyv LicenseProject License:PageProjectPropertiesyvOMn(*)Project Location (*):PageProjectProperties bZbsXv_USelect Virtualenv FolderPageProjectPropertiesZbsXv_UVirtualenv Folder:PageProjectProperties byv|{WChoose the Project TypePageProjectTypeyv|{W Project TypePageProjectTypeT aAcceptPluginErrorDialog cNbTJPlugin error reportPluginErrorDialogNNcNVQsyd)Some plugins have errors and were removedPluginErrorDialogy>S:Su(Community AvailablePluginsManagerWidgetcϏ Description:PluginsManagerWidget][ InstalledPluginsManagerWidget[eSu(Official AvailablePluginsManagerWidget cN{tVhPlugins ManagerPluginsManagerWidgete}QeReloadPluginsManagerWidgetfeUpdatesPluginsManagerWidgetSmCancelPreferencesWidgetVhEditorPreferencesWidgetu(GeneralPreferencesWidgetuLb InterfacePreferencesWidgetNINJA-IDE ry`'NINJA-IDE - PreferencesPreferencesWidgetcNPluginsPreferencesWidgetO[XSavePreferencesWidgetN;ThemePreferencesWidget ^zevMnCreate New ProfileProfilesLoaderR dMnDelete ProfileProfilesLoadereNFiles:ProfilesLoaderbS_Mn Open ProfileProfilesLoaderMn %1 ]feProfile %1 Updated!ProfilesLoader Mn{tVhProfile ManagerProfilesLoaderyv Projects:ProfilesLoaderO[XO`bS_veNTyvR0NN*MneN,SN^^8ŐvW(yvTeNONKRcb. QAO`O[XO`v]O\sX,W(_NQvN֘yvve,SNeOVR0NKRMP\N evr`.Save your opened files and projects into a profile and change really quick between projects and files sessions. This allows you to save your working environment, keep working in another project and then go back exactly where you left.ProfilesLoaderfeMnUpdate ProfileProfilesLoadercϏ Description: ProjectDataLicenceLicence: ProjectDataT yName: ProjectData|{W Project Type: ProjectDatae/cvbi\UT Supported Extensions: ProjectDataURLURL: ProjectDataN;eN Main File:ProjectExecutionSepSRRr Params (comma separated):ProjectExecutionTnbgLg,Post-exec Script:ProjectExecutionRMnbgLg,Pre-exec Script:ProjectExecution\^`' Properties:ProjectExecutionPython _ Python Path:ProjectExecution bN;eNSelect Main FileProjectExecution bTnbgLg,eN!Select Post Execution Script FileProjectExecution bRMnbgLg,eN Select Pre Execution Script FileProjectExecution b Python _Select Python PathProjectExecution bZbsXv_USelect Virtualenv FolderProjectExecution4Ou(SRRrSepOYhelp, verbose 3Separate the params with commas (ie: help, verbose)ProjectExecutionN f/NN*TlvZbsXv_U%This is not a valid Virtualenv FolderProjectExecution ZbsXv_UVirtualenv FolderProjectExecutionZbsXv_UVirtualenv Folder:ProjectExecutionSmCancelProjectPropertiesyvepcn Project DataProjectPropertiesyvbgLProject ExecutionProjectPropertiesyv\^`'Project PropertiesProjectProperties^l\^`'Properties InvalidProjectPropertiesO[XSaveProjectPropertiesyv_Ř{bg T y0The Project must have a name.ProjectPropertieseeHOMnIncorrect LocationPythonProjectHandler yvel^zThe project couldn't be createPythonProjectHandlerpQmRR0e6ϘyvN-!Click to add to favorite projectsRecentProjectItempQNRhN-R dClick to delete from the listRecentProjectItempQP\`R0RhN Click to dock on the listRecentProjectItempQNe6ϘyvN-R d&Click to remove from favorite projectsRecentProjectItem lg cϏSu(no description availableRecentProjectItemfcbReplace ReplaceWidgetQhfcb Replace All ReplaceWidget bfcbReplace Selection ReplaceWidgetQ[ContentResultseNFileResultsLLineResults bgLN-eExecution Interrupted RunWidget bgLbRExecution Successful! RunWidgetT/RY1%Failed to start RunWidgetQeInput: RunWidgetTnbgLg,bRbgL0,Post Execution Script Successfully executed. RunWidgetRMnbgLg,bRbgL0-Pre Execution Script Successfully executed.  RunWidget gb~etN*SUFind Whole Words SearchWidget c N %1Press %1 SearchWidget Y'\QeOaRespect Case Sensitive SearchWidget T/RNf{~[*Activate Bookmarks NavigationShortcutConfiguration T/Rep[*Activate Breakpoints NavigationShortcutConfiguration T/RSS[*Activate History NavigationShortcutConfigurationom;/P\kbT kenRj!_Activate/Deactivate Follow ModeShortcutConfiguration W(_SRMRRrVhNKRcb.vq&p4Change the keyboard focus between the current splitsShortcutConfigurationOe9R0N NN*h{~uChange to the next TabShortcutConfigurationOe9R0N NN*h{~uChange to the previous TabShortcutConfigurationQs_SRMh{~uClose the current tabShortcutConfigurationl _SRML/ bS:WComment line/selectionShortcutConfigurationeQhXfComplete DeclarationsShortcutConfigurationY R6R0Y R6/|^SSCopy into copy/paste historyShortcutConfiguration^zevh{~uCreate a New tabShortcutConfiguration ^zevyvCreate a new ProjectShortcutConfigurationDebugShortcutConfigurationf/T&^ g\QvR d?Do you want to remove it?ShortcutConfigurationY R6 _SRML/ bS:WDuplicate the line/selectionShortcutConfiguration S`_SRMeNExecute current fileShortcutConfiguration bgL_SRMyvExecute current projectShortcutConfigurationgb~FindShortcutConfiguration gb~TfcbFind & ReplaceShortcutConfiguration gb~N NN* Find NextShortcutConfiguration gb~N NN* Find PreviousShortcutConfiguration W(eNN-gb~ Find in FilesShortcutConfiguration gb~_SRMSUFind word under cursorShortcutConfigurationQh\O Full ScreenShortcutConfigurationl[NIGo to definitionShortcutConfiguration S:WHide Editor AreaShortcutConfiguration mOVh Hide ExplorerShortcutConfigurationYRS:WHide Misc ContainerShortcutConfigurationQh薐(VhdY)Hide all (Except Editor)ShortcutConfigurationNQIhYSU+Highlight occurrences for word under cursorShortcutConfigurationQh[QeImport from everywhereShortcutConfigurationQ\) Indent lessShortcutConfigurationcQeNf{~/epInsert Bookmark/BreakpointShortcutConfigurationcQel4^s~lInsert Horizontal lineShortcutConfigurationcQehS:WWlInsert comment TitleShortcutConfiguration c[L Jump to lineShortcutConfiguration`bY ؋ Load defaultsShortcutConfigurationTN yR _SRML/ bS:WMove the line/selection downShortcutConfigurationTN yR _SRML/ bS:WMove the line/selection upShortcutConfigurationTT[* Navigate BackShortcutConfigurationTRM[*Navigate ForwardShortcutConfigurationbS_eN Open a FileShortcutConfigurationbS_yvOpen a ProjectShortcutConfigurationbS_gQseNOpen recent closed fileShortcutConfiguration|^Y R6|^SSPaste from copy/paste historyShortcutConfiguration bSSp_SRMeNPrint current fileShortcutConfigurationPZRedoShortcutConfiguration e}QeeN Reload FileShortcutConfigurationR d _SRML/ bS:WRemove the line/selectionShortcutConfiguration O[X_SRMeNSave the current fileShortcutConfigurationO[X_SRMyvbS_veN%Save the current project opened filesShortcutConfiguration_cw.]Ou(Shortcut is already in useShortcutConfigurationf>y:Nx[OMVhShow Code LocatorShortcutConfigurationf>y:eNR}VhShow File OpenerShortcutConfigurationf>y: Python ^.RShow Python HelpShortcutConfigurationf>y:Y R6/|^SSShow copy/paste historyShortcutConfigurationl4^sRRrh{~uSplit Tabs HorizontallyShortcutConfigurationWvRRrh{~uSplit Tabs VerticallyShortcutConfigurationP\kbЈLStop ExecutionShortcutConfiguration Rcb.vq&pSwitch keyboard focusShortcutConfiguration$SUgavv_cw.eg,\W(T/NKTfeFThe Shortcut's Text in the Menus are going to be refreshed on restart.ShortcutConfigurationSml _SRML/ bS:WUncomment line/selectionShortcutConfigurationT aAcceptShortcutDialogSmCancelShortcutDialog \U_b@g ~Expand all GroupsTabGroup \U_NeNExpand this FilesTabGroupNf{~ Bookmarks TabNavigatorep Breakpoints TabNavigatorNx[* Code Jumps TabNavigator[*Navigate TabNavigatorS.pQOe9[* y(Right click to change navigation options TabNavigator%1%1  TabWidget%1 f/T&^ geR}%1 Do you want to reload it? TabWidget mRR0yvAdd to Project... TabWidget fe9l՚N Change Syntax TabWidgetQsb@g h{~uClose All Tabs TabWidgetQsQvNh{~uClose Other Tabs TabWidgetSmRRr Close Split TabWidgetQs_SRMh{~uClose This Tab TabWidgetY R6eNOMnR0RjRgCopy file location to Clipboard TabWidgetQsNKRMf/T&O[X#Do you want to save before closing? TabWidgetyR_SRMh{~uR0QvNRRrS:W Move this Tab to the other Split TabWidgetebS_NKRMQsveNReopen last closed File TabWidget ЈL_SRMeNRun this File! TabWidgetl4^sRRrh{~uSplit this Tab (Horizontally) TabWidgetzvRRrh{~uSplit this Tab (Vertically) TabWidgeteN %1 lg O[XThe file %1 was not saved TabWidget<b> bN;</b>Select Theme: ThemeChooserR dN; Delete Theme ThemeChooserN; Preview Theme ThemeChooser<b>eN;T y</b>New Theme Name: ThemeDesigner ^u(h7_hApply Style Sheet ThemeDesignerf/T&^ gO[XN;fe9&Do you want to save the theme changes? ThemeDesigner eN][XW(File Already Exists ThemeDesignerO[XN; Save Theme ThemeDesigner h7_h]O[XStyle Sheet Saved ThemeDesigner N;]Oe9Theme Modified ThemeDesigner Vh~gEditor SchemesThemesManagerWidgete}QeReloadThemesManagerWidget N;{tVhThemes ManagerThemesManagerWidgetVn TracebackTracebackWidget bfeVhSUۈLN }) Check the Update Icon Menu to Download!TrayIconUpdates QsfecБClose Update NotificationsTrayIconUpdatesN }rHg,%1Download Version: %1!TrayIconUpdatesNINJA-IDE feNINJA-IDE UpdatesTrayIconUpdates"NINJA-IDE g erHg,Su($New Version of NINJA-IDE Available: TrayIconUpdates mReeN Add New FileTreeProjectsWidget mReyvAdd New FolderTreeProjectsWidget"mRN*yvR0 Python ~z&Add this Project to the Python ConsoleTreeProjectsWidgetQsyv Close ProjectTreeProjectsWidgetY R6eN Copy FileTreeProjectsWidget Y R6eN Copy File toTreeProjectsWidget ^z '__init__' eQhCreate '__init__' CompleteTreeProjectsWidget^z INIT Y1%Create INIT failTreeProjectsWidgetR deN Delete FileTreeProjectsWidget R dv_U Delete FolderTreeProjectsWidgetf/T&^ gR dYN eN*Do you want to delete the following file: TreeProjectsWidgetf/T&^ gR dYN v_U,Do you want to delete the following folder: TreeProjectsWidget UI eN Edit UI FileTreeProjectsWidgetQee^eNT yEnter New File Name:TreeProjectsWidgetQeeNT yEnter the File Name:TreeProjectsWidgetQev_UT yEnter the Folder Name:TreeProjectsWidget eN]~[XW(File Already ExistsTreeProjectsWidget eNT y File Name:TreeProjectsWidget N TlvT y Invalid NameTreeProjectsWidgetyReN Move FileTreeProjectsWidgete^eNNew FileTreeProjectsWidgete^v_U New FolderTreeProjectsWidgetyv\^`'Project PropertiesTreeProjectsWidgetR7eyvRefresh ProjectTreeProjectsWidgetydv_U Remove FolderTreeProjectsWidget"N Python ~zydN*yv+Remove this Project from the Python ConsoleTreeProjectsWidget T}T eN Rename FileTreeProjectsWidgetЈLyv Run ProjectTreeProjectsWidget nN:N;yvSet as Main ProjectTreeProjectsWidgeteNT yN:zz QeNN*T [W+The file name is empty, please enter a nameTreeProjectsWidgetcϏ Description TreeResult_cwe_Shortcut TreeResult\^`' AttributesTreeSymbolsWidget|{ClassesTreeSymbolsWidgetQhbSFold allTreeSymbolsWidgetQep FunctionsTreeSymbolsWidgetQh\U_ Unfold allTreeSymbolsWidget \U_Qh|{Unfold classesTreeSymbolsWidget \U_|{T\^`'Unfold classes and attributesTreeSymbolsWidget\U_Qhv|{TelUnfold classes and methodsTreeSymbolsWidgetfeUpdate UpdatesWidgetSmP\e>Undock WebInspectorVBackWizardNewProject~~ContinueWizardNewProjectHv_UN [XW(bN f/NN*Tlvv_U0YgO``nbOe9 R0yv\^`'0jFolder don't exists or this is not a valid Folder. If you want to set or modify, go to project propertiesWizardNewProjectNINJA - e^yv[_NINJA - New Project WizardWizardNewProject ZbsXv_UVirtualenv FolderWizardNewProjectmReNR0yvAdd File To Project __Actionszzv}TlʈL%1 Blanks and commented lines: %1  __Actions|{V v.0.1Class Diagram v.0.1 __Actions ^zˑMneNCreate Profile __Actions eN]~[XW(File Already Exists __Actions eNT y File Name: __Actions N TlT y Invalid Name __Actions LNx%1Lines code: %1  __ActionsMnT yN TlProfile Name Invalid __ActionsL`;~Summary of lines __Actions._SRMeNTyv\OQsTR0N*Mn0 MnT yPThe Current Files and Projects will be associated to this profile. Profile Name: __ActionsMnT N Tlb]~[XW(0.The Profile name is invalid or already exists. __Actions$N*eNT yyN:zz QeNN*T y0+The file name is empty, please enter a name __Actions `;Lep%1Total lines: %1 __ActionsP\e>Dock__ExplorerContainerErrors__ExplorerContainer N kcxnvyvIncorrect Project__ExplorerContainer bS_yvv_UOpen Project Directory__ExplorerContaineryve/cNQMnN-yu(2Project support has been disabled from Preferences__ExplorerContaineryvProjects__ExplorerContaineryu(yvProjects Disabled__ExplorerContainer{&SSymbols__ExplorerContaineryvR} The project could not be loaded!__ExplorerContainerSmP\e>Undock__ExplorerContainerWeb Inspector Web Inspector__ExplorerContainer&Web Inspector lg e/cWeb Inspector not Supported__ExplorerContainer4O`v Qt rHg,N e/c Web Inspector01Your Qt version doesn't support the Web Inspector__ExplorerContainer lg cϏSu(no description available__ExplorerContainer%1 O`N[cQT%1 Do you want to exit anyway?__IDE Y(&A)&Addins__IDE (&E)&Edit__IDE eN(&F)&File__IDE yv(&P)&Project__IDE nx(&S)&Source__IDE gw (&V)&View__IDE QsN&T Abou&t__IDEc N ^vbRgeyRPress and Drag to Move__IDENNfe9lg O[XSome changes were not saved__IDES  (Read-Only)__MainContainer0f/T&xn[Qs]R dveN Q[\O[QhN"Y10XAre you sure you want to close the deleted file? The content will be completely deleted.__MainContainerQs]R dveNClose Deleted File__MainContainer eN]~[XW(File Already Exists__MainContainereNO[X%1File Saved: %1__MainContainer N kcxnveNIncorrect File__MainContainere^eN New Document__MainContainerbS_eN Open File__MainContainerPython ehcPython Documentation__MainContainerO[X Save Error__MainContainerO[XeN Save File__MainContainereNlg bS_The file couldn't be open__MainContainereNlg O[XThe file couldn't be saved!__MainContainerVPN bS_The image couldn't be open__MainContainer~zConsole__MiscContainer eNN-gb~ Find in Files__MiscContainerQOutput__MiscContainer Web  Web Preview__MiscContainer bS/\U_ Fold/Unfoldtreeninja-ide-2.3/ninja_ide/addins/lang/Dutch.qm000066400000000000000000002033571216641277400207510ustar00rootroot00000000000000:.@.Iڊ-J+~rJ6QJ6yJ6JKMa/Ma4N=zSTV XV-W-+Z5~Z|K+]vI&[EϩAhZw(hZL7DQP$_,VFnkz:. ס=n=N@@PvOQSuVmV)iTZTRgCxjU us)`GDsBbNlNf zֈӏQO8lskJCY :6 k(ne!ia3"r2ǣf5;Kn<3@X$,A0pLCC1hFY2GReM,jRJLTjEsye4g4<hKnonpSYk0v}xĴ: X#b#Wre `(+]uu2ܴR, 7/?7:=nYtAt)BGD)`N)Y[xdOhIQ:iRqt6~+QwVw<`ޞuMwg'-f§EK§EX bא*K$ULYr_ȕnO !h"jM&&VN+Y^%.789E^0L:7bY[erygiyt ytx/ytE} `Je=E~=t\&%3XII.dI|@IIA4R?:L\$=,ɓ9%nbk7] b|~%ɖ'μC,j~fR;րz[րYT / L/CR|zE T{c (@9S4nw7fc8cp@Y!IUEX~X~Kblsl%s BtQ;u2uT| ,y&m5tRV5t{5tū!uPz;2T(?:DEDĵOĵƨȣ-j~Z%H~I^O^V߳.CNcȤ9hF 5.}BZeе˜"w"ej#x#Cs#" &d4"ZC!*L]j%K[ٹ] b%dYAi4jܧn{/~ xoJCINJSl " ! ^!l2l،JsFa,laeoW"$#v'0( +;}< u=&GIPbPS9U!Y_Z% Z%_t _r܂} GE 0 6]I<IHIρ sYjW{Obԋ8N62b "n*%so&mJ'&m}.L<4bN޹kP9؝Sd ?kJP#Asx`3  @[  9m #t *9>j , 7GT A^i B# Kn{ KN Mg ` kw laV |R,Z 4@ c * kZ + . $nd [ Y, + ն նҀ T 9 6mL D (ּ -2 1d+ 2ZZ ;~- =t >z ? HC HC HCݠ HC~ OOP P m~  W  ĐT !UH " E Io >  ߿ 9_ Ex !' )5R F= /Y Rv R9  D -Dm 8:& @eF GbyW H K"¦ j| jr p_M r tm tEќ za4 :&  e !q į 6 :&+ u 1 j Bϡ {:' *% = (3K mj% ub E. z %9_ =}y H~ H MN'O M#Bq W? W$ ]tIgv bI~ c$ p5g `2 K Y\ u  Ĝʝ <, <I <| @> @I ǽ2 +0R ւQ +\ uz 뷀=~L}R/7NwIj\N]Ja#g)k nl+roa{/ " *λ}b"ɟcS7yonh%zKct\pew%Gu-MC/?6S7&=HMMmT_ iT?w,B|3c^QTQ΢zL|`a#uZj;f,~ѝn>\n9_J_=XP{iOver NINJA-IDEAbout NINJA-IDE AboutNinjaNINJA-IDE (van: Ninja Is Not Just Another IDE) is een cross-platform gentegreerde ontwikkelomgeving die speciaal is ontworpen om Python toepassingen te bouwen. NINJA-IDE biedt het gereedschap om software ontwikkeling in Python te vereenvoudigen en ondersteunt uiteenlopende situaties dankzij de rijke uitbreidingsmogelijkheden.#NINJA-IDE (from: "Ninja Is Not Just Another IDE"), is a cross-platform integrated development environment specially design to build Python Applications. NINJA-IDE provides tools to simplify the Python-software development and handles all kinds of situations thanks to its rich extensibility. AboutNinjaBroncode: <a href="%s"><span style=" text-decoration: underline; color:#ff9e21;">%s</span></a>aSource Code: %s AboutNinjaVersie: %s Version: %s AboutNinja]Website: %s AboutNinja:Bestand aan project toevoegenAdd File to Project AddToProjectHier toevoegen! Add here! AddToProjectAnnulerenCancel AddToProject4Externe invoegtoepassingenExternal PluginsAvailableWidgetNINJA moet opnieuw worden opgestart om de wijzigingen door te voeren.7NINJA needs to be restarted for changes to take effect.AvailableWidgetJURL van invoegtoepassing ontbreekt...URL from Plugin missing...AvailableWidgetConsole wissen Clean Console ConsoleWidgetKopirenCopy ConsoleWidget@Kopieer de inhoud van de consoleCopy Console Content ConsoleWidget*Geschiedenis kopiren Copy History ConsoleWidgetKnippenCut ConsoleWidgetPlakkenPaste ConsoleWidget(Tonen/Verbergen (F4)Show/Hide (F4) ConsoleWidgetAccepterenAcceptDependenciesHelpDialogHet lijkt erop datvoor sommige invoegtoepassingen de afhankelijkheden moeten worden opgelost om correct te kunnen functioneren. U dient dezeop de volgende manier te installeren m.b.v. een terminalIt seems that some plugins needs some dependencies to be solved to work properly, you should install them as follows using a TerminalDependenciesHelpDialog>Vereisten voor invoegtoepassingPlugin requirementsDependenciesHelpDialogZoek Usages Find UsagesEditorLint negeren Ignore LintEditor6Geselecteerd gebied negerenIgnore Selected AreaEditor$Deze regel negerenIgnore This LineEditorGa naar regel Jump to LineEditor Regel:Line:EditorDCode aanvulling activeren met: ".""Activate Code Completion with: "."EditorCompletionHaken: [] Brackets: []EditorCompletion Code aanvulling:Code Completion:EditorCompletionDeclarations voltooien (voer de tegenovergestelde actie uit met: %s).=Complete Declarations (execute the opposite action with: %s).EditorCompletionVoltooien: Complete:EditorCompletion8Dubbele aanhalingstekens: ""Double Quotes: ""EditorCompletionKeys: {}EditorCompletionParentheses: ()EditorCompletion6Enkele aanhalingstekens: ''Simple Quotes: ''EditorCompletion (spaties) (spaces)EditorConfiguration (tab grootte) (tab size)EditorConfiguration&Word Wrap toestaan.Allow Word Wrap.EditorConfigurationCenter on Scroll.EditorConfiguration`Controleer op Docstrings in Classes en Functies..Check for Docstrings in Classes and Functions.EditorConfigurationFuncties: Features:EditorConfiguration@Zoek en toon Check Style fouten.!Find and Show Check Style errors.EditorConfiguration(Zoek en toon fouten.Find and Show Errors.EditorConfigurationTMarkeer de hele regel voor fouten en PEP8.-Highlight the whole line for Errors and PEP8.EditorConfiguration$Lengte inspringen:Indentation Length:EditorConfigurationMarge lijn: Margin Line:EditorConfigurationxVerwijder spaties en voeg automatisch een laatste regel toe.7Remove Trailing Spaces and add Last Line automatically.EditorConfiguration$Toon de marge lijnShow Margin LineEditorConfiguration6Toon Python3 migratie tips.Show Python3 Migration Tips.EditorConfiguration*Toon tabs en spaties.Show Tabs and Spaces.EditorConfigurationZToon tool tip informatie over de PEP8 fouten.0Show Tool tip information about the PEP8 errors.EditorConfigurationPToon tool tip informatie over de fouten.+Show Tool tip information about the errors.EditorConfigurationGebruik Tabs. Use Tabs.EditorConfiguration$Editor lettertype: Editor Font: EditorGeneralIn-/uitschakelen van MiniMap (opnieuw opstarten vereist): (dekking wordt niet ondersteund op Mac OS)LEnable/Disable MiniMap (Requires restart): (opacity not supported in MAC OS) EditorGeneral&Lettertype ongeldig Invalid Font EditorGeneralMax dekking: Max Opacity: EditorGeneralMin dekking: Min Opacity: EditorGeneralMiniMap: EditorGeneralSchema kleur: Scheme Color: EditorGeneral@Formaat gebied t.o.v. de Editor:!Size Area relative to the Editor: EditorGeneral(This font can not be used in the Editor. EditorGeneralTypografie: Typography: EditorGeneral2<b>Nieuwe thema naam:</b>New Theme Name:EditorSchemeDesigner*Accolade achtergrond:Brace Background:EditorSchemeDesigner&Accolade voorgrond:Brace Foreground:EditorSchemeDesignerAccolades:Braces:EditorSchemeDesigner Kies kleur voor:Choose Color for: EditorSchemeDesignerCommentaar:Comment:EditorSchemeDesignerHuidige regel: Current Line:EditorSchemeDesignerDefinitie: Definition:EditorSchemeDesignerJWilt u het bestand overschrijven: %s?%Do you want to override the file: %s?EditorSchemeDesigner&Editor achtergrond:Editor Background:EditorSchemeDesigner4Editor geselecteerd woord:Editor Selected Word:EditorSchemeDesigner8Editor selectie achtergrond:Editor Selection Background:EditorSchemeDesigner,Editor selectie kleur:Editor Selection Color:EditorSchemeDesignerEditor tekst: Editor Text:EditorSchemeDesigner$Fout onderstrepen:Error Underline:EditorSchemeDesignerExtra's:Extras:EditorSchemeDesignerGebied vouwen: Fold Area:EditorSchemeDesignerPijl vouwen: Fold Arrow:EditorSchemeDesignerTrefwoord:Keyword:EditorSchemeDesignerLink navigatie:Link Navigate:EditorSchemeDesignerNummers:Numbers:EditorSchemeDesigner Operator:EditorSchemeDesigner$PEP8 onderstrepen:PEP8 Underline:EditorSchemeDesignerKies een kleur Pick ColorEditorSchemeDesignerJuiste object:Proper Object:EditorSchemeDesignerSchema opslaan! Save Scheme!EditorSchemeDesigner,Schema niet opgeslagenScheme Not SavedEditorSchemeDesigner"Schema opgeslagen Scheme SavedEditorSchemeDesigner"Schema bestaat alScheme already existsEditorSchemeDesigner(Zijbalk achtergrond:Sidebar Background:EditorSchemeDesigner$Zijbalk voorgrond:Sidebar Foreground:EditorSchemeDesignerSpaties:Spaces:EditorSchemeDesignerString2:EditorSchemeDesignerString:EditorSchemeDesignerFDe naam is waarschijnlijk ongeldig.The name probably is invalid.EditorSchemeDesigner@Het schema is opgeslagen op: %s.!The scheme has been saved at: %s.EditorSchemeDesignerAanvulling Completion EditorTabConfiguratie Configuration EditorTab,Editor schema designerEditor Scheme Designer EditorTabAlgemeenGeneral EditorTabLint: UIT Lint: OFF ErrorsWidgetLint: AANLint: ON ErrorsWidgetPEP8: UIT PEP8: OFF ErrorsWidgetPEP8: AANPEP8: ON ErrorsWidgetPath:FileSystemOpener(&HoofdlettergevoeligC&ase sensitiveFindInFilesDialogAnnulerenCancelFindInFilesDialog Directory: FindInFilesDialogFilter: FindInFilesDialogZoeken!Find!FindInFilesDialog HoofdMainFindInFilesDialogOpenFindInFilesDialogMap openenOpen DirectoryFindInFilesDialog OptiesOptionsFindInFilesDialogR&egular ExpressionFindInFilesDialogRec&ursief Rec&ursiveFindInFilesDialogVervangen:  Replace: FindInFilesDialogHZoek op woord (exacte overeenkomst).Search by Phrase (Exact Match).FindInFilesDialogZoek naar alle woorden (overal in het document, niet bij elkaar).BSearch for all the words (anywhere in the document, not together).FindInFilesDialogTekst: Text: FindInFilesDialogBestandFileFindInFilesResult RegelLineFindInFilesResultWeet u zeker dat u de inhoud van het bestand wilt vervangen? (de wijziging kan niet ongedaan gemaakt worden)ZAre you sure you want to replace the content in this files? (The change is not reversible)FindInFilesWidgetWissen!Clear!FindInFilesWidgetZoeken!Find!FindInFilesWidgetGeen resultaten No ResultsFindInFilesWidgetVervangenReplaceFindInFilesWidget,Vervang inhoud bestandReplace Files ContentsFindInFilesWidget4Resultaten vervangen door:Replace results with:FindInFilesWidgetStopFindInFilesWidget4Alle projecten samenvouwenFold all projectsFoldingContextMenu&Project samenvouwenFold the projectFoldingContextMenu0Alle projecten uitvouwenUnfold all projectsFoldingContextMenu"Project uitvouwenUnfold the projectFoldingContextMenuToevoegenAddFromImportDialog6Activeer invoegtoepassingenActivate PluginsGeneralConfigurationpWeet u zeker dat u uw voorkeuren opnieuw wilt instellen?0Are you sure you want to reset your preferences?GeneralConfiguration*Afsluiten bevestigen. Confirm Exit.GeneralConfigurationLLaad de bestanden van de vorige sessieLoad files from last sessionGeneralConfigurationDInformeer mij over nieuwe updates.Notify me of new updates.GeneralConfigurationBij afsluiten: On Close:GeneralConfigurationBij opstarten: On Start:GeneralConfigurationNNINJA-IDE voorkeuren opnieuw instellen:Reset NINJA-IDE Preferences:GeneralConfiguration8Voorkeuren opnieuw instellenReset preferencesGeneralConfiguration:Voorkeuren opnieuw instellen?Reset preferences?GeneralConfiguration*Selecteer Python PathSelect Python PathGeneralConfiguration(Selecteer werkruimteSelect WorkspaceGeneralConfiguration Toon startpaginaShow Start PageGeneralConfiguration.Ondersteunde extensies:Supported Extensions:GeneralConfigurationWerkruimte WorkspaceGeneralConfiguration,Werkruimte en project:Workspace and Project:GeneralConfiguration-3: Waarschuwen voor Python 3.x incompabiliteiten die niet door 2to3 kunnen worden opgelostJ-3: warn about Python 3.x incompatibilities that 2to3 cannot trivially fixGeneralExecutiond-B: maak geen .py[co] bestanden aan bij importeren'-B: don't write .py[co] files on importGeneralExecutionr-E: negeer PYTHON* omgevingsvariabelen (zoals PYTHONPATH)=-E: ignore PYTHON* environment variables (such as PYTHONPATH)GeneralExecution`-O: optimaliseer gegenereerde bytecode enigszins(-O: optimize generated bytecode slightlyGeneralExecution-OO: verwijder de doc-strings als aanvulling op de -O optimalisaties;-OO: remove doc-strings in addition to the -O optimizationsGeneralExecution.-Q: afscheidingsopties:-Q: division options:GeneralExecution/-S: don't imply 'import site' on initializationGeneralExecution4-W: waarschuwingscontrole:-W: warning control:GeneralExecution6-d: debug output van parser-d: debug output from parserGeneralExecutionr-s: gebruikers site directory niet toevoegen aan sys.path--s: don't add user site directory to sys.pathGeneralExecutionx-t: waarschuwingen tonen over inconsistent gebruik van tabs/-t: issue warnings about inconsistent tab usageGeneralExecutionx-tt: foutmeldingen tonen over inconsistent gebruik van tabs.-tt: issue errors about inconsistent tab usageGeneralExecutionR-v: verbose ( import statements traceren)%-v: verbose (trace import statements)GeneralExecutionL-x: eerste regel van de bron overslaan-x: skip first line of sourceGeneralExecution Python Path:GeneralExecution*Selecteer Python PathSelect Python PathGeneralExecution,Werkruimte en project:Workspace and Project:GeneralExecutionUitvoering Execution GeneralTabAlgemeenGeneral GeneralTabSneltoetsen Shortcuts GeneralTab Onjuiste locatieIncorrect LocationImportFromSourcesProjectHandlerLHet project kon niet worden aangemaaktThe project couldn't be createImportFromSourcesProjectHandlerVerwijderen UninstallInstalledWidget&Centrale orientatieCentral Orientation InterfaceTabStandaard items Default Items InterfaceTab Verkenner panel:Explorer Panel: InterfaceTabGUI aanpassing:GUI Customization: InterfaceTab Taal: Language: InterfaceTab&Herstart vereist...Requires restart... InterfaceTab Centraal draaienRotate Central InterfaceTab$Zijdelings draaienRotate Lateral InterfaceTab Taal selecteren:Select Language: InterfaceTab(Toon bestandsfouten.Show File Errors. InterfaceTab&Toon migratie tips.Show Migration Tips. InterfaceTab.Toon project verkenner.Show Project Explorer. InterfaceTab(Toon symbolen lijst.Show Symbols List. InterfaceTab&Toon Web Inspector.Show Web Inspector. InterfaceTab~Het nieuwe item zal na het geselecteerde item worden ingevoegd.6The New Item will be inserted after the item selected. InterfaceTab(Werkbalk aanpassing:Tool Bar Customization: InterfaceTabWerkbalk items:Toolbar Items: InterfaceTabSluitenCloseLanguagesManagerWidgetTaal beherenLanguage ManagerLanguagesManagerWidget Talen LanguagesLanguagesManagerWidgetOpnieuw ladenReloadLanguagesManagerWidgetSelecteer het item uit de plak geschiedenis lijst. U kunt items in deze lijst kopiren met: %s of plakken met: %sqSelect the item from the Paste Historial list. You can Copy items into this list with: %s or Paste them using: %s LateralPanel%s van %s%s of %s LineEditCount)Set completion type to: Inline CompletionLineEditTabCompleter(Set completion type to: Popup CompletionLineEditTabCompleter$ LADEN: "%s" LOADING: "%s" LoadingItem.Definitie niet gevondenDefinition Not FoundLocatorXDeze definitie behoort niet tot dit project.0This Definition does not belong to this Project.LocatorOver NINJA-IDEAbout NINJA-IDE MenuAboutOver QtAbout Qt MenuAbout|Hoe kunnen invoegtoepassingen voor NINJA-IDE worden geschrevenHow to Write NINJA-IDE plugins MenuAbout&Plugin documentatiePlugins Documentation MenuAboutPython Help (%s) MenuAboutFouten melden! Report Bugs! MenuAbout Toon startpaginaShow Start Page MenuAbout K&opiren (%s+C) &Copy (%s+C)MenuEdit&Knippen (%s+X) &Cut (%s+X)MenuEdit&Plakken (%s+V) &Paste (%s+V)MenuEdit Code zoeker (%s)Code Locator (%s)MenuEditbGeselecteerde tekst converteren naar: Titel woord$Convert selected Text to: Title WordMenuEditVGeselecteerde tekst converteren naar: UPPERConvert selected Text to: UPPERMenuEditVGeselecteerde tekst converteren naar: lowerConvert selected Text to: lowerMenuEditZoeken (%s) Find (%s)MenuEdit0Zoeken in bestanden (%s)Find in Files (%s)MenuEdit>Zoek woord onder de cursor (%s)!Find using word under cursor (%s)MenuEdit*Zoeken/Vervangen (%s)Find/Replace (%s)MenuEdit&Voorkeuren Preference&sMenuEditOpnieuw (%s) Redo (%s)MenuEdit*Ongedaan maken (%s+Z) Undo (%s+Z)MenuEdit.Alle p&rojecten sluiten&Close All ProjectsMenuFile*&Tabblad sluiten (%s)&Close Tab (%s)MenuFile&Afsluiten&ExitMenuFile&&Nieuw bestand (%s)&New File (%s)MenuFile$&Open bestand (%s) &Open (%s)MenuFileOp&slaan (%s) &Save (%s)MenuFile"Profiel activerenActivate ProfileMenuFile&Profiel deactiverenDeactivate ProfileMenuFile&Nieuw pro&ject (%s)New Pro&ject (%s)MenuFile$Open &project (%s)Open &Project (%s)MenuFile,Open recente bestandenOpen Recent FilesMenuFile.Bestand af&drukken (%s)Pr&int File (%s)MenuFile4Bestand opnieuw laden (%s)Reload File (%s)MenuFileOpslaan &alsSave &AsMenuFileAlles opslaanSave AllMenuFile,Pro&ject opslaan (%s)Save Pro&ject (%s)MenuFileEditor schema'sEditor Schemes MenuPluginsTalen beherenLanguages Manager MenuPlugins4Invoegtoepassingen beherenManage Plugins MenuPlugins4Open project eigenschappenOpen Project Properties MenuProjectLWeb voorvertoning in standaard browserPreview Web in Default Browser MenuProject,Bestand uitvoeren (%s) Run File (%s) MenuProject,Project uitvoeren (%s)Run Project (%s) MenuProject Stop (%s) MenuProject.Regel &verwijderen (%s)&Remove Line (%s) MenuSourceZ&Verwijder spaties aan het einde van de regel&Remove Trailing Spaces MenuSource0Commentaar invoegen (%s) Comment (%s) MenuSourceTel code regelsCount Code Lines MenuSourceDebugging trucsDebugging Tricks MenuSource &Dupliceren (%s)Duplica&te (%s) MenuSourceBGa naar definitie (%s of %s+klik)!Go To Definition (%s or %s+Click) MenuSource,Minder inspringen (%s)Indent Less (%s) MenuSource(Meer inspringen (%s)Indent More (%s) MenuSource*&Import invoegen (%s)Insert &Import (%s) MenuSource<Horizontale lijn invoegen (%s)Insert Horizontal Line (%s) MenuSourceNPrints per geselcteerde regel invoegen. Insert Prints per selected line. MenuSource<Titel commentaar invoegen (%s)Insert Title Comment (%s) MenuSource0pdb.set_trace() invoegenInsert pdb.set_trace() MenuSource,Verplaats om&laag (%s)Move &Down (%s) MenuSource,Verplaats om&hoog (%s) Move &Up (%s) MenuSource4Vervang tabs door &spatiesReplace Tabs With &Spaces MenuSource6Commentaar verwijderen (%s)Uncomment (%s) MenuSource6Groep tabbladen deactiverenDeactivate Group TabsMenuViewFade In (Alt+Wheel-Up)MenuViewFade Out (Alt+Wheel-Down)MenuViewVolg-modus (%s)Follow Mode (%s)MenuView*&Volledig scherm (%s)Full Screen &Mode (%s)MenuView>Tabbladen per project groeperenGroup Tabs by ProjectMenuView6&Alles tonen/verbergen (%s)Show/Hide &All (%s)MenuView:&Console tonen/verbergen (%s)Show/Hide &Console (%s)MenuView8&Editor tonen/verbergen (%s)Show/Hide &Editor (%s)MenuView>&Verkenner tonen/verbergen (%s)Show/Hide &Explorer (%s)MenuView2&Werkbalk tonen/verbergenShow/Hide &ToolbarMenuViewFTabbladen horizontaal splitsen (%s)Split Tabs Horizontally (%s)MenuViewBTabbladen verticaal splitsen (%s)Split Tabs Vertically (%s)MenuView6&Inzoomen (Shift+Wheel-Up)Zoom &In (Shift+Wheel-Up)MenuView:&Uitzoomen (Shift+Wheel-Down)Zoom &Out (Shift+Wheel-Down)MenuView WissenClean OutputWidget0klik om de bron te tonenClick to show the source OutputWidgetOutput OutputWidget Browse...PageProjectProperties|Vul de volgende velden in om de project structuur aan te maken=Complete the following fields to create the Project StructurePageProjectProperties&Nieuwe project dataNew Project DataPageProjectProperties$Nieuwe project mapNew Project FolderPageProjectProperties0Nieuwe project naam (*):New Project Name (*):PageProjectProperties*Project beschrijving:Project Description:PageProjectProperties"Project licentie:Project License:PageProjectProperties(Project locatie (*):Project Location (*):PageProjectProperties0Selecteer Virtualenv mapSelect Virtualenv FolderPageProjectPropertiesVirtualenv map:Virtualenv Folder:PageProjectProperties*Kies het project typeChoose the Project TypePageProjectTypeProject type Project TypePageProjectTypeAccepterenAcceptPluginErrorDialogHInvoegtoepassing - fouten rapportagePlugin error reportPluginErrorDialogzSommige invoegtoepassingen bevatten fouten en zijn verwijderd)Some plugins have errors and were removedPluginErrorDialogSluitenClosePluginsManagerWidget*Community beschikbaarCommunity AvailablePluginsManagerWidgetOmschrijving: Description:PluginsManagerWidgetGenstalleerd InstalledPluginsManagerWidget*Officieel beschikbaarOfficial AvailablePluginsManagerWidget4Invoegtoepassingen beherenPlugins ManagerPluginsManagerWidgetOpnieuw ladenReloadPluginsManagerWidgetUpdatesPluginsManagerWidgetAnnulerenCancelPreferencesWidgetEditorPreferencesWidgetAlgemeenGeneralPreferencesWidget InterfacePreferencesWidget,NINJA-IDE - VoorkeurenNINJA-IDE - PreferencesPreferencesWidget$InvoegtoepassingenPluginsPreferencesWidgetOpslaanSavePreferencesWidget ThemaThemePreferencesWidget,Nieuw profiel aanmakenCreate New ProfileProfilesLoader&Profiel verwijderenDelete ProfileProfilesLoaderBestanden:Files:ProfilesLoaderProfiel openen Open ProfileProfilesLoader2Profiel %s is bijgewerkt!Profile %s Updated!ProfilesLoaderProfiel beherenProfile ManagerProfilesLoaderProjecten: Projects:ProfilesLoaderSla uw geopende bestanden en projecten op in een profiel en schakel heel snel tussen projecten en betands sessies. Dit geeft u de mogelijkheid om uw werkruimte op te slaan, te blijven werken in een ander project en dan terug te keren naar de plaats waar u was gebleven.Save your opened files and projects into a profile and change really quick between projects and files sessions. This allows you to save your working environment, keep working in another project and then go back exactly where you left.ProfilesLoader"Profiel bijwerkenUpdate ProfileProfilesLoaderOmschrijving: Description: ProjectDataInspringing:  Indentation:  ProjectDataLicentie:Licence: ProjectData Naam:Name: ProjectDataProject type: Project Type: ProjectData.Ondersteunde extensies:Supported Extensions: ProjectDataURL: ProjectDataGebruik Tabs. Use Tabs. ProjectDataCustom PYTHONPATH:ProjectExecutionHoofd bestand: Main File:ProjectExecution$n path per regelOne path per lineProjectExecutionJParameters (gescheiden door komma's):Params (comma separated):ProjectExecutionPost-exec Script:ProjectExecutionPre-exec Script:ProjectExecutionEigenschappen: Properties:ProjectExecutionPython Custom Interpreter:ProjectExecution.Selecteer hoofd bestandSelect Main FileProjectExecutionNSelecteer Post Execution Script bestand!Select Post Execution Script FileProjectExecutionLSelecteer Pre Execution Script bestand Select Pre Execution Script FileProjectExecution*Selecteer Python PathSelect Python PathProjectExecution0Selecteer Virtualenv mapSelect Virtualenv FolderProjectExecutionnScheidt de parameters met komma's (bijv. help, verbose)3Separate the params with commas (ie: help, verbose)ProjectExecutionDDit is geen geldige Virtualenv map%This is not a valid Virtualenv FolderProjectExecutionVirtualenv mapVirtualenv FolderProjectExecutionVirtualenv map:Virtualenv Folder:ProjectExecutionVoeg het path toe van Python projecten die gerelateerd zijn aan deze zodat de Code voltooing wordt verbeterd.ZInsert the path of Python Projects relatedto this one in order to improve Code Completion.ProjectMetadata|Deel uw paths door gebruik te maken van nieuwe regels [ENTER].(Split your paths using newlines [ENTER].ProjectMetadataAnnulerenCancelProjectPropertiesProject data Project DataProjectProperties$Project uitvoeringProject ExecutionProjectProperties Project metadataProject MetadataProjectProperties*Project eigenschappenProject PropertiesProjectProperties,Eigenschappen ongeldigProperties InvalidProjectPropertiesOpslaanSaveProjectPropertiesBHet project moet een naam hebben.The Project must have a name.ProjectProperties Onjuiste locatieIncorrect LocationPythonProjectHandlerLHet project kon niet worden aangemaaktThe project couldn't be createPythonProjectHandlerZKlik om aan favoriete projecten toe te voegen!Click to add to favorite projectsRecentProjectItemFKlik om uit de lijst te verwijderenClick to delete from the listRecentProjectItemDKlik om aan de lijst vast te makenClick to dock on the listRecentProjectItem\klik om uit favoriete projecten te verwijderen&Click to remove from favorite projectsRecentProjectItem:geen omschrijving beschikbaarno description availableRecentProjectItemVervangenReplace ReplaceWidgetAlles vervangen Replace All ReplaceWidget$Selectie vervangenReplace Selection ReplaceWidget InhoudContentResultsBestandFileResults RegelLineResultsVFout tijdens uitvoering, QProcess error: %d*Error during execution, QProcess error: %d RunWidget,Uitvoering onderbrokenExecution Interrupted RunWidget*Succesvol uitgevoerd!Execution Successful! RunWidget"Opstarten misluktFailed to start RunWidgetInvoer:Input: RunWidgetVPost Execution Script succesvol uitgevoerd.,Post Execution Script Successfully executed. RunWidgetXPre Execution Script succesvol uitgevoerd. -Pre Execution Script Successfully executed.  RunWidget&Zoek gehele woordenFind Whole Words SearchWidgetDruk %sPress %s SearchWidgetBHoofdletter gevoeligheid behoudenRespect Case Sensitive SearchWidget:Activeer favorieten navigatieActivate Bookmarks NavigationShortcutConfiguration<Activeer Breakpoints navigatieActivate Breakpoints NavigationShortcutConfiguration>Activeer geschiedenis navigatieActivate History NavigationShortcutConfiguration@Volg-modus activeren/deactiverenActivate/Deactivate Follow ModeShortcutConfigurationrWijzig de toetsenbord focus tussen de huidige splitsingen4Change the keyboard focus between the current splitsShortcutConfiguration8Ga naar het volgende tabbladChange to the next TabShortcutConfiguration4Ga naar het vorige tabbladChange to the previous TabShortcutConfiguration&Huidige tab sluitenClose the current tabShortcutConfigurationNCommentaar aan regel/selectie toevoegenComment line/selectionShortcutConfiguration*Voltoooi DeclarationsComplete DeclarationsShortcutConfigurationLKopieer naar kopieer/plak geschiedenisCopy into copy/paste historyShortcutConfiguration&Nieuwe tab aanmakenCreate a New tabShortcutConfiguration,Nieuw project aanmakenCreate a new ProjectShortcutConfigurationDebugShortcutConfiguration.Wilt u het verwijderen?Do you want to remove it?ShortcutConfiguration6Dupliceer de regel/selectieDuplicate the line/selectionShortcutConfiguration0Huidig bestand uitvoerenExecute current fileShortcutConfiguration0Huidig project uitvoerenExecute current projectShortcutConfiguration ZoekenFindShortcutConfiguration$Zoeken & VervangenFind & ReplaceShortcutConfigurationVolgende zoeken Find NextShortcutConfigurationVorige zoeken Find PreviousShortcutConfiguration&Zoeken in bestanden Find in FilesShortcutConfiguration4Zoek woord onder de cursorFind word under cursorShortcutConfigurationVolledig scherm Full ScreenShortcutConfiguration"Ga naar definitieGo to definitionShortcutConfiguration*Verberg editor gebiedHide Editor AreaShortcutConfiguration&Verkenner verbergen Hide ExplorerShortcutConfiguration,Verberg Misc ContainerHide Misc ContainerShortcutConfigurationFAlles verbergen (behalve de editor)Hide all (Except Editor)ShortcutConfigurationfMarkeer het voorkomen van het woord onder de cursor+Highlight occurrences for word under cursorShortcutConfiguration*Overal uit importerenImport from everywhereShortcutConfiguration"Minder inspringen Indent lessShortcutConfiguration8Favoriet/Breakpoint invoegenInsert Bookmark/BreakpointShortcutConfiguration2Horizontale lijn invoegenInsert Horizontal lineShortcutConfiguration2Commentaar titel invoegenInsert comment TitleShortcutConfigurationGa naar regel Jump to lineShortcutConfiguration"Standaarden laden Load defaultsShortcutConfigurationrVerplaats het huidige tabblad naar de volgende splitsing.'Move the current Tab to the next split.ShortcutConfigurationDVerplaats de regel/selectie omlaagMove the line/selection downShortcutConfigurationDVerplaats de regel/selectie omhoogMove the line/selection upShortcutConfigurationTerug navigeren Navigate BackShortcutConfiguration"Vooruit navigerenNavigate ForwardShortcutConfiguration Open een bestand Open a FileShortcutConfiguration Open een projectOpen a ProjectShortcutConfiguration:Open onlangs gesloten bestandOpen recent closed fileShortcutConfigurationDPlak uit kopieer/plak geschiedenisPaste from copy/paste historyShortcutConfiguration2Huidige bestand afdrukkenPrint current fileShortcutConfigurationOpnieuwRedoShortcutConfiguration*Bestand opnieuw laden Reload FileShortcutConfiguration6Verwijder de regel/selectieRemove the line/selectionShortcutConfiguration.Huidige bestand opslaanSave the current fileShortcutConfigurationXSla de huidige geopende project bestanden op%Save the current project opened filesShortcutConfiguration<Snelkoppeling is al in gebruikShortcut is already in useShortcutConfiguration"Toon Code LocatorShow Code LocatorShortcutConfiguration&Toon bestandsopenerShow File OpenerShortcutConfiguration Toon Python HelpShow Python HelpShortcutConfiguration<Toon kopieer/plak geschiedenisShow copy/paste historyShortcutConfiguration^Toon/Verberg de tabbladen in het editor gebied.&Show/Hide the Tabs in the Editor Area.ShortcutConfiguration<Tabbladen horizontaal splitsenSplit Tabs HorizontallyShortcutConfiguration8Tabbladen verticaal splitsenSplit Tabs VerticallyShortcutConfigurationStop uitvoeringStop ExecutionShortcutConfiguration4Toetsenbord focus wijzigenSwitch keyboard focusShortcutConfigurationDe tekst van de snelkoppelingen in de menu's zullen worden gewijzigd nadat opnieuw is opgestart.FThe Shortcut's Text in the Menus are going to be refreshed on restart.ShortcutConfigurationRCommentaar bij regel/selectie verwijderenUncomment line/selectionShortcutConfigurationAccepterenAcceptShortcutDialogAnnulerenCancelShortcutDialog.Alle groepen uitbreidenExpand all GroupsTabGroup,Dit bestand uitbreidenExpand this FilesTabGroupFavorieten Bookmarks TabNavigator Breakpoints TabNavigator Code Jumps TabNavigatorNavigatieNavigate TabNavigatorbRechts klikken om de navigatie opties te wijzigen(Right click to change navigation options TabNavigator%s  TabWidget8%s Wilt u het opnieuw laden?%s Do you want to reload it? TabWidget0Aan project toevoegen...Add to Project... TabWidgetSyntax wijzigen Change Syntax TabWidget"Alle tabs sluitenClose All Tabs TabWidget&Andere tabs sluitenClose Other Tabs TabWidget"Splitsing sluiten Close Split TabWidget Deze tab sluitenClose This Tab TabWidgetJKopieer bestandslocatie naar KlembordCopy file location to Clipboard TabWidgetDWilt u opslaan voor het afsluiten?#Do you want to save before closing? TabWidget\Verplaats dit tabblad naar de andere splitsing Move this Tab to the other Split TabWidget>Heropen laatst gesloten bestandReopen last closed File TabWidget,Dit bestand uitvoeren!Run this File! TabWidgetDDit tabblad splitsen (horizontaal)Split this Tab (Horizontally) TabWidget@Dit tabblad splitsen (verticaal)Split this Tab (Vertically) TabWidget<Bestand %s was niet opgeslagenThe file %s was not saved TabWidget.<b>Selecteer thema:</b>Select Theme: ThemeChooser"Thema verwijderen Delete Theme ThemeChooserThema voorbeeld Preview Theme ThemeChooser2<b>Nieuwe thema naam:</b>New Theme Name: ThemeDesigner(Stylesheet toepassenApply Style Sheet ThemeDesigner$Bestand bestaat alFile Already Exists ThemeDesigner`Ongeldige bestandsnaam: bestand '%s' bestaat al.0Invalid File Name: the file '%s' already exists. ThemeDesignerThema opslaan Save Theme ThemeDesigner*Stylesheet opgeslagenStyle Sheet Saved ThemeDesigner6Thema opgeslagen als: '%s'.Theme saved at: '%s'. ThemeDesignerSluitenCloseThemesManagerWidgetEditor schema'sEditor SchemesThemesManagerWidgetOpnieuw ladenReloadThemesManagerWidgetThema's beherenThemes ManagerThemesManagerWidget TracebackTracebackWidget Controleer het update menu in het pictogram van NINJA-IDE in het systeemvak om te downloaden!F Check the Update Menu in the NINJA-IDE System Tray icon to Download!TrayIconUpdates4Update meldingen afsluitenClose Update NotificationsTrayIconUpdates(Download versie: %s!Download Version: %s!TrayIconUpdatesNINJA-IDE UpdatesTrayIconUpdatesRNieuwe versie van NINJA-IDE beschikbaar: $New Version of NINJA-IDE Available: TrayIconUpdates.Nieuw bestand toevoegen Add New FileTreeProjectsWidget(Nieuwe map toevoegenAdd New FolderTreeProjectsWidgetTVoeg dit project toe aan de Python Console&Add this Project to the Python ConsoleTreeProjectsWidgetProject sluiten Close ProjectTreeProjectsWidget Bestand kopiren Copy FileTreeProjectsWidget*Bestand kopiren naar Copy File toTreeProjectsWidget<Aanmaken van '__init__' gereedCreate '__init__' CompleteTreeProjectsWidget*INIT aanmaken misluktCreate INIT failTreeProjectsWidget&Bestand verwijderen Delete FileTreeProjectsWidgetMap verwijderen Delete FolderTreeProjectsWidgetRWilt u het volgende bestand verwijderen: *Do you want to delete the following file: TreeProjectsWidgetHWilt u de volgende map verwijderen: ,Do you want to delete the following folder: TreeProjectsWidget&UI bestand bewerken Edit UI FileTreeProjectsWidget@Voer een nieuwe bestandsnaam in:Enter New File Name:TreeProjectsWidget0Voer de bestandsnaam in:Enter the File Name:TreeProjectsWidget&Voer de mapnaam in:Enter the Folder Name:TreeProjectsWidget$Bestand bestaat alFile Already ExistsTreeProjectsWidgetBestandsnaam: File Name:TreeProjectsWidgetOngeldige naam Invalid NameTreeProjectsWidgetNOngeldig Path: bestand '%s' bestaat al.+Invalid Path: the file '%s' already exists.TreeProjectsWidget&Bestand verplaatsen Move FileTreeProjectsWidgetNieuw bestandNew FileTreeProjectsWidgetNieuwe map New FolderTreeProjectsWidget*Project eigenschappenProject PropertiesTreeProjectsWidget$Project vernieuwenRefresh ProjectTreeProjectsWidgetMap verwijderen Remove FolderTreeProjectsWidgetVVerwijder dit project uit de Python Console+Remove this Project from the Python ConsoleTreeProjectsWidget"Bestand hernoemen Rename FileTreeProjectsWidget"Project uitvoeren Run ProjectTreeProjectsWidget2Stel in als hoofd projectSet as Main ProjectTreeProjectsWidget`De bestandsnaam is leeg, voer a.u.b. een naam in+The file name is empty, please enter a nameTreeProjectsWidgetOmschrijving Description TreeResultSneltoetsShortcut TreeResultAttributen AttributesTreeSymbolsWidgetClassesTreeSymbolsWidget"Alles samenvouwenFold allTreeSymbolsWidgetFuncties FunctionsTreeSymbolsWidgetAlles uitvouwen Unfold allTreeSymbolsWidget"Classes uitvouwenUnfold classesTreeSymbolsWidget>Classes en attributes uitvouwenUnfold classes and attributesTreeSymbolsWidget8Classes en methods uitvouwenUnfold classes and methodsTreeSymbolsWidgetBijwerkenUpdate UpdatesWidgetLosmakenUndock WebInspector TerugBackWizardNewProjectDoorgaanContinueWizardNewProjectMap bestaat niet of dit is een ongeldige map. Als u dit wilt instellen of aanpassen , ga dan naar project eigenschappenjFolder don't exists or this is not a valid Folder. If you want to set or modify, go to project propertiesWizardNewProject8NINJA - Wizard nieuw projectNINJA - New Project WizardWizardNewProjectVirtualenv mapVirtualenv FolderWizardNewProject:Bestand aan project toevoegenAdd File To Project __Actions>Lege en commentaar regels: %s  Blanks and commented lines: %s  __ActionsClass Diagram v.0.1 __Actions Profiel aanmakenCreate Profile __Actions$Bestand bestaat alFile Already Exists __ActionsBestandsnaam: File Name: __ActionsOngeldige naam Invalid Name __ActionsTOngedig path: het bestand '%s' bestaat al.+Invalid Path: the file '%s' already exists. __ActionsCode regels: %sLines code: %s  __Actions(Profielnaam ongeldigProfile Name Invalid __Actions4Samenvatting van de regelsSummary of lines __ActionsDe huidige bestanden en projecten zullen met dit profiel worden geassocieerd. Profiel naam:PThe Current Files and Projects will be associated to this profile. Profile Name: __ActionsRDe profielnaam is ongeldig of bestaat al..The Profile name is invalid or already exists. __Actions`De bestandsnaam is leeg, voer a.u.b. een naam in+The file name is empty, please enter a name __Actions0Totaal aantal regels: %sTotal lines: %s __ActionsVastmakenDock__ExplorerContainer FoutenErrors__ExplorerContainerOnjuist projectIncorrect Project__ExplorerContainerMigration 2to3__ExplorerContainer,Open project directoryOpen Project Directory__ExplorerContainernProject ondersteuning is uitgeschakeld in de voorkeuren2Project support has been disabled from Preferences__ExplorerContainerProjectenProjects__ExplorerContainer.Projecten uitgeschakeldProjects Disabled__ExplorerContainerSymbolenSymbols__ExplorerContainerHHet project kon niet worden geladen! The project could not be loaded!__ExplorerContainerLosmakenUndock__ExplorerContainer Web Inspector__ExplorerContainerHWeb Inspector wordt niet ondersteundWeb Inspector not Supported__ExplorerContainer\Uw Qt versie ondersteunt de Web Inspector niet1Your Qt version doesn't support the Web Inspector__ExplorerContainer:geen omschrijving beschikbaarno description available__ExplorerContainer4%s Wilt u toch afsluiten?%s Do you want to exit anyway?__IDE&&Invoegtoepassingen&Addins__IDEBe&werken&Edit__IDE&Bestand&File__IDE&Project&Project__IDEG&egevens&Source__IDE Beel&d&View__IDE &OverAbou&t__IDE>klik en sleep om te verplaatsenPress and Drag to Move__IDEPSommige wijzigingen zijn niet opgeslagenSome changes were not saved__IDE (alleen lezen) (Read-Only)__MainContainerWeet u zeker dat u het verwijderde bestand wilt sluiten? De inhoud zal volledig worden verwijderd.XAre you sure you want to close the deleted file? The content will be completely deleted.__MainContainer4Verwijderd bestand sluitenClose Deleted File__MainContainer$Bestand bestaat alFile Already Exists__MainContainer,Bestand opgeslagen: %sFile Saved: %s__MainContainerOnjuist bestandIncorrect File__MainContainerVOngeldig path: het bestand '%s' bestaat al.+Invalid Path: the file '%s' already exists.__MainContainerNieuw document New Document__MainContainerBestand openen Open File__MainContainer&Python documentatiePython Documentation__MainContainer Fout bij opslaan Save Error__MainContainerBestand opslaan Save File__MainContainerFHet bestand kon niet worden geopendThe file couldn't be open__MainContainerNHet bestand kon niet worden opgeslagen!The file couldn't be saved!__MainContainerJDe afbeelding kon niet worden geopendThe image couldn't be open__MainContainerConsole__MiscContainer&F4: Tonen/Verbergen F4: Show/Hide__MiscContainer"Zoek in bestanden Find in Files__MiscContainerOutput__MiscContainer"Web voorvertoning Web Preview__MiscContainer*Samenvouwen/uitvouwen Fold/Unfoldtreeninja-ide-2.3/ninja_ide/addins/lang/French.qm000066400000000000000000002312641216641277400211050ustar00rootroot00000000000000y yY %<6%D[D0E0N=z"sSTbV V_?V6W5_Z|Z|p1Z5Z|WI]voI~2/-&w=ϩ hZ-hZ7OKيQf)V#  Kn:>. ס=n<=NJQSu8VmNV)ZTei gCܯxjo `g:s`SvQD3BZc  zֈQOBZsJN  :@ k*!i}r"P.b2ǣE5b;X<3O@X$A0C̘C;FY<GRM,ZRJZTjE?\e4g4hKntopSYvSxĴ{!J t# #r2 `/1#+yu^ܴ{kF^, R/?A8:"#=nu]At0hB!D/N)Y [\4]dOhIh/iD5iRqYt6~27w$w}ޞMpwK'5§E~§Esb_א1K$o-Z| ȕne !$"jaT&&q+YzN.78u9SE^9L:AbYweƔrKygyt BytVyt} |JGt&% 93 II7vII*IA=?DLxAHz=ɓp%~7Л b]+\ɖ !μ,jyfRJրlր/ ]/Cj&|z T@ If{c (@Ce2_54nk5fc]5E]Q7fc˽8c?\pr@Y'9G݊pIUQ)X~``\a%_bȌllr\+s BʤtQu2\um| 5yW5tk5t)5t!uzED;(IDb,PWdĵĵ ƨȣj~%T*I^A^r߳.Nwd/cS9 .BvXе[""##C #'&d*4"vC&L]j+[s] :_PiAb%dYLi4j{n@{9L~ CUS "e 7 z!l2،Xf[1iz1jJa4%l}eDosZ3"$(':,( 3;< u`I< ur=& IP]PS9U!ݗYWY{Z%Z%_t_Gizrǿyq"܂k ;E 0&#6yIGITIρ Y{b^^B3.2Y\cB2j"g%s&mV&m.<4NiN޹P9Sd k'/~.CaC~uǑ.:sWe>m0R\?eJg#As`Y  @  9 # *9H ,i 7R A^K B) Kn KN& Mg4 ` h k) larT pmEXv |R4j 4K ' *% ta kZ%q .M $n [% 6 ċ u n + նu ն n 9 6A D ( -2 1 2Z\ ;~6> =t) >9 ? HC} HC HC HC OeC PF at! bOzT b bO d<Yk dhh dʪ ee e e g:0 kb4. zs ?z *f Ut F- 1o d (~ ȗe "Ѧ Ӊ*Z ^r $L  p7 c  yl ! F; Xi hJEW kP m ^/ O1, < m ȵk o " Us 2 > > m&   Đm !U "ɹ E I >o   9{+ E^ E !.e )> _ X FX /u `[ R R'  O; -D 8:, @eR Gbys H IcX K" jj jr p{j rn tb6 t tE za4i :- O  e^ e & ֚o [  :,i  1H RI ɠ ɠW ɠ j ! B {:. ** = (3Za mj,& u? E7 z" =} HV H| MN- M#M5 W W$c ]tI bI~ c h[J p5 |c  K Yx  B ĜV <4 <U <& @IF @T ǽ2@ +9 xq ւjZ +y+ u` 뷀Ϫ=~`}/Y7NIj6\ Y]JGa#k ngl+ro ]{8 ( *b"Vc Syh$U%DzK>c|p d%GA-a/?@'7-S=HbMm`_ ?iTtw,|3Qym>΢-zL(r_q`auJZjE,ѝn\nC_V_UHt'#i* propos de NINJA-IDEAbout NINJA-IDE AboutNinjaxNINJA-IDE (de Ninja Is Not Just Another IDE ), est un environnement de dveloppement intgr multi plateforme conu spcifiquement pour construire des Applications Python. NINJA-IDE fourni des outils pour simplifer le dveloppement logiciel en Python et gre toutes les situations grace sa grande extensibilit.%NINJA-IDE (from: "Ninja Is Not Just Another IDE"), is a cross-platform integrated development environment specially designed to build Python Applications. NINJA-IDE provides tools to simplify the Python-software development and handles all kinds of situations thanks to its rich extensibility. AboutNinjaCode Source : <a href="%s"><span style=" text-decoration: underline; color:#ff9e21;">%s</span></a>aSource Code: %s AboutNinjaVersion : %s Version: %s AboutNinjaSite Web : <a href="%s"><span style=" text-decoration: underline; color:#ff9e21;">%s</span></a>]Website: %s AboutNinja8Ajouter un Fichier au ProjetAdd File to Project AddToProjectAjouter ici ! Add here! AddToProjectAnnulerCancel AddToProject"Greffons ExternesExternal PluginsAvailableWidgetInstallerInstallAvailableWidgetNINJA doit tre redmarr afin que les changements prennent effet.7NINJA needs to be restarted for changes to take effect.AvailableWidgetNomNameAvailableWidget2URL du Greffon manquante &URL from Plugin missing...AvailableWidgetVersionVersionAvailableWidgetAller !Go!CodeLocatorWidget$Effacer la Console Clean Console ConsoleWidget CopierCopy ConsoleWidget>Copier le Contenu de la ConsoleCopy Console Content ConsoleWidget&Copier l'Historique Copy History ConsoleWidget CouperCut ConsoleWidget CollerPaste ConsoleWidget(Afficher/Cacher (F4)Show/Hide (F4) ConsoleWidgetAccepterAcceptDependenciesHelpDialogIl semble que certain greffons ont besoin que certaines dpendences soient rsolues pour fonctionner correctement, vous devez les installer de la manire suivante l'aide d'un TerminalIt seems that some plugins needs some dependencies to be solved to work properly, you should install them as follows using a TerminalDependenciesHelpDialog,Dpendances du greffonPlugin requirementsDependenciesHelpDialog6Rechercher les Utilisations Find UsagesEditorIgnorer Lint Ignore LintEditor8Ignorer la Zone SlectionneIgnore Selected AreaEditor&Ignorer Cette LigneIgnore This LineEditor"Sauter la Ligne Jump to LineEditorLigne :Line:EditorTActiver le Compltement de Code avec : ".""Activate Code Completion with: "."EditorCompletionCrochets : [] Brackets: []EditorCompletion,Compltement de Code :Code Completion:EditorCompletionCompleter les Dclarations (excuter l'action oppose avec : %s).=Complete Declarations (execute the opposite action with: %s).EditorCompletionComplter : Complete:EditorCompletion0Apostrophes Doubles : ""Double Quotes: ""EditorCompletionAccolades : {}Keys: {}EditorCompletionPathses : ()Parentheses: ()EditorCompletion0Apostrophes Simples : ''Simple Quotes: ''EditorCompletion (espaces) (spaces)EditorConfiguration0 (taille de tabulation) (tab size)EditorConfigurationVPermettre le Retour la Ligne Automatique.Allow Word Wrap.EditorConfiguration6Centrer lors du Dfilement.Center on Scroll.EditorConfigurationlVrifier les Docstrings dans les Classes et Fonctions..Check for Docstrings in Classes and Functions.EditorConfigurationCoch : Met en vidence les erreurs avec un Soulignement Dcoch : Met en vidence les erreurs avec la couleur de FondRCheck: Highlight errors using Underline Uncheck: Highlight errors using BackgroundEditorConfiguration"Fonctionnalits : Features:EditorConfigurationrTrouver et Afficher les erreurs de Vrification du Style.!Find and Show Check Style errors.EditorConfigurationFRechercher et Afficher les Erreurs.Find and Show Errors.EditorConfiguration0Longueur d'Indentation :Indentation Length:EditorConfiguration Ligne de Marge : Margin Line:EditorConfigurationEnlever les Espaces de Fin et ajouter une Dernire Ligne automatiquement.7Remove Trailing Spaces and add Last Line automatically.EditorConfiguration4Afficher la Ligne de MargeShow Margin LineEditorConfiguration`Afficher les Astuces de Migration pour Python 3.Show Python3 Migration Tips.EditorConfigurationHAfficher les Espaces et Tabulations.Show Tabs and Spaces.EditorConfigurationbAfficher une Infobulle propos des erreurs PEP8.0Show Tool tip information about the PEP8 errors.EditorConfigurationXAfficher une Infobulle propos des erreurs.+Show Tool tip information about the errors.EditorConfigurationRUtiliser la Fin de Ligne de la PlateformeUse Platform End of LineEditorConfiguration2Utiliser des Tabulations. Use Tabs.EditorConfiguration*Police de l'diteur : Editor Font: EditorGeneralActiver/Dsactiver la Mini Carte (Ncessite un redmarrage) : (opacit non prise en charge dans MAC OS)LEnable/Disable MiniMap (Requires restart): (opacity not supported in MAC OS) EditorGeneralPolice Invalide Invalid Font EditorGeneral"Opacit Maximum : Max Opacity: EditorGeneral"Opacit Minimum : Min Opacity: EditorGeneralMini Carte :MiniMap: EditorGeneral(Palette de Couleur : Scheme Color: EditorGeneralPTaille de la Zone relative l'diteur :!Size Area relative to the Editor: EditorGenerallCette police ne peut pas tre utilise dans l'diteur.(This font can not be used in the Editor. EditorGeneralTypographie : Typography: EditorGeneral:<b>Nom du Nouveau Thme :</b>New Theme Name:EditorSchemeDesigner"Fond d'Accolade :Brace Background:EditorSchemeDesigner2Premier Plan d'Accolade :Brace Foreground:EditorSchemeDesignerAccolades :Braces:EditorSchemeDesigner:Choisissez un Couleur pour : Choose Color for: EditorSchemeDesignerCommentaire :Comment:EditorSchemeDesigner Ligne Courante : Current Line:EditorSchemeDesignerDfinition : Definition:EditorSchemeDesignerJVoulez-vous craser le fichier : %s ?%Do you want to override the file: %s?EditorSchemeDesigner&Fond de l'diteur :Editor Background:EditorSchemeDesigner@Mot Slectionn dans l'diteur :Editor Selected Word:EditorSchemeDesignerJFond de la Slection dans l'diteur :Editor Selection Background:EditorSchemeDesigner4Slection dans l'diteur :Editor Selection Color:EditorSchemeDesigner&Text de l'diteur : Editor Text:EditorSchemeDesigner.Soulignement d'Erreur :Error Underline:EditorSchemeDesignerExtras :Extras:EditorSchemeDesigner(Zone de Repliement : Fold Area:EditorSchemeDesigner,Flche de Repliement : Fold Arrow:EditorSchemeDesigner.Nom de Palette InvalideInvalid Scheme NameEditorSchemeDesignerMot Cl :Keyword:EditorSchemeDesigner(Lien de Navigation :Link Navigate:EditorSchemeDesignerNombres :Numbers:EditorSchemeDesignerOprateur : Operator:EditorSchemeDesigner,Soulignement de PEP8 :PEP8 Underline:EditorSchemeDesigner&Choisir une Couleur Pick ColorEditorSchemeDesignerObjet Correct :Proper Object:EditorSchemeDesigner0Enregistrer la Palette ! Save Scheme!EditorSchemeDesigner.Palette Non EnregistreScheme Not SavedEditorSchemeDesigner&Palette Enregistre Scheme SavedEditorSchemeDesigner,La palette existe djScheme already existsEditorSchemeDesigner6Fond de la Barre Latrale :Sidebar Background:EditorSchemeDesignerFPremier Plan de la Barre Latrale :Sidebar Foreground:EditorSchemeDesignerEspaces :Spaces:EditorSchemeDesignerChane2 :String2:EditorSchemeDesignerChane :String:EditorSchemeDesignerBLe nom est probablement invalide.The name probably is invalid.EditorSchemeDesignerNLa palette a t enregistre dans : %s.!The scheme has been saved at: %s.EditorSchemeDesignerLe nom de palette que vous avez choisi est invalide. Veuillez en choisir un autre.IThe scheme name you have chosen is invalid. Please pick a different name.EditorSchemeDesignerCompltement Completion EditorTabConfiguration Configuration EditorTabDCrateur de Palette pour l'diteurEditor Scheme Designer EditorTabGnralGeneral EditorTab Lint : Dsactiv Lint: OFF ErrorsWidgetLint : ActivLint: ON ErrorsWidget"Erreurs PEP8 : %sPEP8 Errors: %s ErrorsWidget PEP8 : Dsactiv PEP8: OFF ErrorsWidgetPEP8 : ActivPEP8: ON ErrorsWidget,Erreurs Statiques : %sStatic Errors: %s ErrorsWidgetChemin :Path:FileSystemOpener(Sensible la c&asseC&ase sensitiveFindInFilesDialogAnnulerCancelFindInFilesDialogRpertoire :  Directory: FindInFilesDialogFiltre : Filter: FindInFilesDialogRechercher !Find!FindInFilesDialogPrincipalMainFindInFilesDialog OuvrirOpenFindInFilesDialog(Ouvrir le RpertoireOpen DirectoryFindInFilesDialogOptionsOptionsFindInFilesDialog*Expr&ession RgulireR&egular ExpressionFindInFilesDialogRc&ursif Rec&ursiveFindInFilesDialogRemplacer :  Replace: FindInFilesDialogXChercher par Phrase (Correspondance Exacte).Search by Phrase (Exact Match).FindInFilesDialogChercher tous les mots (partout dans le documents, pas ensemble).BSearch for all the words (anywhere in the document, not together).FindInFilesDialogTexte : Text: FindInFilesDialogFichierFileFindInFilesResult LigneLineFindInFilesResulttes-vous certain de vouloir remplacer le contenu de ce fichier ? (Les modifications sont irrversibles)ZAre you sure you want to replace the content in this files? (The change is not reversible)FindInFilesWidgetEffacer !Clear!FindInFilesWidgetTrouver !Find!FindInFilesWidget Pas de Rsultats No ResultsFindInFilesWidgetRemplacerReplaceFindInFilesWidgetBRemplacer le Contenu des FichiersReplace Files ContentsFindInFilesWidget:Remplacer les rsultats par :Replace results with:FindInFilesWidgetArrterStopFindInFilesWidget0Replier tous les projetsFold all projectsFoldingContextMenu"Replier le projetFold the projectFoldingContextMenuDplier/Replier Fold/UnfoldFoldingContextMenu0Dplier tous les projetsUnfold all projectsFoldingContextMenu"Dplier le projetUnfold the projectFoldingContextMenuAjouterAddFromImportDialog(Activer les GreffonsActivate PluginsGeneralConfigurationxtes-vous certain de vouloir rinitialiser vos prfrences ?0Are you sure you want to reset your preferences?GeneralConfiguration(Confirmer la Sortie. Confirm Exit.GeneralConfigurationZCharger les fichiers de la session prcdenteLoad files from last sessionGeneralConfigurationNMe prvenir des nouvelles mises jour.Notify me of new updates.GeneralConfiguration la Fermeture : On Close:GeneralConfigurationAu Dmarrage : On Start:GeneralConfigurationVRinitiliser les Prfrences de NINJA-IDE :Reset NINJA-IDE Preferences:GeneralConfiguration:Rinitialiser les prfrencesReset preferencesGeneralConfiguration>Rinitialiser les prfrences ?Reset preferences?GeneralConfiguration:Choisir le Chemin pour PythonSelect Python PathGeneralConfiguration@Slectionner l'Espace de TravailSelect WorkspaceGeneralConfiguration:Afficher la Page de DmarrageShow Start PageGeneralConfiguration:Extensions Prises en Charge :Supported Extensions:GeneralConfiguration"Espace de Travail WorkspaceGeneralConfiguration:Espace de Travail et Projet :Workspace and Project:GeneralConfiguration-3 : avertir des incompatibilits Python 3.x que 2to3 ne peut rsoudre de manire trivialeJ-3: warn about Python 3.x incompatibilities that 2to3 cannot trivially fixGeneralExecutionl-B : ne pas crire les fichier .py[co] l'importation'-B: don't write .py[co] files on importGeneralExecution-E : ignorer les variables d'environnement PYTHON* (telles que PYTHONPATH)=-E: ignore PYTHON* environment variables (such as PYTHONPATH)GeneralExecutionX-O : optimiser lgrement le bytecode gnr(-O: optimize generated bytecode slightlyGeneralExecutiont-OO : enlever les doc-strings en plus des optimisations -O;-OO: remove doc-strings in addition to the -O optimizationsGeneralExecution4-Q : options de division :-Q: division options:GeneralExecutionp-S : ne pas impliquer 'importer site' l'initialisation/-S: don't imply 'import site' on initializationGeneralExecution>-W : contrle d'avertissement :-W: warning control:GeneralExecutiond-d : sortie de dbuguage de l'analyseur syntaxique-d: debug output from parserGeneralExecution-s : ne pas ajouter le rpertoire du site utilisateur sys.path--s: don't add user site directory to sys.pathGeneralExecution-t : mettre des avertissements lors de l'usage inconsistant de tabulations /-t: issue warnings about inconsistent tab usageGeneralExecution-tt : mettre des erreurs lors de l'usage inconsistant de tabulations.-tt: issue errors about inconsistent tab usageGeneralExecution^-v : verbeux (tracer les instructions d'import)%-v: verbose (trace import statements)GeneralExecutionN-x : sauter la premire ligne de source-x: skip first line of sourceGeneralExecution(Chemin pour Python : Python Path:GeneralExecutionDSlectionner le Chemin pour PythonSelect Python PathGeneralExecution:Espace de Travail et Projet :Workspace and Project:GeneralExecutionExcution Execution GeneralTabGnralGeneral GeneralTabRaccourcis Shortcuts GeneralTab*Emplacement IncorrectIncorrect LocationImportFromSourcesProjectHandler<Le projet n'a pas pu tre crThe project couldn't be createImportFromSourcesProjectHandlerNomNameInstalledWidgetDsinstaller UninstallInstalledWidgetVersionVersionInstalledWidget"Activer le ProfilActivate Profile InterfaceTab(Orientation CentraleCentral Orientation InterfaceTab"Fermer le Fichier Close File InterfaceTab$Fermer les ProjetsClose Projects InterfaceTab(Localisateur de Code Code Locator InterfaceTabCommenterComment InterfaceTab CopierCopy InterfaceTab CouperCut InterfaceTab(Dsactiver le ProfilDeactivate Profile InterfaceTab&lments par Dfaut Default Items InterfaceTab,Paneau d'Exploration :Explorer Panel: InterfaceTabRechercherFind InterfaceTab2Trouver dans les Fichiers Find In files InterfaceTab(Rechercher/Remplacer Find/Replace InterfaceTabMode Suivi Follow Mode InterfaceTabBPersonnalisation de l'Interface :GUI Customization: InterfaceTab*Aller la DfinitionGo To Definition InterfaceTabDsindenter Indent Less InterfaceTabIndenter Indent More InterfaceTab"Insrer un Import Insert Import InterfaceTabLangue : Language: InterfaceTabNouveau FichierNew File InterfaceTabNouveau Projet New Project InterfaceTab"Ouvrir le Fichier Open File InterfaceTab Ouvrir le Projet Open Project InterfaceTab CollerPaste InterfaceTab(Prvisualisation Web Preview Web InterfaceTab&Imprimer le Fichier Print File InterfaceTabRefaireRedo InterfaceTab(Recharger le Fichier Reload File InterfaceTab2Ncessite un redmarrage &Requires restart... InterfaceTab"Rotation CentraleRotate Central InterfaceTab"Rotation LatraleRotate Lateral InterfaceTab Tout EnregistrerSave All InterfaceTab Enregistrer SousSave As InterfaceTab,Enregistrer le Fichier Save File InterfaceTab*Enregistrer le Projet Save Project InterfaceTab.Slectioner la Langue :Select Language: InterfaceTab@Afficher les Erreurs de Fichier.Show File Errors. InterfaceTabFAfficher les Conseils de Migration.Show Migration Tips. InterfaceTabBAfficher l'Explorateur de Projet.Show Project Explorer. InterfaceTab\Afficher les Notifications de la Barre d'tat.Show Status Bar Notifications. InterfaceTab>Afficher la Liste des Symboles.Show Symbols List. InterfaceTab4Afficher l'Inspecteur Web.Show Web Inspector. InterfaceTab0Sparer HorizontallementSplit Horizontally InterfaceTab.Sparrer VerticallementSplit Vertically InterfaceTabtLe Nouvel lment sera insr aprs l'lment slectionn.6The New Item will be inserted after the item selected. InterfaceTabNPersonnalisation de la Barre d'Outils :Tool Bar Customization: InterfaceTab<lments de la Barre d'Outil :Toolbar Items: InterfaceTabDcommenter Uncomment InterfaceTabAnnulerUndo InterfaceTab ZoomerZoom In InterfaceTabDzoomerZoom Out InterfaceTabTlchargerDownloadLanguageWidget LangueLanguageLanguageWidgetURLURLLanguageWidget FermerCloseLanguagesManagerWidget,Gestionnaire de LangueLanguage ManagerLanguagesManagerWidgetLangues LanguagesLanguagesManagerWidgetRechargerReloadLanguagesManagerWidget,Slectionner l'lment de la liste d'historique de Collage. Vous pouvez Copier des lments dans cette liste avec : %s or les Coller en utilisant : %sqSelect the item from the Paste Historial list. You can Copy items into this list with: %s or Paste them using: %s LateralPanel%s de %s%s of %s LineEditCountnDfinir le type de compltement : Compltement Direct)Set completion type to: Inline CompletionLineEditTabCompletervDfinir le type de compltement : Compltement Contextuel(Set completion type to: Popup CompletionLineEditTabCompleter4 CHARGEMENT : %s  LOADING: "%s" LoadingItem,Dfinition Non TrouveDefinition Not FoundLocator\Cette Dfinition n'appartient pas ce Projet.0This Definition does not belong to this Project.LocatorInstallerInstallManualInstallWidget(Fichier du Greffon : Plugin File:ManualInstallWidget Nom du Greffon : Plugin Name:ManualInstallWidget(Version du Greffon :Plugin Version:ManualInstallWidgetBSlectionner le chemin du GreffonSelect Plugin PathManualInstallWidget* propos de NINJA-IDEAbout NINJA-IDE MenuAbout propos de QtAbout Qt MenuAboutJComment crire des greffons NINJA-IDEHow to Write NINJA-IDE plugins MenuAbout4Documentation des GreffonsPlugins Documentation MenuAbout&Aide de Python (%s)Python Help (%s) MenuAbout&Signales des Bugs ! Report Bugs! MenuAbout:Afficher la Page de DmarrageShow Start Page MenuAbout&Copier (%s+C) &Copy (%s+C)MenuEdit&Couper (%s+X) &Cut (%s+X)MenuEdit&Coller (%s+V) &Paste (%s+V)MenuEdit2Localisateur de Code (%s)Code Locator (%s)MenuEditZConvertir le texte slectionn en : Mot Titre$Convert selected Text to: Title WordMenuEditbConvertir le Texte slctionn en : HAUT DE CASSEConvert selected Text to: UPPERMenuEdit`Convertir le Texte slectionn en : bas de casseConvert selected Text to: lowerMenuEditRechercher (%s) Find (%s)MenuEdit<Trouver dans les Fichiers (%s)Find in Files (%s)MenuEditLRechercher le mot sous le curseur (%s)!Find using word under cursor (%s)MenuEdit2Rechercher/Remplacer (%s)Find/Replace (%s)MenuEditPrfrence&s Preference&sMenuEditRefaire (%s) Redo (%s)MenuEditDfaire (%s+Z) Undo (%s+Z)MenuEdit0&Fermer Tous les Projets&Close All ProjectsMenuFile*&Fermer l'Onglet (%s)&Close Tab (%s)MenuFile&Quitter&ExitMenuFile*&Nouveau Fichier (%s)&New File (%s)MenuFile&Ouvrir (%s) &Open (%s)MenuFile,Enregistrer &Sous (%s) &Save (%s)MenuFile"Activer le ProfilActivate ProfileMenuFile(Dsactiver le ProfilDeactivate ProfileMenuFile(Nouveau Pro&jet (%s)New Pro&ject (%s)MenuFile&Ouvrir &Projet (%s)Open &Project (%s)MenuFile6Ouvrir les Fichiers RcentsOpen Recent FilesMenuFile0&Imrimer le Fichier (%s)Pr&int File (%s)MenuFile2Recharger le Fichier (%s)Reload File (%s)MenuFile"Enregistrer &SousSave &AsMenuFile Tout EnregistrerSave AllMenuFile6Enregistrer le Pro&jet (%s)Save Pro&ject (%s)MenuFile*Palettes de l'diteurEditor Schemes MenuPlugins,Gestionnaire de LangueLanguages Manager MenuPlugins$Grer les GreffonsManage Plugins MenuPlugins>Ouvrir les Proprits du ProjetOpen Project Properties MenuProjectdPrvisualisation Web dans le Navigateur par DfautPreview Web in Default Browser MenuProject,Lancer le Fichier (%s) Run File (%s) MenuProject*Lancer le Projet (%s)Run Project (%s) MenuProjectArrter (%s) Stop (%s) MenuProject,&Enlever la Ligne (%s)&Remove Line (%s) MenuSource:Supprime&r les Espaces de Fin&Remove Trailing Spaces MenuSourceCommenter (%s) Comment (%s) MenuSource4Compter les Lignes de CodeCount Code Lines MenuSource(Astuces de DboguageDebugging Tricks MenuSourceDupli&quer (%s)Duplica&te (%s) MenuSourceJAller la Dfinition (%s ou %s+Clic)!Go To Definition (%s or %s+Click) MenuSource Dsindenter (%s)Indent Less (%s) MenuSourceIndenter (%s)Indent More (%s) MenuSource.Insrer un &Import (%s)Insert &Import (%s) MenuSourceDInsrer une Ligne Horizontale (%s)Insert Horizontal Line (%s) MenuSourcePInsrer un Print par ligne slectionne. Insert Prints per selected line. MenuSourceHInsrer un Commentaire de Titre (%s)Insert Title Comment (%s) MenuSource.Insrer pdb.set_trace()Insert pdb.set_trace() MenuSource4Dplacer vers le &Bas (%s)Move &Down (%s) MenuSource6Dplacer vers le &Haut (%s) Move &Up (%s) MenuSourceTRemplacer les Tabulations par des E&spacesReplace Tabs With &Spaces MenuSource Dcommenter (%s)Uncomment (%s) MenuSource@Dsactiver le Groupage d'OngletsDeactivate Group TabsMenuViewBFondre Dedans (Alt+Molette Avant)Fade In (Alt+Wheel-Up)MenuViewFFondre Dehors (Alt+Molette Arrire)Fade Out (Alt+Wheel-Down)MenuViewMode Suivi (%s)Follow Mode (%s)MenuView,&Mode Plein cran (%s)Full Screen &Mode (%s)MenuView>Grouper les Onglets par ProjetsGroup Tabs by ProjectMenuView4Afficher/Cacher &Tout (%s)Show/Hide &All (%s)MenuView@Afficher/Cacher la &Console (%s)Show/Hide &Console (%s)MenuView>Afficher/Cacher l'&diteur (%s)Show/Hide &Editor (%s)MenuViewFAfficher/Cacher l'&Explorateur (%s)Show/Hide &Explorer (%s)MenuViewDAfficher/Cacher la Barre d'Ou&tilsShow/Hide &ToolbarMenuViewRSparer les Onglets Horizontallement (%s)Split Tabs Horizontally (%s)MenuViewLSparer les Onglets Verticalement (%s)Split Tabs Vertically (%s)MenuView.&Zoomer (Ctrl+Wheel-Up)Zoom &In (Ctrl+Wheel-Up)MenuView6&Dzoomer (Ctrl+Wheel-Down)Zoom &Out (Ctrl+Wheel-Down)MenuViewLe code actuel est : %s Afin de prendre en charge Python3, il devrait ressembler : %sLThe actual code look like this: %s For Python3 support should look like: %s MigrationTo36Appliquer les changements ! Apply change!MigrationWidgetCode actuel : Current code:MigrationWidget,Changements suggrs :Suggested changes:MigrationWidgetEffacerClean OutputWidget>Cliquer pour afficher la sourceClick to show the source OutputWidget SortieOutput OutputWidgetNaviguer & Browse...PageProjectProperties~Compltez les champs suivants pour crer la Structure du Projet=Complete the following fields to create the Project StructurePageProjectProperties Crer dans (*) :Create in (*):PageProjectProperties6Nouvelles Donnes de ProjetNew Project DataPageProjectProperties8Nouveau Rpertoire de ProjetNew Project FolderPageProjectProperties6Nouveau Nom de Projet (*) :New Project Name (*):PageProjectProperties.Description du Projet :Project Description:PageProjectProperties&Licence du Projet :Project License:PageProjectPropertiesJSlectionner le Rpertoire VirtualenvSelect Virtualenv FolderPageProjectProperties.Rpertoire Virtualenv :Virtualenv Folder:PageProjectProperties8Choisissez le Type de ProjetChoose the Project TypePageProjectTypeType de Projet Project TypePageProjectTypeAccepterAcceptPluginErrorDialog6Rapport d'erreur de greffonPlugin error reportPluginErrorDialogvCertains greffons prsentent des erreurs et ont t enlevs)Some plugins have errors and were removedPluginErrorDialog FermerClosePluginsManagerWidget0Disponible CommunautaireCommunity AvailablePluginsManagerWidgetDescription : Description:PluginsManagerWidgetInstalls InstalledPluginsManagerWidget*Installation ManuelleManual InstallPluginsManagerWidget&Disponible OfficielOfficial AvailablePluginsManagerWidget0Gestionnaire de GreffonsPlugins ManagerPluginsManagerWidgetRechargerReloadPluginsManagerWidgetMises jourUpdatesPluginsManagerWidgetAnnulerCancelPreferencesWidgetditeurEditorPreferencesWidgetGnralesGeneralPreferencesWidgetInterface InterfacePreferencesWidget.NINJA-IDE - PrfrencesNINJA-IDE - PreferencesPreferencesWidgetGreffonsPluginsPreferencesWidgetEnregistrerSavePreferencesWidget ThmeThemePreferencesWidget.Crer un Nouveau ProfilCreate New ProfileProfilesLoader"Effacer le ProfilDelete ProfileProfilesLoaderFichiers :Files:ProfilesLoaderOuvir un Profil Open ProfileProfilesLoader,Profil %s Mis Jour !Profile %s Updated!ProfilesLoader,Gestionnaire de ProfilProfile ManagerProfilesLoaderProjets : Projects:ProfilesLoader.Enregistrer vos fichiers et projets ouverts dans un profil et basculez trs rapidement entre des sessions de projets et fichiers. Cel vous permet d'enregistrer votre environnement de travail, continuer travailler dans un autre projet et revenir exactement l o vous en tiez.Save your opened files and projects into a profile and change really quick between projects and files sessions. This allows you to save your working environment, keep working in another project and then go back exactly where you left.ProfilesLoader.Mettre Jour le ProfilUpdate ProfileProfilesLoaderDescription : Description: ProjectDataIndentation : Indentation:  ProjectDataLicence :Licence: ProjectData Nom :Name: ProjectData.Emplacement du Projet :Project Location: ProjectData Type de Projet : Project Type: ProjectData:Extensions Prises en Charge :Supported Extensions: ProjectData URL :URL: ProjectData0Utilise des Tabulations. Use Tabs. ProjectDataBGlobals/builtins additionnelles :Additional builtins/globals:ProjectExecution2PYTHONPATH personnalis :Custom PYTHONPATH:ProjectExecution&Fichier Principal : Main File:ProjectExecution&Un chemin par ligneOne path per lineProjectExecutionNParamtres (spars par des virgules) :Params (comma separated):ProjectExecution.Script Post-excution :Post-exec Script:ProjectExecution,Script Pr-excution :Pre-exec Script:ProjectExecutionProprits : Properties:ProjectExecutionDInterprteur Python Personnalis :Python Custom Interpreter:ProjectExecutionBSlectionner le Fichier PrincipalSelect Main FileProjectExecution`Slectionner le Fichier de Script Post Excution!Select Post Execution Script FileProjectExecution^Slectionner le Fichier de Script Pr Excution Select Pre Execution Script FileProjectExecutionDSlectionner le Chemin pour PythonSelect Python PathProjectExecutionJSlectionner le Rpertoire VirtualenvSelect Virtualenv FolderProjectExecutionzSparer les paramtres avec des virgules (ie : aide, verbeux)3Separate the params with commas (ie: help, verbose)ProjectExecutionListe de symboles spars par des espaces qui seront considrs comme builtin dans tous les fichiersPSpace-separated list of symbols that will be considered as builtin in every fileProjectExecutionXCe n'est pas un Rpertoire Virtualenv valide%This is not a valid Virtualenv FolderProjectExecution,Rpertoire Virtualenv Virtualenv FolderProjectExecution.Rpertoire Virtualenv :Virtualenv Folder:ProjectExecutionInsrer le chemin des Projets Python en relation avec celui-ci afin d'amliorer le Compltement de Code.ZInsert the path of Python Projects relatedto this one in order to improve Code Completion.ProjectMetadatajSparez vos chemins par de nouvelles lignes [ENTRE].(Split your paths using newlines [ENTER].ProjectMetadataAnnulerCancelProjectProperties"Donnes du Projet Project DataProjectProperties&Excution du ProjetProject ExecutionProjectProperties*Mtadonnes du ProjetProject MetadataProjectProperties(Proprits du ProjetProject PropertiesProjectProperties(Proprits InvalidesProperties InvalidProjectPropertiesEnregistrerSaveProjectProperties8Le Projet doit avoir un nom.The Project must have a name.ProjectProperties*Emplacement IncorrectIncorrect LocationPythonProjectHandler<Le projet n'a pas p tre crThe project couldn't be createPythonProjectHandlerRemplacerReplace ReplaceWidgetTout Remplacer Replace All ReplaceWidget,Remplacer la SlectionReplace Selection ReplaceWidgetContenuContentResultsFichierFileResults LigneLineResults^Erreur durant l'excution, erreur QProcess : %d*Error during execution, QProcess error: %d RunWidget*Excution InterrompueExecution Interrupted RunWidget(Excution Ruissie !Execution Successful! RunWidget&N'a pas pu dmarrerFailed to start RunWidgetEntre :Input: RunWidgetVScript Post Excution excut Correctement.,Post Execution Script Successfully executed. RunWidgetVScript Pr Excution excut Correctement. -Pre Execution Script Successfully executed.  RunWidgetTlchargerDownload SchemeWidgetNomName SchemeWidgetURLURL SchemeWidget6Rechercher des Mots EntiersFind Whole Words SearchWidgetPresser %sPress %s SearchWidgetFRespecter la Sensibilit la CasseRespect Case Sensitive SearchWidgetNActiver la Navigation des Marques PagesActivate Bookmarks NavigationShortcutConfigurationPActiver la Navigation des Points d'ArrtActivate Breakpoints NavigationShortcutConfigurationDActiver l'Historique de NavigationActivate History NavigationShortcutConfiguration@Activer/Dsactiver le Mode SuiviActivate/Deactivate Follow ModeShortcutConfigurationpChanger le focus clavier entre les sparations courantes4Change the keyboard focus between the current splitsShortcutConfiguration0Aller l'Onglet suivantChange to the next TabShortcutConfiguration4Aller l'Onglet prcdentChange to the previous TabShortcutConfiguration,Fermer l'onglet actuelClose the current tabShortcutConfiguration8Commenter la ligne/slectionComment line/selectionShortcutConfiguration4Complter les DclarationsComplete DeclarationsShortcutConfigurationRCopier dans l'historique de copier/collerCopy into copy/paste historyShortcutConfiguration,Crer un Nouvel ongletCreate a New tabShortcutConfiguration.Crer un nouveau ProjetCreate a new ProjectShortcutConfigurationDbuguerDebugShortcutConfiguration4Voulez-vous le supprimer ?Do you want to remove it?ShortcutConfiguration8Dupliquer la ligne/slectionDuplicate the line/selectionShortcutConfiguration4Excuter le fichier actuelExecute current fileShortcutConfiguration2Excuter le projet actuelExecute current projectShortcutConfigurationRechercherFindShortcutConfiguration,Rechercher & RemplacerFind & ReplaceShortcutConfiguration*Rechercher le Suivant Find NextShortcutConfiguration.Rechercher le Prcdent Find PreviousShortcutConfiguration2Trouver dans les Fichiers Find in FilesShortcutConfigurationBRechercher le mot sous le curseurFind word under cursorShortcutConfigurationPlein cran Full ScreenShortcutConfiguration*Aller la dfinitionGo to definitionShortcutConfiguration0Cacher la Zone d'ditionHide Editor AreaShortcutConfiguration(Cacher l'Explorateur Hide ExplorerShortcutConfiguration4Cacher le Conteneur DiversHide Misc ContainerShortcutConfiguration8Tout cacher (Sauf l'diteur)Hide all (Except Editor)ShortcutConfiguration^Surligner les occurences du mot sous le curseur+Highlight occurrences for word under cursorShortcutConfiguration&Importer de partoutImport from everywhereShortcutConfigurationMoins indenter Indent lessShortcutConfigurationFIsrer un Marque Page/Point d'ArrtInsert Bookmark/BreakpointShortcutConfiguration:Insrer une ligne HorizontaleInsert Horizontal lineShortcutConfiguration>Insrer un commentaire de TitreInsert comment TitleShortcutConfiguration"Sauter la Ligne Jump to lineShortcutConfiguration<Charger les valeurs par dfaut Load defaultsShortcutConfigurationlDplacer l'Onglet courant vers la sparation suivante.'Move the current Tab to the next split.ShortcutConfigurationNDplacer la ligne/slection vers le basMove the line/selection downShortcutConfigurationPDplacer la ligne/slection vers le hautMove the line/selection upShortcutConfiguration&Naviguer en Arrire Navigate BackShortcutConfiguration"Naviguer en AvantNavigate ForwardShortcutConfiguration"Ouvrir un Fichier Open a FileShortcutConfiguration Ouvrir un ProjetOpen a ProjectShortcutConfigurationBOuvrir le fichier rcemment fermOpen recent closed fileShortcutConfigurationVColler depuis l'historique de copier/collerPaste from copy/paste historyShortcutConfiguration4Imprimer le fichier actuelPrint current fileShortcutConfigurationRefaireRedoShortcutConfiguration(Recharger le Fichier Reload FileShortcutConfiguration8Supprimer la ligne/slectionRemove the line/selectionShortcutConfiguration8Enregisrer le fichier actuelSave the current fileShortcutConfiguration^Enregister les fichier ouverts du projet actuel%Save the current project opened filesShortcutConfiguration,Raccourci dj utilisShortcut is already in useShortcutConfiguration@Afficher le Localisateur de CodeShow Code LocatorShortcutConfiguration@Afficher le Slecteur de FichierShow File OpenerShortcutConfiguration2Afficher l'Aide de PythonShow Python HelpShortcutConfigurationLAfficher l'historique de copier/collerShow copy/paste historyShortcutConfigurationfAfficher/Cacher les Onglets dans la Zone d'dition.&Show/Hide the Tabs in the Editor Area.ShortcutConfigurationFSparer les Onglets HorizontalementSplit Tabs HorizontallyShortcutConfigurationBSparer les Onglets VerticalementSplit Tabs VerticallyShortcutConfiguration&Arrter l'ExcutionStop ExecutionShortcutConfiguration2Basculer le focus clavierSwitch keyboard focusShortcutConfigurationLes Textes des Raccourcis dans les Menus seront rafrachis au redmarrage.FThe Shortcut's Text in the Menus are going to be refreshed on restart.ShortcutConfiguration<Dcommenter la ligne/slectionUncomment line/selectionShortcutConfigurationAccepterAcceptShortcutDialogAnnulerCancelShortcutDialog6Dvelopper tous les GroupesExpand all GroupsTabGroup.Dvelopper ces FichiersExpand this FilesTabGroupMarques Pages Bookmarks TabNavigatorPoints d'Arrt Breakpoints TabNavigatorSauts de Code Code Jumps TabNavigatorNaviguerNavigate TabNavigatorvCliquez bouton droit pour changer les options de navigation(Right click to change navigation options TabNavigator%s %s  TabWidget:%s Voulez-vous le recharger ?%s Do you want to reload it? TabWidget$Ajouter au Projet &Add to Project... TabWidget$Changer la Syntaxe Change Syntax TabWidget.Fermer Tous les OngletsClose All Tabs TabWidget2Fermer les Autres OngletsClose Other Tabs TabWidget(Fermer la Sparation Close Split TabWidget"Fermer Cet OngletClose This Tab TabWidgetjCopier l'emplacement du fichier vers le Presse-PapierCopy file location to Clipboard TabWidgetRVoulez-vous enregistrer avant de fermer ?#Do you want to save before closing? TabWidgetVDplacer cet Onglet vers l'autre Sparation Move this Tab to the other Split TabWidgetBRouvrir le dernier Fichier fermReopen last closed File TabWidget&Lancer ce Fichier !Run this File! TabWidgetJSparer cet Onglet (Horizontallement)Split this Tab (Horizontally) TabWidgetDSparer cet Onglet (Verticalement)Split this Tab (Vertically) TabWidgetHLe fichier %s n'a pas t enregistrThe file %s was not saved TabWidget<<b>Slectionner le Thme :</b>Select Theme: ThemeChooser$Supprimer le Thme Delete Theme ThemeChooser,Prvisualiser le Thme Preview Theme ThemeChooser:<b>Nouveau Nom de Thme :</b>New Theme Name: ThemeDesigner:Appliquer la Feuille de StyleApply Style Sheet ThemeDesigner,Le Fichier Existe DjFile Already Exists ThemeDesignerlNom de Fichier Invalide : le fichier '%s' existe dj.0Invalid File Name: the file '%s' already exists. ThemeDesigner(Enregistrer le Thme Save Theme ThemeDesigner8Feuille de Style EnregistreStyle Sheet Saved ThemeDesigner:Thme Enregistr dans : '%s'.Theme saved at: '%s'. ThemeDesigner$Slecteur de Thme Theme ChooserThemeTab"Crateur de ThmeTheme DesignerThemeTab FermerCloseThemesManagerWidget*Palettes de l'diteurEditor SchemesThemesManagerWidgetRechargerReloadThemesManagerWidget,Gestionnaire de ThmesThemes ManagerThemesManagerWidgetTraceback TracebackTracebackWidget Consulez le Menu Mise Jour de l'Icne Systme de NINJA-IDE pour Tlcharger !F Check the Update Menu in the NINJA-IDE System Tray icon to Download!TrayIconUpdatesNFermer les Notifications de Mise JourClose Update NotificationsTrayIconUpdates:Tlcharger la Version : %s !Download Version: %s!TrayIconUpdates2Mises Jour de NINJA-IDENINJA-IDE UpdatesTrayIconUpdatesTNouvelle Version de NINJA-IDE Disponible :$New Version of NINJA-IDE Available: TrayIconUpdates4Ajouter un Nouveau Fichier Add New FileTreeProjectsWidget4Ajouter un Nouveau DossierAdd New FolderTreeProjectsWidgetJAjouter ce Projet la Console Python&Add this Project to the Python ConsoleTreeProjectsWidget Fermer le Projet Close ProjectTreeProjectsWidget"Copier le Fichier Copy FileTreeProjectsWidget,Copier le Fichier vers Copy File toTreeProjectsWidget<Crtion de '__init__' TermineCreate '__init__' CompleteTreeProjectsWidget*Cration INIT choueCreate INIT failTreeProjectsWidget(Supprimer le Fichier Delete FileTreeProjectsWidget(Supprimer le Dossier Delete FolderTreeProjectsWidgetVVoulez-vous supprimer le fichier suivant : *Do you want to delete the following file: TreeProjectsWidgetVVoulez-vous supprimer le dossier suivant : ,Do you want to delete the following folder: TreeProjectsWidget(diter le Fichier UI Edit UI FileTreeProjectsWidgetBEntrez le Noveau Nom de Fichier :Enter New File Name:TreeProjectsWidget4Entrez le Nom de Fichier :Enter the File Name:TreeProjectsWidget4Entrez le Nom du Dossier :Enter the Folder Name:TreeProjectsWidget,Le Fichier Existe DjFile Already ExistsTreeProjectsWidget Nom de Fichier : File Name:TreeProjectsWidgetNom Invalide Invalid NameTreeProjectsWidget\Chemin Invalide : le fichier '%s' existe dj.+Invalid Path: the file '%s' already exists.TreeProjectsWidget&Dplacer le Fichier Move FileTreeProjectsWidgetNouveau FichierNew FileTreeProjectsWidgetNouveau Dossier New FolderTreeProjectsWidget(Proprits du ProjetProject PropertiesTreeProjectsWidget(Rafrachir le ProjetRefresh ProjectTreeProjectsWidget(Supprimer le Dossier Remove FolderTreeProjectsWidgetLRetirer ce Projet de la Console Python+Remove this Project from the Python ConsoleTreeProjectsWidget&Renommer le Fichier Rename FileTreeProjectsWidget Lancer le Projet Run ProjectTreeProjectsWidgetHDfinir en tant que Projet PrincipalSet as Main ProjectTreeProjectsWidgetdLe nom de fichier est vide, veuillez entrer un nom+The file name is empty, please enter a nameTreeProjectsWidgetDescription Description TreeResultRaccourciShortcut TreeResultAttributs AttributesTreeSymbolsWidgetClassesClassesTreeSymbolsWidgetTout replierFold allTreeSymbolsWidgetFonctions FunctionsTreeSymbolsWidgetTout dplier Unfold allTreeSymbolsWidget&Dplier les classesUnfold classesTreeSymbolsWidgetHDplier les classes et les attributsUnfold classes and attributesTreeSymbolsWidgetFDplier les classes et les mthodesUnfold classes and methodsTreeSymbolsWidgetNomName UpdatesWidgetMise JourUpdate UpdatesWidgetVersionVersion UpdatesWidgetDissocierUndock WebInspector RetourBackWizardNewProjectContinuerContinueWizardNewProjectLe dossier n'existe pas ou n'est pas valide. Si vous voulez le rgler ou le modifier, allez dans les proprits du projetjFolder don't exists or this is not a valid Folder. If you want to set or modify, go to project propertiesWizardNewProjectFNINJA - Assistant de Nouveau ProjetNINJA - New Project WizardWizardNewProject$Dossier VirtualenvVirtualenv FolderWizardNewProject8Ajouter un Fichier au ProjetAdd File To Project __ActionsFLignes vides et commententes : %s  Blanks and commented lines: %s  __Actions0Digramme de Classe v.0.1Class Diagram v.0.1 __ActionsCrer un ProfilCreate Profile __Actions,Le Fichier Existe DjFile Already Exists __Actions Nom de Fichier : File Name: __ActionsNom Invalide Invalid Name __Actions\Chemin Invalide : le fichier '%s' existe dj.+Invalid Path: the file '%s' already exists. __Actions&Lignes de code : %sLines code: %s  __Actions,Nom de Profil InvalideProfile Name Invalid __Actions"Rsum des lignesSummary of lines __ActionsLes Fichiers et Projets actuels seront associs ce profil. Nom du Profil :PThe Current Files and Projects will be associated to this profile. Profile Name: __ActionsZLe nom de Profil est invalide ou existe dj..The Profile name is invalid or already exists. __ActionsdLe nom de fichier est vide, veuillez entrer un nom+The file name is empty, please enter a name __Actions(Total de lignes : %sTotal lines: %s __ActionsAssocierDock__ExplorerContainerErreursErrors__ExplorerContainer Projet IncorrectIncorrect Project__ExplorerContainer$Migration 2 vers 3Migration 2to3__ExplorerContainer<Ouvrir le Rpertoire de ProjetOpen Project Directory__ExplorerContainerLa prise en chareg de projet a t dsactive dans les Prfrences2Project support has been disabled from Preferences__ExplorerContainerProjetsProjects__ExplorerContainer$Projets DsactivsProjects Disabled__ExplorerContainerSymbolesSymbols__ExplorerContainerDLe projet n'a pas p tre charg ! The project could not be loaded!__ExplorerContainerDissocierUndock__ExplorerContainerInspecteur Web Web Inspector__ExplorerContainerBInspecteur Web non Pris en ChargeWeb Inspector not Supported__ExplorerContainervVotre version de Qt ne prend pas en charge l'Inspecteur Web1Your Qt version doesn't support the Web Inspector__ExplorerContainer:pas de description disponibleno description available__ExplorerContainerB%s Voulez-vous les enregistrer ?%s Do you want to save them?__IDE&Complments&Addins__IDEdit&er&Edit__IDE&Fichier&File__IDE&Projet&Project__IDE&Source&Source__IDE&Vue&View__IDE &proposAbou&t__IDE@Pressez et Glissez pour DplacerPress and Drag to Move__IDE`Certaines modifictions n'on pas t enregistresSome changes were not saved__IDE (Lecture Seule) (Read-Only)__MainContainertes-vous certain de vouloir fermer le fichier supprim ? Le contenu sera entirement supprim.XAre you sure you want to close the deleted file? The content will be completely deleted.__MainContainer4Fermer le Fichier SupprimClose Deleted File__MainContainer,Le Fichier Existe DjFile Already Exists__MainContainer.Fichier Enregistr : %sFile Saved: %s__MainContainer"Fichier IncorrectIncorrect File__MainContainer\Chemin Invalide : le fichier '%s' existe dj.+Invalid Path: the file '%s' already exists.__MainContainer Nouveau Document New Document__MainContainerOuvrir Fichier Open File__MainContainer.Documentation de PythonPython Documentation__MainContainer.Erreur d'Enregistrement Save Error__MainContainer,Enregistrer le Fichier Save File__MainContainerBLe fichier n'a pas p tre ouvertThe file couldn't be open__MainContainerNLe fichier n'a pas p tre enregistr !The file couldn't be saved!__MainContainer>L'image n'a pas p tre ouverteThe image couldn't be open__MainContainerConsoleConsole__MiscContainer(F4 : Afficher/Cacher F4: Show/Hide__MiscContainer8Rechercher dans les Fichiers Find in Files__MiscContainer SortieOutput__MiscContainer(Prvisualisation Web Web Preview__MiscContainer0Entrer le Nom du Titre :Enter the Title Name: editorWidgetInsert un Texte utiliser dans le Print o laisser vide pour n'imprimer que des nombres :GInsert a Text to use in the Print or leave empty to just print numbers: editorWidgetTexte Print Print Text editorWidget(Commentaire de Titre Title Comment editorWidgetninja-ide-2.3/ninja_ide/addins/lang/Spanish.qm000066400000000000000000001323611216641277400213030ustar00rootroot00000000000000**BM*%+\:IڊbRJ+~TMaMaS TbVVZ|/"y?%hZYhZi*7)svQ3{!~np5m: .o!N%PXQnSuyVIKgCsxj8 W_svy`,DnI`M zcֈQO,%NsM^J(j! D"T2ǣI5=;/]<3tlA0R4C>GRKRJ/TjEUbe4g4l# oy-pW9LxĴm}r !ytZyts}D>JH!`td&%83IlI+I^JIaIs=A?yH?"gɓn!PֹF b^ɖμu.,j`fR~րaYTj+ߋD/ 0d%|zn{c (@4nv8gRI U*X~kX~lU/l;tQy ) 5t]5t|?!udz(# B*ĵĵDƨ%-M%d}^:+߳.(l2wc&}YJ aLB=е"Z"H_#Ct.4">[[[dY&ivjAn({~ [C.NT iB{!lX2gֱyoahEGew%mHo:( ;_IPPS9*U!tYCJ_nK_: |ED6Aǒ I!]I-Ieeρ ln9LB{qb22w"P%s}&m . oNMd =_xCqAB]U 'l1/ECEǑ.t菔}R 0e~J4|# cAs  YO\ ! *9# 7,% Knz$ Mg ` la9 4% : $n < 5 նc` ն 9fC 6N D6 (3 -2 1G > HC? HCn HC O2 P atX a a bOz ee el \ S* N} ȗ^ i $ V pg c} A FP Xiz| kP m 8iKp ȵ- o .x # G m`w  Đ7 !Upg ES )Q >u YB E F /=@ RY) Re ) -@O @e+` Gf; Hv. j] pC r>@~ rq t tE X e5 O _T 1Y Dyo~ B = U uw E z ;s =}\6 H M#': W$ ]p)J c5 C KL 9@& W Ĝ <c <. <^ @#k @. ǽ2|} + +@ u\׹S=~0}zIJ\]J>l+rwlo1{ *G[b"~!hS}QThVtHZ%GXH-1H1Mm(iT|3F7zbLb`aMZj \nC_"A;i&Acerca de Ninja-IDEAbout NINJA-IDE AboutNinjaXNINJA-IDE ("Ninja is Not Just Another IDE"), es un entorno de desarrollo multi-plataforma especialmente diseado para construir aplicaciones Python. NINJA-IDE provee herramientas para simplicar el desarrollo de software Python y manejar todo tipo de situaciones gracias a su enorme extensibilidad.#NINJA-IDE (from: "Ninja Is Not Just Another IDE"), is a cross-platform integrated development environment specially design to build Python Applications. NINJA-IDE provides tools to simplify the Python-software development and handles all kinds of situations thanks to its rich extensibility. AboutNinja"Cdigo Fuente: %1Source Code: %1 AboutNinjaVersion: %1 Version: %1 AboutNinjaWebsite: %1 Website: %1 AboutNinja6Agregar archivo al proyectoAdd File to Project AddToProjectAgregar aqu! Add here! AddToProjectCancelarCancel AddToProject Plugins ExternosExternal PluginsAvailableWidgetzNINJA debe ser reiniciado para que los cambios tengan efecto.7NINJA needs to be restarted for changes to take effect.AvailableWidget.Falta URL del plugin...URL from Plugin missing...AvailableWidgetLimpiar Consola Clean Console ConsoleWidget CopiarCopy ConsoleWidget6Copiar contenido de ConsolaCopy Console Content ConsoleWidget Copiar Historial Copy History ConsoleWidget CortarCut ConsoleWidget PegarPaste ConsoleWidget&Mostrar/Ocultar(F4)Show/Hide (F4) ConsoleWidgetEncontrar Usos Find UsagesEditorIr a la lnea Jump to LineEditor Lnea:Line:EditorPActivar Autocopletado de Cdigo con: ".""Activate Code Completion with: "."EditorCompletionCorchetes: [] Brackets: []EditorCompletion2Autocompletado de Cdigo:Code Completion:EditorCompletionCompletar: Complete:EditorCompletion&Comillas Dobles: ""Double Quotes: ""EditorCompletionPActivar Completado dentro de Comentarios!Enable Completion inside CommentsEditorCompletionLlaves: {}Keys: {}EditorCompletionParentesis: ()Parentheses: ()EditorCompletion&Comillas Simples: "Simple Quotes: ''EditorCompletion$Centrar en Scroll.Center on Scroll.EditorConfiguration Caractersticas: Features:EditorConfigurationBuscar y Mostrar Errores de Estilo (siempre en la barra lateral).5Find and Show Check Style errors (at Sidebar Always).EditorConfigurationpBuscar y Mostrar Errorers (siempre en la barra lateral).)Find and Show Errors (at Sidebar Always).EditorConfiguration*Nivel de Indentacin:Indentation Length:EditorConfiguration Lnea de Margen: Margin Line:EditorConfigurationRemover Espacios Finales y Agregar ltima Lnea automticamente.7Remove Trailing Spaces and add Last Line automatically.EditorConfiguration.Mostrar Lnea de MargenShow Margin LineEditorConfiguration0Mostrar Tabs y Espacios.Show Tabs and Spaces.EditorConfigurationMostrar Errores de Estilo y Marcar la Lnea en el editor en la barra lateral (mas lento).LShow check style errors both at Sidebar and highlight Editor Lines (Slower).EditorConfigurationMostrar errores estticos y marcar lneas en el editor (mas lento).GShow static errors both at Sidebar and highlight Editor Lines (Slower).EditorConfigurationFuente: Editor Font: EditorGeneralFuente Invlida Invalid Font EditorGeneral"Esquema de Color: Scheme Color: EditorGeneralXEsta fuente no puede ser usada en el Editor.(This font can not be used in the Editor. EditorGeneralTipografa: Typography: EditorGeneralAutocompletado Completion EditorTabConfiguracin Configuration EditorTabGeneralGeneral EditorTab Ruta:Path:FileSystemOpener,Sensitivo al &contextoC&ase sensitiveFindInFilesDialogCancelarCancelFindInFilesDialogDirectorio: Directory: FindInFilesDialogFiltro:Filter: FindInFilesDialogBuscar!Find!FindInFilesDialogPrincipalMainFindInFilesDialog AbrirOpenFindInFilesDialog Abrir DirectorioOpen DirectoryFindInFilesDialogOpcionesOptionsFindInFilesDialog$Expresin &regularR&egular ExpressionFindInFilesDialogRec&ursivo Rec&ursiveFindInFilesDialog4Buscar por Frase (exacta).Search by Phrase (Exact Match).FindInFilesDialogbBuscar todas las palabras (en todo el documento).BSearch for all the words (anywhere in the document, not together).FindInFilesDialog Texto:Text: FindInFilesDialogArchivoFileFindInFilesResult LneaLineFindInFilesResultBorrar!Clear!FindInFilesWidgetBuscar!Find!FindInFilesWidgetSin Resultados No ResultsFindInFilesWidget PararStopFindInFilesWidgetAgregarAddFromImportDialogActivar PluginsActivate PluginsGeneralConfigurationbEst seguro que quiere resetear sus preferencias?0Are you sure you want to reset your preferences?GeneralConfiguration&Confirmar al Salir. Confirm Exit.GeneralConfigurationFCargar archivos de la ltima sesinLoad files from last sessionGeneralConfiguration^Notificarme cuando haya nuevas actualizaciones.$Nofity me for new available updates.GeneralConfigurationAl Salir: On Close:GeneralConfigurationAl Arranque: On Start:GeneralConfigurationFResetear Preferencias de NINJA-IDE:Reset NINJA-IDE Preferences:GeneralConfiguration*Resetear preferenciasReset preferencesGeneralConfiguration,Resetear preferencias?Reset preferences?GeneralConfiguration4Seleccionar Ruta de PythonSelect Python PathGeneralConfiguration<Seleccionar Espacio de TrabajoSelect WorkspaceGeneralConfiguration0Mostrar Pgina de InicioShow Start PageGeneralConfiguration.Extensiones Soportadas:Supported Extensions:GeneralConfiguration$Espacio de Trabajo WorkspaceGeneralConfiguration<Espacio de Trabajo y Proyecto:Workspace and Project:GeneralConfiguration-3: advertir sobre incompatibilidades de Python 3.X que 2to3 no puede solucionar trivialmenteJ-3: warn about Python 3.x incompatibilities that 2to3 cannot trivially fixGeneralExecutionX-B: no escribir archivos .py[co] al importar'-B: don't write .py[co] files on importGeneralExecution-E: ignorar variables de entorno de PYTHON (por ejemplo, PYTHONPATH)=-E: ignore PYTHON* environment variables (such as PYTHONPATH)GeneralExecutionP-O: optmizar bycode generado ligeramente(-O: optimize generated bytecode slightlyGeneralExecutionf-OO: borrar doc-strings adems de optimizaciones -O;-OO: remove doc-strings in addition to the -O optimizationsGeneralExecution2-Q: opciones de divisin:-Q: division options:GeneralExecutionX-S: no ejecutar 'import site' al inicializar/-S: don't imply 'import site' on initializationGeneralExecution4-W: controles de Warnings:-W: warning control:GeneralExecution>-d: debuggear salida del parser-d: debug output from parserGeneralExecution^-s no agregar directorio del usuario a sys.path--s: don't add user site directory to sys.pathGeneralExecutionX-t informar incosistencias en el uso de tabs/-t: issue warnings about inconsistent tab usageGeneralExecutionr-tt: informar errores de inconsistencia en el uso de tabs.-tt: issue errors about inconsistent tab usageGeneralExecutionP-v: verboso (rastrear sentencias import)%-v: verbose (trace import statements)GeneralExecutionL-x: ignorar la primera lnea de cdigo-x: skip first line of sourceGeneralExecutionRuta de Python: Python Path:GeneralExecution.Seleccionar Ruta PythonSelect Python PathGeneralExecution<Espacio de Trabajo y Proyecto:Workspace and Project:GeneralExecutionEjecucin Execution GeneralTabGeneralGeneral GeneralTab Atajos Shortcuts GeneralTabDesinstalar UninstallInstalledWidget&Orientacin CentralCentral Orientation InterfaceTab*Panel de Exploracin:Explorer Panel: InterfaceTab Opciones de GUI:GUI Customization: InterfaceTabLenguaje: Language: InterfaceTab:(Requiere reinicar NINJA-IDE)Requires restart... InterfaceTab$Rotar CentralmenteRotate Central InterfaceTab$Rotar LateralmenteRotate Lateral InterfaceTab*Seleccionar Lenguaje:Select Language: InterfaceTab6Mostrar Errores de Archivo.Show File Errors. InterfaceTab@Mostrar Explorador de Proyectos.Show Project Explorer. InterfaceTab4Mostrar Lista de Smbolos.Show Symbols List. InterfaceTab,Mostrar Inspector Web.Show Web Inspector. InterfaceTabpEl nuevo elemento ser agregado despus del selecionado.6The New Item will be inserted after the item selected. InterfaceTabDOpciones de Barra de Herramientas:Tool Bar Customization: InterfaceTabFElementos de Barra de Herramientas:Toolbar Items: InterfaceTabElegir el item del historial de pegado. Usted puede copiar elementos en esta lista con: %1 o pegarlos usando : %2qSelect the item from the Paste Historial list. You can Copy items into this list with: %1 or Paste them using: %2 LateralPanel%1 de %2%1 of %2 LineEditCountpSetear tipo de Autocompletado a: Autocompletado en lnea)Set completion type to: Inline CompletionLineEditTabCompleterjSetear tipo de autocompletado a: Autocompletado Popup(Set completion type to: Popup CompletionLineEditTabCompleter0Definicin No EncontradaDefinition Not FoundLocatorVLa definicin no pertenece a este Proyecto.0This Definition does not belong to this Project.Locator&Acerca de NINJA-IDEAbout NINJA-IDE MenuAboutAcerca de QtAbout Qt MenuAboutHCmo escribir plugins para NINJA-IDEHow to Write NINJA-IDE plugins MenuAbout0Documentacin de PluginsPlugins Documentation MenuAbout"Ayuda Python (%1)Python Help (%1) MenuAboutReportar Bugs! Report Bugs! MenuAbout0Mostrar Pgina de InicioShow Start Page MenuAbout &Copiar (%1 + C) &Copy (%1+C)MenuEdit &Cortar (%1 + X) &Cut (%1+X)MenuEdit&Pegar (%1 + V) &Paste (%1+V)MenuEdit4Localizador de Cdigo (%1)Code Locator (%1)MenuEditLConvertir texto seleccionado en Ttulo$Convert selected Text to: Title WordMenuEditRConvertir texto seleccionado a MAYSCULASConvert selected Text to: UPPERMenuEditRConvertir texto seleccionado a minsculasConvert selected Text to: lowerMenuEditBuscar (%1) Find (%1)MenuEdit<Buscar dentro de Archivos (%1)Find in Files (%1)MenuEditXBuscar usando la palabra bajo el cursor (%1)!Find using word under cursor (%1)MenuEdit*Buscar/Reemplazar(%1)Find/Replace (%1)MenuEdit$Ir a la Lnea (%1)Jump to Line (%1)MenuEditPreferencia&s Preference&sMenuEditRehacer (%1) Redo (%1)MenuEdit"Deshacer (%1 + Z) Undo (%1+Z)MenuEdit6&Cerrar Todos Los Proyectos&Close All ProjectsMenuFile &Cerrar Tab (%1)&Close Tab (%1)MenuFile &Salir&ExitMenuFile&&Nuevo Archivo (%1)&New File (%1)MenuFile&Abrir (%1) &Open (%1)MenuFile&Guardar (%1) &Save (%1)MenuFileActivar ProfileActivate ProfileMenuFile$Desactivar ProfileDeactivate ProfileMenuFile(Nuevo Pro&yecto (%1)New Pro&ject (%1)MenuFile(Abrir &Proyecto (%1)Open &Project (%1)MenuFile.Abrir &Tipo de ProyectoOpen Project &TypeMenuFile(Impr&imir Lnea (%1)Pr&int File (%1)MenuFile*Recargar Archivo (%1)Reload File (%1)MenuFileGuardar &ComoSave &AsMenuFileGuardar TodoSave AllMenuFile,Guardar Pro&yecto (%1)Save Pro&ject (%1)MenuFile$Editor de EsquemasEditor Schemes MenuPlugins&Administrar PluginsManage Plugins MenuPluginsRPrevisualizar en el Navegador por defectoPreview Web in Default Browser MenuProject*Ejecutar Archivo (%1) Run File (%1) MenuProject,Ejecutar Proyecto (%1)Run Project (%1) MenuProjectParar (%1) Stop (%1) MenuProject(&Eliminar Lnea (%1)&Remove Line (%1) MenuSource6&Remover Espacios en Blanco&Remove Trailing Spaces MenuSourceComentar (%1) Comment (%1) MenuSource.Contar Lneas de CdigoCount Code Lines MenuSource&Duplicar (%1)Duplica&te (%1) MenuSource>Ir a Definicin (%1 o %2+Click)!Go To Definition (%1 or %2+Click) MenuSource&Indentar Menos (%1)Indent Less (%1) MenuSource"Indentar Mas (%1)Indent More (%1) MenuSource*Insertar &Import (%1)Insert &Import (%1) MenuSource<Insertar Lnea Horizontal (%1)Insert Horizontal Line (%1) MenuSourceDInsertar Ttulo de Comentario (%1)Insert Title Comment (%1) MenuSource"Mover A&bajo (%1)Move &Down (%1) MenuSource$Mover &Arriba (%1) Move &Up (%1) MenuSource:Reemplazar Tabs con &EspaciosReplace Tabs With &Spaces MenuSource Descomentar (%1)Uncomment (%1) MenuSource.Desactivar Agrupar TabsDeactivate Group TabsMenuView<Aclarar (Alt + Rueda adelante)Fade In (Alt+Wheel-Up)MenuView:Oscurecer (Alt + Rueda atrs)Fade Out (Alt+Wheel-Down)MenuView*Modo Seguimiento (%1)Follow Mode (%1)MenuView8&Modo Pantalla Completa (%1)Full Screen &Mode (%1)MenuView2Agrugar Tabs por ProyectoGroup Tabs by ProjectMenuView4Mostrar/Ocultar &Todo (%1)Show/Hide &All (%1)MenuView:Mostrar/Ocultar &Consola (%1)Show/Hide &Console (%1)MenuView8Mostrar/Ocultar &Editor (%1)Show/Hide &Editor (%1)MenuView@Mostrar/Ocultar &Explorador (%1)Show/Hide &Explorer (%1)MenuViewLMostrar/Ocultar Barra de &HerramientasShow/Hide &ToolbarMenuViewBDividir Tabs Horizontalmente (%1)Split Tabs Horizontally (%1)MenuView>Dividir Tabs Verticalmente (%1)Split Tabs Vertically (%1)MenuView@A&grandar (%1 + Rueda Adelante)Zoom &In (%1+Wheel-Up)MenuViewBA&lejar (%1 + Rueda hacia atrs )Zoom &Out (%1+Wheel-Down)MenuViewLimpiarClean OutputWidget8Click para mostrar el cdigoClick to show the source OutputWidget SalidaOutput OutputWidgetrComplete los campos para crear la estructura del Proyecto=Complete the following fields to create the Project StructurePageProjectPropertiesExaminar... Examine...PageProjectProperties,Nueva Data de ProyectoNew Project DataPageProjectProperties8Nuevo Directorio de ProyectoNew Project FolderPageProjectProperties:Nuevo Nombre de Proyecto (*):New Project Name (*):PageProjectProperties2Descripcin del Proyecto:Project Description:PageProjectProperties,Licensia del Proyecto:Project License:PageProjectProperties<Localizacin del Proyecto (*):Project Location (*):PageProjectProperties>Elegir Directorio de VirtualEnvSelect Virtualenv FolderPageProjectProperties2Directorio de VirtualEnv:Virtualenv Folder:PageProjectProperties2Elija el Tipo de ProyectoChoose the Project TypePageProjectType Tipo de Proyecto Project TypePageProjectTypeAceptarAcceptPluginErrorDialog8Reporte de Errores de PluginPlugin error reportPluginErrorDialoghAlgunos Plugins tienen errores y fueron desactivados)Some plugins have errors and were removedPluginErrorDialog4Disponible de la ComunidadCommunity AvailablePluginsManagerWidgetInstalados InstalledPluginsManagerWidget.Disponible OficialmenteOfficial AvailablePluginsManagerWidget0Administrador de PluginsPlugins ManagerPluginsManagerWidgetRecargarReloadPluginsManagerWidgetActualizacionesUpdatesPluginsManagerWidgetCancelarCancelPreferencesWidget EditorEditorPreferencesWidgetGeneralGeneralPreferencesWidgetInterface InterfacePreferencesWidget2Preferencias de NINJA-IDENINJA-IDE - PreferencesPreferencesWidgetPluginsPluginsPreferencesWidgetGuardarSavePreferencesWidget&Crear Nuevo ProfileCreate New ProfileProfilesLoader Eliminar ProfileDelete ProfileProfilesLoaderArchivos:Files:ProfilesLoaderAbrir Profile Open ProfileProfilesLoaderProyectos: Projects:ProfilesLoaderCancelarCancelProjectPropertiesDescripcin: Description:ProjectPropertiesLicensia:Licence:ProjectProperties$Archivo Principal: Main File:ProjectPropertiesNombre:Name:ProjectPropertiesBParametros (separados por comas):Params (comma separated):ProjectProperties.Propiedades de ProyectoProject PropertiesProjectProperties"Tipo de Proyecto: Project Type:ProjectProperties*Propiedades InvlidasProperties InvalidProjectPropertiesRuta Python: Python Path:ProjectPropertiesGuardarSaveProjectProperties0Elegir Archivo PrincipalSelect Main FileProjectProperties*Elegir Ruta de PythonSelect Python PathProjectProperties>Elegir Directorio de VirtualEnvSelect Virtualenv FolderProjectPropertiesjSeparar parametros con comas (por ej, ayuda, verboso)3Separate the params with commas (ie: help, verbose)ProjectProperties.Extensiones Soportadas:Supported Extensions:ProjectPropertiesBEl Proyecto debe tener un nombre.The Project must have a name.ProjectPropertiesXEse no es un Directorio Vlido de VirtualEnv%This is not a valid Virtualenv FolderProjectPropertiesURL:URL:ProjectProperties*Directorio VirtualEnvVirtualenv FolderProjectProperties2Directorio de VirtualEnv:Virtualenv Folder:ProjectPropertiesPClick para agregar a proyectos favoritos!Click to add to favorite projectsRecentProjectItem:Click para borrar de la listaClick to delete from the listRecentProjectItem:Click para acoplar a la listaClick to dock on the listRecentProjectItemTClick para eliminar de proyectos favoritos&Click to remove from favorite projectsRecentProjectItem2descripcin no disponibleno description availableRecentProjectItemReemplazarReplace ReplaceWidget Reemplazar todos Replace All ReplaceWidgetContenidoContentResultsArchivoFileResults LneaLineResults\Error durante la ejecucin, QProcess error: %d*Error during execution, QProcess error: %d RunWidget,Ejecucin InterrumpidaExecution Interrupted RunWidget&Ejecuccin Exitosa!Execution Successful! RunWidget Fall al iniciarFailed to start RunWidget Input:Input: RunWidget,Buscar Toda la PalabraFind Whole Words SearchWidget>Apretar (%1 + Flecha Izquierda)Press (%1 + Left Arrow) SearchWidget:Apretar (%1 + Flecha Derecha)Press (%1 + Right Arrow) SearchWidget>Respectar Sensitivo al ContextoRespect Case Sensitive SearchWidget"Desea eliminarlo?Do you want to remove it?ShortcutConfiguration$Cargar por defecto Load defaultsShortcutConfiguration8Este atajo est siendo usadoShortcut is already in useShortcutConfiguration~Los Atajos de texto en el men sern actualizados al reiniciar.FThe Shortcut's Text in the Menus are going to be refreshed on restart.ShortcutConfigurationAceptarAcceptShortcutDialogCancelarCancelShortcutDialog2Expandir Todos los GruposExpand all GroupsTabGroup*Expandir este ArchivoExpand this FilesTabGroupMarcadores Bookmarks TabNavigatorBreakpoints Breakpoints TabNavigatorSalto de Cdigo Code Jumps TabNavigatorNavegarNavigate TabNavigatorbClick Derecho para cambiar opciones de navegacin(Right click to change navigation options TabNavigator&Agregar Proyecto...Add to Project... TabWidget Cambiar Sintaxis Change Syntax TabWidget(Cerra Todos los TabsClose All Tabs TabWidget*Cerrar los Otros TabsClose Other Tabs TabWidgetCerrar Divisin Close Split TabWidgetCerrar este TabClose This Tab TabWidgetdCopiar la localizacin del archivo al PortapapelesCopy file location to Clipboard TabWidget"Desea recargarlo?Do you want to reload it? TabWidget<Desea guardar antes de cerrar?#Do you want to save before closing? TabWidgetBMover este Tab a la otra divisin Move this Tab to the other Split TabWidgetBReabrir ltimos archivos cerradosReopen last closed File TabWidget,Ejecutar Este Archivo!Run this File! TabWidgetDDividir este Tab (Horizontalmente)Split this Tab (Horizontally) TabWidget@Dividir este Tab (Verticalmente)Split this Tab (Vertically) TabWidget:El archivo %1 no fue guardadoThe file %1 was not saved TabWidget$Editor de EsquemasEditor SchemesThemesManagerWidgetRecargarReloadThemesManagerWidget,Administrador de TemasThemes ManagerThemesManagerWidgetTraceback TracebackTracebackWidget6 Click aqu para Descargar Click here to DownloadTrayIconUpdates8Actualizaciones de NINJA-IDENINJA-IDE UpdatesTrayIconUpdatesLNueva Versin disponible de NINJA-IDE:$New Version of NINJA-IDE Available: TrayIconUpdates*Agregar Nuevo Archivo Add New FileTreeProjectsWidget0Agregar Nuevo DirectorioAdd New FolderTreeProjectsWidgetRAgregar este Proyecto a la Consola Python&Add this Project to the Python ConsoleTreeProjectsWidgetCerrar Proyecto Close ProjectTreeProjectsWidgetCopiar Archivo Copy FileTreeProjectsWidget Copiar Archivo a Copy File toTreeProjectsWidget4Crear '__ini__' CompletadoCreate '__init__' CompleteTreeProjectsWidget Crear INIT fallCreate INIT failTreeProjectsWidget Eliminar Archivo Delete FileTreeProjectsWidget Delete FolderTreeProjectsWidgetLQuiere eliminar el siguiente archivo?:*Do you want to delete the following file: TreeProjectsWidgetRQuiere eliminar el siguiente directorio?:,Do you want to delete the following folder: TreeProjectsWidget"Editar Archivo UI Edit UI FileTreeProjectsWidgetHIngrese el Nuevo Nombre del Archivo:Enter New File Name:TreeProjectsWidget6Ingresar Nombre de Archivo:Enter the File Name:TreeProjectsWidgetBIngrese el Nombre del Directorio:Enter the Folder Name:TreeProjectsWidget(El Archivo ya existeFile Already ExistsTreeProjectsWidget$Nombre de Archivo: File Name:TreeProjectsWidgetNombre Invlido Indalid NameTreeProjectsWidgetRRuta Invlida: el archivo '%s' ya existe.+Invalid Path: the file '%s' already exists.TreeProjectsWidgetMover Archivo Move FileTreeProjectsWidgetNuevo ArchivoNew FileTreeProjectsWidget Nuevo Directorio New FolderTreeProjectsWidget.Propiedades de ProyectoProject PropertiesTreeProjectsWidget$Refrescar ProyectoRefresh ProjectTreeProjectsWidget&Eliminar Directorio Remove FolderTreeProjectsWidgetVEliminar este Proyecto de la Consola Python+Remove this Project from the Python ConsoleTreeProjectsWidget"Renombrar Archivo Rename FileTreeProjectsWidget"Ejecutar Proyecto Run ProjectTreeProjectsWidgetFElegir Proyecto como predeterminadoSet as Main ProjectTreeProjectsWidgettEl nombre de archivo es vaco, por favor escriba un nombre+The file name is empty, please enter a nameTreeProjectsWidgetDescripcin Description TreeResult AtajoShortcut TreeResultAtributos AttributesTreeSymbolsWidgetClassesClassesTreeSymbolsWidgetFunciones FunctionsTreeSymbolsWidgetActualizarUpdate UpdatesWidgetDesacoplarUndock WebInspector VolverBackWizardNewProjectContinuarContinueWizardNewProjectDirectorio no existe o no es un directorio vlido. Si desea setear o modificarlo, vaya a Propiedades de ProyectojFolder don't exists or this is not a valid Folder. If you want to set or modify, go to project propertiesWizardNewProject.Localizacin IncorrectaIncorrect LocationWizardNewProjectJNINJA - Nuevo Asistente para ProyectoNINJA - New Project WizardWizardNewProject<El Proyecto no pudo ser creadoThe project couldn't be createWizardNewProject0Directorio de VirtualEnvVirtualenv FolderWizardNewProject4Agregar Archivo a ProyectoAdd File To Project __ActionsFLneas en blanco y comentadas: %1  Blanks and commented lines: %1  __Actions2Diagrama de Classes v.0.1Class Diagram v.0.1 __ActionsCrear ProfileCreate Profile __Actions(El archivo ya existeFile Already Exists __Actions$Nombre de Archivo: File Name: __ActionsNombre Invlido Indalid Name __ActionsRRuta Invlida: el archivo '%s' ya existe.+Invalid Path: the file '%s' already exists. __Actions(Lneas de cdigo: %1Lines code: %1  __Actions4Nombre de Profile InvlidoProfile Name Invalid __Actions"Resumen de lneasSummary of lines __ActionsLos siguientes archivos y proyectos sern asociados a este profile. Nombre de Profile:PThe Current Files and Projects will be associated to this profile. Profile Name: __ActionsZEl nombre de Profile es invlido o ya existe..The Profile name is invalid or already exists. __Actions^El nombre es vaco, por favor ingrese un nombre+The file name is empty, please enter a name __Actions&Total de lneas: %1Total lines: %1 __ActionsAcoplarDock__ExplorerContainerErroresErrors__ExplorerContainer&Proyecto IncorrectoIncorrect Project__ExplorerContainer8Abrir Directorio de ProyectoOpen Project Directory__ExplorerContainerrSoporte de proyectos fue deshabilitado desde Preferencias2Project support has been disabled from Preferences__ExplorerContainerProyectosProjects__ExplorerContainer0Proyectos DeshabilitadosProjects Disabled__ExplorerContainerSmbolosSymbols__ExplorerContainer@El Proyecto no pudo ser cargado! The project could not be loaded!__ExplorerContainerDesacoplarUndock__ExplorerContainerInspector Web Web Inspector__ExplorerContainer4Inspector Web No SoportadoWeb Inspector not Supported__ExplorerContainerRSu versin de Qt no soporta Inspector Web1Your Qt version doesn't support the Web Inspector__ExplorerContainer2descripcin no disponibleno description available__ExplorerContainer&Acerca de&About__IDE&Editar&Edit__IDE&Archivo&File__IDE&Proyecto&Project__IDE&Fuente&Source__IDE &View&View__IDE:Quiere salir de todas formas?Do you want to exit anyway?__IDEP&luginsP&lugins__IDE<Apretar y Arrastrar para MoverPress and Drag to Move__IDEFAlgunos cambios no fueron guardadosSome changes were not saved__IDE(Solo-Lectura) (Read-Only)__MainContainerEst seguro que desea cerrar el archivo eliminado? El contenido ser eliminado permanentemente.XAre you sure you want to close the deleted file? The content will be completely deleted.__MainContainer0Cerrar Archivo eliminadoClose Deleted File__MainContainer(El Archivo ya existeFile Already Exists__MainContainer(Archivo Guardado: %1File Saved: %1__MainContainer$Archivo IncorrectoIncorrect File__MainContainerRRuta Invlida: el archivo '%s' ya existe.+Invalid Path: the file '%s' already exists.__MainContainerNuevo Documento New Document__MainContainerAbrir Archivo Open File__MainContainer.Documentacin de PythonPython Documentation__MainContainer Error Al Guardar Save Error__MainContainerGuardar Archivo Save File__MainContainer<El archivo no pudo ser abiertoThe file couldn't be open__MainContainer@El archivo no pudo ser guardado!The file couldn't be saved!__MainContainer:La imagen no pudo ser abiertaThe image couldn't be open__MainContainerConsolaConsole__MiscContainer$Buscar en Archivos Find in Files__MiscContainer SalidaOutput__MiscContainer(Previsualizar en Web Web Preview__MiscContainerninja-ide-2.3/ninja_ide/addins/qml/000077500000000000000000000000001216641277400172015ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/addins/qml/Button.qml000066400000000000000000000025471216641277400211770ustar00rootroot00000000000000import QtQuick 1.1 Rectangle { id: button property alias text: txtButton.text signal clicked radius: 10 border.color: "gray" border.width: 2 gradient: Gradient { GradientStop { id: stop1; position: 0.0; color: "#570000" } GradientStop { id: stop2; position: 1.0; color: "#881f1f" } } Text { id: txtButton anchors.centerIn: parent text: button.text color: "white" } states: [ State { name: "ENTERED" PropertyChanges { target: stop1; color: "gray" } }, State { name: "EXITED" PropertyChanges { target: stop1; color: "#570000" } }, State { name: "PRESSED" PropertyChanges { target: stop2; color: "#3d0000" } PropertyChanges { target: stop1; color: "#631717" } }, State { name: "RELEASED" PropertyChanges { target: stop1; color: "#570000" } PropertyChanges { target: stop2; color: "#881f1f" } } ] MouseArea { anchors.fill: parent hoverEnabled: true onEntered: button.state = "ENTERED"; onExited: button.state = "EXITED"; onClicked: button.clicked(); onPressed: button.state = "PRESSED"; onReleased: button.state = "RELEASED"; } } ninja-ide-2.3/ninja_ide/addins/qml/ProjectList.qml000066400000000000000000000101651216641277400221610ustar00rootroot00000000000000import QtQuick 1.1 Rectangle { id: root property alias _projects: listProjects signal markAsFavorite(string path, bool value) signal openProject(string path) signal removeProject(string path) Component { id: contactDelegate Item { id: item width: root.width; height: 50 property string _name: name property string _path: path ListView.onRemove: SequentialAnimation { PropertyAction { target: item; property: "ListView.delayRemove"; value: true } NumberAnimation { target: item; property: "scale"; to: 0; duration: 400; easing.type: Easing.InOutQuad } PropertyAction { target: item; property: "ListView.delayRemove"; value: false } } Image { id: imgFavorite property bool _favorite: favorite source: _favorite ? "img/favorite-project.png" : "img/unfavorite-project.png" anchors.left: parent.left anchors.top: parent.top anchors.leftMargin: 10 anchors.topMargin: (parent.height / 2) - (imgFavorite.height / 2) MouseArea { anchors.fill: parent onClicked: { if(imgFavorite._favorite){ imgFavorite.source = "img/unfavorite-project.png"; imgFavorite._favorite = false; }else{ imgFavorite.source = "img/favorite-project.png"; imgFavorite._favorite = true; } root.markAsFavorite(item._path, imgFavorite._favorite); } } } Rectangle { id: texts color: "transparent" height: item.height width: item.width - imgFavorite.width - imgDelete.width - 60 anchors.left: imgFavorite.right anchors.leftMargin: 10 anchors.top: parent.top anchors.topMargin: 5 Column { id: col anchors.fill: parent Text { text: 'NAME: ' + name; width: texts.width; elide: Text.ElideMiddle } Text { text: 'PATH: ' + path; width: texts.width; elide: Text.ElideMiddle } } MouseArea { anchors.fill: parent onClicked: { var coord = mapToItem(_projects, mouseX, mouseY) var index = _projects.indexAt(coord.x, coord.y); _projects.currentIndex = index; root.openProject(item._path); } } } Image { id: imgDelete source: "img/delete-project.png" anchors.right: parent.right anchors.top: parent.top anchors.rightMargin: 20 anchors.topMargin: (parent.height / 2) - (imgFavorite.height / 2) MouseArea { anchors.fill: parent onClicked: { var coord = mapToItem(_projects, mouseX, mouseY) var index = _projects.indexAt(coord.x, coord.y); _projects.model.remove(index); root.removeProject(item._path); } } } } } ListView { id: listProjects width: root.width height: root.height focus: true model: ListModel {} delegate: contactDelegate highlight: Rectangle { color: "lightsteelblue"; radius: 5; width: (root.width - 10) } Keys.onReturnPressed: { var path = listProjects.model.get(listProjects.currentIndex).path; root.openProject(path); } } function add_project(name, path, favorite){ listProjects.model.append({"name": name, "path": path, "favorite": favorite}); } } ninja-ide-2.3/ninja_ide/addins/qml/StartPage.qml000066400000000000000000000126411216641277400216120ustar00rootroot00000000000000import QtQuick 1.1 Rectangle { id: root property int _padding: (mainArea.width / 4) property bool compressed: true signal markAsFavorite(string pat, bool value) signal openProject(string path) signal removeProject(string path) signal openPreferences gradient: Gradient { GradientStop { position: 0.0; color: "#1e1e1e" } GradientStop { position: 1.0; color: "#a4a4a4" } } onWidthChanged: { if(root.width < 500){ compressed = true; root._padding = (mainArea.width / 2); logo.width = 300; txtProjects.visible = false; projectList.visible = false; }else{ compressed = false; root._padding = (mainArea.width / 4); logo.width = logo.sourceSize.width; txtProjects.visible = true; projectList.visible = true; } } Rectangle { id: mainArea color: "white" anchors.fill: parent radius: 10 anchors.margins: parent.height / 14 smooth: true Image { id: logo source: "img/ninja-ide.png" anchors.left: parent.left anchors.top: parent.top anchors.leftMargin: root.compressed ? 10 : root.get_padding(logo); anchors.topMargin: 10 fillMode: Image.PreserveAspectFit } Text { id: txtWelcome anchors.left: parent.left anchors.top: logo.bottom anchors.leftMargin: root.compressed ? 10 : root.get_padding(txtWelcome); anchors.topMargin: 15 color: "#2f2d2d" text: "Welcome!" font.bold: true font.pointSize: 45 style: Text.Raised styleColor: "black" } Text { id: txtDescription width: compressed ? parent.width - 20 : root._padding * 1.5 anchors.left: parent.left anchors.top: txtWelcome.bottom anchors.leftMargin: root.compressed ? 10 : root.get_padding(txtDescription); anchors.topMargin: 10 text: "NINJA-IDE (from: \"Ninja-IDE Is Not Just Another IDE\"), is a cross-platform integrated development environment specially designed to build Python Applications. NINJA-IDE provides tools to simplify the Python-software development and handles all kinds of situations thanks to its rich extensibility." wrapMode: Text.WordWrap } Column { id: colButtons anchors.top: txtDescription.bottom anchors.left: parent.left anchors.leftMargin: root.compressed ? 10 : root.get_padding(colButtons); anchors.topMargin: root.compressed ? 10 : 50 property int buttonWidth: compressed ? (mainArea.width / 2) - 20 : (mainArea.width / 4) - 50 Row { spacing: 10 Button { width: colButtons.buttonWidth height: 35 text: "Chat with us!" onClicked: Qt.openUrlExternally("https://kiwiirc.com/client/chat.freenode.net/?nick=Ninja|?&theme=cli#ninja-ide") } Button { width: colButtons.buttonWidth height: 35 text: "Preferences" onClicked: openPreferences(); } } } Text { id: txtProjects anchors.right: parent.right anchors.top: parent.top anchors.rightMargin: root.get_padding(txtProjects); anchors.topMargin: 30 color: "#2f2d2d" text: "Recent Projects:" font.bold: true font.pointSize: 30 style: Text.Raised styleColor: "black" } ProjectList { id: projectList width: (parent.width / 2) - 20 height: parent.height - txtProjects.height - 70 anchors.right: parent.right anchors.top: txtProjects.bottom anchors.rightMargin: root.get_padding(projectList) anchors.topMargin: 10 onMarkAsFavorite: root.markAsFavorite(path, value); onOpenProject: root.openProject(path); onRemoveProject: root.removeProject(path); } } Row { spacing: 10 anchors.right: parent.right anchors.top: parent.top anchors.topMargin: 5 anchors.rightMargin: parent.height / 14 Text { text: "Powered by:" color: "white" style: Text.Raised styleColor: "black" height: logoPy.height verticalAlignment: Text.AlignVCenter } Image { id: logoPy source: "img/powered_py.png" } Image { id: logoQt source: "img/powered_qt.png" } } Text { anchors.bottom: parent.bottom anchors.left: parent.left anchors.leftMargin: parent.height / 14 anchors.bottomMargin: 5 color: "black" text: "Copyright © 2011-2013 NINJA-IDE under GPLv3 License agreements" } function get_padding(item){ var newPadding = (root._padding - (item.width / 2)) - 10; return newPadding; } function add_project(name, path, favorite){ projectList.add_project(name, path, favorite); } } ninja-ide-2.3/ninja_ide/addins/qml/img/000077500000000000000000000000001216641277400177555ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/addins/qml/img/delete-project.png000066400000000000000000000064021216641277400233730ustar00rootroot00000000000000PNG  IHDRrP6 OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-bKGD pHYs  tIME4)XP4IDAT8˥jQgfn[)$nR0`2vgpmE] ݸ,m42ˤԐE 2Nq:3||#VUR((0٬(Ff~OExj"n)h4 46 N3xOK~/)uν<]f? | #"&vuuiiiFU/2XU:v#%)o4wpn= EU(7YIIڍ8&9W56.vvv^:q1Z ;ιC*Ix<h4ZSZT*8W5NrHh2|ÐtJt]=k-Zyfvv㣪=<οEU}`\Dsռ;RyamU)"d0ƴ15Ƹ5xEGDc˱JM^M /d0fIENDB`ninja-ide-2.3/ninja_ide/addins/qml/img/favorite-project.png000066400000000000000000000010221216641277400237410ustar00rootroot00000000000000PNG  IHDRܬ.zsRGBbKGD̿ pHYs.#.#x?vtIME6'/ֈtEXtCommentCreated with GIMPWqIDAT(}=hQ%JjT 4YP8;H"".R#*:E] $ Zp1SGE@*BVb(""8brϽw,UQH!X]>"pf\6o8 Ktn 'H 0Mf¥ş TNB(*#flkEߺEyny2FGp 0iH &"OfĪ/3SG(,@`H~@/Czp5x0C:Qb N[:=F;7Jw~}ibI/vo׸=`i/=:¿7zi#wJ0IENDB`ninja-ide-2.3/ninja_ide/addins/qml/img/ninja-ide.png000066400000000000000000000303251216641277400223240ustar00rootroot00000000000000PNG  IHDRJo]ctEXtSoftwareAdobe ImageReadyqe<"iTXtXML:com.adobe.xmp -IIDATx ==cr!R:CIR}STIRNE_ёℨ%R'ܙq>Z{z>ٟw]Z﹭gK,)v%Kޖ,Ydɒ%K,-Ydmɒ% oK,-Ydmɒ% oK,Yx[dے%Kޖ,Yx[dے%Kޖ,Ydɒ%KA1wNNN޹s={RRR2228|% 77 *.]z5k֬W^ʕYt{ٲeƍ[|]ӁnNN.+VlӦwݩS';,#_qnȓFܳϤ>4Axr@GgfqoҒS 'c⢣Vv;l@Ο{x_dgɽiι1 2iaO=0onڵZS1fΧLuаzGR%8;qcy/;: ?^So훺%y&aݦ+ZT\5/:\fw^\ټř>I(q|tLlFjw]hCrKDƣ2,Yֻs믿3݂_ bV)P/]iiQ1%:k%?k»XhVpqca704 EGefyoS֭k»Xh';w{]Ε];gZ}g=~aM/]K6.JI ZlEx's5;Od:1`;;ls.˺Kwoܦ4x,YxU::ٵysB<>lǸu2Αqb|bϨ@?>dgf^ЃM&>rPZTL^G/'֦8<8r>XӒ39X_R[,ɟTKKH;ʈ\L hZɧ{[Vrs8l%K ׫7@72lJJ׮]=z@ѥ5;'ǗkҙEGٳg OHH(_ F8;;{O=T}AGTj,8b̢7{1=zv)&_~~?J k`Ez`O~ϢE-J*uMJJ:쳭*dę-[QjЕ\M $J N 7UGJgO#ovbjON]2H8D!1b?X_hAm`KFKW_}5jժ lقwuTT#p=W6_rIC|"aq;B; ]RRl&իg4ٳ|Xpgnk iGcԎy:U/ (tɺu~w(}J1>q|4,5?p5ƥ1x['ƥq&MB/7GJNC/?sLqp=߿?~B\(7Ge!$[?0(8kD/0*U)DqaQ;/7[єØP5d`tV.(`Gm/=z|a%%/r!CJw}{R?Di~ojzA۶5 <;iRzZyPt>C 9^B- .+gȑ* ¥/x|S:wF0wpz$tQgD8T r g$k tOʁ 뮻X_|݁1F!Ѭ<7D hx֚5kVZg}Q/ᅩM1~۷ڵ_~y/Ҭ zfE߯YS:j٤IѣSƎ[u=׫QO?={aoG=h_9"XrG(q=hޘ!X>Q&#&}P~&Nhv_-FEgʋj]`XZD V;$JП|JNRʛ*>xϚ5 ȉsDD .%VO,Q@%7'@v.{T=x+^Έt;=1HgB 18]]vY.]fϞ/@©ês!pᅕ'L4be:|gmzΒ. mƉ$NAHY7"\"joVܮ| N! Do:BN6-dI޽{:D[|f@Řz ,8jKozxcW~5H(B=ߊ:7+VP D>$EG.zII+_/^ hCHTAPAoop;wUW]wQK*.Z4n2G^ɔ)>w2t+2!ȓ2^ybU2$wmժW%=iRdž 5+W:SS6ƚڼvZDkH郟Opu/ԛ62)۱yDPF\*-2ĺXujtX/$+i:6VC z"]#|̘1+U4gΜիW?x$tAf8)T7rG!q61$O` x[t)Yf4;999\:T7۶mGё;hq$aa(Y/AÚ@(AG})Hb)$m߾]Աtf3J\E.H ~?f;%%kc6Pn|q6vСaFP*VT ?[-s`^.QP,A; ]/?,_|ڵީx6"ʐИ WCe=O>cǎ72eJn.\ȨuL۶m7ln Iðs7|A|ѐU Xɝ6B)B|*BQ˝~ˠ$8@a05j4dB! 2")`!DCrB -}_9is5@LwꫯxaLUk3zGXoU$ҥKs\OڤI`<,kqW,JYJ=ˌ ~;<ަM+3*C>tm; )EYACuj*E?6Pg$5JK XzGAc'S5Ù !&J |^ydK.ؘwƍqopcQR9ӵkUVhwϘ¨l͛Cz{ǎx8_(5jD*Bdɳx4Хg8#yT}vک*(‷J{igItsZ^P%:#?;3l޼J~%^†ćy wS.|z5tut03gͿz;TXk +K+'3hQuƨVLFMɽ^l(q[l{[B\02mDpJMrTzzBw l2~ aIOkjZOf\L 7|+?;H˗[ў^zPQMJ3tD/ P]> զKN<%*Ots'0\龨)i04WUsU7(6 \uڋICQ_׋ 'TGkr w+W6B_\8‹ *s(ۉ!g:_6_ @}'\ꢋ.!b$0!܍p ʿJuV.z墆;t! "kcB i0d2D(4:Z}U2D*fH4MZzG1*SExJOc(\OH+mtn ,UkJL mhj氆/=;t.C 1(Tdʐ](x6ԩӠA|I )MX? ٟ 4SJqŃrI[xDž+.Ȃ"] ^HȄyJPlʕRWD&y%X}\pQ7\Y']lf mذT#/bݻi'2*Uݻs?cX9JbU U7ץ\woN8\xTc'<ʨUZiӦsB+={d2t,akժէOY[VIwg}§g>dλ6I )))ݻwnM6Y$JФL?n_~5g>׬Y¶nբ< ~4ϿۉwD[pT^]|`]2%.t9s nt=n8@ QNVo!R(BDG%1QD)c~\V6 3jި֓xիZ_r5ݺ5YjBBRbJޟ'--i9cpe~{̘1-[$!{ erRO?=*_wHc GyD*u3 (Î0k<7l5rzVV7ݰaڵk4eP.0:\Z6 \Ӑ)zQp9W^֯_dnY7(.M( $iԢO=2''}ƌw9iӾɴWlB2wwڷ Po M5X ׯC)%=zRUW]t9F Bxs̙j@8ι^UH!С=zegJbVu ^0(MWV jZ$HguW۴iyfB/"M6-^X2P:S.\ ÔyMU CGAaE0o|cLL؏ d;z^ doII\rɁn*\4:źLCsk:^Yl얭'Aw._o-~N}ݸq#Z]|j >"߮T%%4JS둒ݻ+WuuVBquJz9ꮐPc'*rnx`0p7>  ;vd\s5 z /#`CxP7ĉ) ;$kf֬YK.H$p@Rx+!r~n:|۴iS'"&񕼤'[Ac0`7|3sΝ; _H{4K& qMsSK__q"dM0Y11[\Oے62x[ʨ07pCyx7;:(1 :Y^/fhU&"[Iz{[opԩS1n 66lXVj[0OIixSlY'Hv -qd"[r}G>|XJ>-G/%#'ڧo%eӥDxj zu]L 8{ŋٓ{8`Po`8C/5jԲu%KSVK^s7hb2Is˙TZq4C='&L2dX>}:"Hxh}`/h,!!'hР.~׬YӿITާC,rUVU[8z" o7,J^]eרۧݺuY&L0QHYË[oU @:Աr; k 2  zcp6~)SCe*j|4 I#o!Rh=W ZG?T=yψG#2:u=8i$4xgo2ь+pIv L$;!ћ'N CY8`jPwݻw8p5krgͪף;NjJuk jë1xwN0ҥX~4Bqy뮻m…x|͚5۳gF])pn/e+DYp=]өS'<-Z b-.׫,zѢEyWbZyS^Įnr+*\}ׯ%JȾ޽(/sNmvx"H+Ed} @m޽9UJFAF-rʓ^2i'!2bdO2q|kU">E/ScРvW_uy#GTO>$/sH2! CҤ+݋,I@!C下c08 *`qY2q9 ڷo$\UrI'|[lYC<իW %%/ݹ_[UvIݖ{r7.~mZ~|rJRe D|n>[v| ̀;b/2(aYXk׮=eќ߬(*:TYSCa1c^r1}W|.(B)'lL3I\wپiN{_߁39pP|zzFb,zJjJԮPnɆ +Wپ]q{$1Þ-iL^mj,᭒d3*TF6!"G FvD=OwopcC5GB_U)?$Y`F(i\揋;С8l^x;'nm-YS;@/ 'dQ?gn@`Ö-jώ%K hsYJ͍9>w5Op2&=J-l8LK ٙG7.ȁuڶ9srrQN 8Eg|Vt_=&9xȉwA~PZT\N`ܭـZRRlnnqqَ V&sO3>IJ䘟G897pbcKW:8^_NKG\Ɍ b|Q)[`K >gq p=vX7;')_dIb񰷿,lc[҉-;5nu~{b+VciRq o5@nnfZZN>^*^[8G vΖ- NR;,*u[ \6p}Q#`j. /tT/j'9 GpsZ^`Ғb YNu<}頻 ӗ2ַ?5՟؟,?pSqrpmsB#Gs.hnҒ%ӏ>yH,4y_n:ը>p2~_T; zQ0fA;潳2z@NZZ7_KK^ʺ;oʔ QƦC7|#ޯoiҒS o]j/7nڱn"(qqavJ̒B3g\t5kRRRe,^&e&%%noРBKN/x+թީĄdҥKەa,d#[mɒ%K,-YtPӓlN.'U.G*o(r?Z""L;.g}E/8Q|lDozbFɂw~̳馓וަKvB1dz L3bROx;.BWgd'*SqszL!e/ p6 @/?СȺ)?溘An b-}IDAThZklTݵvy(`nCCHZ҄j+dvgWTT P_RvwAQH M&)"4"m&$Qhx8dm;v-H#|sSbdx8-C}}}i81<c>`WJ=nݺ D",?(y1f43fF."pe˖-ڰa?ʳ|f("YP|e3&?R isemiiyj8*7[5If~[)Uaߏ1EԪUΈqJRbmڲ"Z[[)"BDwR1zŊ3f?"`1i"wWJD,#/(Ƙf6f^#24IR_sƔ5k7.Ct _Ebb"D&n\PD{Ƙb/x[Dxv0T뺇dJ-KZUJդRu YټyTkDMKJJEnٲeP󩫫^x1>FdDFc*G JeYp8,8=L't_t)%FJRⲲ2)..6J)㺮8븮2qX2aΨofXYpIENDB`ninja-ide-2.3/ninja_ide/addins/qml/img/powered_qt.png000066400000000000000000000046471216641277400226470ustar00rootroot00000000000000PNG  IHDRG~1 sRGBbKGD pHYs B(xtIME,r 'IDAThXmpT~{7MH؅:SH*M4Y2-С:SQ:ֶNN*`vS4 eX):(LBH (4(SI$$B>{e'|lEy{99s/qA碢L4ƥ]߿UӴ-˖-;GNqq⦛nBffyRm[Z8NmD5kt\`w~~>&OafضovEJ^)yժUTr<@DSYYYDuݣ0JKK7UWWϼ5 ~h eA)ӧ# AJf^<(9===Ē%KzԄu]Xx< >y/*,ʪ;wNOONKK#!))% zrmxbMI$i5 #T2a۶ϟAر:t(򐟟#G˲zގ\̜9z+jkkH$N+beBt]FDɆo #KD0? 0^$>@[`Au!"z)|}>.f^}|'<io@6":]׍WUU{PA&"7?0R "̚5 555B͛ǏGmm-ZZZގm۶QϪ^ 㘹}lf ![DdAD1?` J3&x;f^AD?KӃDfDT,Љh]{2szEEoPr ''٨Ć Rbdff"`ڵx}x"G̼W+|Q#3 RJ= `m\}@ 3w2K4M[8`ȑ#htN"2BG(`3` \Ԙ8F8>+: @[[{TD Gn~b%70f !N yJ˛>܌p8{7eeY^pСy˗/쪜F^^ك#Gb vW*1"ѣ؈ I)eY=x)vL:ؔ0'l>.lvmq-<ɫjz6Yv6 }}}m'l>,gvC__k֭W9RhxAA^q;uݍ+W|_KG%[T.IENDB`ninja-ide-2.3/ninja_ide/addins/qml/img/unfavorite-project.png000066400000000000000000000010721216641277400243110ustar00rootroot00000000000000PNG  IHDRܬ.zsRGBbKGD̿ pHYs.#.#x?vtIME6+&`tEXtCommentCreated with GIMPWIDAT(υOHq?l*ӥX(yKvBPD bނ0RPz0 VM$(C/!*>&>yRueHUhǛT", "?", "=", "&" ], "keywords": [ "include", "file", "Const", "Dim", "Option", "Explicit", "Implicit", "Set", "Select", "ReDim", "Preserve", "ByVal", "ByRef", "End", "Private", "Public", "If", "Then", "Else", "ElseIf", "Case", "With", "NOT", "While", "Wend", "For", "Loop", "Do", "Request", "Response", "Server", "ADODB", "Session", "Application", "Each", "In", "Get", "Next", "INT", "CINT", "CBOOL", "CDATE", "CBYTE", "CCUR", "CDBL", "CLNG", "CSNG", "CSTR", "Fix", "Is", "Sgn", "String", "Boolean", "Currency", "Me", "Single", "Long", "Integer", "Byte", "Variant", "Double", "To", "Let", "Xor", "Resume", "On", "Error", "Imp", "GoTo", "Call", "Global", "data", "Null", "Nothing", "And", "False", "True", "var", "Or", "BOF", "EOF", "xor", "Function", "Class", "New", "Sub", "CreateObject", "Write", "Redirect", "Cookies", "BinaryRead", "ClientCertificate", "Form", "QueryString", "ServerVariables", "TotalBytes", "AddHeader", "AppendToLog", "BinaryWrite", "Buffer", "CacheControl", "Charset", "Clear", "ContentType", "Expires", "ExpiresAbsolute", "IsClientConnected", "PICS", "Status", "Connection", "Recordset", "Execute", "Abandon", "Lock", "UnLock", "Command", "Fields", "Properties", "Property", "Send", "Replace", "InStr", "TRIM", "NOW", "Day", "Month", "Hour", "Minute", "Second", "Year", "MonthName", "LCase", "UCase", "Abs", "Array", "As", "LEN", "MoveFirst", "MoveLast", "MovePrevious", "MoveNext", "LBound", "UBound", "Transfer", "Open", "Close", "MapPath", "FileExists", "OpenTextFile", "ReadAll" ] } ninja-ide-2.3/ninja_ide/addins/syntax/assembler.json000066400000000000000000000126731216641277400226170ustar00rootroot00000000000000{ "comment": [ ";" ], "extension": [ "asm" ], "definition": [ "def", "ah", "al", "ax", "bh", "bl", "bp", "bx", "ch", "cl", "cr0", "cr2", "cr3", "cs", "cx", "dh", "di", "dl", "dr0", "dr1", "dr2", "dr3", "dr6", "dr7", "ds", "dx", "eax", "ebp", "ebx", "ecx", "edi", "edx", "es", "esi", "esp", "fs", "gs", "si", "sp", "ss", "st", "tr3", "tr4", "tr5", "tr6", "tr7" ], "string": [ ";" ], "properObject": [ "self" ], "operators": [ "[", "]:", "(", ")", "+", "-", "/", "%", ".", ",", ":" ], "keywords": [ "@b", "@f", "addr", "basic", "byte", "dword", "far", "far16", "fortran", "fword", "near", "near16", "pascal", "qword", "tbyte", "syscall", "sword", "stdcall", "sdword", "sbyte", "real10", "real8", "real4", "vararg", "word", "flat", "near32", "far32", "abs", "all", "assumes", "at", "casemap", "common", "compact", "cpu", "dotname", "emulator", "epilogue", "error", "export", "expr16", "expr32", "farstack", "memory", "medium", "m510", "loadds", "ljmp", "listing", "large", "language", "huge", "forceframe", "nearstack", "nodotname", "noemulator", "nokeyword", "noljmp", "nom510", "nonunique", "nothing", "nosignextend", "noscoped", "noreadonly", "nooldstructs", "nooldmacros", "notpublic", "readonly", "scoped", "setif2", "smallstack", "tiny", "use16", "use32", "uses", "f2xm1", "fabs", "fadd", "faddp", "fbld", "fbstp", "fchs", "fclex", "fcom", "fcomp", "fcompp", "fdecstp", "fidiv", "ficomp", "ficom", "fiadd", "ffree", "feni", "fdivrp", "fdivr", "fdivp", "fdiv", "fdisi", "fidivr", "fild", "fimul", "fincstp", "finit", "fist", "fistp", "fisub", "fisubr", "fld", "fld1", "fldcw", "fldenv", "fldenvw", "fldl2e", "fldl2t", "fldlg2", "fldln2", "fldpi", "fldz", "fmul", "fmulp", "fnclex", "fmulp", "fnstenvw", "fnstsw", "fpatan", "fprem", "fptan", "frndint", "frstor", "frstorw", "fsave", "fsavew", "fscale", "fsqrt", "fst", "fstcw", "fstenv", "fstenvw", "fstp", "fstsw", "fsub", "fsubr", "fsubrp", "ftst", "fwait", "fxam", "fxch", "fxtract", "fyl2x", "fyl2xp1", "fsetpm", "fcos", "fldenvd", "fnsaved", "fnstenvd", "fprem1", "frstord", "fsaved", "fstenvd", "fucom", "fucomp", "fucompp", "aaa", "aad", "aam", "aas", "adc", "add", "and", "call", "cbw", "clc", "cld", "cli", "cmc", "cmp", "cmps", "cmpsb", "cmpsw", "cwd", "daa", "das", "dec", "div", "esc", "hlt", "idiv", "imul", "int", "into", "iret", "ja", "jae", "jb", "jbe", "jc", "jcxz", "je", "jg", "jge", "jl", "jle", "jmp", "jna", "jnae", "jnb", "jnbe", "jnc", "jne", "jng", "jnge", "jnl", "jnle", "jno", "jnp", "jns", "jnz", "loope", "loopew", "loopne", "loopnew", "loopnz", "loopnzw", "loopw", "loopz", "movs", "movsb", "movsw", "mul", "neg", "nop", "not", "or", "out", "pop", "popf", "push", "rcl", "rcr", "ret", "retf", "retn", "rol", "ror", "sahf", "sal", "sar", "sbb", "scas", "scasb", "scasw", "shl", "shr", "stc", "std", "sti", "stos", "stosb", "stosw", "sub", "test", "wait", "xchg", "xlatb", "xor", "bound", "enter", "ins", "insb", "insw", "leave", "outs", "outsb", "outsw", "popa", "pusha", "pushw", "arpl", "lar", "lsl", "sgdt", "sidt", "sldt", "smsw", "str", "verr", "verw", "clts", "lgdt", "lidt", "lldt", "lmsw", "ltr", "bsf", "bsr", "bt", "btc", "btr", "bts", "cdq", "cmpsd", "cwde", "insd", "iretd", "iretdf", "iretf", "jecxz", "lfs", "lgs", "lodsd", "loopd", "looped", "loopned", "loopnzd", "loopzd", "lss", "movsd", "movsx", "movzx", "outsd", "popad", "popfd", "pushad", "pushd", "pushfd", "scasd", "seta", "setae", "setb", "setbe", "setc", "sete", "setg", "setge", "setl", "setle", "setna", "setnae", "setnb", "setnbe", "setnc", "setne", "setng", "setnge", "setnl", "setnle", "setno", "setnp", "setns", "setnz", "seto", "setp", "setpe", "setpo", "sets", "setz", "shld", "shrd", "stosd", "bswap", "cmpxchg", "invd", "invlpg", "wbinvd", "xadd", "lock", "rep", "repe", "repne", "repnz", "repz", "data" ] } ninja-ide-2.3/ninja_ide/addins/syntax/bibtex.json000066400000000000000000000024231216641277400221070ustar00rootroot00000000000000{ "comment": [ "%%" ], "extension": [ "tex" ], "definition": [ "def" ], "string": [ "%%" ], "properObject": [ "@comment", "@preamble", "@string", "@article", "@book", "@booklet", "@conference", "@inbook", "@incollection", "@inproceedings", "@manual", "@mastersthesis", "@misc", "@phdthesis", "@proceedings", "@techreport", "@unpublished", "@collection", "@patent", "@webpage" ], "operators": [ "{", "}", "#", "=", "," ], "keywords": [ "address", "annote", "author", "booktitle", "chapter", "crossref", "edition", "editor", "howpublished", "institution", "journal", "key", "month", "note", "number", "organization", "pages", "publisher", "school", "series", "title", "type", "volume", "year", "abstract", "affiliation", "chaptername", "cited-by", "cites", "contents", "copyright", "date-added", "date-modified", "doi", "eprint", "isbn", "issn", "keywords", "language", "lccn", "lib-congress", "location", "price", "rating", "read", "size", "source", "url" ] } ninja-ide-2.3/ninja_ide/addins/syntax/c.json000066400000000000000000000015721216641277400210600ustar00rootroot00000000000000{ "comment": [ "//" ], "multiline_comment": { "open": "/*", "close": "*/" }, "extension": [ "c" ], "string": [ "\"" ], "operators": [ "=", "==", "!=", "<", "<=", ">", ">=", "\\+", "-", "\\*", "/", "//", "\\%", "\\*\\*", "\\+=", "-=", "\\*=", "/=", "\\%=", "!", "\\^", "\\|", "\\&", "\\~", ">>", "<<" ], "keywords": [ "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while" ] } ninja-ide-2.3/ninja_ide/addins/syntax/coffeescript.json000066400000000000000000000065211216641277400233110ustar00rootroot00000000000000{ "comment": [ "#" ], "extension": [ "coffee" ], "definition": [ "class" ], "string": [ "'", "\"" ], "properObject": [ "this" ], "operators": [ "=", "==", "!=", "<", "<=", ">", ">=", "\\+", "->", "-", "\\*", "/", "//", "\\%", "\\*\\*", "\\+=", "-=", "\\*=", "/=", "\\%=", "\\^", "\\|", "\\&", "\\~", ">>", "<<", ">>>", "++", "--" ], "keywords": [ "true", "false", "null", "this", "new", "delete", "typeof", "in", "instanceof", "return", "throw", "break", "continue", "debugger", "if", "else", "switch", "for", "while", "do", "try", "catch", "finally", "class", "extends", "super", "undefined", "then", "unless", "until", "loop", "of", "by", "when", "and", "or", "is", "isnt", "not", "yes", "no", "on", "off" ], "extras": [ "prototype", "length", "constructor", "Array", "concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueof", "Boolean", "Date", "getDate", "getDay", "getFullYear", "getHours", "getMilliseconds", "getMinutes", "getMonth", "getSeconds", "getTime", "getTimezoneOffset", "getUTCDate", "getUTCDay", "getUTCFullYear", "getUTCHours", "getUTCMilliseconds", "getUTCMinutes", "getUTCMonth", "getUTCSeconds", "parse", "setDate", "setFullYear", "setHours", "setMilliseconds", "setMinutes", "setMonth", "setSeconds", "setTime", "setUTCDate", "setUTCFullYear", "setUTCHours", "setUTCMilliseconds", "setUTCMinutes", "setUTCSeconds", "toDateString", "toISOString", "toJSON", "toLocaleDateString", "toLocaleTimeString", "toLocaleString", "toTimeString", "toUTCString", "UTC", "Math", "E", "LN2", "LN10", "LOG2E", "LOG10E", "SQRT1_2", "SQRT2", "abs", "acos", "asin", "atan", "atan2", "ceil", "cos", "exp", "floor", "log", "max", "min", "pow", "random", "round", "sin", "sqrt", "tan", "Number", "MAX_VALUE", "MIN_VALUE", "NEGATIVE_INFINITY", "NaN", "POSITIVE_INFINITY", "toExponential", "toFixed", "toPrecision", "String", "charAt", "charCodeAt", "concat", "fromCharCode", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "RegExp", "global", "ignoreCase", "lastIndex", "multiline", "source", "compile", "exec", "test", "decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent", "escape", "isFinite", "isNaN", "parseFloat", "parseInt", "unescape", "window", "document", "alert", "clearInterval", "clearTimeout", "confirm", "prompt", "setInterval", "setTimeout" ], "regex": [ ["@\\w+", "properObject", "italic"] ] } ninja-ide-2.3/ninja_ide/addins/syntax/cpp.json000066400000000000000000000026771216641277400214270ustar00rootroot00000000000000{ "comment": [ "//" ], "multiline_comment": { "open": "/*", "close": "*/" }, "extension": [ "cpp" ], "string": [ "\"" ], "properObject": [ "this" ], "definition": [ "#define", "#include" ], "operators": [ "=", "==", "!=", "<", "<=", ">", ">=", "\\+", "-", "\\*", "/", "//", "\\%", "\\*\\*", "\\+=", "-=", "\\*=", "/=", "\\%=", "!", "\\^", "\\|", "\\&", "\\~", ">>", "<<" ], "keywords": [ "auto", "asm", "break", "bool", "case", "char", "catch", "const", "continue", "class", "const_cast", "delete", "dynamic_cast", "default", "do", "double", "else", "explicit", "enum", "extern", "float", "for", "false", "friend", "goto", "if", "int", "inline", "long", "mutable", "namespace", "new", "operator", "register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "template", "throw", "true", "try", "typeid", "typename", "using", "union", "unsigned", "void", "volatile", "virtual", "wchar_t", "public", "private", "protected", "reinterpret_cast", "static_cast", "while" ], "regex": [ ["(\\s)*#(\\w)+", "keyword", "bold"] ] } ninja-ide-2.3/ninja_ide/addins/syntax/csharp.json000066400000000000000000000030271216641277400221130ustar00rootroot00000000000000{ "comment": [ "//" ], "multiline_comment": { "open": "/*", "close": "*/" }, "extension": [ "cs" ], "definition": [ "class" ], "string": [ "\"" ], "properObject": [ "this" ], "operators": [ "=", "==", "!=", "<", "<=", ">", ">=", "\\+", "-", "\\*", "/", "//", "\\%", "\\*\\*", "\\+=", "-=", "\\*=", "/=", "\\%=", "!", "\\^", "\\|", "\\&", "\\~", ">>", "<<" ], "keywords": [ "new", "as", "base", "private", "class", "continue", "protected", "sealed", "public", "catch", "else", "short", "static", "finally", "extern", "implicit", "sizeof", "int", "interface", "long", "in", "out", "stackalloc", "double", "enum", "float", "goto", "explicit", "internal", "structbyte", "case", "char", "const", "default", "do", "event", "foreach", "operator", "unsafe", "abstract", "bool", "break", "delegate", "fixed", "is", "object", "typeof", "ushort", "for", "switch", "if", "lock", "override", "uint", "using", "void", "volatile", "checked", "decimal", "namespace", "sbyte", "ulong", "virtual", "throw", "return", "try", "while", "string", "params", "readonly", "ref", "unchecked", "null", "true", "false" ] } ninja-ide-2.3/ninja_ide/addins/syntax/css.json000066400000000000000000000135301216641277400214230ustar00rootroot00000000000000{ "extension": [ "css", "qss" ], "comment": [ "//" ], "string": [ "\"", "'" ], "operators": [ ":" ], "keywords": [ "html", "body", "head", "azimuth", "background", "background-attachment", "background-color", "background-image", "background-position", "background-repeat", "border", "border-collapse", "border-color", "border-spacing", "border-style", "border-top", "border-right", "border-bottom", "border-left", "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", "border-top-style", "border-right-style", "border-bottom-style", "border-left-style", "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", "border-width", "bottom", "caption-side", "clear", "clip", "color", "content", "counter-increment", "counter-reset", "cue", "cue-after", "cue-before", "cursor", "direction", "display", "elevation", "empty-cells", "float", "font", "font-family", "font-size", "font-size-adjust", "font-style", "font-variant", "font-weight", "height", "left", "letter-spacing", "line-height", "list-style", "list-style-image", "list-style-position", "list-style-type", "margin", "margin-top", "margin-right", "margin-bottom", "margin-left", "marker-offset", "marks", "max-height", "max-width", "min-height", "min-width", "orphans", "outline", "outline-color", "outline-style", "outline-width", "overflow", "padding", "padding-top", "padding-right", "padding-bottom", "padding-left", "page", "page-break-after", "page-break-before", "page-break-inside", "pause", "pause-after", "pause-before", "pitch", "pitch-range", "play-during", "position", "quotes", "richness", "right", "size", "speak", "speak-header", "speak-numerical", "speak-punctuation", "speech-rate", "stress", "table-layout", "text-align", "text-decoration", "text-indent", "text-shadow", "text-transform", "top", "unicode-bidi", "vertical-align", "visibility", "voice-family", "volume", "white-space", "widows", "width", "word-spacing", "z-index" ], "extras": [ "auto", "none", "inherit", "transparent", "hidden", "left", "right", "top", "bottom", "center", "left-side", "far-left", "center-left", "center-right", "far-right", "right-side", "behind", "leftwards", "rightwards", "aqua", "black", "blue", "fuchsia", "gray", "green", "lime", "maroon", "navy", "olive", "purple", "red", "silver", "teal", "white", "yellow", "repeat", "repeat-x", "repeat-y", "no-repeat", "scroll", "fixed", "thin", "medium", "thick", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", "collapse", "separate", "open-quote", "close-quote", "no-open-quote", "no-close-quote", "cue-before", "cue-after", "crosshair", "default", "pointer", "move", "e-resize", "ne-resize", "nw-resize", "n-resize", "se-resize", "sw-resize", "s-resize", "w-resize", "text", "wait", "help", "ltr", "rlt", "inline", "block", "list-item", "run-in", "compact", "marker", "table", "inline-table", "table-row-group", "table-header-group", "table-footer-group", "table-row", "table-column-group", "table-column", "table-cell", "table-caption", "below", "level", "above", "higher", "lower", "show", "hide", "normal", "italic", "oblique", "small-caps", "bold", "bolder", "lighter", "wider", "narrower", "ultra-condensed", "extra-condensed", "condensed", "semi-condensed", "semi-expanded", "expanded", "extra-expanded", "ultra-expanded", "inside", "outside", "disc", "circle", "square", "decimal", "decimal-leading-zero", "lower-roman", "upper-roman", "lower-greek", "lower-alpha", "lower-latin", "upper-alpha", "upper-latin", "hebrew", "armenian", "georgian", "cjk-ideographic", "hiragana", "katakana", "hiragana-iroha", "katakana-iroha", "crop", "cross", "invert", "visible", "scroll", "always", "avoid", "x-low", "low", "medium", "high", "x-high", "mix", "repeat", "static", "relative", "absolute", "fixed", "portait", "landscape", "spell-out", "once", "digits", "continuous", "code", "x-slow", "slow", "fast", "x-fast", "faster", "slower", "justify", "underline", "overline", "line-through", "blink", "capitalize", "uppercase", "lowercase", "embed", "bidi-override", "baseline", "sub", "super", "top", "text-top", "middle", "text-bottom", "silent", "x-soft", "soft", "loud", "x-loud", "pre", "nowrap", "smaller", "larger", "xx-small", "x-small", "small", "large", "x-large", "xx-large", "serif", "sans-serif", "cursive", "fantasy", "monospace" ], "regex": [ ["#(\\w(-*)(_*))+", "properObject"], ["\\.(\\w(-*)(_*))+", "properObject"], ["[uU]+[a-fA-F0-9]{1,6}(-[a-fA-F0-9]{1,6})?", "numbers"], ["[\\+-]?([0-9]+|[0-9]*\\.[0-9]+)(%|e(m|x)|p(x|t|c)|in|ft|(m|c)m|k?Hz|deg|g?rad|m?s)", "operator"] ] } ninja-ide-2.3/ninja_ide/addins/syntax/cython.json000066400000000000000000000027561216641277400221470ustar00rootroot00000000000000{ "comment": [ "#" ], "extension": [ "pyx", "pxd", "pxi" ], "definition": [ "cdef", "class", "ctypedef", "struct" ], "string": [ "'", "\"" ], "properObject": [ "self" ], "operators": [ "=", "==", "!=", "<", "<=", ">", ">=", "\\+", "-", "\\*", "/", "//", "\\%", "\\*\\*", "\\+=", "-=", "\\*=", "/=", "\\%=", "\\^", "\\|", "\\&", "\\~", ">>", "<<" ], "keywords": [ "and", "assert", "break", "by", "class", "continue", "def", "cdef", "ctypedef", "include", "del", "elif", "else", "except", "exec", "extern", "finally", "for", "from", "global", "if", "import", "in", "is", "lambda", "not", "or", "pass", "print", "raise", "return", "super", "struct", "try", "while", "with", "yield", "None", "True", "False", "DEF", "IF", "ELIF", "ELSE" ], "extras": [ "abs", "delattr", "dir", "hasattr", "hash", "intern", "isinstance", "issubclass", "iter", "len", "pow", "reload", "repr", "setattr", "char", "double", "float", "long", "short", "signed", "static", "unsigned", "void" ], "regex": [ ["\\*(\\w)+", "properObject", "italic"], ["<(\\w)+(\\*)?\\>", "properObject", "italic"] ] } ninja-ide-2.3/ninja_ide/addins/syntax/gettext.json000066400000000000000000000011651216641277400223200ustar00rootroot00000000000000{ "comment": [ "#" ], "extension": [ "mo", "po", "pot" ], "definition": [ "class" ], "string": [ "#:", "#.", "#,", "#|", "#" ], "properObject": [ "self" ], "operators": [ "=", "==", "!=", "<", "<=", ">", ">=", "\\+", "-", "\\*", "/", "//", "\\%", "\\*\\*", "\\+=", "-=", "\\*=", "/=", "\\%=", "\\^", "\\|", "\\&", "\\~", ">>", "<<", "AND", "NOT", "OR", "XOR" ], "keywords": [ "msgctxt", "msgid_plural", "msgid", "msgstr" ] } ninja-ide-2.3/ninja_ide/addins/syntax/go.json000066400000000000000000000015531216641277400212420ustar00rootroot00000000000000{ "comment": [ "//" ], "extension": [ "go" ], "definition": [ "struct", "func", "interface", "type" ], "string": [ "\"" ], "properObject": [ "this" ], "operators": [ "=", "==", "!=", "<", "<=", ">", ">=", "\\+", "-", "\\*", "/", "//", "\\%", "\\*\\*", "\\+=", "-=", "\\*=", "/=", "\\%=", "!", "\\^", "\\|", "\\&", "\\~", ">>", "<<" ], "keywords": [ "break", "case", "chan", "const", "continue", "default", "defer", "else", "fallthrough", "for", "go", "goto", "if", "import", "map", "package", "range", "return", "select", "switch", "var", "struct", "interface", "type", "func" ] } ninja-ide-2.3/ninja_ide/addins/syntax/header.json000066400000000000000000000017721216641277400220700ustar00rootroot00000000000000{ "comment": [ "//" ], "multiline_comment": { "open": "/*", "close": "*/" }, "extension": [ "h" ], "string": [ "\"" ], "definition": [ "private:", "public:", "protected:", "class" ], "operators": [ "=", "==", "!=", "<", "<=", ">", ">=", "\\+", "-", "\\*", "/", "//", "\\%", "\\*\\*", "\\+=", "-=", "\\*=", "/=", "\\%=", "!", "\\^", "\\|", "\\&", "\\~", ">>", "<<", "#" ], "keywords": [ "include", "class", "public", "protected", "private", "ifdef", "ifndef", "define", "endif", "import", "pragma", "if", "elif", "auto", "char", "const", "double", "enum", "float", "int", "long", "return", "short", "signed", "static", "struct", "union", "typedef", "unsigned", "void" ], "regex": [ ["<((\\w)+[\\s\\W]*(\\w)+)+>"] ] } ninja-ide-2.3/ninja_ide/addins/syntax/html.json000066400000000000000000000150301216641277400215740ustar00rootroot00000000000000{ "extension": [ "html" ], "string": [ "\"" ], "multiline_comment": { "open": "" }, "operators": [ "=" ], "keywords": [ "a", "abbr", "acronym", "address", "applet", "area", "b", "base", "basefont", "bdo", "big", "blockquote", "body", "br", "button", "caption", "center", "cite", "code", "col", "colgroup", "dd", "del", "dfn", "dir", "div", "dl", "dt", "em", "fieldset", "font", "form", "frame", "frameset", "h[1-6]", "head", "hr", "html", "i", "iframe", "img", "input", "ins", "kbd", "label", "legend", "li", "link", "map", "menu", "meta", "noframes", "noscript", "object", "ol", "optgroup", "option", "p", "param", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "style", "sub", "sup", "table", "tbody", "td", "text", "textarea", "title", "tfoot", "th", "thead", "tr", "tt", "u", "ul", "var" ], "extras": [ "abbr", "accept-charset", "accept", "accesskey", "action", "align", "alink", "alt", "archive", "axis", "background", "bgcolor", "border", "cellpadding", "cellspacing", "char", "charoff", "charset", "checked", "cite", "class", "classid", "clear", "code", "codebase", "codetype", "color", "cols", "colspan", "content", "coords", "data", "datetime", "declare", "defer", "dir", "disabled", "enctype", "face", "for", "frame", "frameborder", "headers", "height", "href", "hreflang", "hspace", "http-equiv", "id", "ismap", "label", "lang", "language", "longdesc", "marginheight", "marginwidth", "maxlength", "media", "method", "multiple", "name", "nohref", "noresize", "noshade", "nowrap", "object", "onblur", "onchange", "onclick", "ondblclick", "onfocus", "onkeydown", "onkeypress", "onkeyup", "onload", "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onreset", "onselect", "onsubmit", "onunload", "profile", "prompt", "readonly", "rel", "rev", "rows", "rowspan", "rules", "scheme", "scope", "scrolling", "selected", "shape", "size", "span", "src", "standby", "start", "style", "summary", "tabindex", "target", "text", "title", "type", "usemap", "valign", "value", "valuetype", "version", "vlink", "vspace", "width" ], "regex": [ ["&\\w+;", "extras", "bold"], ["<", "keyword", "bold"], [">", "keyword", "bold"], ["", "keyword", "bold"], ["\\$\\(\\w+\\)", "properObject", "bold"], ["\\$\\('#\\w+'\\)", "properObject", "bold"], ["function", "properObject"], ["\\{#[^}]*#\\}", "comment", ""], ["\\{\\{[^}]*\\}\\}", "editor-text", "italic"], ["\\{\\{", "operator", "bold italic"], ["\\}\\}", "operator", "bold italic"], ["\\{% ?end\\w* ?%\\}", "keyword", "bold"], ["\\{% ?endblock( \\w+)? ?%\\}", "definition", "bold"], ["\\{% ?block \\w+ ?%\\}", "definition", "bold"], ["\\{% ?cycle", "keyword", "bold"], ["\\{% ?debug ?%\\}", "keyword", "bold"], ["\\{% ?extends", "keyword", "bold"], ["\\{% ?filter", "keyword", "bold"], ["\\{% ?firstof", "keyword", "bold"], ["\\{% ?for", "keyword", "bold"], ["\\{% ?empty ?%\\}", "keyword", "bold"], ["\\{% ?if", "keyword", "bold"], ["\\{% ?else ?%\\}", "keyword", "bold"], ["\\{% ?ifchanged", "keyword", "bold"], ["\\{% ?include", "keyword", "bold"], ["\\{% ?load", "keyword", "bold"], ["\\{% ?now", "keyword", "bold"], ["\\{% ?regroup", "keyword", "bold"], ["\\{% ?spaceless ?%\\}", "keyword", "bold"], ["\\{% ?templatetag", "keyword", "bold"], ["\\{% ?url", "keyword", "bold"], ["\\{% ?widthratio", "keyword", "bold"], ["\\{% ?with", "keyword", "bold"], ["\\|add:", "operator", "bold"], ["\\|addslashes", "operator", "bold"], ["\\|capfirst", "operator", "bold"], ["\\|center:", "operator", "bold"], ["\\|cut:", "operator", "bold"], ["\\|date:", "operator", "bold"], ["\\|default:", "operator", "bold"], ["\\|default_if_none:", "operator", "bold"], ["\\|dictsort:", "operator", "bold"], ["\\|dictsortreversed:", "operator", "bold"], ["\\|divisibleby:", "operator", "bold"], ["\\|escape", "operator", "bold"], ["\\|escapejs", "operator", "bold"], ["\\|filesizeformat", "operator", "bold"], ["\\|first", "operator", "bold"], ["\\|fix_ampersands", "operator", "bold"], ["\\|floatformat:?", "operator", "bold"], ["\\|force_escape", "operator", "bold"], ["\\|get_digit:", "operator", "bold"], ["\\|iriencode", "operator", "bold"], ["\\|join:", "operator", "bold"], ["\\|length", "operator", "bold"], ["\\|length_is:", "operator", "bold"], ["\\|linebreaks", "operator", "bold"], ["\\|linebreaksbr", "operator", "bold"], ["\\|linenumbers", "operator", "bold"], ["\\|ljust:", "operator", "bold"], ["\\|lower", "operator", "bold"], ["\\|make_list", "operator", "bold"], ["\\|phone2numeric", "operator", "bold"], ["\\|pluralize:?", "operator", "bold"], ["\\|pprint", "operator", "bold"], ["\\|random", "operator", "bold"], ["\\|remotetags:", "operator", "bold"], ["\\|rjust:", "operator", "bold"], ["\\|safe", "operator", "bold"], ["\\|safeseq", "operator", "bold"], ["\\|slice:", "operator", "bold"], ["\\|slugify", "operator", "bold"], ["\\|stringformat:", "operator", "bold"], ["\\|striptags", "operator", "bold"], ["\\|time:", "operator", "bold"], ["\\|timesince:?", "operator", "bold"], ["\\|timeuntil:?", "operator", "bold"], ["\\|truncatewords:", "operator", "bold"], ["\\|truncatewords_html:", "operator", "bold"], ["\\|unordered_list", "operator", "bold"], ["\\|upper", "operator", "bold"], ["\\|urlencode:?", "operator", "bold"], ["\\|urlize", "operator", "bold"], ["\\|urlizetrunc:", "operator", "bold"], ["\\|wordcount", "operator", "bold"], ["\\|wordwrap:", "operator", "bold"], ["\\|yesno:", "operator", "bold"] ] } ninja-ide-2.3/ninja_ide/addins/syntax/java.json000066400000000000000000000023311216641277400215510ustar00rootroot00000000000000{ "comment": [ "//" ], "multiline_comment": { "open": "/*", "close": "*/" }, "extension": [ "java" ], "definition": [ "class" ], "string": [ "\"" ], "properObject": [ "this" ], "operators": [ "=", "==", "!=", "<", "<=", ">", ">=", "\\+", "-", "\\*", "/", "//", "\\%", "\\*\\*", "\\+=", "-=", "\\*=", "/=", "\\%=", "!", "\\^", "\\|", "\\&", "\\~", ">>", "<<" ], "keywords": [ "new", "package", "private", "class", "continue", "protected", "public", "catch", "else", "short", "static", "finally", "implements", "instanceof", "int", "interface", "long", "native", "double", "enum", "extends", "final", "float", "goto", "byte", "case", "char", "const", "default", "do", "abstract", "assert", "boolean", "break", "for", "switch", "synchronized", "if", "import", "throws", "transient", "void", "volatile", "throw", "return", "try", "while", "String", "null", "true", "false" ] } ninja-ide-2.3/ninja_ide/addins/syntax/javascript.json000066400000000000000000000071241216641277400230030ustar00rootroot00000000000000{ "comment": [ "//" ], "multiline_comment": { "open": "/*", "close": "*/" }, "extension": [ "js", "qml" ], "definition": [ "function" ], "string": [ "'", "\"" ], "properObject": [ "this" ], "operators": [ "=", "==", "!=", "<", "<=", ">", ">=", "\\+", "-", "\\*", "/", "//", "\\%", "\\*\\*", "\\+=", "-=", "\\*=", "/=", "\\%=", "\\^", "\\|", "\\&", "\\~", ">>", "<<" ], "keywords": [ "null", "true", "false", "function", "var", "const", "break", "case", "continue", "default", "do", "else", "for", "if", "in", "return", "switch", "while", "with", "delete", "instanceof", "new", "throw", "typeof", "void", "try", "catch", "finally", "abstract", "boolean", "byte", "char", "class", "debugger", "double", "enum", "export", "extends", "final", "float", "goto", "implements", "import", "int", "interface", "long", "native", "package", "private", "protected", "public", "short", "static", "super", "synchronized", "throws", "transient", "volatile" ], "extras": [ "Object", "Function", "Array", "String", "Boolean", "Number", "Date", "RegExp", "Error", "EvalError", "RangeError", "SyntaxError", "TypeError", "URIError", "constructor", "prototype", "Infinity", "NaN", "Math", "NEGATIVE_INFINITY", "POSITIVE_INFINITY", "eval", "parseInt", "parseFloat", "isNaN", "isFinite", "encodeURI", "decodeURI", "encodeURIComponent", "decodeURIComponent", "length", "lastIndex", "global", "ignoreCase", "multiline", "source", "name", "message", "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "apply", "call", "concat", "join", "pop", "push", "reverse", "shift", "slice", "sort", "unshift", "fromCharCode", "charAt", "charCodeAt", "indexOf", "lastIndexOf", "localeCompare", "match", "exec", "replace", "search", "split", "substr", "substring", "toLowerCase", "toLocaleLowerCase", "toUpperCase", "toLocaleUpperCase", "toFixed", "toExponential", "toPrecision", "abs", "acos", "asin", "atan", "atan2", "ceil", "cos", "exp", "floor", "log", "max", "min", "pow", "random", "round", "sin", "sqrt", "tan", "parse", "UTC", "toDateString", "toTimeString", "toLocaleString", "toLocaleDateString", "toLocaleTimeString", "getTime", "getFullYear", "getUTCFullYear", "getMonth", "getUTCMonth", "getDate", "getUTCDate", "getDay", "getUTCDay", "getHours", "getUTCHours", "getMinutes", "getUTCMinutes", "getSeconds", "getUTCSeconds", "getMilliseconds", "getUTCMilliseconds", "getTimezoneOffset", "setTime", "setMilliseconds", "setUTCMilliseconds", "setSeconds", "setUTCSeconds", "setMinutes", "setUTCMinutes", "setHours", "setUTCHours", "setDate", "setUTCDate", "setMonth", "setUTCMonth", "setFullYear", "setUTCFullYear", "toUTCString", "escape", "unescape", "getYear", "setYear", "toGMTString" ] } ninja-ide-2.3/ninja_ide/addins/syntax/json.json000066400000000000000000000001501216641277400215760ustar00rootroot00000000000000{ "extension": [ "json" ], "string": [ "\"", "'" ], "operators": [ ":" ] } ninja-ide-2.3/ninja_ide/addins/syntax/latex.json000066400000000000000000000031061216641277400217460ustar00rootroot00000000000000{ "comment": [ "%" ], "extension": [ "tex" ], "operators": [ "&", "\\", "{", "}", "[", "]" ], "keywords": [ "addlinespace", "and", "address", "appendix", "author", "backmatter", "bfseries", "bibitem", "bigskip", "blindtext", "caption", "captionabove", "captionbelow", "cdot", "centering", "chapter", "cite", "color", "colorbox", "date", "dedication", "def", "definecolor", "documentclass", "edef", "else", "email", "emph", "eqref", "extratitle", "fbox", "fi", "flushleft", "flushright", "footnote", "frac", "frontmatter", "graphicspath", "hfill", "hline", "hspace", "huge", "ifx", "include", "includegraphics", "infty", "input", "int", "item", "itemsep", "KOMAoption", "KOMAoptions", "label", "LaTeX", "left", "let", "limits", "listfiles", "listoffigures", "listoftables", "lowertitleback", "mainmatter", "makeatletter", "makeatother", "makebox", "makeindex", "maketitle", "mbox", "mediumskip", "newcommand", "newenvironment", "newpage", "nocite", "nonumber", "pagestyle", "par", "paragraph", "parbox", "parident", "parskip", "partial", "publishers", "raggedleft", "raggedright", "raisebox", "ref", "renewcommand", "renewenvironment", "right", "rule", "section", "setlength", "sffamily", "subject", "subparagraph", "subsection", "subsubsection", "subtitle", "sum", "table", "tableofcontents", "textbf", "textcolor", "textit", "textnormal", "textsuperscript", "texttt", "textwidth", "thanks", "title", "titlehead", "today", "ttfamily", "uppertitleback", "urlstyle", "usepackage", "vspace" ] } ninja-ide-2.3/ninja_ide/addins/syntax/lolcode.json000066400000000000000000000016701216641277400222560ustar00rootroot00000000000000{ "extension": [ "lol" ], "comment": [ "#" ], "operators": [ ".", ",", "?", "!!", "AN", "AND", "NOT", "UP", "YR", "UPPIN", "NERF", "NERFIN", "NERFZ", "SMASHING", "UR", "KINDA", "LIKE", "SAEM", "BIG", "SMALL", "BIGGR", "SMALLR", "BIGGER", "SMALLER", "GOOD", "CUTE", "THAN" ], "keywords": [ "VISIBLE", "HAI", "KTHX", "KTHXBYE", "SMOOSH", "GIMMEH", "PLZ", "ON", "INVISIBLE", "R", "ITZ", "GTFO", "COMPLAIN", "GIMME", "OPEN", "FILE", "I HAS A", "AWSUM THX", "O NOES", "CAN", "HAS", "HAZ", "HOW DOES I", "IF U SAY SO", "FOUND YR", "BORROW", "OWN", "ALONG", "WITH", "WIT", "LOOK", "AT", "AWSUM", "THX", "IZ", "YARLY", "NOWAI", "WTF?", "MEBBE", "OMG", "OMGWTF", "ORLY?", "OF", "NOPE", "SO", "IM", "MAI", "O RLY?", "SUM", "BOTH SAEM", "DIFFRINT", "BOTH", "EITHER", "WON", "DIFF", "PRODUKT", "QUOSHUNT", "MOD", "MKAY", "OK", "THING", "BIGNESS", "IN", "OUTTA", "LOOP", "WHILE" ] } ninja-ide-2.3/ninja_ide/addins/syntax/pascal.json000066400000000000000000000025761216641277400221060ustar00rootroot00000000000000{ "extension": [ "pas" ], "comment": [ "//" ], "operators": [ ",", ":", "=", "+", "-", "*", "/" ], "keywords": [ "absolute","asm","assembler","begin","break","case","catch","cdecl", "const","constructor","default","destructor","div","do","downto", "else","end","except","export","exports","external","far", "finalization","finally","for","forward","function","goto","if", "implementation","in","index","inherited","initialization","inline", "interface","interrupt","label","library","mod","name","not","of", "or","overload","override","private","procedure","program", "property","protected","public","published","raise","repeat", "resourcestring","shl","shr","stdcall","stored","switch","then", "to","try","type","unit","until","uses","var","while","xor", "nil", "false", "true", "abs","and","arc","arctan","blockread","blockwrite","chr","dispose", "cos","eof","eoln","exp","get","ln","new","odd","ord","ordinal", "pred","read","readln","sin","sqrt","succ","write","writeln", "ansistring","array","boolean","byte","bytebool","char","file", "integer","longbool","longint","object","packed","pointer","real", "record","set","shortint","smallint","string","union","word" ] } ninja-ide-2.3/ninja_ide/addins/syntax/php.json000066400000000000000000000030031216641277400214140ustar00rootroot00000000000000{ "comment": [ "//" ], "multiline_comment": { "open": "/*", "close": "*/" }, "extension": [ "php", "php4", "php5" ], "definition": [ "class" ], "string": [ "\"", "'" ], "properObject": [ "$this" ], "operators": [ "=", "==", "!=", "<", "<=", ">", ">=", "\\+", "-", "\\*", "/", "//", "\\%", "\\*\\*", "\\+=", "-=", "\\*=", "/=", "\\%=", "!", "\\^", "\\|", "\\&", "\\~", ">>", "<<" ], "keywords": [ "abstract", "and", "array", "as", "break", "case", "catch", "cfunction", "class", "clone", "const", "continue", "declare", "default", "do", "else", "elseif", "enddeclare", "endfor", "endforeach", "endif", "endswitch", "endwhile", "extends", "final", "for", "foreach", "function", "global", "goto", "if", "implements", "interface", "instanceof", "namespace", "new", "old_function", "or", "private", "protected", "public", "static", "switch", "throw", "return", "try", "while", "use", "var", "while", "xor", "true", "false", "die", "exit", "echo", "empty", "exit", "eval", "include", "include_once", "isset", "list", "require", "require_once", "print", "unset" ], "regex": [ ["\\$(\\w)+"] ] } ninja-ide-2.3/ninja_ide/addins/syntax/python.json000066400000000000000000000037041216641277400221560ustar00rootroot00000000000000{ "comment": [ "#" ], "extension": [ "py", "pyw", "rpy", "tac" ], "definition": [ "def", "class" ], "string": [ "'", "\"" ], "properObject": [ "self" ], "operators": [ "=", "==", "!=", "<", "<=", ">", ">=", "\\+", "-", "\\*", "/", "//", "\\%", "\\*\\*", "\\+=", "-=", "\\*=", "/=", "\\%=", "\\^", "\\|", "\\&", "\\~", ">>", "<<" ], "keywords": [ "and", "assert", "break", "class", "continue", "def", "del", "elif", "else", "except", "exec", "finally", "for", "from", "global", "if", "import", "in", "is", "as", "lambda", "not", "or", "pass", "print", "raise", "return", "super", "try", "while", "with", "yield", "None", "True", "False" ], "extras": [ "abs", "all", "any", "basestring", "bin", "bool", "bytearray", "callable", "chr", "classmethod", "cmp", "compile", "complex", "delattr", "dict", "dir", "divmod", "enumerate", "eval", "execfile", "file", "filter", "float", "format", "frozenset", "getattr", "globals", "hasattr", "hash", "help", "hex", "id", "input", "int", "isinstance", "issubclass", "iter", "len", "list", "locals", "long", "map", "max", "memoryview", "min", "next", "object", "oct", "open", "ord", "pow", "property", "range", "raw_input", "reduce", "reload", "repr", "reversed", "round", "set", "setattr", "slice", "sorted", "staticmethod", "str", "sum", "tuple", "type", "unichr", "unicode", "vars", "xrange", "zip", "apply", "buffer", "coerce", "intern" ] } ninja-ide-2.3/ninja_ide/addins/syntax/rst.json000066400000000000000000000013151216641277400214410ustar00rootroot00000000000000{ "extension": [ "rst" ], "string": [ "\"" ], "keywords": [ ], "regex": [ ["\\.\\. [a-zA-Z0-9_-| ]*::", "definition"], ["::", "definition"], [":[a-zA-Z0-9_- ]+:", "properObject"], ["\\[.*\\]_", "keyword"], ["=+", "keyword"], ["\\*+", "keyword"], ["\\-+", "keyword"], ["\\|#.", "keyword"], ["`.*`_" , "keyword"], [".*__" , "keyword"], ["\\*\\*.*\\*\\*" , "keyword", "bold"], ["\\*.*\\*" , "keyword"], ["``.*``" , "keyword", "italic"], ["^(https?|mailto|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]", "keyword"], ["\\|+", "keyword"], ["_`.*`", "keyword"], ["\\|.*\\|", "keyword"], [".+_", "keyword"] ] } ninja-ide-2.3/ninja_ide/addins/syntax/ruby_on_rails.json000066400000000000000000000370751216641277400235140ustar00rootroot00000000000000{ "extension": [ "rb" ], "definition": [ "def", "class" ], "comment": [ "#" ], "properObject": [ "self" ], "operators": [ "(", ")", "[", "]", "{", "}", "\\%", "&", "\\*", "\\|", "/", "<", ">", "\\+", "\\-", "=>", "<<" ], "keywords": [ "alias", "and", "begin", "break", "case", "defined", "do", "else", "elsif", "end", "return", "ensure", "for", "if", "in", "module", "while", "next", "not", "or", "redo", "rescue", "yield", "retry", "super", "then", "undef", "unless", "until", "when", "BEGIN", "END", "include", "__FILE__", "__LINE__", "false", "nil", "self", "true", "Array", "Float", "Integer", "String", "at_exit", "autoload", "binding", "caller", "catch", "chop", "chop!", "chomp", "chomp!", "eval", "exec", "exit", "exit!", "fail", "fork", "format", "gets", "global_variables", "gsub", "gsub!", "iterator?", "lambda", "load", "local_variables", "loop", "open", "p", "print", "printf", "proc", "putc", "puts", "raise", "rand", "readline", "readlines", "require", "select", "sleep", "split", "sprintf", "srand", "sub", "sub!", "syscall", "system", "trace_var", "trap", "untrace_var", "image_tag", "link_to", "link_to_remote", "javascript_include_tag", "assert_equal", "assert_not_equal", "before_filter", "after_filter", "render", "redirect_to", "hide_action", "render_to_string", "url_for", "controller_name", "controller_class_name", "controller_path", "session", "render_component", "render_component_as_string", "cookie", "layout", "flash", "auto_complete_for", "in_place_editor_for", "respond_to", "paginate", "current_page", "each", "first", "first_page", "last_page", "last", "length", "new", "page_count", "previous", "scaffold", "send_data", "send_file", "deliver", "receive", "error_messages_for", "error_message_on", "form", "input", "stylesheet_link_tag", "stylesheet_path", "content_for", "select_date", "ago", "month", "day", "check_box", "fields_for", "file_field", "form_for", "hidden_field", "text_area", "password_field", "collection_select", "options_for_select", "options_from_collection_for_select", "file_field_tag", "form_for_tag", "hidden_field_tag", "text_area_tag", "password_field_tag", "link_to_function", "javascript_tag", "human_size", "number_to_currency", "pagination_links", "form_remote_tag", "form_remote_for", "submit_to_remote", "remote_function", "observe_form", "observe_field", "remote_form_for", "options_for_ajax", "alert", "call", "assign", "show", "hide", "insert_html", "sortable", "toggle", "visual_effect", "replace", "replace_html", "remove", "save", "save!", "draggable", "drop_receiving", "literal", "draggable_element", "drop_receiving_element", "sortable_element", "content_tag", "tag", "link_to_image", "link_to_if", "link_to_unless", "mail_to", "link_image_to", "button_to", "current_page?", "act_as_list", "act_as_nested", "act_as_tree", "has_many", "has_one", "belongs_to", "has_many_and_belogns_to", "delete", "destroy", "destroy_all", "clone", "deep_clone", "copy", "update", "table_name", "primary_key", "sum", "maximun", "minimum", "count", "size", "after_save", "after_create", "before_save", "before_create", "add_to_base", "errors", "add", "validate", "validates_presence_of", "validates_numericality_of", "validates_uniqueness_of", "validates_length_of", "validates_format_of", "validates_size_of", "to_a", "to_s", "to_xml", "to_i" ], "extras": [ "Abbrev", "ArgumentError", "Base64", "Benchmark", "Benchmark::Tms", "Bignum", "Binding", "CGI", "CGI::Cookie", "CGI::HtmlExtension", "CGI::QueryExtension", "CGI::Session", "CGI::Session::FileStore", "CGI::Session::MemoryStore", "Class", "Comparable", "Complex", "ConditionVariable", "Continuation", "Data", "Date", "DateTime", "Delegator", "Dir", "EOFError", "ERB", "ERB::Util", "Enumerable", "Enumerable::Enumerator", "Errno", "Exception", "FalseClass", "File", "File::Constants", "File::Stat", "FileTest", "FileUtils", "FileUtils::DryRun", "FileUtils::NoWrite", "FileUtils::StreamUtils_", "FileUtils::Verbose", "Find", "Fixnum", "FloatDomainError", "Forwardable", "GC", "Generator", "Hash", "IO", "IOError", "Iconv", "Iconv::BrokenLibrary", "Iconv::Failure", "Iconv::IllegalSequence", "Iconv::InvalidCharacter", "Iconv::InvalidEncoding", "Iconv::OutOfRange", "IndexError", "Interrupt", "Kernel", "LoadError", "LocalJumpError", "Logger", "Logger::Application", "Logger::Error", "Logger::Formatter", "Logger::LogDevice", "Logger::LogDevice::LogDeviceMutex", "Logger::Severity", "Logger::ShiftingError", "Marshal", "MatchData", "Math", "Matrix", "Method", "Module", "Mutex", "NameError", "NameError::message", "NilClass", "NoMemoryError", "NoMethodError", "NotImplementedError", "Numeric", "Object", "ObjectSpace", "Observable", "PStore", "PStore::Error", "Pathname", "Precision", "Proc", "Process", "Process::GID", "Process::Status", "Process::Sys", "Process::UID", "Queue", "Range", "RangeError", "Rational", "Regexp", "RegexpError", "RuntimeError", "ScriptError", "SecurityError", "Set", "Shellwords", "Signal", "SignalException", "SimpleDelegator", "SingleForwardable", "Singleton", "SingletonClassMethods", "SizedQueue", "SortedSet", "StandardError", "StringIO", "StringScanner", "StringScanner::Error", "Struct", "Symbol", "SyncEnumerator", "SyntaxError", "SystemCallError", "SystemExit", "SystemStackError", "Tempfile", "Test::Unit::TestCase", "Test::Unit", "Test", "Thread", "ThreadError", "ThreadGroup", "ThreadsWait", "Time", "TrueClass", "TypeError", "URI", "URI::BadURIError", "URI::Error", "URI::Escape", "URI::FTP", "URI::Generic", "URI::HTTP", "URI::HTTPS", "URI::InvalidComponentError", "URI::InvalidURIError", "URI::LDAP", "URI::MailTo", "URI::REGEXP", "URI::REGEXP::PATTERN", "UnboundMethod", "Vector", "YAML", "ZeroDivisionError", "Zlib", "Zlib::BufError", "Zlib::DataError", "Zlib::Deflate", "Zlib::Error", "Zlib::GzipFile", "Zlib::GzipFile::CRCError", "Zlib::GzipFile::Error", "Zlib::GzipFile::LengthError", "Zlib::GzipFile::NoFooter", "Zlib::GzipReader", "Zlib::GzipWriter", "Zlib::Inflate", "Zlib::MemError", "Zlib::NeedDict", "Zlib::StreamEnd", "Zlib::StreamError", "Zlib::VersionError", "Zlib::ZStream", "ActionController::AbstractRequest", "ActionController::Assertions::DomAssertions", "ActionController::Assertions::ModelAssertions", "ActionController::Assertions::ResponseAssertions", "ActionController::Assertions::RoutingAssertions", "ActionController::Assertions::SelectorAssertions", "ActionController::Assertions::TagAssertions", "ActionController::Base", "ActionController::Benchmarking::ClassMethods", "ActionController::Caching", "ActionController::Caching::Actions", "ActionController::Caching::Actions::ActionCachePath", "ActionController::Caching::Fragments", "ActionController::Caching::Pages", "ActionController::Caching::Pages::ClassMethods", "ActionController::Caching::Sweeping", "ActionController::Components", "ActionController::Components::ClassMethods", "ActionController::Components::InstanceMethods", "ActionController::Cookies", "ActionController::Filters::ClassMethods", "ActionController::Flash", "ActionController::Flash::FlashHash", "ActionController::Helpers::ClassMethods", "ActionController::Integration::Session", "ActionController::IntegrationTest", "ActionController::Layout::ClassMethods", "ActionController::Macros", "ActionController::Macros::AutoComplete::ClassMethods", "ActionController::Macros::InPlaceEditing::ClassMethods", "ActionController::MimeResponds::InstanceMethods", "ActionController::Pagination", "ActionController::Pagination::ClassMethods", "ActionController::Pagination::Paginator", "ActionController::Pagination::Paginator::Page", "ActionController::Pagination::Paginator::Window", "ActionController::Rescue", "ActionController::Resources", "ActionController::Routing", "ActionController::Scaffolding::ClassMethods", "ActionController::SessionManagement::ClassMethods", "ActionController::Streaming", "ActionController::TestProcess", "ActionController::TestUploadedFile", "ActionController::UrlWriter", "ActionController::Verification::ClassMethods", "ActionMailer::Base", "ActionView::Base", "ActionView::Helpers::ActiveRecordHelper", "ActionView::Helpers::AssetTagHelper", "ActionView::Helpers::BenchmarkHelper", "ActionView::Helpers::CacheHelper", "ActionView::Helpers::CaptureHelper", "ActionView::Helpers::DateHelper", "ActionView::Helpers::DebugHelper", "ActionView::Helpers::FormHelper", "ActionView::Helpers::FormOptionsHelper", "ActionView::Helpers::FormTagHelper", "ActionView::Helpers::JavaScriptHelper", "ActionView::Helpers::JavaScriptMacrosHelper", "ActionView::Helpers::NumberHelper", "ActionView::Helpers::PaginationHelper", "ActionView::Helpers::PrototypeHelper", "ActionView::Helpers::PrototypeHelper::JavaScriptGenerator::GeneratorMethods", "ActionView::Helpers::ScriptaculousHelper", "ActionView::Helpers::TagHelper", "ActionView::Helpers::TextHelper", "ActionView::Helpers::UrlHelper", "ActionView::Partials", "ActionWebService::API::Method", "ActionWebService::Base", "ActionWebService::Client::Soap", "ActionWebService::Client::XmlRpc", "ActionWebService::Container::ActionController::ClassMethods", "ActionWebService::Container::Delegated::ClassMethods", "ActionWebService::Container::Direct::ClassMethods", "ActionWebService::Invocation::ClassMethods", "ActionWebService::Scaffolding::ClassMethods", "ActionWebService::SignatureTypes", "ActionWebService::Struct", "ActiveRecord::Acts::List::ClassMethods", "ActiveRecord::Acts::List::InstanceMethods", "ActiveRecord::Acts::NestedSet::ClassMethods", "ActiveRecord::Acts::NestedSet::InstanceMethods", "ActiveRecord::Acts::Tree::ClassMethods", "ActiveRecord::Acts::Tree::InstanceMethods", "ActiveRecord::Aggregations::ClassMethods", "ActiveRecord::Associations::ClassMethods", "ActiveRecord::AttributeMethods::ClassMethods", "ActiveRecord::Base", "ActiveRecord::Calculations::ClassMethods", "ActiveRecord::Callbacks", "ActiveRecord::ConnectionAdapters::AbstractAdapter", "ActiveRecord::ConnectionAdapters::Column", "ActiveRecord::ConnectionAdapters::DB2Adapter", "ActiveRecord::ConnectionAdapters::DatabaseStatements", "ActiveRecord::ConnectionAdapters::FirebirdAdapter", "ActiveRecord::ConnectionAdapters::FrontBaseAdapter", "ActiveRecord::ConnectionAdapters::MysqlAdapter", "ActiveRecord::ConnectionAdapters::OpenBaseAdapter", "ActiveRecord::ConnectionAdapters::OracleAdapter", "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter", "ActiveRecord::ConnectionAdapters::Quoting", "ActiveRecord::ConnectionAdapters::SQLServerAdapter", "ActiveRecord::ConnectionAdapters::SQLiteAdapter", "ActiveRecord::ConnectionAdapters::SchemaStatements", "ActiveRecord::ConnectionAdapters::SybaseAdapter::ColumnWithIdentity", "ActiveRecord::ConnectionAdapters::SybaseAdapterContext", "ActiveRecord::ConnectionAdapters::TableDefinition", "ActiveRecord::Errors", "ActiveRecord::Locking", "ActiveRecord::Locking::Optimistic", "ActiveRecord::Locking::Optimistic::ClassMethods", "ActiveRecord::Locking::Pessimistic", "ActiveRecord::Migration", "ActiveRecord::Observer", "ActiveRecord::Observing::ClassMethods", "ActiveRecord::Reflection::ClassMethods", "ActiveRecord::Reflection::MacroReflection", "ActiveRecord::Schema", "ActiveRecord::Timestamp", "ActiveRecord::Transactions::ClassMethods", "ActiveRecord::Validations", "ActiveRecord::Validations::ClassMethods", "ActiveRecord::XmlSerialization", "ActiveSupport::CachingTools::HashCaching", "ActiveSupport::CoreExtensions::Array::Conversions", "ActiveSupport::CoreExtensions::Array::Grouping", "ActiveSupport::CoreExtensions::Date::Conversions", "ActiveSupport::CoreExtensions::Hash::Conversions", "ActiveSupport::CoreExtensions::Hash::Conversions::ClassMethods", "ActiveSupport::CoreExtensions::Hash::Diff", "ActiveSupport::CoreExtensions::Hash::Keys", "ActiveSupport::CoreExtensions::Hash::ReverseMerge", "ActiveSupport::CoreExtensions::Integer::EvenOdd", "ActiveSupport::CoreExtensions::Integer::Inflections", "ActiveSupport::CoreExtensions::Numeric::Bytes", "ActiveSupport::CoreExtensions::Numeric::Time", "ActiveSupport::CoreExtensions::Pathname::CleanWithin", "ActiveSupport::CoreExtensions::Range::Conversions", "ActiveSupport::CoreExtensions::String::Access", "ActiveSupport::CoreExtensions::String::Conversions", "ActiveSupport::CoreExtensions::String::Inflections", "ActiveSupport::CoreExtensions::String::Iterators", "ActiveSupport::CoreExtensions::String::StartsEndsWith", "ActiveSupport::CoreExtensions::String::Unicode", "ActiveSupport::CoreExtensions::Time::Calculations", "ActiveSupport::CoreExtensions::Time::Calculations::ClassMethods", "ActiveSupport::CoreExtensions::Time::Conversions", "ActiveSupport::Multibyte::Chars", "ActiveSupport::Multibyte::Handlers::UTF8Handler", "Breakpoint", "Builder::BlankSlate", "Builder::XmlMarkup", "Fixtures", "HTML::Selector", "HashWithIndifferentAccess", "Inflector", "Inflector::Inflections", "Mime", "Mime::Type", "OCI8AutoRecover", "TimeZone", "XmlSimple" ] } ninja-ide-2.3/ninja_ide/addins/syntax/shell.json000066400000000000000000000037231216641277400217450ustar00rootroot00000000000000{ "_comment": "This file might not be perfect. Its work in progress. Patches are welcome", "comment": [ "#" ], "extension": [ "sh", "shell", "bash", "ksh" ], "definition": [ "function" ], "string": [ "'", "\"" ], "properObject": [ "---------------" ], "operators": [ "=", "==", "!=", "<", "<=", ">", ">=", "\\+", "-", "\\*", "/", "//", "\\%", "\\*\\*", "\\+=", "-=", "\\*=", "/=", "\\%=", "\\^=", "\\|=", "\\&=", "\\^", "\\|", "\\|\\|", "\\&", "\\&\\&", "\\~", ">>", "<<", "++", "--", "~", "!", "-eq", "-ne", "-lt", "-le", "-gt", "-ge", ">\\&", "<<<" ], "keywords": [ "break", "cd", "chdir", "continue", "eval", "exec", "exit", "kill", "newgrp", "ps", "pwd", "read", "readonly", "return", "shift", "test", "trap", "ulimit", "umask", "wait", "for", "select", "case", "while", "until", "do", "done", "if", "then", "else", "fi", "time" ], "extras": [ "source", "alias", "bg", "bind", "builtin", "caller", "command", "compgen", "complete", "compopt", "declare", "typeset", "dirs", "disown", "echo", "enable", "export", "fc", "fg", "getopts", "hash", "help", "history", "jobs", "let", "local", "logout", "mapfile", "readarray", "popd", "printf", "pushd", "set", "shopt", "suspend", "times", "type", "ulimit", "umask", "unalias", "unset", "true", "false", "ls", "echo", "grep", "awk", "gawk", "sed", "sort", "head", "tail", "tee", "diff", "login", "sudo", "su", "patch", "cat", "touch", "seq" ] } ninja-ide-2.3/ninja_ide/addins/syntax/sourceslist.json000066400000000000000000000013001216641277400232020ustar00rootroot00000000000000{ "comment": [ "#" ], "extension": [ "list" ], "definition": [ "http", "ftp" ], "string": [ "##" ], "properObject": [ "file", "cdrom" ], "operators": [ "/", ":" ], "keywords": [ "deb", "deb-src", "stable", "old-stable", "testing", "testing-proposed-updates", "unstable", "unstable-proposed-updates", "experimental", "security", "volatile", "volatile-sloppy", "partner", "backport", "contrib", "restricted", "main", "preview", "non-free", "commercial", "universe", "multiverse", "extras", "medibuntu", "getdeb" ] } ninja-ide-2.3/ninja_ide/addins/theme/000077500000000000000000000000001216641277400175125ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/addins/theme/ninja_dark.qss000066400000000000000000000206561216641277400223530ustar00rootroot00000000000000QWidget{ background: #333; color: white; } QMenuBar::item { spacing: 3px; padding: 1px 4px; background: #333; border-radius: 4px; } QMenuBar::item:selected { background: #a8a8a8; } QMenuBar::item:pressed { background: #888888; } QToolBar { background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #202020, stop: 1.0 #404040); border: 1px solid transparent; } QToolBar::separator { background: #111; width: 1px; /* when vertical */ height: 1px; /* when horizontal */ } QToolBar#custom { border-radius: 3px; border: 1px solid #222; } PopupCompleter > QListView::item:selected { background: #e4e4e4; } QPushButton#combo_button { border: none; } MiniMap { background-color: rgba(0,0,0,60); border: none; margin-right: 5px; } WebPluginList { background-color: white; border-radius: 5px; color: black; padding-top: 5px; selection-color: blue; selection-background-color: #437DCD; } WebPluginList:Item:hover { color: black; border-style: solid; background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #FAFBFE, stop: 1 #6181E0); } RecentProjectItem > QPushButton { } RecentProjectItem > QLineEdit { background:transparent; border:none; border-radius: 6px; color: black; } TabNavigator > QPushButton { border: 0; padding: 10px 5px; border-radius: 5px; border-bottom: 1px solid #8DB8C6; } TabNavigator > QPushButton:hover { } TabNavigator > QPushButton:pressed { border-bottom: 0; margin-top: 1px; } OutputWidget { font-family: monospace; font-size: 10px; color: black; background-color: white; selection-color: white; selection-background-color: #437DCD; } QScrollBar:vertical{ background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #212121,stop: 1.0 #323232); width: 12px; } QScrollBar:horizontal{ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #212121,stop: 1.0 #323232); height: 12px; } QScrollBar::handle:vertical{ padding: 2px; min-height: 50px; background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #585858,stop: 1.0 #404040); border-radius: 5px; border: 1px solid #191919; } QScrollBar::handle:horizontal{ padding: 2px; min-width: 50px; background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #585858,stop: 1.0 #404040); border-radius: 5px; border: 1px solid #191919; } QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical, QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical, QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal, QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal{ background: none; border: none; } QLineEdit{ color: #444; background: #ddd; border-radius: 6px; padding: 1px 4px 3px 2px; border: 1px solid #ddd; border-left: 2px solid #ddd; border-top: 2px solid #ddd; margin-top: 2px; } QLineEdit:focus { color: #000; border-left-color: #888; border-top-color: #555; } QTabWidget::pane { border-color: #555; border-style: solid; border-width: 1px; background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #60615f, stop: 1.0 #4a4b45); position: absolute; top: -2px; } QTabBar { background-color: #333; border: none; } QTabBar::close-button { } QTabBar::tab { color: #bbb; padding: 4px 8px; border: 1px solid #222; background: #252525; border-radius: 5px; } QTabBar::tab:top, QTabBar::tab:bottom { padding: 6px 8px; border-right: 2px solid #111; } QTabBar::tab:left, QTabBar::tab:right { padding: 8px 6px; border-top: 1px solid #111; } QTabBar::tab:top { border-bottom-right-radius: 0; border-bottom-left-radius: 0; border-bottom: 1px solid transparent; margin: 3px 3px 0 0; } QTabBar::tab:bottom { border-top-right-radius: 0px; border-top-left-radius: 0px; border-top: 1px solid transparent; margin: 0 2px 3px 0; } QTabBar::tab:right { border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-right: 1px solid transparent; margin: 0 0 2px 3px; } QTabBar::tab:left { border-top-left-radius: 0px; border-bottom-left-radius: 0px; border-left: 1px solid transparent; margin: 0 3px 2px 0; } QTabBar::tab:selected { color: #FFA500; } QTabBar::tab:top:hover { border-bottom-color: #FFA500; } QTabBar::tab:top:selected { border-bottom: 2px solid #FFA500; } QTabBar::tab:bottom:selected, QTabBar::tab:bottom:hover { border-top-color: #FFA500; } QTabBar::tab:right:selected, QTabBar::tab:right:hover { border-right-color: #FFA500; } QTabBar::tab:left:selected, QTabBar::tab:left:hover { border-left-color: #FFA500; } QTabBar::tab:hover, QTabBar::tab:focus { color: #fff; } QTabBar::tab:top:first:selected { } QTabBar::tab:bottom:first:selected { } QTabBar::tab:right:first:selected { margin-top: 0; } QTabBar::tab:left:first:selected { margin-top: 0; } QTabBar::tab:top:last:selected { margin-right: 0; /* the last selected tab has nothing to overlap with on the right */ } QTabBar::tab:bottom:last:selected { margin-right: 0; } QTabBar::tab:right:last:selected { margin-bottom: 0; } QTabBar::tab:left:last:selected { margin-bottom: 0; } QTabBar::tab:only-one { margin: 0; /* if there is only one tab, we don't want overlapping margins */ } QTreeWidget{ background: #5e5f5d; color: white; } QLabel, QListWidget { color: #f3f3f3; background: transparent; } QPushButton { background: #97b9c4; border-radius: 5px; padding: 4px 8px; border: 0; margin-left: 5px; color: #000; margin-top: 0px; border-bottom: 2px solid #266d96; } QPushButton:only-one { padding: 4px 15px; } QPushButton:hover { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #b8deea, stop: 1.0 #97b9c4); border-bottom: 2px solid #5D7886; } QPushButton:focus { border: 2px solid #39508c; padding: 3px 7px; } QPushButton:pressed { background: #9DC8D6; border-bottom: 0; border-top: 1px solid #3D8886; margin-top: 2px; } QPushButton#navigation_button { border-style: solid; border-width: 1px; border-color: gray; background: none; padding: 0px; } QPushButton#web_list_button { border: none; background: none } QPushButton#combo_button { border: none; background: none; margin-left: 0px; padding: 0px; } QPushButton#line_button { border: none; background: none; margin-left: 0px; padding: 0px; } QHeaderView::section { background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #616161, stop: 0.5 #505050, stop: 0.6 #434343, stop:1 #656565); color: white; padding-left: 4px; border: 1px solid #6c6c6c; } QHeaderView::section:checked { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #202020, stop: 1.0 #404040); } QTableView QTableCornerButton::section { background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #616161, stop: 0.5 #505050, stop: 0.6 #434343, stop:1 #656565); } QComboBox { border: 1px solid gray; padding: 1px 18px 1px 20px; min-width: 6em; } QComboBox:item:selected { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #888dd9, stop: 1 #6a6ea9); } QListView { show-decoration-selected: 1; } QListView::item:alternate { background: #4d4949; } QListView::item:selected { border: 1px solid #6a6ea9; } QListView::item:selected:!active { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ABAFE5, stop: 1 #8588B2); } QListView::item:selected:active { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #6a6ea9, stop: 1 #888dd9); } QListView::item:hover { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #888dd9, stop: 1 #6a6ea9); } QMenu::item { padding: 2px 25px 2px 20px; border: 1px solid transparent; } QMenu::item:selected { border-color: darkblue; background: rgba(100, 100, 100, 150); } QSpinBox { background : white; color: black; } TabWidget::tab-bar { alignment: left; }ninja-ide-2.3/ninja_ide/addins/theme/ninja_theme.qss000066400000000000000000000057301216641277400225300ustar00rootroot00000000000000/*QToolBar { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #636363,stop: 1.0 #FFFFFF); }*/ QToolBar::separator { border-radius: 10px; background: gray; width: 2px; /* when vertical */ height: 2px; /* when horizontal */ } QToolBar#custom { border-radius: 5px; border-width: 1px; border-color:gray; border-style: solid; } PopupCompleter > QListView::item:selected { background: #e4e4e4; } QPushButton#combo_button { border: none; } MiniMap { border: none; border-left: 1px solid grey; border-top: 1px solid grey; border-bottom: 1px solid grey; margin-right: 5px; } WebPluginList { padding-top: 5px; color: black; background-color: white; selection-color: blue; border-radius: 10px; selection-background-color: #437DCD; } WebPluginList:Item:hover { color: black; border-radius: 10px; border-style: solid; background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #FAFBFE, stop: 1 #6181E0); } RecentProjectItem > QPushButton { background:transparent; border:none; border-radius: 10px; color: black; } RecentProjectItem > QLineEdit { background:transparent; border:none; border-radius: 10px; color: black; } TabNavigator > QPushButton { border:none; } TabNavigator > QPushButton:hover { border-radius: 5px; border-style: solid; background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #0a0b0b, stop: 1 #606161); } TabNavigator > QPushButton:pressed { border-radius: 5px; border-style: solid; background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #606161, stop: 1 #0a0b0b); } OutputWidget { font-family: monospace; font-size: 10; color: black; background-color: white; selection-color: white; selection-background-color: #437DCD; } QScrollBar:vertical{ border-left-width: 1px; border-style:solid; border-left-color: #9c9c9c; background: #fcfcfc; width: 11px; } QScrollBar:horizontal{ border-top-width: 1px; border-style:solid; border-top-color: #9c9c9c; background: #fcfcfc; height:12px; } QScrollBar::handle:vertical{ padding: 2px; min-height: 50px; background: #adadad; border-radius: 5px; border-width: 5px; } QScrollBar::handle:horizontal{ padding: 2px; min-width: 50px; background: #adadad; border-radius: 5px; border-width: 5px; } QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical, QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical, QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal, QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal{ background: none; border: none; } QLineEdit:focus { border-style: solid; border-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #aae3ef, stop: 1.0 #75d8ed); border-width: 2px; border-radius: 1px; } TabWidget::tab-bar { alignment: left; }ninja-ide-2.3/ninja_ide/core/000077500000000000000000000000001216641277400160765ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/core/__init__.py000066400000000000000000000025701216641277400202130ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import ############################################################################### # IMPORT SETTINGS ############################################################################### from ninja_ide.core import settings ############################################################################### # IMPORT CORE FUNCTIONS ############################################################################### from ninja_ide.core.core import run_ninja ############################################################################### # IMPORT OTHER MODULES ############################################################################### from ninja_ide.core import file_manager ninja-ide-2.3/ninja_ide/core/cliparser.py000066400000000000000000000133321216641277400204360ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import sys import ninja_ide try: # For Python2 str = unicode # lint:ok except NameError: # We are in Python3 pass usage = "$python ninja-ide.py " epilog = ("This program comes with ABSOLUTELY NO WARRANTY." "This is free software, and you are welcome to redistribute " "it under certain conditions; for details see LICENSE.txt.") try: import argparse new_parser = True def _get_parser(): global usage global epilog parser = argparse.ArgumentParser(description=usage, epilog=epilog) parser.add_argument('file', metavar='file', type=str, nargs='*', help='A file/s to edit', default=[]) parser.add_argument('-f', '--files', metavar='file', type=str, nargs='+', help='A file/s to edit', default=[]) parser.add_argument('-l', '--lineno', metavar='lineno', type=int, nargs='+', help='Line number for the files to open', default=[]) parser.add_argument('-p', '--project', metavar='project', type=str, nargs='+', help='A project/s to edit', default=[]) parser.add_argument('--plugin', metavar='plugin', type=str, nargs='+', help='A plugin to load', default=[]) parser.add_argument('--loglevel', help="Level to use for logging, " "one of 'DEBUG', 'INFO', 'WARNING', 'ERROR', " "'CRITICAL'", default=None, metavar="loglevel") parser.add_argument('--logfile', help="A file path to log, special " "words STDOUT or STDERR are accepted", default=None, metavar="logfile") return parser except ImportError: import optparse new_parser = False def _resolve_nargs(*opts): final_nargs = 1 for opt in opts: nargs = 0 try: start = sys.argv.index(opt) + 1 for idx, arg in enumerate(sys.argv[start:]): if str(arg).startswith("-"): break nargs += 1 return nargs except ValueError: nargs = 1 if final_nargs < nargs: final_nargs = nargs return final_nargs def _get_parser(): # lint:ok global usage global epilog parser = optparse.OptionParser(usage, version=ninja_ide.__version__, epilog=epilog) parser.add_option("-f", "--file", type="string", action="store", dest="file", default=[], help="A file/s to edit", nargs=_resolve_nargs("-f", "--file")) parser.add_option("-p", "--project", type="string", action="store", dest="project", default=[], help="A project/s to edit", nargs=_resolve_nargs("-p", "--project")) parser.add_option("-l", "--lineno", type="int", action="store", dest="lineno", default=[], help="Line number for the files to open", nargs=_resolve_nargs("-l", "--lineno")) parser.add_option("--plugin", type="string", action="store", dest="plugin", default=[], help="A plugin to load", nargs=_resolve_nargs("--plugin")) return parser def parse(): filenames = None projects_path = None linenos = None extra_plugins = None log_level = None log_file = None try: if new_parser: opts = _get_parser().parse_args() else: opts = _get_parser().parse_args()[0] filenames = opts.file \ if isinstance(opts.file, list) \ else [opts.file] filenames += opts.files \ if hasattr(opts, 'files') \ else [] projects_path = opts.project \ if isinstance(opts.project, list) \ else [opts.project] linenos = opts.lineno \ if hasattr(opts, 'lineno') \ else [opts.lineno] extra_plugins = opts.plugin \ if isinstance(opts.plugin, list) \ else [opts.plugin] log_level = opts.loglevel log_file = opts.logfile except Exception as reason: print("Args couldn't be parsed.") print(reason) return (filenames, projects_path, extra_plugins, linenos, log_level, log_file) ninja-ide-2.3/ninja_ide/core/core.py000066400000000000000000000033661216641277400174100ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import sys import signal from ninja_ide import resources from ninja_ide.core import cliparser def run_ninja(): """First obtain the execution args and create the resources folder.""" signal.signal(signal.SIGINT, signal.SIG_DFL) # Change the process name only for linux yet if sys.platform != 'win32' and sys.platform != 'darwin': try: import ctypes libc = ctypes.CDLL('libc.so.6') procname = 'ninja-ide' libc.prctl(15, '%s\0' % procname, 0, 0, 0) except: print("The process couldn't be renamed'") #Set the application name (filenames, projects_path, extra_plugins, linenos, log_level, log_file) = cliparser.parse() # Create NINJA-IDE user folder structure for plugins, themes, etc resources.create_home_dir_structure() from ninja_ide.tools.logger import NinjaLogger NinjaLogger.argparse(log_level, log_file) # Start the UI from ninja_ide.gui import ide ide.start(filenames, projects_path, extra_plugins, linenos) ninja-ide-2.3/ninja_ide/core/file_manager.py000066400000000000000000000235321216641277400210660ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import unicode_literals import sys import os import re import threading import shutil from PyQt4 import QtCore from ninja_ide.core import settings if sys.version_info.major == 3: python3 = True else: python3 = False #Lock to protect the file's writing operation file_store_content_lock = threading.Lock() class NinjaIOException(Exception): """ IO operation's exception """ pass class NinjaFileExistsException(Exception): """ Try to override existing file without confirmation exception. """ def __init__(self, filename=''): Exception.__init__(self, 'The file already exists.') self.filename = filename def create_init_file(folderName): """Create a __init__.py file in the folder received.""" if not os.path.isdir(folderName): raise NinjaIOException("The destination folder does not exist") name = os.path.join(folderName, '__init__.py') if file_exists(name): raise NinjaFileExistsException(name) f = open(name, 'w') f.flush() f.close() def create_init_file_complete(folderName): """Create a __init__.py file in the folder received. This __init__.py will contain the information of the files inside this folder.""" if not os.path.isdir(folderName): raise NinjaIOException("The destination folder does not exist") patDef = re.compile('^def .+') patClass = re.compile('^class .+') patExt = re.compile('.+\\.py') files = os.listdir(folderName) files = list(filter(patExt.match, files)) files.sort() imports_ = [] for f in files: read = open(os.path.join(folderName, f), 'r') imp = [re.split('\\s|\\(', line)[1] for line in read.readlines() if patDef.match(line) or patClass.match(line)] imports_ += ['from ' + f[:-3] + ' import ' + i for i in imp] name = os.path.join(folderName, '__init__.py') fi = open(name, 'w') for import_ in imports_: fi.write(import_ + '\n') fi.flush() fi.close() def create_folder(folderName, add_init_file=True): """Create a new Folder inside the one received as a param.""" if os.path.exists(folderName): raise NinjaIOException("The folder already exist") os.mkdir(folderName) if add_init_file: create_init_file(folderName) def create_tree_folders(folderName): """Create a group of folders, one inside the other.""" if os.path.exists(folderName): raise NinjaIOException("The folder already exist") os.makedirs(folderName) def folder_exists(folderName): """Check if a folder already exists.""" return os.path.isdir(folderName) def file_exists(path, fileName=''): """Check if a file already exists.""" if fileName != '': path = os.path.join(path, fileName) return os.path.isfile(path) def _search_coding_line(txt): """Search a pattern like this: # -*- coding: utf-8 -*-.""" coding_pattern = "coding[:=]\s*([-\w.]+)" pat_coding = re.search(coding_pattern, txt) if pat_coding and pat_coding.groups()[0] != 'None': return pat_coding.groups()[0] return None def get_file_encoding(content): """Try to get the encoding of the file using the PEP 0263 rules search the first or the second line of the file Returns the encoding or the default UTF-8 """ encoding = None lines_to_check = content.split("\n", 2) for index in range(2): if len(lines_to_check) > index: line_encoding = _search_coding_line(lines_to_check[index]) if line_encoding: encoding = line_encoding break #if not encoding is set then use UTF-8 as default if encoding is None: encoding = "UTF-8" return encoding def read_file_content(fileName): """Read a file content, this function is used to load Editor content.""" try: with open(fileName, 'rU') as f: content = f.read() except IOError as reason: raise NinjaIOException(reason) except: raise return content def get_basename(fileName): """Get the name of a file or folder specified in a path.""" if fileName.endswith(os.path.sep): fileName = fileName[:-1] return os.path.basename(fileName) def get_folder(fileName): """Get the name of the folder containing the file or folder received.""" return os.path.dirname(fileName) def store_file_content(fileName, content, addExtension=True, newFile=False): """Save content on disk with the given file name.""" if fileName == '': raise Exception() ext = (os.path.splitext(fileName)[-1])[1:] if ext == '' and addExtension: fileName += '.py' if newFile and file_exists(fileName): raise NinjaFileExistsException(fileName) try: flags = QtCore.QIODevice.WriteOnly | QtCore.QIODevice.Truncate f = QtCore.QFile(fileName) if settings.use_platform_specific_eol(): flags |= QtCore.QIODevice.Text if not f.open(flags): raise NinjaIOException(f.errorString()) stream = QtCore.QTextStream(f) encoding = get_file_encoding(content) if encoding: stream.setCodec(encoding) encoded_stream = stream.codec().fromUnicode(content) f.write(encoded_stream) f.flush() f.close() except: raise return os.path.abspath(fileName) def open_project(path): """Return a dict structure containing the info inside a folder.""" if not os.path.exists(path): raise NinjaIOException("The folder does not exist") d = {} for root, dirs, files in os.walk(path, followlinks=True): d[root] = [[f for f in files if (os.path.splitext(f.lower())[-1]) in settings.SUPPORTED_EXTENSIONS], dirs] return d def open_project_with_extensions(path, extensions): """Return a dict structure containing the info inside a folder. This function uses the extensions specified by each project.""" if not os.path.exists(path): raise NinjaIOException("The folder does not exist") d = {} for root, dirs, files in os.walk(path, followlinks=True): d[root] = [[f for f in files if (os.path.splitext(f.lower())[-1]) in extensions or '.*' in extensions], dirs] return d def delete_file(path, fileName=None): """Delete the proper file. If fileName is None, path and fileName are joined to create the complete path, otherwise path is used to delete the file.""" if fileName: path = os.path.join(path, fileName) if os.path.isfile(path): os.remove(path) def delete_folder(path, fileName=None): """Delete the proper folder.""" if fileName: path = os.path.join(path, fileName) if os.path.isdir(path): shutil.rmtree(path) def rename_file(old, new): """Rename a file, changing its name from 'old' to 'new'.""" if os.path.isfile(old): if file_exists(new): raise NinjaFileExistsException(new) os.rename(old, new) return new return '' def get_file_extension(fileName): """Get the file extension in the form of: 'py'""" return os.path.splitext(fileName.lower())[-1][1:] def get_file_name(fileName): """Get the file name, without the extension.""" return os.path.splitext(fileName)[0] def get_module_name(fileName): """Get the name of the file without the extension.""" module = os.path.basename(fileName) return (os.path.splitext(module)[0]) def convert_to_relative(basePath, fileName): """Convert a absolut path to relative based on its start with basePath.""" if fileName.startswith(basePath): fileName = fileName.replace(basePath, '') if fileName.startswith(os.path.sep): fileName = fileName[1:] return fileName def create_path(*args): """Join the paths provided in order to create an absolut path.""" return os.path.join(*args) def belongs_to_folder(path, fileName): """Determine if fileName is located under path structure.""" if not path.endswith(os.path.sep): path += os.path.sep return fileName.startswith(path) def get_last_modification(fileName): """Get the last time the file was modified.""" return QtCore.QFileInfo(fileName).lastModified() def has_write_permission(fileName): """Check if the file has writing permissions.""" return os.access(fileName, os.W_OK) def check_for_external_modification(fileName, old_mtime): """Check if the file was modified outside ninja.""" new_modification_time = get_last_modification(fileName) #check the file mtime attribute calling os.stat() if new_modification_time > old_mtime: return True return False def get_files_from_folder(folder, ext): """Get the files in folder with the specified extension.""" try: filesExt = os.listdir(folder) except: filesExt = [] filesExt = [f for f in filesExt if f.endswith(ext)] return filesExt def is_supported_extension(filename, extensions=None): if extensions is None: extensions = settings.SUPPORTED_EXTENSIONS if os.path.splitext(filename.lower())[-1] in extensions: return True return False ninja-ide-2.3/ninja_ide/core/filesystem_notifications/000077500000000000000000000000001216641277400232135ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/core/filesystem_notifications/__init__.py000066400000000000000000000026571216641277400253360ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import sys try: #if sys.platform == 'win32': #from ninja_ide.core.filesystem_notifications import windows #source = windows #elif sys.platform == 'darwin': from ninja_ide.core.filesystem_notifications import darwin source = darwin #elif sys.platform.startswith("linux"): #from ninja_ide.core.filesystem_notifications import linux #source = linux #else: ##Aything we do not have a clue how to handle #from ninja_ide.core.filesystem_notifications import openbsd #source = openbsd except: pass #from ninja_ide.core.filesystem_notifications import openbsd #source = openbsd NinjaFileSystemWatcher = source.NinjaFileSystemWatcher() ninja-ide-2.3/ninja_ide/core/filesystem_notifications/base_watcher.py000066400000000000000000000100371216641277400262150ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . # import os from PyQt4.QtCore import QObject from PyQt4.QtCore import SIGNAL, QThread from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.core.filesystem_notifications.Watcher') DEBUG = logger.debug ADDED = 1 MODIFIED = 2 DELETED = 3 RENAME = 4 REMOVE = 5 def do_stat(file_path): status = None try: status = os.stat(file_path) except OSError: pass return status class SingleFileWatcher(QThread): def __init__(self, callback): self._watches = dict() self._do_run = True self._emit_call = callback super(SingleFileWatcher, self).__init__() def stop_running(self): self._do_run = False def add_watch(self, file_to_watch): status = do_stat(file_to_watch) #only add if the file still exists if (file_to_watch not in self._watches) and status: self._watches[file_to_watch] = do_stat(file_to_watch) elif not status: self._emit_call(DELETED, file_to_watch) def is_empty(self): return len(self._watches) == 0 def del_watch(self, file_to_unwatch): if file_to_unwatch in self._watches: self._watches.pop(file_to_unwatch) def tick(self): keys = list(self._watches.keys()) for each_file in keys: status = do_stat(each_file) if not status: self._emit_call(DELETED, each_file) self.del_watch(each_file) if status.st_mtime > self._watches[each_file].st_mtime: self._emit_call(MODIFIED, each_file) self._watches[each_file] = status def run(self): while self._do_run: self.tick() QThread.msleep(1000) self.deleteLater() class BaseWatcher(QObject): ############################################################################### # SIGNALS # # fileChanged(int, QString) [added, deleted, modified, rename, remove] ############################################################################### def __init__(self): super(BaseWatcher, self).__init__() self._single_file_watcher = None self.allow_kill = True def add_file_watch(self, file_path): if not self._single_file_watcher: self._single_file_watcher = \ SingleFileWatcher(self._emit_signal_on_change) self.connect(self._single_file_watcher, SIGNAL("destroyed(QObject*)"), self.on_destroy) self._single_file_watcher.start() self._single_file_watcher.add_watch(file_path) def remove_file_watch(self, file_path): if self._single_file_watcher: self._single_file_watcher.del_watch(file_path) if self._single_file_watcher.is_empty() and self.allow_kill: self._single_file_watcher.stop_running() self._single_file_watcher.quit() def on_destroy(self): self._single_file_watcher.wait() self._single_file_watcher = None def shutdown_notification(self): if hasattr(self, "_single_file_watcher") and self._single_file_watcher: self._single_file_watcher.stop_running() self._single_file_watcher.quit() def _emit_signal_on_change(self, event, path): DEBUG("About to emit the signal" + repr(event)) #self.emit(SIGNAL("fileChanged(int, QString)"), event, path) ninja-ide-2.3/ninja_ide/core/filesystem_notifications/darwin.py000066400000000000000000000065501216641277400250570ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import #import fsevents #from PyQt4.QtCore import SIGNAL from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.core.filesystem_notifications.darwin') DEBUG = logger.debug from ninja_ide.core.filesystem_notifications import base_watcher ADDED = base_watcher.ADDED DELETED = base_watcher.DELETED REMOVE = base_watcher.REMOVE RENAME = base_watcher.RENAME MODIFIED = base_watcher.MODIFIED class NinjaFileSystemWatcher(base_watcher.BaseWatcher): def __init__(self): super(NinjaFileSystemWatcher, self).__init__() #self.observer = fsevents.Observer() #self.watching_paths = {} #self.event_mapping = { #fsevents.IN_CREATE: ADDED, #fsevents.IN_MODIFY: MODIFIED, #fsevents.IN_DELETE: DELETED, #fsevents.IN_MOVED_FROM: REMOVE, #fsevents.IN_MOVED_TO: ADDED} def shutdown_notification(self): pass #base_watcher.BaseWatcher.shutdown_notification(self) #try: #for path in self.watching_paths: #stream = self.watching_paths[path] #self.observer.unschedule(stream) #except: #logger.debug("Some of the stream could not be unscheduled.") #self.observer.stop() #self.observer.join() def add_watch(self, path): pass #if path not in self.watching_paths: #try: #if isinstance(path, unicode): #path = path.encode('utf-8') #stream = fsevents.Stream(self._emit_signal_on_change, #path, file_events=True) #self.observer.schedule(stream) #self.watching_paths[path] = stream #if not self.observer.is_alive(): #self.observer.start() #except Exception as reason: #print reason #logger.debug("Path could not be added: %r" % path) def remove_watch(self, path): pass #try: #if path in self.watching_paths: #stream = self.watching_paths[path] #self.observer.unschedule(stream) #self.watching_paths.remove(path) #self.observer.join() #if not self.observer.is_alive(): #self.observer = fsevents.Observer() #except: #logger.debug("Stream could not be removed for path: %r" % path) def _emit_signal_on_change(self, event): pass #oper = self.event_mapping.get(event.mask, None) #if oper is None: #return #path = event.name #self.emit(SIGNAL("fileChanged(int, QString)"), oper, path) ninja-ide-2.3/ninja_ide/core/filesystem_notifications/linux.py000066400000000000000000000116371216641277400247340ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import os from PyQt4.QtCore import QThread from pyinotify import ProcessEvent, IN_CREATE, IN_DELETE, IN_DELETE_SELF, \ IN_MODIFY, WatchManager, Notifier, ExcludeFilter from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.core.filesystem_notifications.linux') DEBUG = logger.debug from ninja_ide.core.filesystem_notifications import base_watcher ADDED = base_watcher.ADDED DELETED = base_watcher.DELETED REMOVE = base_watcher.REMOVE RENAME = base_watcher.RENAME MODIFIED = base_watcher.MODIFIED #FIXME: For some reaseon the code below raises an import error with name ADDED #from ninja_ide.core.filesystem_notifications.base_watcher import ADDED, \ # DELETED, REMOVE, RENAME, MODIFIED mask = IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY class NinjaProcessEvent(ProcessEvent): def __init__(self, process_callback): self._process_callback = process_callback ProcessEvent.__init__(self) def process_IN_CREATE(self, event): self._process_callback((ADDED, event.pathname)) def process_IN_DELETE(self, event): self._process_callback((DELETED, event.pathname)) def process_IN_DELETE_SELF(self, event): self._process_callback((DELETED, event.pathname)) def process_IN_MODIFY(self, event): self._process_callback((MODIFIED, event.pathname)) def process_IN_MOVED_TO(self, event): self._process_callback((REMOVE, event.pathname)) def process_IN_MOVED_FROM(self, event): self._process_callback((REMOVE, event.pathname)) def process_IN_MOVE_SELF(self, event): self._process_callback((RENAME, event.pathname)) class QNotifier(QThread): def __init__(self, wm, processor): self.event_queue = list() self._processor = processor self.notifier = Notifier(wm, NinjaProcessEvent( self.event_queue.append)) self.notifier.coalesce_events(True) self.keep_running = True QThread.__init__(self) def run(self): while self.keep_running: try: self.notifier.process_events() except OSError: pass # OSError: [Errno 2] No such file or directory happens e_dict = {} while len(self.event_queue): e_type, e_path = self.event_queue.pop(0) e_dict.setdefault(e_path, []).append(e_type) keys = list(e_dict.keys()) while len(keys): key = keys.pop(0) event = e_dict.pop(key) if (ADDED in event) and (DELETED in event): event = [e for e in event if e not in (ADDED, DELETED)] for each_event in event: self._processor(each_event, key) if self.notifier.check_events(): self.notifier.read_events() self.notifier.stop() class NinjaFileSystemWatcher(base_watcher.BaseWatcher): def __init__(self): self.watching_paths = {} super(NinjaFileSystemWatcher, self).__init__() self._ignore_hidden = ('.git', '.hg', '.svn', '.bzr') def add_watch(self, path): if path not in self.watching_paths: try: wm = WatchManager() notifier = QNotifier(wm, self._emit_signal_on_change) notifier.start() exclude = ExcludeFilter([os.path.join(path, folder) for folder in self._ignore_hidden]) wm.add_watch(path, mask, rec=True, auto_add=True, exclude_filter=exclude) self.watching_paths[path] = notifier except (OSError, IOError): pass #Shit happens, most likely temp file def remove_watch(self, path): if path in self.watching_paths: notifier = self.watching_paths.pop(path) notifier.keep_running = False notifier.quit() def shutdown_notification(self): base_watcher.BaseWatcher.shutdown_notification(self) for each_path in self.watching_paths: notifier = self.watching_paths[each_path] notifier.keep_running = False notifier.quit() ninja-ide-2.3/ninja_ide/core/filesystem_notifications/openbsd.py000066400000000000000000000012321216641277400252150ustar00rootroot00000000000000# -*- coding: utf-8 *-* from ninja_ide.core.filesystem_notifications import base_watcher from PyQt4.QtCore import SIGNAL class NinjaFileSystemWatcher(base_watcher.BaseWatcher): def __init__(self): self.watching_paths = {} super(NinjaFileSystemWatcher, self).__init__() self._ignore_hidden = ('.git', '.hg', '.svn', '.bzr') def add_watch(self, path): pass def remove_watch(self, path): pass def shutdown_notification(self): base_watcher.BaseWatcher.shutdown_notification(self) def _emit_signal_on_change(self, event, path): self.emit(SIGNAL("fileChanged(int, QString)"), event, path) ninja-ide-2.3/ninja_ide/core/filesystem_notifications/windows.py000066400000000000000000000215641216641277400252670ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from threading import Thread import win32con import win32file import win32event import pywintypes import os from ninja_ide.core import file_manager from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.core.filesystem_notifications.windows') DEBUG = logger.debug from ninja_ide.core.filesystem_notifications import base_watcher ADDED = base_watcher.ADDED DELETED = base_watcher.DELETED REMOVE = base_watcher.REMOVE RENAME = base_watcher.RENAME MODIFIED = base_watcher.MODIFIED ACTIONS = { 1: ADDED, 2: DELETED, 3: MODIFIED, 4: RENAME, 5: RENAME } # Thanks to Claudio Grondi for the correct set of numbers FILE_LIST_DIRECTORY = 0x0001 watchmask = (win32con.FILE_NOTIFY_CHANGE_FILE_NAME | win32con.FILE_NOTIFY_CHANGE_LAST_WRITE | win32con.FILE_NOTIFY_CHANGE_DIR_NAME) def listdir(path): fdict = file_manager.open_project(path) for each_folder in fdict: files, folders = fdict[each_folder] yield each_folder for each_file in files: yield os.path.join(each_folder, each_file) #Credit on this workaround for the shortsightness of windows developers goes #to Malthe Borch http://pypi.python.org/pypi/MacFSEvents class FileEventCallback(object): def __init__(self, callback, paths): self.snapshots = {} for path in paths: self.snapshot(path) self.callback = callback self._path_last_time = {} self.cookie = 0 def pulentastack(self, path): return os.stat(path) def __call__(self, paths): events = [] deleted = {} for path in sorted(paths): path = path.rstrip('/') snapshot = self.snapshots[path] current = {} try: for name in listdir(path): try: current[name] = self.pulentastack(os.path.join(path, name)) except OSError: pass except OSError: # recursive delete causes problems with path being non-existent pass observed = set(current) for name, snap_stat in list(snapshot.items()): filename = os.path.join(path, name) if name in observed: stat = current[name] if stat.st_mtime > snap_stat.st_mtime: events.append((MODIFIED, filename)) observed.discard(name) else: event = (DELETED, filename) deleted[snap_stat.st_ino] = event events.append(event) for name in observed: if name != path: stat = current[name] filename = os.path.join(path, name) event = deleted.get(stat.st_ino) if event is not None: event = (REMOVE, filename) else: event = (ADDED, filename) if os.path.isdir(filename): self.snapshot(filename) events.append(event) snapshot.clear() snapshot.update(current) for event in events: self.callback(*event) def snapshot(self, path): path = os.path.realpath(path) refs = self.snapshots refs[path] = {} for root, dirs, files in os.walk(path): entry = refs[root] for filename in files: try: entry[filename] = self.pulentastack(os.path.join(root, filename)) except OSError: continue for directory in dirs: refs[os.path.join(root, directory)] = {} if os.path.isdir(path): refs[os.path.join(root, path)] = {} for name in listdir(os.path.join(root, path)): try: refs[path][name] = self.pulentastack(os.path.join(path, name)) except OSError: pass class ThreadedFSWatcher(Thread): def __init__(self, path, callback): self._watch_path = path self._callback = callback # FileEventCallback(callback, (path, )) self._windows_sucks_flag = True self._wait_stop = win32event.CreateEvent(None, 0, 0, None) self._overlapped = pywintypes.OVERLAPPED() self._overlapped.hEvent = win32event.CreateEvent(None, 0, 0, None) super(ThreadedFSWatcher, self).__init__() def stop(self): self._windows_sucks_flag = False win32event.SetEvent(self._wait_stop) def run(self): hDir = win32file.CreateFileW(self._watch_path, FILE_LIST_DIRECTORY, win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE, None, win32con.OPEN_EXISTING, win32con.FILE_FLAG_BACKUP_SEMANTICS | win32con.FILE_FLAG_OVERLAPPED, None ) while self._windows_sucks_flag: buf = win32file.AllocateReadBuffer(1024) win32file.ReadDirectoryChangesW( hDir, buf, True, win32con.FILE_NOTIFY_CHANGE_FILE_NAME | win32con.FILE_NOTIFY_CHANGE_DIR_NAME | win32con.FILE_NOTIFY_CHANGE_SIZE | win32con.FILE_NOTIFY_CHANGE_LAST_WRITE, self._overlapped ) result_stack = {} rc = win32event.WaitForMultipleObjects((self._wait_stop, self._overlapped.hEvent), 0, win32event.INFINITE) if rc == win32event.WAIT_OBJECT_0: # Stop event break data = win32file.GetOverlappedResult(hDir, self._overlapped, True) # lets read the data and store it in the results results = win32file.FILE_NOTIFY_INFORMATION(buf, data) for action, afile in results: if action in ACTIONS: full_filename = os.path.join(self._watch_path, afile) result_stack.setdefault(full_filename, []).append(ACTIONS.get(action)) keys = list(result_stack.keys()) while len(keys): key = keys.pop(0) event = result_stack.pop(key) if (ADDED in event) and (DELETED in event): event = [e for e in event if e not in (ADDED, DELETED)] noticed = [] for each_event in event: if each_event not in noticed: self._callback(each_event, full_filename) noticed.append(each_event) class NinjaFileSystemWatcher(base_watcher.BaseWatcher): def __init__(self): super(NinjaFileSystemWatcher, self).__init__() # do stuff self.watching_paths = {} def add_watch(self, path): if path not in self.watching_paths: watch = ThreadedFSWatcher(path, self._emit_signal_on_change) watch.start() self.watching_paths[path] = watch # Add real watcher using platform specific things def remove_watch(self, path): if path in self.watching_paths: self.watching_paths[path].stop() self.watching_paths[path].join() del(self.watching_paths[path]) # Remove real watcher using platform specific things def shutdown_notification(self): base_watcher.BaseWatcher.shutdown_notification(self) for each_path in self.watching_paths: each_path = self.watching_paths[each_path] each_path.stop() each_path.join() ninja-ide-2.3/ninja_ide/core/ipc.py000066400000000000000000000034741216641277400172330ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import os from PyQt4.QtNetwork import QLocalSocket file_delimiter = '<-nf>' project_delimiter = '<-np>' def is_running(): local_socket = QLocalSocket() local_socket.connectToServer("ninja_ide") if local_socket.state(): result = (True, local_socket) else: result = (False, local_socket) return result def send_data(socket, filenames, projects_path, linenos): global file_delimiter global project_delimiter file_with_nro = ['%s:%i' % (os.path.abspath(f[0]), f[1] - 1) for f in zip(filenames, linenos)] file_without_nro = ['%s:%i' % (os.path.abspath(f), 0) for f in filenames[len(linenos):]] filenames = file_with_nro + file_without_nro files = file_delimiter.join(filenames) projects = project_delimiter.join(projects_path) data = files + project_delimiter + projects data_sended = False try: result = socket.write(data) socket.flush() socket.close() if result >= 0: data_sended = True except: data_sended = False return data_sended ninja-ide-2.3/ninja_ide/core/plugin.py000066400000000000000000000035231216641277400177510ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import os import sys from PyQt4.QtCore import QObject from ninja_ide.tools.logger import NinjaLogger class Plugin(QObject): ''' Base class for ALL Plugin All plugins should inherit from this class ''' def __init__(self, locator, metadata=None): QObject.__init__(self) self.locator = locator if metadata is None: self.metadata = {} else: self.metadata = metadata klass = self.__class__ plugin_name = "%s.%s" % (klass.__module__, klass.__name__) self.logger = NinjaLogger('ninja_ide.plugins.%s' % plugin_name) #set the path! try: self_module = self.__module__ path = os.path.abspath(sys.modules[self_module].__file__) self._path = os.path.dirname(path) except: self._path = '' def initialize(self): """The initialization of the Plugin should be here.""" self.logger.info("Initializing Plugin...") def finish(self): pass def get_preferences_widget(self): pass @property def path(self): return self._path ninja-ide-2.3/ninja_ide/core/plugin_interfaces.py000066400000000000000000000065671216641277400221670ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . ############################################################################### # ABSTRACT CLASSES (This file contains useful interfaces for plugins) #We know, Python does not need interfaces, but this file is useful as #documentation. Is not mandatory inherit from these interfaces but you SHOULD #implement the methods inside them. ############################################################################### class MethodNotImplemented(Exception): pass def implements(iface): """ A decorator to check if interfaces are correctly implmented #TODO: check if functions parameters are correct """ def implementsIA(cls, *args, **kwargs): """ Find out which methods should be and are not in the implementation of the interface, raise errors if class is not correctly implementing. """ should_implement = set(dir(iface)).difference(set(dir(object))) should_implement = set(should for should in should_implement if not should.startswith("_")) not_implemented = should_implement.difference(set(dir(cls))) if len(not_implemented) > 0: raise MethodNotImplemented("Methods %s not implemented" % ", ".join(not_implemented)) if cls.__name__ not in globals(): #if decorated a class is not in globals globals()[cls.__name__] = cls return cls return implementsIA class IProjectTypeHandler(object): """ Interface to create a Project type handler """ #mandatory def get_pages(self): """ Returns a collection of QWizardPage """ pass #mandatory def on_wizard_finish(self, wizard): """ Called when the user finish the wizard @wizard: QWizard instance """ pass def get_context_menus(self): """" Returns a iterable of QMenu """ pass class ISymbolsHandler: """ Interface to create a symbol handler EXAMPLE: { 'attributes': {name: line, name: line}, 'functions': {name: line, name: line}, 'classes': { name: (line, { 'attributes': {name: line}, 'function': {name: line}} ) } } """ #mandatory def obtain_symbols(self, source): """ Returns the dict needed by the tree @source: Source code in plain text """ pass class IPluginPreferences: """ Interface for plugin preferences widget """ #mandatory def save(self): """ Save the plugin data as NINJA-IDE settings """ pass ninja-ide-2.3/ninja_ide/core/plugin_manager.py000066400000000000000000000462501216641277400214470ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import os import sys import shutil import copy import zipfile import traceback #lint:disable try: from urllib.request import urlopen from urllib.error import URLError except ImportError: from urllib2 import urlopen from urllib2 import URLError #lint:enable from ninja_ide import resources from ninja_ide.tools.logger import NinjaLogger from ninja_ide.tools import json_manager logger = NinjaLogger('ninja_ide.core.plugin_manager') REQUIREMENTS = 'requirements.txt' COMMAND_FOR_PIP_INSTALL = 'pip install -r %s' try: # For Python2 str = unicode # lint:ok except NameError: # We are in Python3 pass class ServiceLocator(object): ''' Hold the services and allows the interaction between NINJA-IDE and plugins ''' def __init__(self, services=None): self.__services = services if services else {} def get_service(self, name): return self.__services.get(name) def get_availables_services(self): return list(self.__services.keys()) ''' NINJA-IDE Plugin my_plugin.plugin { "module": "my_plugin", "class": "MyPluginExample", "authors": "Martin Alderete ", "version": "0.1", "description": "Este plugin es de prueba" } class MyPluginExample(Plugin): def initialize(self): #Configure the plugin using the NINJA-IDE API!!! self.editor_s = self.service_locator.get_service('editor') self.toolbar_s = self.service_locator.get_service('toolbar') self.toolbar_s.add_action(QAction(...........)) self.appmenu_s = self.service_locator.get_service('appmenu') self.appmenu_s.add_menu(QMenu(......)) #connect events! self.editor_s.editorKeyPressEvent.connect(self.my_plugin_key_pressed) def my_plugin_key_pressed(self, ...): print 'se apreto alguna tecla en el ide...' ''' ############################################################################### # NINJA-IDE Plugin Manager ############################################################################### class PluginManagerException(Exception): pass #Singleton __pluginManagerInstance = None def PluginManager(*args, **kw): global __pluginManagerInstance if __pluginManagerInstance is None: __pluginManagerInstance = __PluginManager(*args, **kw) return __pluginManagerInstance #Extension of the NINJA-IDE plugin PLUGIN_EXTENSION = '.plugin' class __PluginManager(object): ''' Plugin manager allows to load, unload, initialize plugins. ''' def __init__(self, plugins_dir, service_locator): ''' @param plugins_dir: Path to search plugins. @param service_loctor: ServiceLocator object. ''' self._service_locator = service_locator #new! self._plugins_by_dir = {} #add all the plugins paths for path in self.__create_list(plugins_dir): self.add_plugin_dir(path) #end new! #self._plugins_dir = plugins_dir self._errors = [] #found plugins #example: ["logger", "my_plugin"] self._found_plugins = [] #active plugins #example: {"logger": (LoggerIntance, metadata), # "my_plugin": (MyPluginInstance, metadata)} self._active_plugins = {} def __create_list(self, obj): if isinstance(obj, (list, tuple)): return obj #string then returns a list of one item! return [obj] def add_plugin_dir(self, plugin_dir): ''' Add a new directory to search plugins. @param plugin_dir: absolute path. ''' if not plugin_dir in self._plugins_by_dir: self._plugins_by_dir[plugin_dir] = [] def get_actives_plugins(self): import warnings warnings.warn("Deprecated in behalf of a TYPO free method name") return self.get_active_plugins() def get_active_plugins(self): ''' Returns a list the instances ''' return [plugin[0] for plugin in list(self._active_plugins.values())] def _get_dir_from_plugin_name(self, plugin_name): ''' Returns the dir of the plugin_name ''' for dir_, plug_names in list(self._plugins_by_dir.items()): if plugin_name in plug_names: return dir_ def __getitem__(self, plugin_name): ''' Magic method to get a plugin instance from a given name. @Note: This method has the logic below. Check if the plugin is known, if it is active return it, otherwise, active it and return it. If the plugin name does not exist raise KeyError exception. @param plugin_name: plugin name. @return: Plugin instance or None ''' global PLUGIN_EXTENSION ext = PLUGIN_EXTENSION if not plugin_name.endswith(ext): plugin_name += ext if plugin_name in self._found_plugins: if not plugin_name in self._active_plugins: dir_ = self._get_dir_from_plugin_name(plugin_name) self.load(plugin_name, dir_) return self._active_plugins[plugin_name][0] raise KeyError(plugin_name) def __contains__(self, plugin_name): ''' Magic method to know whether the PluginManager contains a plugin with a given name. @param plugin_name: plugin name. @return: True or False. ''' return plugin_name in self._found_plugins def __iter__(self): ''' Magic method to iterate over all the plugin's names. @return: iterator. ''' return iter(self._found_plugins) def __len__(self): ''' Magic method to know the plugins quantity. @return: length. ''' return len(self._found_plugins) def __bool__(self): ''' Magic method to indicate that any instance must pass the if conditional if x: ''' return True def get_plugin_name(self, file_name): ''' Get the plugin's name from a file name. @param file_name: A file object name. @return: A plugin name from a file. ''' plugin_file_name, file_ext = os.path.splitext(file_name) return plugin_file_name def list_plugins(self, dir_name): ''' Crawl a directory and collect plugins. @return: List with plugin names. ''' global PLUGIN_EXTENSION ext = PLUGIN_EXTENSION try: listdir = os.listdir(dir_name) return [plug for plug in listdir if plug.endswith(ext)] except OSError: return () def is_plugin_active(self, plugin_name): ''' Check if a plugin is or not active @param plugin_name: Plugin name to check. @return: True or False ''' return plugin_name in self._active_plugins def discover(self): ''' Search all files for directory and get the valid plugin's names. ''' for dir_name in self._plugins_by_dir: for file_name in self.list_plugins(dir_name): plugin_name = file_name if not plugin_name in self._found_plugins: self._found_plugins.append(plugin_name) self._plugins_by_dir[dir_name].append(plugin_name) def _load_module(self, module, klassname, metadata, dir_name): old_syspath = copy.copy(sys.path) try: sys.path.insert(1, dir_name) module = __import__(module, globals(), locals(), []) klass = getattr(module, klassname) #Instanciate the plugin plugin_instance = klass(self._service_locator, metadata=metadata) #return the plugin instance return plugin_instance except(ImportError, AttributeError) as reason: raise PluginManagerException('Error loading "%s": %s' % (module, reason)) finally: sys.path = old_syspath return None def load(self, plugin_name, dir_name): global PLUGIN_EXTENSION if plugin_name in self._active_plugins: return for dir_name, plugin_list in list(self._plugins_by_dir.items()): if plugin_name in plugin_list: ext = PLUGIN_EXTENSION plugin_filename = os.path.join(dir_name, plugin_name) plugin_structure = json_manager.read_json(plugin_filename) plugin_structure['name'] = plugin_name.replace(ext, '') module = plugin_structure.get('module', None) klassname = plugin_structure.get('class', None) if module is not None and klassname is not None: try: plugin_instance = self._load_module(module, klassname, plugin_structure, dir_name) #set a get_plugin method to get the reference to other #setattr(plugin_instance,'get_plugin',self.__getitem__) #call a special method *initialize* in the plugin! plugin_instance.metadata = plugin_structure logger.info("Calling initialize (%s)", plugin_name) plugin_instance.initialize() #tuple (instance, metadata) plugin_metadata = (plugin_instance, plugin_structure) self._active_plugins[plugin_name] = plugin_metadata except (PluginManagerException, Exception) as reason: logger.error("Not instanciated (%s): %s", plugin_name, reason) #remove the plugin because has errors self._found_plugins.remove(plugin_name) traceback_msg = traceback.format_exc() plugin_name = plugin_name.replace(ext, '') #add the traceback to errors self._add_error(plugin_name, traceback_msg) else: logger.info("Successfuly initialized (%s)", plugin_name) def load_all(self): for dir, pl in list(self._plugins_by_dir.items()): #Copy the list because may be we REMOVE item while iterate! found_plugins_aux = copy.copy(pl) for plugin_name in found_plugins_aux: self.load(plugin_name, dir) def load_all_external(self, plugin_path): #Copy the list because may be we REMOVE item while iterate! found_plugins_aux = copy.copy(self._found_plugins) for plugin_name in found_plugins_aux: self.load(plugin_name, plugin_path) def unload(self, plugin_name): try: plugin_object = self._active_plugins[plugin_name][0] #call a special method *finish* in the plugin! plugin_object.finish() del self._active_plugins[plugin_name] except Exception as reason: logger.error("Finishing plugin (%s): %s", plugin_name, reason) else: logger.info("Successfuly finished (%s)", plugin_name) def unload_all(self): #Copy the list because may be we REMOVE item while iterate! active_plugins_aux = copy.copy(self._active_plugins) for plugin_name in active_plugins_aux: self.unload(plugin_name) def shutdown(self): self.unload_all() def get_availables_services(self): """ Returns all services availables """ self._service_locator.get_availables_services() def _add_error(self, plugin_name, traceback_msg): self._errors.append((plugin_name, traceback_msg)) @property def errors(self): """ Returns a comma separated values of errors """ return self._errors def _availables_plugins(url): """ Return the availables plugins from an url in NINJA-IDE web page """ try: descriptor = urlopen(url) plugins = json_manager.read_json_from_stream(descriptor) return plugins except URLError: return {} def available_oficial_plugins(): ''' Returns a dict with OFICIAL availables plugins in NINJA-IDE web page ''' return _availables_plugins(resources.PLUGINS_WEB) def available_community_plugins(): ''' Returns a dict with COMMUNITY availables plugins in NINJA-IDE web page ''' return _availables_plugins(resources.PLUGINS_COMMUNITY) def local_plugins(): ''' Returns the local plugins ''' if not os.path.isfile(resources.PLUGINS_DESCRIPTOR): return [] plugins = json_manager.read_json(resources.PLUGINS_DESCRIPTOR) return plugins def __get_all_plugin_descriptors(): ''' Returns all the .plugin files ''' global PLUGIN_EXTENSION return [pf for pf in os.listdir(resources.PLUGINS) if pf.endswith(PLUGIN_EXTENSION)] def download_plugin(file_): ''' Download a plugin specified by file_ ''' global PLUGIN_EXTENSION #get all the .plugin files in local filesystem plugins_installed_before = set(__get_all_plugin_descriptors()) #download the plugin fileName = os.path.join(resources.PLUGINS, os.path.basename(file_)) content = urlopen(file_) f = open(fileName, 'wb') f.write(content.read()) f.close() #create the zip zipFile = zipfile.ZipFile(fileName, 'r') zipFile.extractall(resources.PLUGINS) zipFile.close() #clean up the enviroment os.remove(fileName) #get the name of the last installed plugin plugins_installed_after = set(__get_all_plugin_descriptors()) #using set operations get the difference that is the new plugin new_plugin = (plugins_installed_after - plugins_installed_before).pop() return new_plugin def manual_install(file_): """Copy zip file and install.""" global PLUGIN_EXTENSION #get all the .plugin files in local filesystem plugins_installed_before = set(__get_all_plugin_descriptors()) #copy the plugin fileName = os.path.join(resources.PLUGINS, os.path.basename(file_)) shutil.copyfile(file_, fileName) #extract the zip zipFile = zipfile.ZipFile(fileName, 'r') zipFile.extractall(resources.PLUGINS) zipFile.close() #clean up the enviroment os.remove(fileName) #get the name of the last installed plugin plugins_installed_after = set(__get_all_plugin_descriptors()) #using set operations get the difference that is the new plugin new_plugin = (plugins_installed_after - plugins_installed_before).pop() return new_plugin def has_dependencies(plug): global REQUIREMENTS, COMMAND_FOR_PIP_INSTALL plugin_name = plug[0] structure = [] if os.path.isfile(resources.PLUGINS_DESCRIPTOR): structure = json_manager.read_json(resources.PLUGINS_DESCRIPTOR) PLUGINS = resources.PLUGINS for p in structure: if p['name'] == plugin_name: pd_file = os.path.join(PLUGINS, p['plugin-descriptor']) p_json = json_manager.read_json(pd_file) module = p_json.get('module') #plugin_module/requirements.txt req_file = os.path.join(os.path.join(PLUGINS, module), REQUIREMENTS) if os.path.isfile(req_file): return (True, COMMAND_FOR_PIP_INSTALL % req_file) #the plugin was found but no requirement then break! break return (False, None) def update_local_plugin_descriptor(plugins): ''' updates the local plugin description The description.json file holds the information about the plugins downloaded with NINJA-IDE This is a way to track the versions of the plugins ''' structure = [] if os.path.isfile(resources.PLUGINS_DESCRIPTOR): structure = json_manager.read_json(resources.PLUGINS_DESCRIPTOR) for plug_list in plugins: #create the plugin data plug = {} plug['name'] = plug_list[0] plug['version'] = plug_list[1] plug['description'] = plug_list[2] plug['authors'] = plug_list[3] plug['home'] = plug_list[4] plug['download'] = plug_list[5] plug['plugin-descriptor'] = plug_list[6] #append the plugin data structure.append(plug) json_manager.write_json(structure, resources.PLUGINS_DESCRIPTOR) def uninstall_plugin(plug): """ Uninstall the given plugin """ plugin_name = plug[0] structure = [] if os.path.isfile(resources.PLUGINS_DESCRIPTOR): structure = json_manager.read_json(resources.PLUGINS_DESCRIPTOR) #copy the strcuture we iterate and remove at the same time structure_aux = copy.copy(structure) for plugin in structure_aux: if plugin["name"] == plugin_name: fileName = plugin["plugin-descriptor"] structure.remove(plugin) break #open .plugin file and get the module to remove fileName = os.path.join(resources.PLUGINS, fileName) plugin = json_manager.read_json(fileName) module = plugin.get('module') if module: pluginDir = os.path.join(resources.PLUGINS, module) folders = [pluginDir] for root, dirs, files in os.walk(pluginDir): pluginFiles = [os.path.join(root, f) for f in files] #remove all files list(map(os.remove, pluginFiles)) #collect subfolders folders += [os.path.join(root, d) for d in dirs] folders.reverse() for f in folders: if os.path.isdir(f): os.removedirs(f) #remove ths plugin_name.plugin file os.remove(fileName) #write the new info json_manager.write_json(structure, resources.PLUGINS_DESCRIPTOR) ############################################################################### # Module Test ############################################################################### if __name__ == '__main__': folders = resources.PLUGINS services = {} sl = ServiceLocator(services) pm = PluginManager(folders, sl) #There are not plugins yet...lets discover pm.discover() logger.info("listing plugins names...") for p in pm: print(p) logger.info("Activating plugins...") pm.load_all() logger.info("Plugins already actives...") logger.info(pm.get_active_plugins()) ninja-ide-2.3/ninja_ide/core/plugin_services.py000066400000000000000000000376161216641277400216660ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals from PyQt4.QtCore import QObject from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import pyqtSignal from ninja_ide.core import settings from ninja_ide.core import file_manager from ninja_ide.core import plugin_util from ninja_ide.gui.main_panel import itab_item from ninja_ide.gui.main_panel import main_container from ninja_ide.gui import actions from ninja_ide.gui.explorer import explorer_container ############################################################################### # PLUGINS SERVICES ############################################################################### class MainService(QObject): """ Main Interact whith NINJA-IDE """ # SIGNALS editorKeyPressEvent = pyqtSignal("QEvent") beforeFileSaved = pyqtSignal("QString") fileSaved = pyqtSignal("QString") currentTabChanged = pyqtSignal("QString") fileExecuted = pyqtSignal("QString") fileOpened = pyqtSignal("QString") def __init__(self): QObject.__init__(self) self._main = main_container.MainContainer() self._action = actions.Actions() self._explorer = explorer_container.ExplorerContainer() #Connect signals self.connect(self._main, SIGNAL("editorKeyPressEvent(QEvent)"), self._keyPressEvent) self.connect(self._main, SIGNAL("beforeFileSaved(QString)"), self._beforeFileSaved) self.connect(self._main, SIGNAL("fileSaved(QString)"), self._fileSaved) self.connect(self._main, SIGNAL("currentTabChanged(QString)"), self._currentTabChanged) self.connect(self._action, SIGNAL("fileExecuted(QString)"), self._fileExecuted) self.connect(self._main, SIGNAL("fileOpened(QString)"), self._fileOpened) ############################################################################### # Get main GUI Objects ############################################################################### def get_tab_manager(self): """ Returns the TabWidget (ninja_ide.gui.main_panel.tab_widget.TabWidget) subclass of QTabWidget """ return self._main.actualTab ############################################################################### # END main GUI Objects ############################################################################### def add_menu(self, menu, lang=".py"): """ Add an *extra context menu* to the editor context menu """ itab_item.ITabItem.add_extra_menu(menu, lang=lang) def get_opened_documents(self): """ Returns the opened documents """ documents_data = self._main.get_opened_documents() #returns ONLY names! return [doc_data[0] for doc_list in documents_data for doc_data in doc_list] def get_project_owner(self, editorWidget=None): """ Return the project where this file belongs, or an empty string. """ #if not editor try to get the current if editorWidget is None: editorWidget = self._main.get_actual_editor() belongs = '' if editorWidget is None: return belongs #get the opened projects opened_projects_obj = self._explorer.get_opened_projects() for project in opened_projects_obj: if file_manager.belongs_to_folder(project.path, editorWidget.ID): belongs = project.path break return belongs def get_file_syntax(self, editorWidget=None): """Return the syntax for this file -> {}.""" if editorWidget is None: editorWidget = self._main.get_actual_editor() if editorWidget is not None: ext = file_manager.get_file_extension(editorWidget.ID) lang = settings.EXTENSIONS.get(ext, '') syntax = settings.SYNTAX.get(lang, {}) return syntax return {} def add_editor(self, fileName="", content=None, syntax=None): """ Create a new editor """ editor = self._main.add_editor(fileName=fileName, syntax=syntax) if content: editor.setPlainText(content) return editor def get_editor(self): """ Returns the actual editor (instance of ninja_ide.gui.editor.Editor) This method could return None """ return self._main.get_actual_editor() def get_editor_path(self): """ Returns the actual editor path This method could return None if there is not an editor """ editor = self._main.get_actual_editor() if editor: return editor.ID return None def get_editor_encoding(self, editorWidget=None): """ Returns the editor encoding """ if editorWidget is None: editorWidget = self._main.get_actual_editor() if editorWidget is not None: return editorWidget.encoding return None def get_text(self): """ Returns the plain text of the current editor or None if thre is not an editor. """ editor = self._main.get_actual_editor() if editor: return editor.get_text() return def get_selected_text(self): """ Returns the selected text of and editor. This method could return None """ editor = self._main.get_actual_editor() if editor: return editor.textCursor().selectedText() return None def insert_text(self, text): """ Insert text in the current cursor position @text: string """ editor = self._main.get_actual_editor() if editor: editor.insertPlainText(text) def jump_to_line(self, lineno): """ Jump to a specific line in the current editor """ self._main.editor_jump_to_line(lineno=lineno) def get_lines_count(self): """ Returns the count of lines in the current editor """ editor = self._main.get_actual_editor() if editor: return editor.get_lines_count() return None def save_file(self): """ Save the actual file """ self._main.save_file() def open_files(self, files, mainTab=True): """ Open many files """ self._main.open_files(self, files, mainTab=mainTab) def open_file(self, fileName='', cursorPosition=0, positionIsLineNumber=False): """ Open a single file, if the file is already open it get focus """ self._main.open_file(filename=fileName, cursorPosition=cursorPosition, positionIsLineNumber=positionIsLineNumber) def open_image(self, filename): """ Open a single image """ self._main.open_image(filename) def get_actual_tab(self): """ Returns the actual widget """ return self._main.get_actual_widget() # SIGNALS def _keyPressEvent(self, qEvent): """ Emit the signal when a key is pressed @event: QEvent """ self.editorKeyPressEvent.emit(qEvent) def _beforeFileSaved(self, fileName): """ Signal emitted before save a file """ self.beforeFileSaved.emit(fileName) def _fileSaved(self, fileName): """ Signal emitted after save a file """ fileName = fileName.split(":")[-1].strip() self.fileSaved.emit(fileName) def _currentTabChanged(self, fileName): """ Signal emitted when the current tab changes """ self.currentTabChanged.emit(fileName) def _fileExecuted(self, fileName): """ Signal emitted when the file is executed """ self.fileExecuted.emit(fileName) def _fileOpened(self, fileName): """ Signal emitted when the file is opened """ self.fileOpened.emit(fileName) class ToolbarService(QObject): """ Interact with the Toolbar """ def __init__(self, toolbar): QObject.__init__(self) self._toolbar = toolbar def add_action(self, action): """ Add an action to the Toolbar @action: Should be an instance(or subclass) of QAction """ settings.add_toolbar_item_for_plugins(action) self._toolbar.addAction(action) class MenuAppService(QObject): """ Interact with the Plugins Menu """ def __init__(self, plugins_menu): QObject.__init__(self) self._plugins_menu = plugins_menu def add_menu(self, menu): """ Add an extra menu to the Plugin Menu of NINJA """ self._plugins_menu.addMenu(menu) def add_action(self, action): """ Add an action to the Plugin Menu of NINJA """ self._plugins_menu.addAction(action) #class ProjectTypeService(QObject): # """ # Interact with the New Project Wizard # """ # def __init__(self): # QObject.__init__(self) # # def set_project_type_handler(self, project_type, project_type_handler): # """ # Add a new Project Type and the handler for it # example: # foo_project_handler = FooProjectHandler(...) # set_project_type_handler('Foo Project', foo_project_handler) # Then 'Foo Project' will appear in the New Project wizard # and foo_project_handler instance controls the wizard # # Note: project_type_handler SHOULD have a special interface see # ninja_ide.core.plugin_interfaces # """ # settings.set_project_type_handler(project_type, project_type_handler) # # #class TreeSymbolsService(QObject): # """ # Interact with the symbols tree # """ # def __init__(self): # QObject.__init__(self) # # def set_symbols_handler(self, file_extension, symbols_handler): # """ # Add a new Symbol's handler for the given file extension # example: # cpp_symbols_handler = CppSymbolHandler(...) # set_symbols_handler('cpp', cpp_symbols_handler) # Then all symbols in .cpp files will be handle by cpp_symbols_handler # # Note: symbols_handler SHOULD have a special interface see # ninja_ide.core.plugin_interfaces # """ # settings.set_symbols_handler(file_extension, symbols_handler) class MiscContainerService(QObject): def __init__(self, miscContainer): QObject.__init__(self) self._misc = miscContainer def add_widget(self, widget, icon_path, description): self._misc.add_to_stack(widget, icon_path, description) class ExplorerService(QObject): # SIGNALS projectOpened = pyqtSignal("QString") projectExecuted = pyqtSignal("QString") def __init__(self): QObject.__init__(self) self._explorer = explorer_container.ExplorerContainer() self._action = actions.Actions() self.connect(self._explorer, SIGNAL("projectOpened(QString)"), self._projectOpened) self.connect(self._action, SIGNAL("projectExecuted(QString)"), self._projectExecuted) def get_tree_projects(self): """ Returns the projects tree """ return self._explorer._treeProjects def get_item(self, path): if self._explorer._treeProjects: return self._explorer._treeProjects.get_item_for_path(path) def get_current_project_item(self): """ Returns the current item of the tree projects this method is a shortcut of self.get_tree_projects().currentItem() """ if self._explorer._treeProjects: return self._explorer._treeProjects.currentItem() return None def get_project_item_by_name(self, projectName): """ Return a ProjectItem that has the name provided. """ if self._explorer._treeProjects: return self._explorer._treeProjects.get_project_by_name( projectName) return None def get_tree_symbols(self): """ Returns the symbols tree """ return self._explorer._treeSymbols def set_symbols_handler(self, file_extension, symbols_handler): """ Add a new Symbol's handler for the given file extension example: cpp_symbols_handler = CppSymbolHandler(...) set_symbols_handler('cpp', cpp_symbols_handler) Then all symbols in .cpp files will be handle by cpp_symbols_handler Note: symbols_handler SHOULD have a special interface see ninja_ide.core.plugin_interfaces """ settings.set_symbols_handler(file_extension, symbols_handler) def set_project_type_handler(self, project_type, project_type_handler): """ Add a new Project Type and the handler for it example: foo_project_handler = FooProjectHandler(...) set_project_type_handler('Foo Project', foo_project_handler) Then 'Foo Project' will appear in the New Project wizard and foo_project_handler instance controls the wizard Note: project_type_handler SHOULD have a special interface see ninja_ide.core.plugin_interfaces """ settings.set_project_type_handler(project_type, project_type_handler) def add_tab(self, tab, title): """ Add a tab with the given title @tab: Should be an instance (or subclass )of QTabWidget @title: Name of the tab string """ self._explorer.addTab(tab, title) def get_actual_project(self): """ Returns the path of the opened projects """ return self._explorer.get_actual_project() def get_opened_projects(self): """ Return the opened projects in the Tree Project Explorer. list of """ opened_projects = self._explorer.get_opened_projects() return opened_projects def add_project_menu(self, menu, lang='all'): """ Add an extra menu to the project explorer for the files with the given extension. @lang: String with file extension format (py, php, json) """ if self._explorer._treeProjects: self._explorer._treeProjects.add_extra_menu(menu, lang=lang) def add_project_menu_by_scope(self, menu, scope=None): """ Add an extra menu to the project explorer to the specific scope @scope: String with the menu scope (all, project, folder, file) """ if scope is None: #default behavior show ALL scope = plugin_util.ContextMenuScope(project=True, folder=True, files=True) if self._explorer._treeProjects: self._explorer._treeProjects.add_extra_menu_by_scope(menu, scope) # SIGNALS def _projectOpened(self, projectPath): """ Signal emitted when the project is opened """ self.projectOpened.emit(projectPath) def _projectExecuted(self, projectPath): """ Signal emitted when the project is executed """ self.projectExecuted.emit(projectPath) ninja-ide-2.3/ninja_ide/core/plugin_util.py000066400000000000000000000017641216641277400210130ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . class ContextMenuScope(object): """ This class is just a domain class for the plugin API it hold the info about the project explorer context menu """ def __init__(self, project=False, folder=False, files=False): self.project = project self.folder = folder self.file = files ninja-ide-2.3/ninja_ide/core/settings.py000066400000000000000000000421651216641277400203200ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . import os import sys from PyQt4.QtCore import QDir from PyQt4.QtCore import QFileInfo from PyQt4.QtCore import QSettings from ninja_ide import resources from ninja_ide.dependencies import pep8mod ############################################################################### # OS DETECTOR ############################################################################### # Use this flags instead of sys.platform spreaded in the source code IS_WINDOWS = False OS_KEY = "Ctrl" FONT_FAMILY = 'Monospace' FONT_SIZE = 11 if sys.platform == "darwin": from PyQt4.QtGui import QKeySequence from PyQt4.QtCore import Qt FONT_FAMILY = 'Monaco' FONT_SIZE = 11 OS_KEY = QKeySequence(Qt.CTRL).toString(QKeySequence.NativeText) elif sys.platform == "win32": FONT_FAMILY = 'Courier' FONT_SIZE = 10 IS_WINDOWS = True def detect_python_path(): if (IS_WINDOWS and PYTHON_PATH_CONFIGURED_BY_USER) or not IS_WINDOWS: return [] suggested = [] try: drives = [QDir.toNativeSeparators(d.absolutePath()) for d in QDir.drives()] dirs = [] for drive in drives: info = QFileInfo(drive) if info.isReadable(): dirs += [os.path.join(drive, folder) for folder in os.listdir(drive)] for folder in dirs: file_path = os.path.join(folder, "python.exe") if ("Python" in folder) and os.path.exists(file_path): suggested.append(file_path) except: print("Detection couldnt be executed") return suggested ############################################################################### # IDE ############################################################################### MAX_OPACITY = 1 MIN_OPACITY = 0.3 TOOLBAR_AREA = 1 #UI LAYOUT #001 : Central Rotate #010 : Panels Rotate #100 : Central Orientation UI_LAYOUT = 0 LANGUAGE = "" SHOW_START_PAGE = True CONFIRM_EXIT = True NOTIFY_UPDATES = True HIDE_TOOLBAR = False SHOW_STATUS_NOTIFICATIONS = True PYTHON_PATH = "python" PYTHON_PATH_CONFIGURED_BY_USER = False EXECUTION_OPTIONS = "" PROFILES = {} TOOLBAR_ITEMS = [ "new-file", "new-project", "open-file", "open-project", "save-file", "separator", "splitv", "splith", "follow-mode", "separator", "cut", "copy", "paste", "separator", "run-project", "run-file", "stop", "separator", ] TOOLBAR_ITEMS_DEFAULT = [ "new-file", "new-project", "open-file", "open-project", "save-file", "separator", "splitv", "splith", "follow-mode", "separator", "cut", "copy", "paste", "separator", "run-project", "run-file", "stop", "separator", ] #hold the toolbar actions added by plugins TOOLBAR_ITEMS_PLUGINS = [] NINJA_SKIN = 'Default' ############################################################################### # EDITOR ############################################################################### USE_TABS = False ALLOW_WORD_WRAP = False INDENT = 4 # by default Unix (\n) is used USE_PLATFORM_END_OF_LINE = False MARGIN_LINE = 80 SHOW_MARGIN_LINE = True REMOVE_TRAILING_SPACES = True SHOW_TABS_AND_SPACES = True BRACES = {'{': '}', '[': ']', '(': ')'} QUOTES = {'"': '"', "'": "'"} FONT_MAX_SIZE = 28 FONT_MIN_SIZE = 6 MAX_REMEMBER_TABS = 50 COPY_HISTORY_BUFFER = 20 FIND_ERRORS = True ERRORS_HIGHLIGHT_LINE = True CHECK_STYLE = True CHECK_HIGHLIGHT_LINE = True CODE_COMPLETION = True COMPLETE_DECLARATIONS = True SHOW_MIGRATION_TIPS = True VALID_2TO3 = True UNDERLINE_NOT_BACKGROUND = True CENTER_ON_SCROLL = True SYNTAX = {} EXTENSIONS = {} BREAKPOINTS = {} BOOKMARKS = {} ############################################################################### # CHECKERS ############################################################################### CHECK_FOR_DOCSTRINGS = True ############################################################################### # MINIMAP ############################################################################### SHOW_MINIMAP = False MINIMAP_MAX_OPACITY = 0.8 MINIMAP_MIN_OPACITY = 0.1 SIZE_PROPORTION = 0.17 ############################################################################### # FILE MANAGER ############################################################################### SUPPORTED_EXTENSIONS = [ '.py', '.html', '.jpg', '.png', '.ui', '.css', '.json', '.js', '.ini'] ############################################################################### # PROJECTS DATA ############################################################################### #PROJECT_TYPES = {'Python': None} PROJECT_TYPES = {} LANGS = [] ############################################################################### # EXPLORER ############################################################################### SHOW_PROJECT_EXPLORER = True SHOW_SYMBOLS_LIST = True SHOW_WEB_INSPECTOR = False SHOW_ERRORS_LIST = False SHOW_MIGRATION_LIST = True #Symbols handler per language (file extension) SYMBOLS_HANDLER = {} #Backward compatibility with older Qt versions WEBINSPECTOR_SUPPORTED = True ############################################################################### # WORKSPACE ############################################################################### WORKSPACE = "" ############################################################################### # FUNCTIONS ############################################################################### def set_project_type_handler(project_type, project_type_handler): """ Set a project type handler for the given project_type """ global PROJECT_TYPES PROJECT_TYPES[project_type] = project_type_handler def get_project_type_handler(project_type): """ Returns the handler for the given project_type """ global PROJECT_TYPES return PROJECT_TYPES.get(project_type) def get_all_project_types(): """ Returns the availables project types """ global PROJECT_TYPES return list(PROJECT_TYPES.keys()) def set_symbols_handler(file_extension, symbols_handler): """ Set a symbol handler for the given file_extension """ global SYMBOLS_HANDLER SYMBOLS_HANDLER[file_extension] = symbols_handler def get_symbols_handler(file_extension): """ Returns the symbol handler for the given file_extension """ global SYMBOLS_HANDLER return SYMBOLS_HANDLER.get(file_extension, None) def add_toolbar_item_for_plugins(toolbar_action): """ Add a toolbar action set from some plugin """ global TOOLBAR_ITEMS_PLUGINS TOOLBAR_ITEMS_PLUGINS.append(toolbar_action) def get_toolbar_item_for_plugins(): """ Returns the toolbar actions set by plugins """ global TOOLBAR_ITEMS_PLUGINS return TOOLBAR_ITEMS_PLUGINS def use_platform_specific_eol(): global USE_PLATFORM_END_OF_LINE return USE_PLATFORM_END_OF_LINE ############################################################################### # Utility functions to update (patch at runtime) pep8mod.py ############################################################################### def pep8mod_refresh_checks(): """ Force to reload all checks in pep8mod.py """ pep8mod.refresh_checks() def pep8mod_add_ignore(ignore_code): """ Patch pep8mod.py to ignore a given check by code EXAMPLE: pep8mod_add_ignore('W191') 'W1919': 'indentation contains tabs' """ pep8mod.options.ignore.append(ignore_code) def pep8mod_remove_ignore(ignore_code): """ Patch pep8mod.py to remove the ignore of a give check EXAMPLE: pep8mod_remove_ignore('W191') 'W1919': 'indentation contains tabs' """ if ignore_code in pep8mod.options.ignore: pep8mod.options.ignore.remove(ignore_code) def pep8mod_update_margin_line_length(new_margin_line): """ Patch pep8mod.py to update the margin line length with a new value """ pep8mod.MAX_LINE_LENGTH = new_margin_line pep8mod.options.max_line_length = new_margin_line ############################################################################### # LOAD SETTINGS ############################################################################### def load_settings(): qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) #Globals global TOOLBAR_AREA global LANGUAGE global SHOW_START_PAGE global CONFIRM_EXIT global UI_LAYOUT global NOTIFY_UPDATES global PYTHON_PATH global PYTHON_PATH_CONFIGURED_BY_USER global PROFILES global NINJA_SKIN global EXECUTION_OPTIONS global SUPPORTED_EXTENSIONS global WORKSPACE global INDENT global USE_PLATFORM_END_OF_LINE global MARGIN_LINE global REMOVE_TRAILING_SPACES global SHOW_TABS_AND_SPACES global USE_TABS global ALLOW_WORD_WRAP global COMPLETE_DECLARATIONS global UNDERLINE_NOT_BACKGROUND global FONT_FAMILY global FONT_SIZE global SHOW_MARGIN_LINE global FIND_ERRORS global ERRORS_HIGHLIGHT_LINE global CHECK_STYLE global CHECK_HIGHLIGHT_LINE global SHOW_MIGRATION_TIPS global CODE_COMPLETION global CENTER_ON_SCROLL global SHOW_PROJECT_EXPLORER global SHOW_SYMBOLS_LIST global SHOW_WEB_INSPECTOR global SHOW_ERRORS_LIST global SHOW_MIGRATION_LIST global BOOKMARKS global CHECK_FOR_DOCSTRINGS global BREAKPOINTS global BRACES global HIDE_TOOLBAR global SHOW_STATUS_NOTIFICATIONS global TOOLBAR_ITEMS global SHOW_MINIMAP global MINIMAP_MAX_OPACITY global MINIMAP_MIN_OPACITY global SIZE_PROPORTION #General HIDE_TOOLBAR = qsettings.value("window/hide_toolbar", False, type=bool) SHOW_STATUS_NOTIFICATIONS = qsettings.value( "preferences/interface/showStatusNotifications", True, type=bool) TOOLBAR_AREA = qsettings.value('preferences/general/toolbarArea', 1, type=int) LANGUAGE = qsettings.value('preferences/interface/language', '', type='QString') SHOW_START_PAGE = qsettings.value( 'preferences/general/showStartPage', True, type=bool) CONFIRM_EXIT = qsettings.value('preferences/general/confirmExit', True, type=bool) UI_LAYOUT = qsettings.value('preferences/interface/uiLayout', 0, type=int) NOTIFY_UPDATES = qsettings.value( 'preferences/general/notifyUpdates', True, type=bool) PYTHON_PATH = qsettings.value('preferences/execution/pythonPath', 'python', type='QString') PYTHON_PATH_CONFIGURED_BY_USER = qsettings.value( 'preferences/execution/pythonPathConfigured', False, type=bool) NINJA_SKIN = qsettings.value('preferences/theme/skin', 'Default', type='QString') profileDict = dict(qsettings.value('ide/profiles', {})) for key in profileDict: profile_list = list(profileDict[key]) files = [] if profile_list: files = [item for item in list(profile_list[0])] tempFiles = [] for file_ in files: fileData = list(file_) if len(fileData) > 0: tempFiles.append([fileData[0], int(fileData[1])]) files = tempFiles projects = [] if len(profile_list) > 1: projects = [item for item in list(profile_list[1])] PROFILES[key] = [files, projects] toolbar_items = [item for item in list(qsettings.value( 'preferences/interface/toolbar', []))] if toolbar_items: TOOLBAR_ITEMS = toolbar_items #EXECUTION OPTIONS EXECUTION_OPTIONS = qsettings.value( 'preferences/execution/executionOptions', defaultValue='', type='QString') extensions = [item for item in list(qsettings.value( 'preferences/general/supportedExtensions', []))] if extensions: SUPPORTED_EXTENSIONS = extensions WORKSPACE = qsettings.value( 'preferences/general/workspace', "", type='QString') #Editor SHOW_MINIMAP = qsettings.value( 'preferences/editor/minimapShow', False, type=bool) MINIMAP_MAX_OPACITY = float(qsettings.value( 'preferences/editor/minimapMaxOpacity', 0.8, type=float)) MINIMAP_MIN_OPACITY = float(qsettings.value( 'preferences/editor/minimapMinOpacity', 0.1, type=float)) SIZE_PROPORTION = float(qsettings.value( 'preferences/editor/minimapSizeProportion', 0.17, type=float)) INDENT = int(qsettings.value('preferences/editor/indent', 4, type=int)) USE_PLATFORM_END_OF_LINE = qsettings.value( 'preferences/editor/platformEndOfLine', False, type=bool) MARGIN_LINE = qsettings.value('preferences/editor/marginLine', 80, type=int) pep8mod_update_margin_line_length(MARGIN_LINE) REMOVE_TRAILING_SPACES = qsettings.value( 'preferences/editor/removeTrailingSpaces', True, type=bool) SHOW_TABS_AND_SPACES = qsettings.value( 'preferences/editor/showTabsAndSpaces', True, type=bool) USE_TABS = qsettings.value('preferences/editor/useTabs', False, type=bool) if USE_TABS: pep8mod_add_ignore("W191") pep8mod_refresh_checks() ALLOW_WORD_WRAP = qsettings.value( 'preferences/editor/allowWordWrap', False, type=bool) COMPLETE_DECLARATIONS = qsettings.value( 'preferences/editor/completeDeclarations', True, type=bool) UNDERLINE_NOT_BACKGROUND = qsettings.value( 'preferences/editor/errorsUnderlineBackground', True, type=bool) font_family = qsettings.value( 'preferences/editor/fontFamily', "", type='QString') if font_family: FONT_FAMILY = font_family font_size = qsettings.value('preferences/editor/fontSize', 0, type=int) if font_size != 0: FONT_SIZE = font_size SHOW_MARGIN_LINE = qsettings.value( 'preferences/editor/showMarginLine', True, type=bool) FIND_ERRORS = qsettings.value('preferences/editor/errors', True, type=bool) SHOW_MIGRATION_TIPS = qsettings.value( 'preferences/editor/showMigrationTips', True, type=bool) ERRORS_HIGHLIGHT_LINE = qsettings.value( 'preferences/editor/errorsInLine', True, type=bool) CHECK_STYLE = qsettings.value('preferences/editor/checkStyle', True, type=bool) CHECK_HIGHLIGHT_LINE = qsettings.value( 'preferences/editor/checkStyleInline', True, type=bool) CODE_COMPLETION = qsettings.value( 'preferences/editor/codeCompletion', True, type=bool) CENTER_ON_SCROLL = qsettings.value( 'preferences/editor/centerOnScroll', True, type=bool) parentheses = qsettings.value('preferences/editor/parentheses', True, type=bool) if not parentheses: del BRACES['('] brackets = qsettings.value('preferences/editor/brackets', True, type=bool) if not brackets: del BRACES['['] keys = qsettings.value('preferences/editor/keys', True, type=bool) if not keys: del BRACES['{'] simpleQuotes = qsettings.value('preferences/editor/simpleQuotes', True, type=bool) if not simpleQuotes: del QUOTES["'"] doubleQuotes = qsettings.value('preferences/editor/doubleQuotes', True, type=bool) if not doubleQuotes: del QUOTES['"'] #Projects SHOW_PROJECT_EXPLORER = qsettings.value( 'preferences/interface/showProjectExplorer', True, type=bool) SHOW_SYMBOLS_LIST = qsettings.value( 'preferences/interface/showSymbolsList', True, type=bool) SHOW_WEB_INSPECTOR = qsettings.value( 'preferences/interface/showWebInspector', False, type=bool) SHOW_ERRORS_LIST = qsettings.value( 'preferences/interface/showErrorsList', False, type=bool) SHOW_MIGRATION_LIST = qsettings.value( 'preferences/interface/showMigrationList', True, type=bool) #Bookmarks and Breakpoints bookmarks = dict(qsettings.value('preferences/editor/bookmarks', {})) for key in bookmarks: if key: BOOKMARKS[key] = [int(i) for i in list(bookmarks[key])] breakpoints = dict(qsettings.value('preferences/editor/breakpoints', {})) for key in breakpoints: if key: BREAKPOINTS[key] = [int(i) for i in list(breakpoints[key])] # Checkers CHECK_FOR_DOCSTRINGS = qsettings.value( 'preferences/editor/checkForDocstrings', False, type=bool) # Import introspection here, it not needed in the namespace of # the rest of the file. from ninja_ide.tools import introspection #Set Default Symbol Handler set_symbols_handler('py', introspection) ninja-ide-2.3/ninja_ide/dependencies/000077500000000000000000000000001216641277400175745ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/dependencies/__init__.py000066400000000000000000000012641216641277400217100ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . ninja-ide-2.3/ninja_ide/dependencies/pep8mod.py000066400000000000000000001346641216641277400215400ustar00rootroot00000000000000#!/usr/bin/env python # pep8.py - Check Python source code formatting, according to PEP 8 # Copyright (C) 2006 Johann C. Rocholl # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. r""" Check Python source code formatting, according to PEP 8: http://www.python.org/dev/peps/pep-0008/ For usage and a list of options, try this: $ python pep8.py -h This program and its regression test suite live here: http://github.com/jcrocholl/pep8 Groups of errors and warnings: E errors W warnings 100 indentation 200 whitespace 300 blank lines 400 imports 500 line length 600 deprecation 700 statements 900 syntax error You can add checks to this program by writing plugins. Each plugin is a simple function that is called for each line of source code, either physical or logical. Physical line: - Raw line of text from the input file. Logical line: - Multi-line statements converted to a single line. - Stripped left and right. - Contents of strings replaced with 'xxx' of same length. - Comments removed. The check function requests physical or logical lines by the name of the first argument: def maximum_line_length(physical_line) def extraneous_whitespace(logical_line) def blank_lines(logical_line, blank_lines, indent_level, line_number) The last example above demonstrates how check plugins can request additional information with extra arguments. All attributes of the Checker object are available. Some examples: lines: a list of the raw lines from the input file tokens: the tokens that contribute to this logical line line_number: line number in the input file blank_lines: blank lines before this one indent_char: first indentation character in this file (' ' or '\t') indent_level: indentation (with tabs expanded to multiples of 8) previous_indent_level: indentation on previous line previous_logical: previous logical line The docstring of each check function shall be the relevant part of text from PEP 8. It is printed if the user enables --show-pep8. Several docstrings contain examples directly from the PEP 8 document. Okay: spam(ham[1], {eggs: 2}) E201: spam( ham[1], {eggs: 2}) These examples are verified automatically when pep8.py is run with the --doctest option. You can add examples for your own check functions. The format is simple: "Okay" or error/warning code followed by colon and space, the rest of the line is example source code. If you put 'r' before the docstring, you can use \n for newline, \t for tab and \s for space. """ from __future__ import unicode_literals __version__ = '1.3.4a0' import os import sys import re import inspect import keyword import tokenize from ninja_ide import resources DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git' DEFAULT_IGNORE = 'E24' if sys.platform == 'win32': DEFAULT_CONFIG = os.path.join(resources.HOME_PATH, r'.pep8') else: DEFAULT_CONFIG = os.path.join(os.getenv('XDG_CONFIG_HOME') or os.path.join( resources.HOME_PATH, '.config'), 'pep8') MAX_LINE_LENGTH = 79 REPORT_FORMAT = { 'default': '%(path)s:%(row)d:%(col)d: %(code)s %(text)s', 'pylint': '%(path)s:%(row)d: [%(code)s] %(text)s', } SINGLETONS = frozenset(['False', 'None', 'True']) KEYWORDS = frozenset(keyword.kwlist + ['print']) - SINGLETONS BINARY_OPERATORS = frozenset([ '**=', '*=', '+=', '-=', '!=', '<>', '%=', '^=', '&=', '|=', '==', '/=', '//=', '<=', '>=', '<<=', '>>=', '%', '^', '&', '|', '=', '/', '//', '<', '>', '<<']) UNARY_OPERATORS = frozenset(['>>', '**', '*', '+', '-']) OPERATORS = BINARY_OPERATORS | UNARY_OPERATORS WHITESPACE = frozenset(' \t') SKIP_TOKENS = frozenset([tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE, tokenize.INDENT, tokenize.DEDENT]) BENCHMARK_KEYS = ['directories', 'files', 'logical lines', 'physical lines'] INDENT_REGEX = re.compile(r'([ \t]*)') RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*(,)') RERAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,\s*\w+\s*,\s*\w+') SELFTEST_REGEX = re.compile(r'(Okay|[EW]\d{3}):\s(.*)') ERRORCODE_REGEX = re.compile(r'[EW]\d{3}') DOCSTRING_REGEX = re.compile(r'u?r?["\']') EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]') WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)') COMPARE_SINGLETON_REGEX = re.compile(r'([=!]=)\s*(None|False|True)') COMPARE_TYPE_REGEX = re.compile(r'([=!]=|is|is\s+not)\s*type(?:s\.(\w+)Type' r'|\(\s*(\(\s*\)|[^)]*[^ )])\s*\))') KEYWORD_REGEX = re.compile(r'(?:[^\s])(\s*)\b(?:%s)\b(\s*)' % r'|'.join(KEYWORDS)) OPERATOR_REGEX = re.compile(r'(?:[^\s])(\s*)(?:[-+*/|!<=>%&^]+)(\s*)') LAMBDA_REGEX = re.compile(r'\blambda\b') HUNK_REGEX = re.compile(r'^@@ -\d+,\d+ \+(\d+),(\d+) @@.*$') # Work around Python < 2.6 behaviour, which does not generate NL after # a comment which is on a line by itself. COMMENT_WITH_NL = tokenize.generate_tokens(['#\n'].pop).send(None)[1] == '#\n' #Define instance options to set options to be analized class Options: pass options = Options() #Set the options to be analized options.show_source = True options.repeat = True options.show_pep8 = False options.messages = {} options.select = [] options.ignore = [] options.verbose = 0 options.quiet = 0 options.max_line_length = MAX_LINE_LENGTH options.ignore_continuation_indentation = True ############################################################################## # Plugins (check functions) for physical lines ############################################################################## def tabs_or_spaces(physical_line, indent_char): r""" Never mix tabs and spaces. The most popular way of indenting Python is with spaces only. The second-most popular way is with tabs only. Code indented with a mixture of tabs and spaces should be converted to using spaces exclusively. When invoking the Python command line interpreter with the -t option, it issues warnings about code that illegally mixes tabs and spaces. When using -tt these warnings become errors. These options are highly recommended! Okay: if a == 0:\n a = 1\n b = 1 E101: if a == 0:\n a = 1\n\tb = 1 """ indent = INDENT_REGEX.match(physical_line).group(1) for offset, char in enumerate(indent): if char != indent_char: return offset, "E101 indentation contains mixed spaces and tabs" def tabs_obsolete(physical_line): r""" For new projects, spaces-only are strongly recommended over tabs. Most editors have features that make this easy to do. Okay: if True:\n return W191: if True:\n\treturn """ indent = INDENT_REGEX.match(physical_line).group(1) if '\t' in indent: return indent.index('\t'), "W191 indentation contains tabs" def trailing_whitespace(physical_line): r""" JCR: Trailing whitespace is superfluous. FBM: Except when it occurs as part of a blank line (i.e. the line is nothing but whitespace). According to Python docs[1] a line with only whitespace is considered a blank line, and is to be ignored. However, matching a blank line to its indentation level avoids mistakenly terminating a multi-line statement (e.g. class declaration) when pasting code into the standard Python interpreter. [1] http://docs.python.org/reference/lexical_analysis.html#blank-lines The warning returned varies on whether the line itself is blank, for easier filtering for those who want to indent their blank lines. Okay: spam(1) W291: spam(1)\s W293: class Foo(object):\n \n bang = 12 """ physical_line = physical_line.rstrip('\n') # chr(10), newline physical_line = physical_line.rstrip('\r') # chr(13), carriage return physical_line = physical_line.rstrip('\x0c') # chr(12), form feed, ^L stripped = physical_line.rstrip(' \t\v') if physical_line != stripped: if stripped: return len(stripped), "W291 trailing whitespace" else: return 0, "W293 blank line contains whitespace" def trailing_blank_lines(physical_line, lines, line_number): r""" JCR: Trailing blank lines are superfluous. Okay: spam(1) W391: spam(1)\n """ if not physical_line.rstrip() and line_number == len(lines): return 0, "W391 blank line at end of file" def missing_newline(physical_line): """ JCR: The last line should have a newline. Reports warning W292. """ if physical_line.rstrip() == physical_line: return len(physical_line), "W292 no newline at end of file" def maximum_line_length(physical_line, max_line_length): """ Limit all lines to a maximum of 79 characters. There are still many devices around that are limited to 80 character lines; plus, limiting windows to 80 characters makes it possible to have several windows side-by-side. The default wrapping on such devices looks ugly. Therefore, please limit all lines to a maximum of 79 characters. For flowing long blocks of text (docstrings or comments), limiting the length to 72 characters is recommended. Reports error E501. """ line = physical_line.rstrip() length = len(line) if length > max_line_length: if hasattr(line, 'decode'): # Python 2 # The line could contain multi-byte characters try: length = len(line.decode('utf-8')) except UnicodeError: pass if length > max_line_length: return (max_line_length, "E501 line too long " "(%d > %d characters)" % (length, max_line_length)) ############################################################################## # Plugins (check functions) for logical lines ############################################################################## def blank_lines(logical_line, blank_lines, indent_level, line_number, previous_logical, previous_indent_level): r""" Separate top-level function and class definitions with two blank lines. Method definitions inside a class are separated by a single blank line. Extra blank lines may be used (sparingly) to separate groups of related functions. Blank lines may be omitted between a bunch of related one-liners (e.g. a set of dummy implementations). Use blank lines in functions, sparingly, to indicate logical sections. Okay: def a():\n pass\n\n\ndef b():\n pass Okay: def a():\n pass\n\n\n# Foo\n# Bar\n\ndef b():\n pass E301: class Foo:\n b = 0\n def bar():\n pass E302: def a():\n pass\n\ndef b(n):\n pass E303: def a():\n pass\n\n\n\ndef b(n):\n pass E303: def a():\n\n\n\n pass E304: @decorator\n\ndef a():\n pass """ if line_number == 1: return # Don't expect blank lines before the first line if previous_logical.startswith('@'): if blank_lines: yield 0, "E304 blank lines found after function decorator" elif blank_lines > 2 or (indent_level and blank_lines == 2): yield 0, "E303 too many blank lines (%d)" % blank_lines elif logical_line.startswith(('def ', 'class ', '@')): if indent_level: if not (blank_lines or previous_indent_level < indent_level or DOCSTRING_REGEX.match(previous_logical)): yield 0, "E301 expected 1 blank line, found 0" elif blank_lines != 2: yield 0, "E302 expected 2 blank lines, found %d" % blank_lines def extraneous_whitespace(logical_line): """ Avoid extraneous whitespace in the following situations: - Immediately inside parentheses, brackets or braces. - Immediately before a comma, semicolon, or colon. Okay: spam(ham[1], {eggs: 2}) E201: spam( ham[1], {eggs: 2}) E201: spam(ham[ 1], {eggs: 2}) E201: spam(ham[1], { eggs: 2}) E202: spam(ham[1], {eggs: 2} ) E202: spam(ham[1 ], {eggs: 2}) E202: spam(ham[1], {eggs: 2 }) E203: if x == 4: print x, y; x, y = y , x E203: if x == 4: print x, y ; x, y = y, x E203: if x == 4 : print x, y; x, y = y, x """ line = logical_line for match in EXTRANEOUS_WHITESPACE_REGEX.finditer(line): text = match.group() char = text.strip() found = match.start() if text == char + ' ': # assert char in '([{' yield found + 1, "E201 whitespace after '%s'" % char elif line[found - 1] != ',': code = ('E202' if char in '}])' else 'E203') # if char in ',;:' yield found, "%s whitespace before '%s'" % (code, char) def whitespace_around_keywords(logical_line): r""" Avoid extraneous whitespace around keywords. Okay: True and False E271: True and False E272: True and False E273: True and\tFalse E274: True\tand False """ for match in KEYWORD_REGEX.finditer(logical_line): before, after = match.groups() if '\t' in before: yield match.start(1), "E274 tab before keyword" elif len(before) > 1: yield match.start(1), "E272 multiple spaces before keyword" if '\t' in after: yield match.start(2), "E273 tab after keyword" elif len(after) > 1: yield match.start(2), "E271 multiple spaces after keyword" def missing_whitespace(logical_line): """ JCR: Each comma, semicolon or colon should be followed by whitespace. Okay: [a, b] Okay: (3,) Okay: a[1:4] Okay: a[:4] Okay: a[1:] Okay: a[1:4:2] E231: ['a','b'] E231: foo(bar,baz) """ line = logical_line for index in range(len(line) - 1): char = line[index] if char in ',;:' and line[index + 1] not in WHITESPACE: before = line[:index] if char == ':' and before.count('[') > before.count(']'): continue # Slice syntax, no space required if char == ',' and line[index + 1] == ')': continue # Allow tuple with only one element: (3,) yield index, "E231 missing whitespace after '%s'" % char def indentation(logical_line, previous_logical, indent_char, indent_level, previous_indent_level): r""" Use 4 spaces per indentation level. For really old code that you don't want to mess up, you can continue to use 8-space tabs. Okay: a = 1 Okay: if a == 0:\n a = 1 E111: a = 1 Okay: for item in items:\n pass E112: for item in items:\npass Okay: a = 1\nb = 2 E113: a = 1\n b = 2 """ if indent_char == ' ' and indent_level % 4: yield 0, "E111 indentation is not a multiple of four" indent_expect = previous_logical.endswith(':') if indent_expect and indent_level <= previous_indent_level: yield 0, "E112 expected an indented block" if indent_level > previous_indent_level and not indent_expect: yield 0, "E113 unexpected indentation" def continuation_line_indentation(logical_line, tokens, indent_level, verbose): r""" Continuation lines should align wrapped elements either vertically using Python's implicit line joining inside parentheses, brackets and braces, or using a hanging indent. When using a hanging indent the following considerations should be applied: - there should be no arguments on the first line, and - further indentation should be used to clearly distinguish itself as a continuation line. Okay: a = (\n) E123: a = (\n ) Okay: a = (\n 42) E121: a = (\n 42) E122: a = (\n42) E123: a = (\n 42\n ) E124: a = (24,\n 42\n) E125: if (a or\n b):\n pass E126: a = (\n 42) E127: a = (24,\n 42) E128: a = (24,\n 42) """ if options.ignore_continuation_indentation: return first_row = tokens[0][2][0] nrows = 1 + tokens[-1][2][0] - first_row if nrows == 1: return # indent_next tells us whether the next block is indented; assuming # that it is indented by 4 spaces, then we should not allow 4-space # indents on the final continuation line; in turn, some other # indents are allowed to have an extra 4 spaces. indent_next = logical_line.endswith(':') row = depth = 0 # remember how many brackets were opened on each line parens = [0] * nrows # relative indents of physical lines rel_indent = [0] * nrows # visual indents indent = [indent_level] indent_chances = {} last_indent = tokens[0][2] for token_type, text, start, end, line in tokens: newline = row < start[0] - first_row if newline: row = start[0] - first_row newline = (not last_token_multiline and token_type not in (tokenize.NL, tokenize.NEWLINE)) if newline: # this is the beginning of a continuation line. last_indent = start # record the initial indent. rel_indent[row] = start[1] - indent_level if depth: # a bracket expression in a continuation line. # find the line that it was opened on for open_row in range(row - 1, -1, -1): if parens[open_row]: break else: # an unbracketed continuation line (ie, backslash) open_row = 0 hang = rel_indent[row] - rel_indent[open_row] visual_indent = indent_chances.get(start[1]) if token_type == tokenize.OP and text in ']})': # this line starts with a closing bracket if indent[depth]: if start[1] != indent[depth]: yield (start, 'E124 closing bracket does not match ' 'visual indentation') elif hang: yield (start, 'E123 closing bracket does not match ' 'indentation of opening bracket\'s line') elif visual_indent is True: # visual indent is verified if not indent[depth]: indent[depth] = start[1] elif visual_indent in (text, str): # ignore token lined up with matching one from a previous line pass elif indent[depth] and start[1] < indent[depth]: # visual indent is broken yield (start, 'E128 continuation line ' 'under-indented for visual indent') elif hang == 4 or (indent_next and rel_indent[row] == 8): # hanging indent is verified pass else: # indent is broken if hang <= 0: error = 'E122', 'missing indentation or outdented' elif indent[depth]: error = 'E127', 'over-indented for visual indent' elif hang % 4: error = 'E121', 'indentation is not a multiple of four' else: error = 'E126', 'over-indented for hanging indent' yield start, "%s continuation line %s" % error # look for visual indenting if (parens[row] and token_type not in (tokenize.NL, tokenize.COMMENT) and not indent[depth]): indent[depth] = start[1] indent_chances[start[1]] = True # deal with implicit string concatenation elif token_type == tokenize.STRING or text in ('u', 'ur', 'b', 'br'): indent_chances[start[1]] = str # keep track of bracket depth if token_type == tokenize.OP: if text in '([{': depth += 1 indent.append(0) parens[row] += 1 elif text in ')]}' and depth > 0: # parent indents should not be more than this one prev_indent = indent.pop() or last_indent[1] for d in range(depth): if indent[d] > prev_indent: indent[d] = 0 for ind in list(indent_chances): if ind >= prev_indent: del indent_chances[ind] depth -= 1 if depth: indent_chances[indent[depth]] = True for idx in range(row, -1, -1): if parens[idx]: parens[idx] -= 1 break assert len(indent) == depth + 1 if start[1] not in indent_chances: # allow to line up tokens indent_chances[start[1]] = text last_token_multiline = (start[0] != end[0]) if indent_next and rel_indent[-1] == 4: yield (last_indent, "E125 continuation line does not distinguish " "itself from next logical line") def whitespace_before_parameters(logical_line, tokens): """ Avoid extraneous whitespace in the following situations: - Immediately before the open parenthesis that starts the argument list of a function call. - Immediately before the open parenthesis that starts an indexing or slicing. Okay: spam(1) E211: spam (1) Okay: dict['key'] = list[index] E211: dict ['key'] = list[index] E211: dict['key'] = list [index] """ prev_type = tokens[0][0] prev_text = tokens[0][1] prev_end = tokens[0][3] for index in range(1, len(tokens)): token_type, text, start, end, line = tokens[index] if (token_type == tokenize.OP and text in '([' and start != prev_end and (prev_type == tokenize.NAME or prev_text in '}])') and # Syntax "class A (B):" is allowed, but avoid it (index < 2 or tokens[index - 2][1] != 'class') and # Allow "return (a.foo for a in range(5))" not keyword.iskeyword(prev_text)): yield prev_end, "E211 whitespace before '%s'" % text prev_type = token_type prev_text = text prev_end = end def whitespace_around_operator(logical_line): r""" Avoid extraneous whitespace in the following situations: - More than one space around an assignment (or other) operator to align it with another. Okay: a = 12 + 3 E221: a = 4 + 5 E222: a = 4 + 5 E223: a = 4\t+ 5 E224: a = 4 +\t5 """ for match in OPERATOR_REGEX.finditer(logical_line): before, after = match.groups() if '\t' in before: yield match.start(1), "E223 tab before operator" elif len(before) > 1: yield match.start(1), "E221 multiple spaces before operator" if '\t' in after: yield match.start(2), "E224 tab after operator" elif len(after) > 1: yield match.start(2), "E222 multiple spaces after operator" def missing_whitespace_around_operator(logical_line, tokens): r""" - Always surround these binary operators with a single space on either side: assignment (=), augmented assignment (+=, -= etc.), comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not), Booleans (and, or, not). - Use spaces around arithmetic operators. Okay: i = i + 1 Okay: submitted += 1 Okay: x = x * 2 - 1 Okay: hypot2 = x * x + y * y Okay: c = (a + b) * (a - b) Okay: foo(bar, key='word', *args, **kwargs) Okay: baz(**kwargs) Okay: negative = -1 Okay: spam(-1) Okay: alpha[:-i] Okay: if not -5 < x < +5:\n pass Okay: lambda *args, **kw: (args, kw) E225: i=i+1 E225: submitted +=1 E225: x = x*2 - 1 E225: hypot2 = x*x + y*y E225: c = (a+b) * (a-b) E225: c = alpha -4 E225: z = x **y """ parens = 0 need_space = False prev_type = tokenize.OP prev_text = prev_end = None for token_type, text, start, end, line in tokens: if token_type in (tokenize.NL, tokenize.NEWLINE, tokenize.ERRORTOKEN): # ERRORTOKEN is triggered by backticks in Python 3000 continue if text in ('(', 'lambda'): parens += 1 elif text == ')': parens -= 1 if need_space: if start != prev_end: need_space = False elif text == '>' and prev_text in ('<', '-'): # Tolerate the "<>" operator, even if running Python 3 # Deal with Python 3's annotated return value "->" pass else: yield prev_end, "E225 missing whitespace around operator" need_space = False elif token_type == tokenize.OP and prev_end is not None: if text == '=' and parens: # Allow keyword args or defaults: foo(bar=None). pass elif text in BINARY_OPERATORS: need_space = True elif text in UNARY_OPERATORS: # Allow unary operators: -123, -x, +1. # Allow argument unpacking: foo(*args, **kwargs). if prev_type == tokenize.OP: if prev_text in '}])': need_space = True elif prev_type == tokenize.NAME: if prev_text not in KEYWORDS: need_space = True elif prev_type not in SKIP_TOKENS: need_space = True if need_space and start == prev_end: yield prev_end, "E225 missing whitespace around operator" need_space = False prev_type = token_type prev_text = text prev_end = end def whitespace_around_comma(logical_line): r""" Avoid extraneous whitespace in the following situations: - More than one space around an assignment (or other) operator to align it with another. Note: these checks are disabled by default Okay: a = (1, 2) E241: a = (1, 2) E242: a = (1,\t2) """ line = logical_line for m in WHITESPACE_AFTER_COMMA_REGEX.finditer(line): found = m.start() + 1 if '\t' in m.group(): yield found, "E242 tab after '%s'" % m.group()[0] else: yield found, "E241 multiple spaces after '%s'" % m.group()[0] def whitespace_around_named_parameter_equals(logical_line, tokens): """ Don't use spaces around the '=' sign when used to indicate a keyword argument or a default parameter value. Okay: def complex(real, imag=0.0): Okay: return magic(r=real, i=imag) Okay: boolean(a == b) Okay: boolean(a != b) Okay: boolean(a <= b) Okay: boolean(a >= b) E251: def complex(real, imag = 0.0): E251: return magic(r = real, i = imag) """ parens = 0 no_space = False prev_end = None for token_type, text, start, end, line in tokens: if no_space: no_space = False if start != prev_end: yield (prev_end, "E251 no spaces around keyword / parameter equals") elif token_type == tokenize.OP: if text == '(': parens += 1 elif text == ')': parens -= 1 elif parens and text == '=': no_space = True if start != prev_end: yield (prev_end, "E251 no spaces around keyword / parameter equals") prev_end = end def whitespace_before_inline_comment(logical_line, tokens): """ Separate inline comments by at least two spaces. An inline comment is a comment on the same line as a statement. Inline comments should be separated by at least two spaces from the statement. They should start with a # and a single space. Okay: x = x + 1 # Increment x Okay: x = x + 1 # Increment x E261: x = x + 1 # Increment x E262: x = x + 1 #Increment x E262: x = x + 1 # Increment x """ prev_end = (0, 0) for token_type, text, start, end, line in tokens: if token_type == tokenize.COMMENT: if not line[:start[1]].strip(): continue if prev_end[0] == start[0] and start[1] < prev_end[1] + 2: yield (prev_end, "E261 at least two spaces before inline comment") if text.startswith('# ') or not text.startswith('# '): yield start, "E262 inline comment should start with '# '" elif token_type != tokenize.NL: prev_end = end def imports_on_separate_lines(logical_line): r""" Imports should usually be on separate lines. Okay: import os\nimport sys E401: import sys, os Okay: from subprocess import Popen, PIPE Okay: from myclas import MyClass Okay: from foo.bar.yourclass import YourClass Okay: import myclass Okay: import foo.bar.yourclass """ line = logical_line if line.startswith('import '): found = line.find(',') if -1 < found and ';' not in line[:found]: yield found, "E401 multiple imports on one line" def compound_statements(logical_line): r""" Compound statements (multiple statements on the same line) are generally discouraged. While sometimes it's okay to put an if/for/while with a small body on the same line, never do this for multi-clause statements. Also avoid folding such long lines! Okay: if foo == 'blah':\n do_blah_thing() Okay: do_one() Okay: do_two() Okay: do_three() E701: if foo == 'blah': do_blah_thing() E701: for x in lst: total += x E701: while t < 10: t = delay() E701: if foo == 'blah': do_blah_thing() E701: else: do_non_blah_thing() E701: try: something() E701: finally: cleanup() E701: if foo == 'blah': one(); two(); three() E702: do_one(); do_two(); do_three() """ line = logical_line found = line.find(':') if -1 < found < len(line) - 1: before = line[:found] if (before.count('{') <= before.count('}') and # {'a': 1} (dict) before.count('[') <= before.count(']') and # [1:2] (slice) before.count('(') <= before.count(')') and # (Python 3 annotation) not LAMBDA_REGEX.search(before)): # lambda x: x yield found, "E701 multiple statements on one line (colon)" found = line.find(';') if -1 < found: yield found, "E702 multiple statements on one line (semicolon)" def explicit_line_join(logical_line, tokens): r""" Avoid explicit line join between brackets. The preferred way of wrapping long lines is by using Python's implied line continuation inside parentheses, brackets and braces. Long lines can be broken over multiple lines by wrapping expressions in parentheses. These should be used in preference to using a backslash for line continuation. E502: aaa = [123, \\n 123] E502: aaa = ("bbb " \\n "ccc") Okay: aaa = [123,\n 123] Okay: aaa = ("bbb "\n "ccc") Okay: aaa = "bbb " \\n "ccc" """ prev_start = prev_end = parens = 0 for token_type, text, start, end, line in tokens: if start[0] != prev_start and parens and backslash: yield backslash, "E502 the backslash is redundant between brackets" if end[0] != prev_end: if line.rstrip('\r\n').endswith('\\'): backslash = (end[0], len(line.splitlines()[-1]) - 1) else: backslash = None prev_start = prev_end = end[0] else: prev_start = start[0] if token_type == tokenize.OP: if text in '([{': parens += 1 elif text in ')]}': parens -= 1 def comparison_to_singleton(logical_line): """ Comparisons to singletons like None should always be done with "is" or "is not", never the equality operators. Okay: if arg is not None: E711: if arg != None: E712: if arg == True: Also, beware of writing if x when you really mean if x is not None -- e.g. when testing whether a variable or argument that defaults to None was set to some other value. The other value might have a type (such as a container) that could be false in a boolean context! """ match = COMPARE_SINGLETON_REGEX.search(logical_line) if match: same = (match.group(1) == '==') singleton = match.group(2) msg = "'if cond is %s:'" % (('' if same else 'not ') + singleton) if singleton in ('None',): code = 'E711' else: code = 'E712' nonzero = ((singleton == 'True' and same) or (singleton == 'False' and not same)) msg += " or 'if %scond:'" % ('' if nonzero else 'not ') yield match.start(1), ("%s comparison to %s should be %s" % (code, singleton, msg)) def comparison_type(logical_line): """ Object type comparisons should always use isinstance() instead of comparing types directly. Okay: if isinstance(obj, int): E721: if type(obj) is type(1): When checking if an object is a string, keep in mind that it might be a unicode string too! In Python 2.3, str and unicode have a common base class, basestring, so you can do: Okay: if isinstance(obj, basestring): Okay: if type(a1) is type(b1): """ match = COMPARE_TYPE_REGEX.search(logical_line) if match: inst = match.group(3) if inst and isidentifier(inst) and inst not in SINGLETONS: return # Allow comparison for types which are not obvious yield match.start(1), "E721 do not compare types, use 'isinstance()'" def python_3000_has_key(logical_line): r""" The {}.has_key() method will be removed in the future version of Python. Use the 'in' operation instead. Okay: if "alph" in d:\n print d["alph"] W601: assert d.has_key('alph') """ pos = logical_line.find('.has_key(') if pos > -1: yield pos, "W601 .has_key() is deprecated, use 'in'" def python_3000_raise_comma(logical_line): """ When raising an exception, use "raise ValueError('message')" instead of the older form "raise ValueError, 'message'". The paren-using form is preferred because when the exception arguments are long or include string formatting, you don't need to use line continuation characters thanks to the containing parentheses. The older form will be removed in Python 3000. Okay: raise DummyError("Message") W602: raise DummyError, "Message" """ match = RAISE_COMMA_REGEX.match(logical_line) if match and not RERAISE_COMMA_REGEX.match(logical_line): yield match.start(1), "W602 deprecated form of raising exception" def python_3000_not_equal(logical_line): """ != can also be written <>, but this is an obsolete usage kept for backwards compatibility only. New code should always use !=. The older syntax is removed in Python 3000. Okay: if a != 'no': W603: if a <> 'no': """ pos = logical_line.find('<>') if pos > -1: yield pos, "W603 '<>' is deprecated, use '!='" def python_3000_backticks(logical_line): """ Backticks are removed in Python 3000. Use repr() instead. Okay: val = repr(1 + 2) W604: val = `1 + 2` """ pos = logical_line.find('`') if pos > -1: yield pos, "W604 backticks are deprecated, use 'repr()'" ############################################################################## # Helper functions ############################################################################## if '' == ''.encode(): # Python 2: implicit encoding. isidentifier = re.compile(r'[a-zA-Z_]\w*').match else: # Python 3 isidentifier = str.isidentifier def expand_indent(line): r""" Return the amount of indentation. Tabs are expanded to the next multiple of 8. >>> expand_indent(' ') 4 >>> expand_indent('\t') 8 >>> expand_indent(' \t') 8 >>> expand_indent(' \t') 8 >>> expand_indent(' \t') 16 """ if '\t' not in line: return len(line) - len(line.lstrip()) result = 0 for char in line: if char == '\t': result = result // 8 * 8 + 8 elif char == ' ': result += 1 else: break return result def mute_string(text): """ Replace contents with 'xxx' to prevent syntax matching. >>> mute_string('"abc"') '"xxx"' >>> mute_string("'''abc'''") "'''xxx'''" >>> mute_string("r'abc'") "r'xxx'" """ # String modifiers (e.g. u or r) start = text.index(text[-1]) + 1 end = len(text) - 1 # Triple quotes if text[-3:] in ('"""', "'''"): # ''' start += 2 end -= 2 return text[:start] + 'x' * (end - start) + text[end:] ############################################################################## # Framework to run all checks ############################################################################## def find_checks(argument_name): """ Find all globally visible functions where the first argument name starts with argument_name. """ checks = [] for name, function in list(globals().items()): if not inspect.isfunction(function): continue args = inspect.getargspec(function)[0] if args and args[0].startswith(argument_name): codes = ERRORCODE_REGEX.findall(function.__doc__ or '') # yield name, codes, function, args for code in codes or ['']: if not code or not ignore_code(code): checks.append((name, function, args)) break checks.sort() return checks def ignore_code(code): """ Check if options.ignore contains a prefix of the error code. If options.select contains a prefix of the error code, do not ignore it. """ for select in options.select: if code.startswith(select): return False for ignore in options.ignore: if code.startswith(ignore): return True #Set Physical and Logical check functions options.physical_checks = find_checks('physical_line') options.logical_checks = find_checks('logical_line') def refresh_checks(): #Refresh the Physical and Logical check functions options.physical_checks = find_checks('physical_line') options.logical_checks = find_checks('logical_line') class Checker(object): """ Load a Python source file, tokenize it, check coding style. """ def __init__(self, filename, lines=None): self._physical_checks = options.physical_checks self._logical_checks = options.logical_checks self.max_line_length = options.max_line_length self.verbose = options.verbose self.filename = filename self.lines = lines self.results = [] def readline(self): """ Get the next line from the input buffer. """ self.line_number += 1 if self.line_number > len(self.lines): return '' return self.lines[self.line_number - 1] def readline_check_physical(self): """ Check and return the next physical line. This method can be used to feed tokenize.generate_tokens. """ line = self.readline() if line: self.check_physical(line) return line def run_check(self, check, argument_names): """ Run a check plugin. """ arguments = [] for name in argument_names: arguments.append(getattr(self, name)) return check(*arguments) def check_physical(self, line): """ Run all physical checks on a raw input line. """ self.physical_line = line if self.indent_char is None and line[:1] in WHITESPACE: self.indent_char = line[0] for name, check, argument_names in self._physical_checks: result = self.run_check(check, argument_names) if result is not None: offset, text = result self.report_error(self.line_number, offset, text, check) def build_tokens_line(self): """ Build a logical line from tokens. """ self.mapping = [] logical = [] length = 0 previous = None for token in self.tokens: token_type, text = token[0:2] if token_type in SKIP_TOKENS: continue if token_type == tokenize.STRING: text = mute_string(text) if previous: end_row, end = previous[3] start_row, start = token[2] if end_row != start_row: # different row prev_text = self.lines[end_row - 1][end - 1] if prev_text == ',' or (prev_text not in '{[(' and text not in '}])'): logical.append(' ') length += 1 elif end != start: # different column fill = self.lines[end_row - 1][end:start] logical.append(fill) length += len(fill) self.mapping.append((length, token)) logical.append(text) length += len(text) previous = token self.logical_line = ''.join(logical) assert self.logical_line.strip() == self.logical_line def check_logical(self): """ Build a line from tokens and run all logical checks on it. """ self.build_tokens_line() first_line = self.lines[self.mapping[0][1][2][0] - 1] indent = first_line[:self.mapping[0][1][2][1]] self.previous_indent_level = self.indent_level self.indent_level = expand_indent(indent) for name, check, argument_names in self._logical_checks: for result in self.run_check(check, argument_names): offset, text = result if isinstance(offset, tuple): orig_number, orig_offset = offset else: for token_offset, token in self.mapping: if offset >= token_offset: orig_number = token[2][0] orig_offset = (token[2][1] + offset - token_offset) self.report_error(orig_number, orig_offset, text, check) self.previous_logical = self.logical_line def generate_tokens(self): tokengen = tokenize.generate_tokens(self.readline_check_physical) try: for token in tokengen: yield token except (SyntaxError, tokenize.TokenError): exc_type, exc = sys.exc_info()[:2] offset = exc.args[1] if len(offset) > 2: offset = offset[1:3] self.report_error(offset[0], offset[1], 'E901 %s: %s' % (exc_type.__name__, exc.args[0]), self.generate_tokens) generate_tokens.__doc__ = " Check if the syntax is valid." def check_all(self, expected=None, line_offset=0): """ Run all checks on the input file. """ self.line_number = 0 self.line_offset = line_offset self.indent_char = None self.indent_level = 0 self.previous_logical = '' self.tokens = [] self.blank_lines = blank_lines_before_comment = 0 parens = 0 for token in self.generate_tokens(): self.tokens.append(token) token_type, text = token[0:2] if self.verbose >= 3: if token[2][0] == token[3][0]: pos = '[%s:%s]' % (token[2][1] or '', token[3][1]) else: pos = 'l.%s' % token[3][0] if token_type == tokenize.OP: if text in '([{': parens += 1 elif text in '}])': parens -= 1 elif not parens: if token_type == tokenize.NEWLINE: if self.blank_lines < blank_lines_before_comment: self.blank_lines = blank_lines_before_comment self.check_logical() self.tokens = [] self.blank_lines = blank_lines_before_comment = 0 elif token_type == tokenize.NL: if len(self.tokens) == 1: # The physical line contains only this token. self.blank_lines += 1 self.tokens = [] elif token_type == tokenize.COMMENT and len(self.tokens) == 1: if blank_lines_before_comment < self.blank_lines: blank_lines_before_comment = self.blank_lines self.blank_lines = 0 if COMMENT_WITH_NL: # The comment also ends a physical line self.tokens = [] return self.results def report_error(self, line_number, offset, text, check): """ Report an error, according to options. """ if options.repeat: self.results.append("%s:%s:%d: %s" % (self.filename, self.line_offset + line_number, offset + 1, text)) if options.show_source and len(self.lines) >= line_number: line = self.lines[line_number - 1] self.results.append(line.rstrip()) self.results.append(' ' * offset + '^') if options.show_pep8: self.results.append(check.__doc__.lstrip('\n').rstrip()) def run_check(fileName, source): """ Parse options and run checks on Python source. """ try: lines = ['%s\n' % line for line in source.splitlines()] return Checker(fileName, lines).check_all() except Exception as reason: print(('pep8mod couldn\'t parse file: {0}'.format(fileName))) print(reason) raise return [] ninja-ide-2.3/ninja_ide/dependencies/pyflakes_mod/000077500000000000000000000000001216641277400222515ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/dependencies/pyflakes_mod/__init__.py000066400000000000000000000000271216641277400243610ustar00rootroot00000000000000 __version__ = '0.4.0' ninja-ide-2.3/ninja_ide/dependencies/pyflakes_mod/checker.py000066400000000000000000000470141216641277400242350ustar00rootroot00000000000000# -*- test-case-name: pyflakes -*- # (c) 2005-2008 Divmod, Inc. # See LICENSE file for details import os.path from compiler import ast #lint:disable try: import builtins except ImportError: import __builtin__ as builtins #lint:enable from ninja_ide.core import settings from ninja_ide.dependencies.pyflakes_mod import messages PYTHON_BUILTINS = dir(builtins) class Binding(object): """ Represents the binding of a value to a name. The checker uses this to keep track of which names have been bound and which names have not. See L{Assignment} for a special type of binding that is checked with stricter rules. @ivar used: pair of (L{Scope}, line-number) indicating the scope and line number that this binding was last used """ def __init__(self, name, source): self.name = name self.source = source self.used = False def __str__(self): return self.name def __repr__(self): return '<%s object %r from line %r at 0x%x>' % ( self.__class__.__name__, self.name, self.source.lineno, id(self)) class UnBinding(Binding): '''Created by the 'del' operator.''' class Importation(Binding): """ A binding created by an import statement. @ivar fullName: The complete name given to the import statement, possibly including multiple dotted components. @type fullName: C{str} """ def __init__(self, name, source): self.fullName = name name = name.split('.')[0] super(Importation, self).__init__(name, source) class Argument(Binding): """ Represents binding a name as an argument. """ class Assignment(Binding): """ Represents binding a name with an explicit assignment. The checker will raise warnings for any Assignment that isn't used. Also, the checker does not consider assignments in tuple/list unpacking to be Assignments, rather it treats them as simple Bindings. """ class FunctionDefinition(Binding): pass class ExportBinding(Binding): """ A binding created by an C{__all__} assignment. If the names in the list can be determined statically, they will be treated as names for export and additional checking applied to them. The only C{__all__} assignment that can be recognized is one which takes the value of a literal list containing literal strings. For example:: __all__ = ["foo", "bar"] Names which are imported and not otherwise used but appear in the value of C{__all__} will not have an unused import warning reported for them. """ def names(self): """ Return a list of the names referenced by this binding. """ names = [] if isinstance(self.source, ast.List): for node in self.source.nodes: if isinstance(node, ast.Const): names.append(node.value) return names class Scope(dict): importStarred = False # set to True when import * is found def __repr__(self): return '<%s at 0x%x %s>' % (self.__class__.__name__, id(self), dict.__repr__(self)) def __init__(self): super(Scope, self).__init__() class ClassScope(Scope): pass class FunctionScope(Scope): """ I represent a name scope for a function. @ivar globals: Names declared 'global' in this function. """ def __init__(self): super(FunctionScope, self).__init__() self.globals = {} class ModuleScope(Scope): pass # Globally defined names which are not attributes of the builtins module. _MAGIC_GLOBALS = ['__file__', '__builtins__'] class Checker(object): """ I check the cleanliness and sanity of Python code. @ivar _deferredFunctions: Tracking list used by L{deferFunction}. Elements of the list are two-tuples. The first element is the callable passed to L{deferFunction}. The second element is a copy of the scope stack at the time L{deferFunction} was called. @ivar _deferredAssignments: Similar to C{_deferredFunctions}, but for callables which are deferred assignment checks. """ nodeDepth = 0 traceTree = False builtIns = set(dir(builtins)) | set(_MAGIC_GLOBALS) def __init__(self, tree, filename='(none)', builtins=None): self._deferredFunctions = [] self._deferredAssignments = [] self.dead_scopes = [] self.messages = [] self.filename = filename if builtins: self.builtIns = self.builtIns.union(builtins) self.scopeStack = [ModuleScope()] self.futuresAllowed = True self.handleChildren(tree) self._runDeferred(self._deferredFunctions) # Set _deferredFunctions to None so that deferFunction will fail # noisily if called after we've run through the deferred functions. self._deferredFunctions = None self._runDeferred(self._deferredAssignments) # Set _deferredAssignments to None so that deferAssignment will fail # noisly if called after we've run through the deferred assignments. self._deferredAssignments = None del self.scopeStack[1:] self.popScope() self.check_dead_scopes() def deferFunction(self, callable): ''' Schedule a function handler to be called just before completion. This is used for handling function bodies, which must be deferred because code later in the file might modify the global scope. When `callable` is called, the scope at the time this is called will be restored, however it will contain any new bindings added to it. ''' self._deferredFunctions.append((callable, self.scopeStack[:])) def deferAssignment(self, callable): """ Schedule an assignment handler to be called just after deferred function handlers. """ self._deferredAssignments.append((callable, self.scopeStack[:])) def _runDeferred(self, deferred): """ Run the callables in C{deferred} using their associated scope stack. """ for handler, scope in deferred: self.scopeStack = scope handler() def scope(self): return self.scopeStack[-1] scope = property(scope) def popScope(self): self.dead_scopes.append(self.scopeStack.pop()) def check_dead_scopes(self): """ Look at scopes which have been fully examined and report names in them which were imported but unused. """ for scope in self.dead_scopes: export = isinstance(scope.get('__all__'), ExportBinding) if export: alls = scope['__all__'].names() if os.path.split(self.filename)[1] != '__init__.py': # Look for possible mistakes in the export list undefined = set(alls) - set(scope) for name in undefined: self.report( messages.UndefinedExport, scope['__all__'].source.lineno, name) else: alls = [] # Look for imported names that aren't used. for importation in scope.itervalues(): if isinstance(importation, Importation): if not importation.used and importation.name not in alls: self.report( messages.UnusedImport, importation.source.lineno, importation.name) def pushFunctionScope(self): self.scopeStack.append(FunctionScope()) def pushClassScope(self): self.scopeStack.append(ClassScope()) def report(self, messageClass, *args, **kwargs): self.messages.append(messageClass(self.filename, *args, **kwargs)) def handleChildren(self, tree): for node in tree.getChildNodes(): self.handleNode(node, tree) def handleNode(self, node, parent): node.parent = parent if self.traceTree: print(' ' * self.nodeDepth + node.__class__.__name__) self.nodeDepth += 1 nodeType = node.__class__.__name__.upper() if nodeType not in ('STMT', 'FROM'): self.futuresAllowed = False try: handler = getattr(self, nodeType) handler(node) finally: self.nodeDepth -= 1 if self.traceTree: print(' ' * self.nodeDepth + 'end ' + node.__class__.__name__) def ignore(self, node): pass STMT = SET = PRINT = PRINTNL = TUPLE = LIST = ASSTUPLE = ASSATTR = \ ASSLIST = GETATTR = SLICE = SLICEOBJ = IF = CALLFUNC = DISCARD = \ RETURN = ADD = MOD = SUB = NOT = UNARYSUB = INVERT = ASSERT = COMPARE = \ SUBSCRIPT = AND = OR = TRYEXCEPT = RAISE = YIELD = DICT = LEFTSHIFT = \ RIGHTSHIFT = KEYWORD = TRYFINALLY = WHILE = EXEC = MUL = DIV = POWER = \ FLOORDIV = BITAND = BITOR = BITXOR = LISTCOMPFOR = LISTCOMPIF = \ AUGASSIGN = BACKQUOTE = UNARYADD = GENEXPR = GENEXPRFOR = GENEXPRIF = \ IFEXP = handleChildren CONST = PASS = CONTINUE = BREAK = ELLIPSIS = ignore def addBinding(self, lineno, value, reportRedef=True): '''Called when a binding is altered. - `lineno` is the line of the statement responsible for the change - `value` is the optional new value, a Binding instance, associated with the binding; if None, the binding is deleted if it exists. - if `reportRedef` is True (default), rebinding while unused will be reported. ''' if (isinstance(self.scope.get(value.name), FunctionDefinition) and isinstance(value, FunctionDefinition)): self.report(messages.RedefinedFunction, lineno, value.name, self.scope[value.name].source.lineno) if not isinstance(self.scope, ClassScope): for scope in self.scopeStack[::-1]: existing = scope.get(value.name) if (isinstance(existing, Importation) and not existing.used and (not isinstance(value, Importation) or value.fullName == existing.fullName) and reportRedef): self.report(messages.RedefinedWhileUnused, lineno, value.name, scope[value.name].source.lineno) if isinstance(value, UnBinding): try: del self.scope[value.name] except KeyError: self.report(messages.UndefinedName, lineno, value.name) else: self.scope[value.name] = value if settings.CHECK_FOR_DOCSTRINGS and \ ((value.__class__ is FunctionDefinition) or (value.source.__class__.__name__.upper() == "CLASS")): doc = value.source.doc if doc is None: self.report(messages.DocstringMissing, lineno, value.name) if isinstance(value, Assignment) and (value.name in PYTHON_BUILTINS): self.report(messages.BuiltinOverlap, lineno, value.name) def WITH(self, node): """ Handle C{with} by checking the target of the statement (which can be an identifier, a list or tuple of targets, an attribute, etc) for undefined names and defining any it adds to the scope and by continuing to process the suite within the statement. """ # Check the "foo" part of a "with foo as bar" statement. Do this no # matter what, since there's always a "foo" part. self.handleNode(node.expr, node) if node.vars is not None: self.handleNode(node.vars, node) self.handleChildren(node.body) def GLOBAL(self, node): """ Keep track of globals declarations. """ if isinstance(self.scope, FunctionScope): self.scope.globals.update(dict.fromkeys(node.names)) def LISTCOMP(self, node): for qual in node.quals: self.handleNode(qual, node) self.handleNode(node.expr, node) GENEXPRINNER = LISTCOMP def FOR(self, node): """ Process bindings for loop variables. """ variables = [] def collectLoopVars(n): if hasattr(n, 'name'): variables.append(n.name) else: for c in n.getChildNodes(): collectLoopVars(c) collectLoopVars(node.assign) for varn in variables: if (isinstance(self.scope.get(varn), Importation) # unused ones will get an unused import warning and self.scope[varn].used): self.report(messages.ImportShadowedByLoopVar, node.lineno, varn, self.scope[varn].source.lineno) self.handleChildren(node) def NAME(self, node): """ Locate the name in locals / function / globals scopes. """ # try local scope importStarred = self.scope.importStarred try: self.scope[node.name].used = (self.scope, node.lineno) except KeyError: pass else: return # try enclosing function scopes for scope in self.scopeStack[-2:0:-1]: importStarred = importStarred or scope.importStarred if not isinstance(scope, FunctionScope): continue try: scope[node.name].used = (self.scope, node.lineno) except KeyError: pass else: return # try global scope importStarred = importStarred or self.scopeStack[0].importStarred try: self.scopeStack[0][node.name].used = (self.scope, node.lineno) except KeyError: if not importStarred and node.name not in self.builtIns: if (os.path.basename(self.filename) == '__init__.py' and node.name == '__path__'): # the special name __path__ is valid only in packages pass else: self.report(messages.UndefinedName, node.lineno, node.name) def FUNCTION(self, node): if getattr(node, "decorators", None) is not None: self.handleChildren(node.decorators) self.addBinding(node.lineno, FunctionDefinition(node.name, node)) self.LAMBDA(node) def LAMBDA(self, node): for default in node.defaults: self.handleNode(default, node) def runFunction(): args = [] def addArgs(arglist): for arg in arglist: if isinstance(arg, tuple): addArgs(arg) else: if arg in args: self.report(messages.DuplicateArgument, node.lineno, arg) args.append(arg) self.pushFunctionScope() addArgs(node.argnames) for name in args: self.addBinding(node.lineno, Argument(name, node), reportRedef=False) self.handleNode(node.code, node) def checkUnusedAssignments(): """ Check to see if any assignments have not been used. """ for name, binding in self.scope.iteritems(): if (not binding.used and not name in self.scope.globals and isinstance(binding, Assignment)): self.report(messages.UnusedVariable, binding.source.lineno, name) self.deferAssignment(checkUnusedAssignments) self.popScope() self.deferFunction(runFunction) def CLASS(self, node): """ Check names used in a class definition, including its decorators, base classes, and the body of its definition. Additionally, add its name to the current scope. """ if getattr(node, "decorators", None) is not None: self.handleChildren(node.decorators) for baseNode in node.bases: self.handleNode(baseNode, node) self.addBinding(node.lineno, Binding(node.name, node)) self.pushClassScope() self.handleChildren(node.code) self.popScope() def ASSNAME(self, node): if node.flags == 'OP_DELETE': if isinstance(self.scope, FunctionScope) and \ node.name in self.scope.globals: del self.scope.globals[node.name] else: self.addBinding(node.lineno, UnBinding(node.name, node)) else: # if the name hasn't already been defined in the current scope if isinstance(self.scope, FunctionScope) and \ node.name not in self.scope: # for each function or module scope above us for scope in self.scopeStack[:-1]: if not isinstance(scope, (FunctionScope, ModuleScope)): continue # if the name was defined in that scope, and the name has # been accessed already in the current scope, and hasn't # been declared global if (node.name in scope and scope[node.name].used and scope[node.name].used[0] is self.scope and node.name not in self.scope.globals): # then it's probably a mistake self.report(messages.UndefinedLocal, scope[node.name].used[1], node.name, scope[node.name].source.lineno) break if isinstance(node.parent, (ast.For, ast.ListCompFor, ast.GenExprFor, ast.AssTuple, ast.AssList)): binding = Binding(node.name, node) elif (node.name == '__all__' and isinstance(self.scope, ModuleScope) and isinstance(node.parent, ast.Assign)): binding = ExportBinding(node.name, node.parent.expr) else: binding = Assignment(node.name, node) if node.name in self.scope: binding.used = self.scope[node.name].used self.addBinding(node.lineno, binding) def ASSIGN(self, node): self.handleNode(node.expr, node) for subnode in node.nodes[::-1]: self.handleNode(subnode, node) def IMPORT(self, node): for name, alias in node.names: name = alias or name importation = Importation(name, node) self.addBinding(node.lineno, importation) def FROM(self, node): if node.modname == '__future__': if not self.futuresAllowed: self.report(messages.LateFutureImport, node.lineno, [n[0] for n in node.names]) else: self.futuresAllowed = False for name, alias in node.names: if name == '*': self.scope.importStarred = True continue name = alias or name importation = Importation(name, node) if node.modname == '__future__': importation.used = (self.scope, node.lineno) self.addBinding(node.lineno, importation) ninja-ide-2.3/ninja_ide/dependencies/pyflakes_mod/messages.py000066400000000000000000000067311216641277400244410ustar00rootroot00000000000000# Fork from PyFlakes customized for NINJA-IDE class Message(object): message = '' message_args = () def __init__(self, filename, lineno): self.filename = filename self.lineno = lineno def __str__(self): return '%s:%s: %s' % (self.filename, self.lineno, self.message % self.message_args) class UnusedImport(Message): message = '%r imported but unused' def __init__(self, filename, lineno, name): Message.__init__(self, filename, lineno) self.message_args = (name,) class RedefinedWhileUnused(Message): message = 'redefinition of unused %r from line %r' def __init__(self, filename, lineno, name, orig_lineno): Message.__init__(self, filename, lineno) self.message_args = (name, orig_lineno) class ImportShadowedByLoopVar(Message): message = 'import %r from line %r shadowed by loop variable' def __init__(self, filename, lineno, name, orig_lineno): Message.__init__(self, filename, lineno) self.message_args = (name, orig_lineno) class ImportStarUsed(Message): message = "'from %s import *' used; unable to detect undefined names" def __init__(self, filename, lineno, modname): Message.__init__(self, filename, lineno) self.message_args = (modname,) class UndefinedName(Message): message = 'undefined name %r' def __init__(self, filename, lineno, name): Message.__init__(self, filename, lineno) self.message_args = (name,) class UndefinedExport(Message): message = 'undefined name %r in __all__' def __init__(self, filename, lineno, name): Message.__init__(self, filename, lineno) self.message_args = (name,) class UndefinedLocal(Message): message = ("local variable %r (defined in enclosing scope on " "line %r) referenced before assignment") def __init__(self, filename, lineno, name, orig_lineno): Message.__init__(self, filename, lineno) self.message_args = (name, orig_lineno) class DuplicateArgument(Message): message = 'duplicate argument %r in function definition' def __init__(self, filename, lineno, name): Message.__init__(self, filename, lineno) self.message_args = (name,) class RedefinedFunction(Message): message = 'redefinition of function %r from line %r' def __init__(self, filename, lineno, name, orig_lineno): Message.__init__(self, filename, lineno) self.message_args = (name, orig_lineno) class LateFutureImport(Message): message = 'future import(s) %r after other statements' def __init__(self, filename, lineno, names): Message.__init__(self, filename, lineno) self.message_args = (names,) class UnusedVariable(Message): """ Indicates that a variable has been explicity assigned to but not actually used. """ message = 'local variable %r is assigned to but never used' def __init__(self, filename, lineno, names): Message.__init__(self, filename, lineno) self.message_args = (names,) class BuiltinOverlap(Message): message = 'variable name "%s" overlaps with __builtin__.%s' def __init__(self, filename, lineno, names): Message.__init__(self, filename, lineno) self.message_args = (names, names) class DocstringMissing(Message): message = 'missing docstring for %s' def __init__(self, filename, lineno, names): Message.__init__(self, filename, lineno) self.message_args = (names,) ninja-ide-2.3/ninja_ide/dependencies/pyflakes_mod/scripts/000077500000000000000000000000001216641277400237405ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/dependencies/pyflakes_mod/scripts/__init__.py000066400000000000000000000000001216641277400260370ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/dependencies/pyflakes_mod/scripts/pyflakes.py000066400000000000000000000054161216641277400261360ustar00rootroot00000000000000 """ Implementation of the command-line I{pyflakes} tool. """ import sys import compiler import os checker = __import__('ninja_ide.dependencies.pyflakes.checker').checker def check(codeString, filename): """ Check the Python source given by C{codeString} for flakes. @param codeString: The Python source to check. @type codeString: C{str} @param filename: The name of the file the source came from, used to report errors. @type filename: C{str} @return: The number of warnings emitted. @rtype: C{int} """ # Since compiler.parse does not reliably report syntax errors, use the # built in compiler first to detect those. try: try: compile(codeString, filename, "exec") except MemoryError: # Python 2.4 will raise MemoryError if the source can't be # decoded. if sys.version_info[:2] == (2, 4): raise SyntaxError(None) raise except (SyntaxError, IndentationError) as value: msg = value.args[0] (lineno, offset, text) = value.lineno, value.offset, value.text # If there's an encoding problem with the file, the text is None. if text is None: # Avoid using msg, since for the only known case, it contains a # bogus message that claims the encoding the file declared was # unknown. pass else: line = text.splitlines()[-1] if offset is not None: offset = offset - (len(text) - len(line)) if offset is not None: pass return 1 else: # Okay, it's syntactically valid. Now parse it into an ast and check # it. tree = compiler.parse(codeString) w = checker.Checker(tree, filename) w.messages.sort(lambda a, b: cmp(a.lineno, b.lineno)) for warning in w.messages: print(warning) return len(w.messages) def checkPath(filename): """ Check the given path, printing out any warnings detected. @return: the number of warnings printed """ try: return check(file(filename, 'U').read() + '\n', filename) except IOError as msg: return 1 def main(): warnings = 0 args = sys.argv[1:] if args: for arg in args: if os.path.isdir(arg): for dirpath, dirnames, filenames in os.walk(arg): for filename in filenames: if filename.endswith('.py'): warnings += checkPath( os.path.join(dirpath, filename)) else: warnings += checkPath(arg) else: warnings += check(sys.stdin.read(), '') raise SystemExit(warnings > 0) ninja-ide-2.3/ninja_ide/gui/000077500000000000000000000000001216641277400157325ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/gui/__init__.py000066400000000000000000000012641216641277400200460ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . ninja-ide-2.3/ninja_ide/gui/actions.py000066400000000000000000001373061216641277400177560ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import re import sys import webbrowser from PyQt4.QtCore import Qt from PyQt4.QtCore import QObject from PyQt4.QtCore import QSettings from PyQt4.QtCore import SIGNAL from PyQt4.QtGui import QApplication from PyQt4.QtGui import QKeySequence from PyQt4.QtGui import QInputDialog from PyQt4.QtGui import QMessageBox from PyQt4.QtGui import QShortcut from ninja_ide.core import file_manager from ninja_ide.core import settings from ninja_ide import resources from ninja_ide.tools import ui_tools from ninja_ide.tools import locator from ninja_ide.tools import json_manager from ninja_ide.gui.editor import editor from ninja_ide.gui.editor import helpers from ninja_ide.gui.dialogs import from_import_dialog from ninja_ide.gui.main_panel import class_diagram from ninja_ide.gui.main_panel import tab_group __actionsInstance = None # Actions Singleton def Actions(*args, **kw): global __actionsInstance if __actionsInstance is None: __actionsInstance = __Actions(*args, **kw) return __actionsInstance class __Actions(QObject): """This class is like the Sauron's Ring: One ring to rule them all, One ring to find them, One ring to bring them all and in the darkness bind them. This Class knows all the containers, and its know by all the containers, but the containers don't need to know between each other, in this way we can keep a better api without the need to tie the behaviour between the widgets, and let them just consume the 'actions' they need.""" def __init__(self): QObject.__init__(self) #Definition Locator self._locator = locator.Locator() self.__codeBack = [] self.__codeForward = [] self.__bookmarksFile = '' self.__bookmarksPos = -1 self.__breakpointsFile = '' self.__breakpointsPos = -1 self.__operations = { 0: self._navigate_code_jumps, 1: self._navigate_bookmarks, 2: self._navigate_breakpoints} def install_shortcuts(self, ide): """Install the shortcuts to the IDE.""" self.ide = ide short = resources.get_shortcut self.shortChangeTab = QShortcut(short("Change-Tab"), self.ide) self.shortChangeTabReverse = QShortcut( short("Change-Tab-Reverse"), self.ide) self.shortMoveTabToRight = QShortcut( short("Move-Tab-to-right"), self.ide) self.shortMoveTabToLeft = QShortcut( short("Move-Tab-to-left"), self.ide) self.shortDuplicate = QShortcut(short("Duplicate"), self.ide) self.shortRemove = QShortcut(short("Remove-line"), self.ide) self.shortMoveUp = QShortcut(short("Move-up"), self.ide) self.shortMoveDown = QShortcut(short("Move-down"), self.ide) self.shortCloseTab = QShortcut(short("Close-tab"), self.ide) self.shortNew = QShortcut(short("New-file"), self.ide) self.shortNewProject = QShortcut(short("New-project"), self.ide) self.shortOpen = QShortcut(short("Open-file"), self.ide) self.shortOpenProject = QShortcut(short("Open-project"), self.ide) self.shortSave = QShortcut(short("Save-file"), self.ide) self.shortSaveProject = QShortcut(short("Save-project"), self.ide) self.shortPrint = QShortcut(short("Print-file"), self.ide) self.shortRedo = QShortcut(short("Redo"), self.ide) self.shortAddBookmark = QShortcut(short("Add-Bookmark-or-Breakpoint"), self.ide) self.shortComment = QShortcut(short("Comment"), self.ide) self.shortUncomment = QShortcut(short("Uncomment"), self.ide) self.shortHorizontalLine = QShortcut(short("Horizontal-line"), self.ide) self.shortTitleComment = QShortcut(short("Title-comment"), self.ide) self.shortIndentLess = QShortcut(short("Indent-less"), self.ide) self.shortHideMisc = QShortcut(short("Hide-misc"), self.ide) self.shortHideEditor = QShortcut(short("Hide-editor"), self.ide) self.shortHideExplorer = QShortcut(short("Hide-explorer"), self.ide) self.shortRunFile = QShortcut(short("Run-file"), self.ide) self.shortRunProject = QShortcut(short("Run-project"), self.ide) self.shortSwitchFocus = QShortcut(short("Switch-Focus"), self.ide) self.shortStopExecution = QShortcut(short("Stop-execution"), self.ide) self.shortHideAll = QShortcut(short("Hide-all"), self.ide) self.shortFullscreen = QShortcut(short("Full-screen"), self.ide) self.shortFind = QShortcut(short("Find"), self.ide) self.shortFindNext = QShortcut(short("Find-next"), self.ide) self.shortFindPrevious = QShortcut(short("Find-previous"), self.ide) self.shortFindReplace = QShortcut(short("Find-replace"), self.ide) self.shortFindWithWord = QShortcut(short("Find-with-word"), self.ide) self.shortHelp = QShortcut(short("Help"), self.ide) self.shortSplitHorizontal = QShortcut(short("Split-horizontal"), self.ide) self.shortSplitVertical = QShortcut(short("Split-vertical"), self.ide) self.shortFollowMode = QShortcut(short("Follow-mode"), self.ide) self.shortReloadFile = QShortcut(short("Reload-file"), self.ide) self.shortFindInFiles = QShortcut(short("Find-in-files"), self.ide) self.shortImport = QShortcut(short("Import"), self.ide) self.shortGoToDefinition = QShortcut(short("Go-to-definition"), self.ide) self.shortCompleteDeclarations = QShortcut( short("Complete-Declarations"), self.ide) self.shortCodeLocator = QShortcut(short("Code-locator"), self.ide) self.shortFileOpener = QShortcut(short("File-Opener"), self.ide) self.shortNavigateBack = QShortcut(short("Navigate-back"), self.ide) self.shortNavigateForward = QShortcut(short("Navigate-forward"), self.ide) self.shortOpenLastTabOpened = QShortcut(short("Open-recent-closed"), self.ide) self.shortShowCodeNav = QShortcut(short("Show-Code-Nav"), self.ide) self.shortShowPasteHistory = QShortcut(short("Show-Paste-History"), self.ide) self.shortPasteHistory = QShortcut(short("History-Paste"), self.ide) self.shortCopyHistory = QShortcut(short("History-Copy"), self.ide) self.shortHighlightWord = QShortcut(short("Highlight-Word"), self.ide) self.shortChangeSplitFocus = QShortcut(short("change-split-focus"), self.ide) self.shortMoveTabSplit = QShortcut(short("move-tab-to-next-split"), self.ide) self.shortChangeTabVisibility = QShortcut( short("change-tab-visibility"), self.ide) #Connect Shortcuts Signals self.connect(self.shortNavigateBack, SIGNAL("activated()"), lambda: self.__navigate_with_keyboard(False)) self.connect(self.shortNavigateForward, SIGNAL("activated()"), lambda: self.__navigate_with_keyboard(True)) self.connect(self.shortCodeLocator, SIGNAL("activated()"), self.ide.status.show_locator) self.connect(self.shortFileOpener, SIGNAL("activated()"), self.ide.status.show_file_opener) self.connect(self.shortGoToDefinition, SIGNAL("activated()"), self.editor_go_to_definition) self.connect(self.shortCompleteDeclarations, SIGNAL("activated()"), self.editor_complete_declaration) self.connect(self.shortRedo, SIGNAL("activated()"), self.editor_redo) self.connect(self.shortHorizontalLine, SIGNAL("activated()"), self.editor_insert_horizontal_line) self.connect(self.shortTitleComment, SIGNAL("activated()"), self.editor_insert_title_comment) self.connect(self.shortFollowMode, SIGNAL("activated()"), self.ide.mainContainer.show_follow_mode) self.connect(self.shortReloadFile, SIGNAL("activated()"), self.ide.mainContainer.reload_file) self.connect(self.shortSplitHorizontal, SIGNAL("activated()"), lambda: self.ide.mainContainer.split_tab(True)) self.connect(self.shortSplitVertical, SIGNAL("activated()"), lambda: self.ide.mainContainer.split_tab(False)) self.connect(self.shortNew, SIGNAL("activated()"), self.ide.mainContainer.add_editor) self.connect(self.shortNewProject, SIGNAL("activated()"), self.ide.explorer.create_new_project) self.connect(self.shortHideMisc, SIGNAL("activated()"), self.view_misc_visibility) self.connect(self.shortHideEditor, SIGNAL("activated()"), self.view_main_visibility) self.connect(self.shortHideExplorer, SIGNAL("activated()"), self.view_explorer_visibility) self.connect(self.shortHideAll, SIGNAL("activated()"), self.hide_all) self.connect(self.shortFullscreen, SIGNAL("activated()"), self.fullscreen_mode) self.connect(self.shortOpen, SIGNAL("activated()"), self.ide.mainContainer.open_file) self.connect(self.shortOpenProject, SIGNAL("activated()"), self.open_project) self.connect(self.shortCloseTab, SIGNAL("activated()"), self.ide.mainContainer.close_tab) self.connect(self.shortSave, SIGNAL("activated()"), self.ide.mainContainer.save_file) self.connect(self.shortSaveProject, SIGNAL("activated()"), self.save_project) self.connect(self.shortPrint, SIGNAL("activated()"), self.print_file) self.connect(self.shortFind, SIGNAL("activated()"), self.ide.status.show) self.connect(self.shortFindPrevious, SIGNAL("activated()"), self.ide.status._searchWidget.find_previous) self.connect(self.shortFindNext, SIGNAL("activated()"), self.ide.status._searchWidget.find_next) self.connect(self.shortFindWithWord, SIGNAL("activated()"), self.ide.status.show_with_word) self.connect(self.shortFindReplace, SIGNAL("activated()"), self.ide.status.show_replace) self.connect(self.shortRunFile, SIGNAL("activated()"), self.execute_file) self.connect(self.shortRunProject, SIGNAL("activated()"), self.execute_project) self.connect(self.shortSwitchFocus, SIGNAL("activated()"), self.switch_focus) self.connect(self.shortStopExecution, SIGNAL("activated()"), self.kill_execution) self.connect(self.shortIndentLess, SIGNAL("activated()"), self.editor_indent_less) self.connect(self.shortComment, SIGNAL("activated()"), self.editor_comment) self.connect(self.shortUncomment, SIGNAL("activated()"), self.editor_uncomment) self.connect(self.shortHelp, SIGNAL("activated()"), self.ide.mainContainer.show_python_doc) self.connect(self.shortImport, SIGNAL("activated()"), self.import_from_everywhere) self.connect(self.shortFindInFiles, SIGNAL("activated()"), self.ide.misc.show_find_in_files_widget) self.connect(self.shortMoveUp, SIGNAL("activated()"), self.editor_move_up) self.connect(self.shortMoveDown, SIGNAL("activated()"), self.editor_move_down) self.connect(self.shortRemove, SIGNAL("activated()"), self.editor_remove_line) self.connect(self.shortDuplicate, SIGNAL("activated()"), self.editor_duplicate) self.connect(self.shortOpenLastTabOpened, SIGNAL("activated()"), self.reopen_last_tab) self.connect(self.shortChangeTab, SIGNAL("activated()"), self.ide.mainContainer.change_tab) self.connect(self.shortChangeTabReverse, SIGNAL("activated()"), self.ide.mainContainer.change_tab_reverse) self.connect(self.shortMoveTabToRight, SIGNAL("activated()"), self.move_tab) self.connect(self.shortMoveTabToLeft, SIGNAL("activated()"), lambda: self.move_tab(next=False)) self.connect(self.shortShowCodeNav, SIGNAL("activated()"), self.ide.mainContainer.show_navigation_buttons) self.connect(self.shortAddBookmark, SIGNAL("activated()"), self._add_bookmark_breakpoint) self.connect(self.shortShowPasteHistory, SIGNAL("activated()"), self.ide.central.lateralPanel.combo.showPopup) self.connect(self.shortCopyHistory, SIGNAL("activated()"), self._copy_history) self.connect(self.shortPasteHistory, SIGNAL("activated()"), self._paste_history) self.connect(self.shortHighlightWord, SIGNAL("activated()"), self.editor_highlight_word) self.connect(self.shortChangeSplitFocus, SIGNAL("activated()"), self.ide.mainContainer.change_split_focus) self.connect(self.shortMoveTabSplit, SIGNAL("activated()"), self.move_tab_to_next_split) self.connect(self.shortChangeTabVisibility, SIGNAL("activated()"), self.ide.mainContainer.change_tabs_visibility) key = Qt.Key_1 for i in range(10): if sys.platform == "darwin": short = TabShortcuts( QKeySequence(Qt.CTRL + Qt.ALT + key), self.ide, i) else: short = TabShortcuts(QKeySequence(Qt.ALT + key), self.ide, i) key += 1 self.connect(short, SIGNAL("activated()"), self._change_tab_index) short = TabShortcuts(QKeySequence(Qt.ALT + Qt.Key_0), self.ide, 10) self.connect(short, SIGNAL("activated()"), self._change_tab_index) #Connect SIGNALs from other objects self.connect(self.ide.mainContainer._tabMain, SIGNAL("runFile()"), self.execute_file) self.connect(self.ide.mainContainer._tabSecondary, SIGNAL("runFile()"), self.execute_file) self.connect(self.ide.mainContainer._tabMain, SIGNAL("addToProject(QString)"), self._add_file_to_project) self.connect(self.ide.mainContainer._tabSecondary, SIGNAL("addToProject(QString)"), self._add_file_to_project) self.connect(self.ide.mainContainer, SIGNAL("openProject(QString)"), self.open_project) # Not Configurable Shortcuts self._shortEscStatus = QShortcut(QKeySequence(Qt.Key_Escape), self.ide.status) self._shortEscMisc = QShortcut(QKeySequence(Qt.Key_Escape), self.ide.misc) self.connect(self._shortEscStatus, SIGNAL("activated()"), self.ide.status.hide_status) self.connect(self._shortEscMisc, SIGNAL("activated()"), self.ide.misc.hide) def update_shortcuts(self): """If the user update the key binded to any shortcut, update them.""" resources.load_shortcuts() short = resources.get_shortcut self.shortDuplicate.setKey(short("Duplicate")) self.shortRemove.setKey(short("Remove-line")) self.shortMoveUp.setKey(short("Move-up")) self.shortMoveDown.setKey(short("Move-down")) self.shortCloseTab.setKey(short("Close-tab")) self.shortNew.setKey(short("New-file")) self.shortNewProject.setKey(short("New-project")) self.shortOpen.setKey(short("Open-file")) self.shortOpenProject.setKey(short("Open-project")) self.shortSave.setKey(short("Save-file")) self.shortSaveProject.setKey(short("Save-project")) self.shortPrint.setKey(short("Print-file")) self.shortRedo.setKey(short("Redo")) self.shortComment.setKey(short("Comment")) self.shortUncomment.setKey(short("Uncomment")) self.shortHorizontalLine.setKey(short("Horizontal-line")) self.shortTitleComment.setKey(short("Title-comment")) self.shortIndentLess.setKey(short("Indent-less")) self.shortHideMisc.setKey(short("Hide-misc")) self.shortHideEditor.setKey(short("Hide-editor")) self.shortHideExplorer.setKey(short("Hide-explorer")) self.shortRunFile.setKey(short("Run-file")) self.shortRunProject.setKey(short("Run-project")) self.shortSwitchFocus.setKey(short("Switch-Focus")) self.shortStopExecution.setKey(short("Stop-execution")) self.shortHideAll.setKey(short("Hide-all")) self.shortFullscreen.setKey(short("Full-screen")) self.shortFind.setKey(short("Find")) self.shortFindNext.setKey(short("Find-next")) self.shortFindPrevious.setKey(short("Find-previous")) self.shortFindReplace.setKey(short("Find-replace")) self.shortFindWithWord.setKey(short("Find-with-word")) self.shortHelp.setKey(short("Help")) self.shortSplitHorizontal.setKey(short("Split-horizontal")) self.shortSplitVertical.setKey(short("Split-vertical")) self.shortFollowMode.setKey(short("Follow-mode")) self.shortReloadFile.setKey(short("Reload-file")) self.shortFindInFiles.setKey(short("Find-in-files")) self.shortImport.setKey(short("Import")) self.shortGoToDefinition.setKey(short("Go-to-definition")) self.shortCompleteDeclarations.setKey(short("Complete-Declarations")) self.shortCodeLocator.setKey(short("Code-locator")) self.shortFileOpener.setKey(short("File-Opener")) self.shortNavigateBack.setKey(short("Navigate-back")) self.shortNavigateForward.setKey(short("Navigate-forward")) self.shortOpenLastTabOpened.setKey(short("Open-recent-closed")) self.shortChangeTab.setKey(short("Change-Tab")) self.shortChangeTabReverse.setKey(short("Change-Tab-Reverse")) self.shortMoveTabToRight.setKey(short("Move-Tab-to-right")) self.shortMoveTabToLeft.setKey(short("Move-Tab-to-left")) self.shortAddBookmark.setKey(short("Add-Bookmark-or-Breakpoint")) self.shortShowCodeNav.setKey(short("Show-Code-Nav")) self.shortShowPasteHistory.setKey(short("Show-Paste-History")) self.shortPasteHistory.setKey(short("History-Paste")) self.shortCopyHistory.setKey(short("History-Copy")) self.shortHighlightWord.setKey(short("Highlight-Word")) self.shortChangeSplitFocus.setKey(short("change-split-focus")) self.shortMoveTabSplit.setKey(short("move-tab-to-next-split")) self.shortChangeTabVisibility.setKey(short("change-tab-visibility")) def move_tab_to_next_split(self): self.ide.mainContainer.move_tab_to_next_split( self.ide.mainContainer.actualTab) def switch_focus(self): widget = QApplication.focusWidget() if widget: if widget in (self.ide.mainContainer.actualTab, self.ide.mainContainer.actualTab.currentWidget()): self.ide.explorer.currentWidget().setFocus() elif widget in (self.ide.explorer, self.ide.explorer.currentWidget()): if self.ide.misc.isVisible(): self.ide.misc.stack.currentWidget().setFocus() else: self.ide.mainContainer.actualTab.currentWidget().setFocus() elif widget.parent() is self.ide.misc.stack: self.ide.mainContainer.actualTab.currentWidget().setFocus() def _change_tab_index(self): editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): container = self.ide.mainContainer.actualTab else: container = self.ide.explorer obj = self.sender() if obj.index < container.count(): container.setCurrentIndex(obj.index) def _copy_history(self): """Copy the selected text into the copy/paste history.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): cursor = editorWidget.textCursor() copy = cursor.selectedText() self.ide.central.lateralPanel.add_new_copy(copy) def _paste_history(self): """Paste the text from the copy/paste history.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): cursor = editorWidget.textCursor() paste = self.ide.central.lateralPanel.get_paste() cursor.insertText(paste) def _add_bookmark_breakpoint(self): """Add a bookmark or breakpoint to the current file in the editor.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): if self.ide.mainContainer.actualTab.navigator.operation == 1: editorWidget._sidebarWidget.set_bookmark( editorWidget.textCursor().blockNumber()) elif self.ide.mainContainer.actualTab.navigator.operation == 2: editorWidget._sidebarWidget.set_breakpoint( editorWidget.textCursor().blockNumber()) def __navigate_with_keyboard(self, val): """Navigate between the positions in the jump history stack.""" op = self.ide.mainContainer._tabMain.navigator.operation self.navigate_code_history(val, op) def _add_file_to_project(self, path): """Add the file for 'path' in the project the user choose here.""" pathProject = [self.ide.explorer.get_actual_project()] addToProject = ui_tools.AddToProject(pathProject, self.ide) addToProject.exec_() if not addToProject.pathSelected: return editorWidget = self.ide.mainContainer.get_actual_editor() if not editorWidget.ID: name = QInputDialog.getText(None, self.tr("Add File To Project"), self.tr("File Name:"))[0] if not name: QMessageBox.information(None, self.tr("Invalid Name"), self.tr("The file name is empty, please enter a name")) return else: name = file_manager.get_basename(editorWidget.ID) path = file_manager.create_path(addToProject.pathSelected, name) try: path = file_manager.store_file_content( path, editorWidget.get_text(), newFile=True) self.ide.mainContainer._file_watcher.allow_kill = False if path != editorWidget.ID: self.ide.mainContainer.remove_standalone_watcher( editorWidget.ID) editorWidget.ID = path self.ide.mainContainer.add_standalone_watcher(path) self.ide.mainContainer._file_watcher.allow_kill = True self.ide.explorer.add_existing_file(path) self.ide.change_window_title(path) name = file_manager.get_basename(path) self.ide.mainContainer.actualTab.setTabText( self.ide.mainContainer.actualTab.currentIndex(), name) editorWidget._file_saved() except file_manager.NinjaFileExistsException as ex: QMessageBox.information(None, self.tr("File Already Exists"), (self.tr("Invalid Path: the file '%s' already exists.") % ex.filename)) def add_project_to_console(self, projectFolder): """Add the namespace of the project received into the ninja-console.""" self.ide.misc._console.load_project_into_console(projectFolder) def remove_project_from_console(self, projectFolder): """Remove the namespace of the project received from the console.""" self.ide.misc._console.unload_project_from_console(projectFolder) def import_from_everywhere(self): """Show the dialog to insert an import from any place in the editor.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget: text = editorWidget.get_text() froms = re.findall('^from (.*)', text, re.MULTILINE) fromSection = list(set([f.split(' import')[0] for f in froms])) dialog = from_import_dialog.FromImportDialog(fromSection, editorWidget, self.ide) dialog.show() def open_project(self, path=''): """Open a Project and load the symbols in the Code Locator.""" self.ide.explorer.open_project_folder(path) def open_project_properties(self): """Open a Project and load the symbols in the Code Locator.""" self.ide.explorer.open_project_properties() def create_profile(self): """Create a profile binding files and projects to a key.""" profileInfo = QInputDialog.getText(None, self.tr("Create Profile"), self.tr( "The Current Files and Projects will " "be associated to this profile.\n" "Profile Name:")) if profileInfo[1]: profileName = profileInfo[0] if not profileName or profileName in settings.PROFILES: QMessageBox.information(None, self.tr("Profile Name Invalid"), self.tr("The Profile name is invalid or already exists.")) return self.save_profile(profileName) return profileName def save_profile(self, profileName): """Save the updates from a profile.""" projects_obj = self.ide.explorer.get_opened_projects() projects = [p.path for p in projects_obj] files = self.ide.mainContainer.get_opened_documents() files = files[0] + files[1] settings.PROFILES[profileName] = [files, projects] qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.setValue('ide/profiles', settings.PROFILES) def activate_profile(self): """Show the Profile Manager dialog.""" profilesLoader = ui_tools.ProfilesLoader(self._load_profile_data, self.create_profile, self.save_profile, settings.PROFILES, self.ide) profilesLoader.show() def deactivate_profile(self): """Close the Profile Session.""" self.ide.Profile = None def _load_profile_data(self, key): """Activate the selected profile, closing the current files/projects""" self.ide.explorer.close_opened_projects() self.ide.mainContainer.open_files(settings.PROFILES[key][0]) self.ide.explorer.open_session_projects(settings.PROFILES[key][1]) def close_files_from_project(self, project): """Close the files related to this project.""" if project: tabMain = self.ide.mainContainer._tabMain for tabIndex in reversed(list(range(tabMain.count()))): if file_manager.belongs_to_folder( project, tabMain.widget(tabIndex).ID): tabMain.removeTab(tabIndex) tabSecondary = self.ide.mainContainer._tabSecondary for tabIndex in reversed(list(range(tabSecondary.count()))): if file_manager.belongs_to_folder( project, tabSecondary.widget(tabIndex).ID): tabSecondary.removeTab(tabIndex) self.ide.profile = None def count_file_code_lines(self): """Count the lines of code in the current file.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget: block_count = editorWidget.blockCount() blanks = re.findall('(^\n)|(^(\s+)?#)|(^( +)?($|\n))', editorWidget.get_text(), re.M) blanks_count = len(blanks) resume = self.tr("Lines code: %s\n") % (block_count - blanks_count) resume += (self.tr("Blanks and commented lines: %s\n\n") % blanks_count) resume += self.tr("Total lines: %s") % block_count msgBox = QMessageBox(QMessageBox.Information, self.tr("Summary of lines"), resume, QMessageBox.Ok, editorWidget) msgBox.exec_() def execute_file(self): """Execute the current file.""" editorWidget = self.ide.mainContainer.get_actual_editor() #emit a signal for plugin! self.emit(SIGNAL("fileExecuted(QString)"), editorWidget.ID) if editorWidget: self.ide.mainContainer.save_file(editorWidget) ext = file_manager.get_file_extension(editorWidget.ID) #TODO: Remove the IF statment with polymorphism using Handler if ext == 'py': self.ide.misc.run_application(editorWidget.ID) elif ext == 'html': self.ide.misc.render_web_page(editorWidget.ID) def execute_project(self): """Execute the project marked as Main Project.""" mainFile = self.ide.explorer.get_project_main_file() if not mainFile and self.ide.explorer._treeProjects and \ self.ide.explorer._treeProjects._actualProject: self.ide.explorer._treeProjects.open_project_properties() elif mainFile: self.save_project() path = self.ide.explorer.get_actual_project() #emit a signal for plugin! self.emit(SIGNAL("projectExecuted(QString)"), path) # load our jutsus! project = json_manager.read_ninja_project(path) python_exec = project.get('venv', False) if not python_exec: python_exec = project.get('pythonPath', 'python') PYTHONPATH = project.get('PYTHONPATH', None) params = project.get('programParams', '') preExec = project.get('preExecScript', '') postExec = project.get('postExecScript', '') mainFile = file_manager.create_path(path, mainFile) self.ide.misc.run_application(mainFile, pythonPath=python_exec, PYTHONPATH=PYTHONPATH, programParams=params, preExec=preExec, postExec=postExec) def kill_execution(self): """Kill the execution of the current file or project.""" self.ide.misc.kill_application() def fullscreen_mode(self): """Change to fullscreen mode.""" if self.ide.isFullScreen(): self.ide.showMaximized() else: self.ide.showFullScreen() def editor_redo(self): """Execute the redo action in the current editor.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.redo() def editor_indent_less(self): """Indent 1 position to the left for the current line or selection.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.indent_less() def editor_indent_more(self): """Indent 1 position to the right for the current line or selection.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.indent_more() def editor_insert_debugging_prints(self): """Insert a print statement in each selected line.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget: helpers.insert_debugging_prints(editorWidget) def editor_insert_pdb(self): """Insert a pdb.set_trace() statement in tjhe current line.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget: helpers.insert_pdb(editorWidget) def editor_comment(self): """Mark the current line or selection as a comment.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): helpers.comment(editorWidget) def editor_uncomment(self): """Uncomment the current line or selection.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): helpers.uncomment(editorWidget) def editor_insert_horizontal_line(self): """Insert an horizontal lines of comment symbols.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): helpers.insert_horizontal_line(editorWidget) def editor_insert_title_comment(self): """Insert a Title surrounded by comment symbols.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): helpers.insert_title_comment(editorWidget) def editor_remove_trailing_spaces(self): """Remove the trailing spaces in the current editor.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget: helpers.remove_trailing_spaces(editorWidget) def editor_replace_tabs_with_spaces(self): """Replace the Tabs with Spaces in the current editor.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget: helpers.replace_tabs_with_spaces(editorWidget) def editor_move_up(self): """Move the current line or selection one position up.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): helpers.move_up(editorWidget) def editor_move_down(self): """Move the current line or selection one position down.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): helpers.move_down(editorWidget) def editor_remove_line(self): """Remove the current line or selection.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): helpers.remove_line(editorWidget) def editor_duplicate(self): """Duplicate the current line or selection.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): helpers.duplicate(editorWidget) def editor_go_to_definition(self): """Search the definition of the method or variable under the cursor. If more than one method or variable is found with the same name, shows a table with the results and let the user decide where to go.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.go_to_definition() def editor_highlight_word(self): """Highlight the occurrences of the current word in the editor.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.highlight_selected_word() def editor_complete_declaration(self): """Do the opposite action that Complete Declaration expect.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.complete_declaration() def editor_go_to_line(self, line): """Jump to the specified line in the current editor.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget: editorWidget.jump_to_line(line) def reset_editor_flags(self): """Reset the Flags for all the opened editors.""" self.ide.mainContainer.reset_editor_flags() def call_editors_function(self, call_function, *args, **kwargs): self.ide.mainContainer.call_editors_function( call_function, args, kwargs) def preview_in_browser(self): """Load the current html file in the default browser.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget: if not editorWidget.ID: self.ide.mainContainer.save_file() ext = file_manager.get_file_extension(editorWidget.ID) if ext == 'html': webbrowser.open(editorWidget.ID) def hide_all(self): """Hide/Show all the containers except the editor.""" if self.ide.menuBar().isVisible(): self.ide.central.lateralPanel.hide() self.ide.misc.hide() self.ide.toolbar.hide() self.ide.menuBar().hide() else: self.ide.central.lateralPanel.show() self.ide.toolbar.show() self.ide.menuBar().show() self.ide._menuView.hideAllAction.setChecked( self.ide.menuBar().isVisible()) self.ide._menuView.hideConsoleAction.setChecked( self.ide.central.misc.isVisible()) self.ide._menuView.hideEditorAction.setChecked( self.ide.central.mainContainer.isVisible()) self.ide._menuView.hideExplorerAction.setChecked( self.ide.central.lateralPanel.isVisible()) self.ide._menuView.hideToolbarAction.setChecked( self.ide.toolbar.isVisible()) def view_misc_visibility(self): self.ide.central.change_misc_visibility() self.ide._menuView.hideConsoleAction.setChecked( self.ide.central.misc.isVisible()) def view_main_visibility(self): self.ide.central.change_main_visibility() self.ide._menuView.hideEditorAction.setChecked( self.ide.central.mainContainer.isVisible()) def view_explorer_visibility(self): self.ide.central.change_explorer_visibility() self.ide._menuView.hideExplorerAction.setChecked( self.ide.central.lateralPanel.isVisible()) def save_project(self): """Save all the opened files that belongs to the actual project.""" path = self.ide.explorer.get_actual_project() if path: self.ide.mainContainer.save_project(path) def save_all(self): """Save all the opened files.""" self.ide.mainContainer.save_all() def print_file(self): """Call the print of ui_tool Call print of ui_tool depending on the focus of the application""" #TODO: Add funtionality for proyect tab and methods tab editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget is not None: fileName = "newDocument.pdf" if editorWidget.ID: fileName = file_manager.get_basename( editorWidget.ID) fileName = fileName[:fileName.rfind('.')] + '.pdf' ui_tools.print_file(fileName, editorWidget.print_) def locate_function(self, function, filePath, isVariable): """Move the cursor to the proper position in the navigate stack.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget: self.__codeBack.append((editorWidget.ID, editorWidget.textCursor().position())) self.__codeForward = [] self._locator.navigate_to(function, filePath, isVariable) def update_explorer(self): """Update the symbols in the Symbol Explorer when a file is saved.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget: ext = file_manager.get_file_extension(editorWidget.ID) #obtain a symbols handler for this file extension symbols_handler = settings.get_symbols_handler(ext) if symbols_handler: source = editorWidget.toPlainText() if editorWidget.encoding is not None: source = source.encode(editorWidget.encoding) if ext == 'py': args = (source, True) else: args = (source,) symbols = symbols_handler.obtain_symbols(*args) self.ide.explorer.update_symbols(symbols, editorWidget.ID) #TODO: Should we change the code below similar to the code above? exts = settings.SYNTAX.get('python')['extension'] if ext in exts or editorWidget.newDocument: self.ide.explorer.update_errors( editorWidget.errors, editorWidget.pep8) def update_migration_tips(self): """Update the migration tips in the Explorer.""" # This should be refactored with the new definition of singals in # the MainContainer editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget: self.ide.explorer.update_migration(editorWidget.migration) def navigate_code_history(self, val, op): """Navigate the code history.""" self.__operations[op](val) def _navigate_code_jumps(self, val): """Navigate between the jump points.""" node = None if not val and self.__codeBack: node = self.__codeBack.pop() editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget: self.__codeForward.append((editorWidget.ID, editorWidget.textCursor().position())) elif val and self.__codeForward: node = self.__codeForward.pop() editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget: self.__codeBack.append((editorWidget.ID, editorWidget.textCursor().position())) if node: self.ide.mainContainer.open_file(node[0], node[1]) def _navigate_breakpoints(self, val): """Navigate between the breakpoints.""" breakList = list(settings.BREAKPOINTS.keys()) breakList.sort() if not breakList: return if self.__breakpointsFile not in breakList: self.__breakpointsFile = breakList[0] index = breakList.index(self.__breakpointsFile) breaks = settings.BREAKPOINTS.get(self.__breakpointsFile, []) lineNumber = 0 #val == True: forward if val: if (len(breaks) - 1) > self.__breakpointsPos: self.__breakpointsPos += 1 lineNumber = breaks[self.__breakpointsPos] elif len(breaks) > 0: if index < (len(breakList) - 1): self.__breakpointsFile = breakList[index + 1] else: self.__breakpointsFile = breakList[0] self.__breakpointsPos = 0 breaks = settings.BREAKPOINTS[self.__breakpointsFile] lineNumber = breaks[0] else: if self.__breakpointsPos > 0: self.__breakpointsPos -= 1 lineNumber = breaks[self.__breakpointsPos] elif len(breaks) > 0: self.__breakpointsFile = breakList[index - 1] breaks = settings.BREAKPOINTS[self.__breakpointsFile] self.__breakpointsPos = len(breaks) - 1 lineNumber = breaks[self.__breakpointsPos] if file_manager.file_exists(self.__breakpointsFile): self.ide.mainContainer.open_file(self.__breakpointsFile, lineNumber, None, True) else: settings.BREAKPOINTS.pop(self.__breakpointsFile) def _navigate_bookmarks(self, val): """Navigate between the bookmarks.""" bookList = list(settings.BOOKMARKS.keys()) bookList.sort() if not bookList: return if self.__bookmarksFile not in bookList: self.__bookmarksFile = bookList[0] index = bookList.index(self.__bookmarksFile) bookms = settings.BOOKMARKS.get(self.__bookmarksFile, []) lineNumber = 0 #val == True: forward if val: if (len(bookms) - 1) > self.__bookmarksPos: self.__bookmarksPos += 1 lineNumber = bookms[self.__bookmarksPos] elif len(bookms) > 0: if index < (len(bookList) - 1): self.__bookmarksFile = bookList[index + 1] else: self.__bookmarksFile = bookList[0] self.__bookmarksPos = 0 bookms = settings.BOOKMARKS[self.__bookmarksFile] lineNumber = bookms[0] else: if self.__bookmarksPos > 0: self.__bookmarksPos -= 1 lineNumber = bookms[self.__bookmarksPos] elif len(bookms) > 0: self.__bookmarksFile = bookList[index - 1] bookms = settings.BOOKMARKS[self.__bookmarksFile] self.__bookmarksPos = len(bookms) - 1 lineNumber = bookms[self.__bookmarksPos] if file_manager.file_exists(self.__bookmarksFile): self.ide.mainContainer.open_file(self.__bookmarksFile, lineNumber, None, True) else: settings.BOOKMARKS.pop(self.__bookmarksFile) def add_back_item_navigation(self): """Add an item to the back stack and reset the forward stack.""" editorWidget = self.ide.mainContainer.get_actual_editor() if editorWidget: self.__codeBack.append((editorWidget.ID, editorWidget.textCursor().position())) self.__codeForward = [] def group_tabs_together(self): """Group files that belongs to the same project together.""" if self.ide.explorer._treeProjects is None: return projects_obj = self.ide.explorer.get_opened_projects() projects = [p.path for p in projects_obj] for project in projects: projectName = self.ide.explorer.get_project_name(project) if not projectName: projectName = file_manager.get_basename(project) tabGroup = tab_group.TabGroup(project, projectName, self) for index in reversed(list(range( self.ide.mainContainer._tabMain.count()))): widget = self.ide.mainContainer._tabMain.widget(index) if type(widget) is editor.Editor and \ file_manager.belongs_to_folder(project, widget.ID): tabGroup.add_widget(widget) self.ide.mainContainer._tabMain.removeTab(index) if tabGroup.tabs: self.ide.mainContainer._tabMain.add_tab(tabGroup, projectName) def deactivate_tabs_groups(self): """Deactivate tab grouping based in the project they belong.""" for index in reversed(list(range( self.ide.mainContainer._tabMain.count()))): widget = self.ide.mainContainer._tabMain.widget(index) if type(widget) is tab_group.TabGroup: widget.only_expand() def reopen_last_tab(self): """Reopen the last closed tab.""" self.ide.mainContainer.actualTab._reopen_last_tab() def open_class_diagram(self): """Open the Class Diagram Generator.""" diagram = class_diagram.ClassDiagram(self) self.ide.mainContainer.add_tab(diagram, self.tr("Class Diagram v.0.1")) def reload_toolbar(self): """Reload the Toolbar.""" self.ide.load_toolbar() def move_tab(self, next=True, widget=None): actualTab = self.ide.mainContainer.actualTab if widget is None: widget = actualTab.currentWidget() if widget is not None: old_widget_index = actualTab.indexOf(widget) if next and old_widget_index < actualTab.count() - 1: new_widget_index = old_widget_index + 1 elif old_widget_index > 0 and not next: new_widget_index = old_widget_index - 1 else: return tabName = actualTab.tabText(old_widget_index) actualTab.insertTab(new_widget_index, widget, tabName) actualTab.setCurrentIndex(new_widget_index) class TabShortcuts(QShortcut): def __init__(self, key, parent, index): super(TabShortcuts, self).__init__(key, parent) self.index = index ninja-ide-2.3/ninja_ide/gui/central_widget.py000066400000000000000000000222451216641277400213040ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QWidget from ninja_ide.tools import ui_tools from PyQt4.QtGui import QKeySequence from PyQt4.QtGui import QSizePolicy from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QComboBox from PyQt4.QtGui import QLabel from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QSplitter from PyQt4.QtGui import QScrollBar from PyQt4.QtCore import Qt from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import QSettings from ninja_ide import resources from ninja_ide.core import settings __centralWidgetInstance = None def CentralWidget(*args, **kw): global __centralWidgetInstance if __centralWidgetInstance is None: __centralWidgetInstance = __CentralWidget(*args, **kw) return __centralWidgetInstance class __CentralWidget(QWidget): ############################################################################### # CentralWidget SIGNALS ############################################################################### """ splitterCentralRotated() """ ############################################################################### def __init__(self, parent=None): QWidget.__init__(self, parent) self.parent = parent #This variables are used to save the splitter sizes before hide self._splitterMainSizes = None self._splitterAreaSizes = None self.lateralPanel = None hbox = QHBoxLayout(self) hbox.setContentsMargins(0, 0, 0, 0) hbox.setSpacing(0) #Create Splitters to divide the UI in: MainPanel, Explorer, Misc self._splitterArea = QSplitter(Qt.Horizontal) self._splitterMain = QSplitter(Qt.Vertical) #Create scrollbar for follow mode self.scrollBar = QScrollBar(Qt.Vertical, self) self.scrollBar.setFixedWidth(20) self.scrollBar.setToolTip('Follow Mode: Scroll the Editors together') self.scrollBar.hide() self.connect(self.scrollBar, SIGNAL("valueChanged(int)"), self.move_follow_scrolls) #Add to Main Layout hbox.addWidget(self.scrollBar) hbox.addWidget(self._splitterArea) def insert_central_container(self, container): self.mainContainer = container self._splitterMain.insertWidget(0, container) def insert_lateral_container(self, container): self.lateralPanel = LateralPanel(container) self._splitterArea.insertWidget(0, self.lateralPanel) def insert_bottom_container(self, container): self.misc = container self._splitterMain.insertWidget(1, container) def showEvent(self, event): #Show Event QWidget.showEvent(self, event) #Avoid recalculate the panel sizes if they are already loaded if self._splitterArea.count() == 2: return #Rearrange widgets on Window self._splitterArea.insertWidget(0, self._splitterMain) if not event.spontaneous(): self.change_misc_visibility() if bin(settings.UI_LAYOUT)[-1] == '1': self.splitter_central_rotate() if bin(settings.UI_LAYOUT >> 1)[-1] == '1': self.splitter_misc_rotate() if bin(settings.UI_LAYOUT >> 2)[-1] == '1': self.splitter_central_orientation() qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) #Lists of sizes as list of QVariant- heightList = [QVariant, QVariant] heightList = list(qsettings.value("window/central/mainSize", [(self.height() / 3) * 2, self.height() / 3])) widthList = list(qsettings.value("window/central/areaSize", [(self.width() / 6) * 5, self.width() / 6])) self._splitterMainSizes = [int(heightList[0]), int(heightList[1])] self._splitterAreaSizes = [int(widthList[0]), int(widthList[1])] #Set the sizes to splitters #self._splitterMain.setSizes(self._splitterMainSizes) self._splitterMain.setSizes(self._splitterMainSizes) self._splitterArea.setSizes(self._splitterAreaSizes) self.misc.setVisible( qsettings.value("window/show_misc", False, type=bool)) def change_misc_visibility(self, on_start=False): if self.misc.isVisible(): self._splitterMainSizes = self._splitterMain.sizes() self.misc.hide() widget = self.mainContainer.get_actual_widget() if widget: widget.setFocus() else: self.misc.show() self.misc.gain_focus() def change_main_visibility(self): if self.mainContainer.isVisible(): self.mainContainer.hide() else: self.mainContainer.show() def change_explorer_visibility(self, force_hide=False): if self.lateralPanel.isVisible() or force_hide: self._splitterAreaSizes = self._splitterArea.sizes() self.lateralPanel.hide() else: self.lateralPanel.show() def splitter_central_rotate(self): w1, w2 = self._splitterArea.widget(0), self._splitterArea.widget(1) self._splitterArea.insertWidget(0, w2) self._splitterArea.insertWidget(1, w1) self.emit(SIGNAL("splitterCentralRotated()")) def splitter_central_orientation(self): if self._splitterArea.orientation() == Qt.Horizontal: self._splitterArea.setOrientation(Qt.Vertical) else: self._splitterArea.setOrientation(Qt.Horizontal) def splitter_misc_rotate(self): w1, w2 = self._splitterMain.widget(0), self._splitterMain.widget(1) self._splitterMain.insertWidget(0, w2) self._splitterMain.insertWidget(1, w1) def splitter_misc_orientation(self): if self._splitterMain.orientation() == Qt.Horizontal: self._splitterMain.setOrientation(Qt.Vertical) else: self._splitterMain.setOrientation(Qt.Horizontal) def get_area_sizes(self): if self.lateralPanel.isVisible(): self._splitterAreaSizes = self._splitterArea.sizes() return self._splitterAreaSizes def get_main_sizes(self): if self.misc.isVisible(): self._splitterMainSizes = self._splitterMain.sizes() return self._splitterMainSizes def enable_follow_mode_scrollbar(self, val): if val: editorWidget = self.mainContainer.get_actual_editor() maxScroll = editorWidget.verticalScrollBar().maximum() position = editorWidget.verticalScrollBar().value() self.scrollBar.setMaximum(maxScroll) self.scrollBar.setValue(position) self.scrollBar.setVisible(val) def move_follow_scrolls(self, val): widget = self.mainContainer._tabMain.currentWidget() diff = widget._sidebarWidget.highest_line - val s1 = self.mainContainer._tabMain.currentWidget().verticalScrollBar() s2 = self.mainContainer._tabSecondary.\ currentWidget().verticalScrollBar() s1.setValue(val) s2.setValue(val + diff) class LateralPanel(QWidget): def __init__(self, explorer): QWidget.__init__(self) vbox = QVBoxLayout(self) vbox.setContentsMargins(0, 0, 0, 0) vbox.addWidget(explorer) hbox = QHBoxLayout() hbox.setContentsMargins(0, 0, 0, 0) self.labelText = "Ln: %s, Col: %s" self.labelCursorPosition = QLabel(self.trUtf8(self.labelText % (0, 0))) hbox.addWidget(self.labelCursorPosition) self.combo = QComboBox() ui_tools.ComboBoxButton(self.combo, self.combo.clear, self.style().standardPixmap(self.style().SP_TrashIcon)) self.combo.setToolTip(self.trUtf8("Select the item from the Paste " "Historial list.\nYou can Copy items into this list with: " "%s\nor Paste them using: %s") % (resources.get_shortcut("History-Copy").toString( QKeySequence.NativeText), resources.get_shortcut("History-Paste").toString( QKeySequence.NativeText))) self.combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) hbox.addWidget(self.combo) vbox.addLayout(hbox) def update_line_col(self, line, col): self.labelCursorPosition.setText(self.trUtf8( self.labelText % (line, col))) def add_new_copy(self, copy): self.combo.insertItem(0, copy) self.combo.setCurrentIndex(0) if self.combo.count() > settings.COPY_HISTORY_BUFFER: self.combo.removeItem(self.combo.count() - 1) def get_paste(self): return self.combo.currentText() ninja-ide-2.3/ninja_ide/gui/dialogs/000077500000000000000000000000001216641277400173545ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/gui/dialogs/__init__.py000066400000000000000000000012641216641277400214700ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . ninja-ide-2.3/ninja_ide/gui/dialogs/about_ninja.py000066400000000000000000000057611216641277400222300ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import webbrowser from PyQt4.QtGui import QDialog from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QLabel from PyQt4.QtGui import QPixmap from PyQt4.QtCore import Qt from PyQt4.QtCore import QSize from PyQt4.QtCore import SIGNAL import ninja_ide from ninja_ide import resources class AboutNinja(QDialog): def __init__(self, parent=None): QDialog.__init__(self, parent, Qt.Dialog) self.setWindowTitle(self.tr("About NINJA-IDE")) self.setMaximumSize(QSize(0, 0)) vbox = QVBoxLayout(self) #Create an icon for the Dialog pixmap = QPixmap(resources.IMAGES['icon']) self.lblIcon = QLabel() self.lblIcon.setPixmap(pixmap) hbox = QHBoxLayout() hbox.addWidget(self.lblIcon) lblTitle = QLabel( '

NINJA-IDE

\nNinja-IDE Is Not Just Another IDE') lblTitle.setTextFormat(Qt.RichText) lblTitle.setAlignment(Qt.AlignLeft) hbox.addWidget(lblTitle) vbox.addLayout(hbox) #Add description vbox.addWidget(QLabel( self.tr("""NINJA-IDE (from: "Ninja Is Not Just Another IDE"), is a cross-platform integrated development environment specially designed to build Python Applications. NINJA-IDE provides tools to simplify the Python-software development and handles all kinds of situations thanks to its rich extensibility."""))) vbox.addWidget(QLabel(self.tr("Version: %s") % ninja_ide.__version__)) link_ninja = QLabel( self.tr('Website: ' '%s') % (ninja_ide.__url__, ninja_ide.__url__)) vbox.addWidget(link_ninja) link_source = QLabel( self.tr('Source Code: %s') % (ninja_ide.__source__, ninja_ide.__source__)) vbox.addWidget(link_source) self.connect(link_ninja, SIGNAL("linkActivated(QString)"), self.link_activated) self.connect(link_source, SIGNAL("linkActivated(QString)"), self.link_activated) def link_activated(self, link): webbrowser.open(str(link)) ninja-ide-2.3/ninja_ide/gui/dialogs/from_import_dialog.py000066400000000000000000000053421216641277400236060ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QDialog from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QLabel from PyQt4.QtGui import QLineEdit from PyQt4.QtGui import QTextCursor from PyQt4.QtGui import QCompleter from PyQt4.QtGui import QPushButton from PyQt4.QtCore import Qt from PyQt4.QtCore import SIGNAL class FromImportDialog(QDialog): def __init__(self, fromSection, editorWidget, parent=None): QDialog.__init__(self, parent, Qt.Dialog) self.setWindowTitle('from ... import ...') self._editorWidget = editorWidget self._fromSection = fromSection hbox = QHBoxLayout(self) hbox.addWidget(QLabel('from')) self._lineFrom = QLineEdit() self._completer = QCompleter(fromSection) self._lineFrom.setCompleter(self._completer) hbox.addWidget(self._lineFrom) hbox.addWidget(QLabel('import')) self._lineImport = QLineEdit() hbox.addWidget(self._lineImport) self._btnAdd = QPushButton(self.tr('Add')) hbox.addWidget(self._btnAdd) self.connect(self._lineImport, SIGNAL("returnPressed()"), self._add_import) self.connect(self._btnAdd, SIGNAL("clicked()"), self._add_import) def _add_import(self): fromItem = self._lineFrom.text() importItem = self._lineImport.text() if fromItem in self._fromSection: cursor = self._editorWidget.document().find(fromItem) elif self._fromSection: cursor = self._editorWidget.document().find(self._fromSection[-1]) else: cursor = self._editorWidget.textCursor() cursor.movePosition(QTextCursor.Start) cursor.movePosition(QTextCursor.EndOfLine) if fromItem: importLine = '\nfrom {0} import {1}'.format(fromItem, importItem) else: importLine = '\nimport {0}'.format(importItem) if self._editorWidget.document().find( importLine[1:]).position() == -1: cursor.insertText(importLine) self.close() ninja-ide-2.3/ninja_ide/gui/dialogs/language_manager.py000066400000000000000000000130051216641277400232020ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . import os #lint:disable try: from urllib.request import urlopen from urllib.error import URLError except ImportError: from urllib2 import urlopen from urllib2 import URLError #lint:enable from PyQt4.QtGui import QWidget from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QSpacerItem from PyQt4.QtGui import QSizePolicy from PyQt4.QtGui import QTabWidget from PyQt4.QtGui import QTableWidget from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QDialog from PyQt4.QtCore import Qt from PyQt4.QtCore import SIGNAL from ninja_ide import resources from ninja_ide.core import file_manager from ninja_ide.tools import ui_tools from ninja_ide.tools import json_manager class LanguagesManagerWidget(QDialog): def __init__(self, parent): QDialog.__init__(self, parent, Qt.Dialog) self.setWindowTitle(self.tr("Language Manager")) self.resize(700, 500) vbox = QVBoxLayout(self) self._tabs = QTabWidget() vbox.addWidget(self._tabs) # Footer hbox = QHBoxLayout() btn_close = QPushButton(self.tr('Close')) btnReload = QPushButton(self.tr("Reload")) hbox.addWidget(btn_close) hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding)) hbox.addWidget(btnReload) vbox.addLayout(hbox) self.overlay = ui_tools.Overlay(self) self.overlay.show() self._languages = [] self._loading = True self.downloadItems = [] #Load Themes with Thread self.connect(btnReload, SIGNAL("clicked()"), self._reload_languages) self._thread = ui_tools.ThreadExecution(self.execute_thread) self.connect(self._thread, SIGNAL("finished()"), self.load_languages_data) self.connect(btn_close, SIGNAL('clicked()'), self.close) self._reload_languages() def _reload_languages(self): self.overlay.show() self._loading = True self._thread.execute = self.execute_thread self._thread.start() def load_languages_data(self): if self._loading: self._tabs.clear() self._languageWidget = LanguageWidget(self, self._languages) self._tabs.addTab(self._languageWidget, self.tr("Languages")) self._loading = False self.overlay.hide() self._thread.wait() def download_language(self, language): self.overlay.show() self.downloadItems = language self._thread.execute = self._download_language_thread self._thread.start() def resizeEvent(self, event): self.overlay.resize(event.size()) event.accept() def execute_thread(self): try: descriptor_languages = urlopen(resources.LANGUAGES_URL) languages = json_manager.parse(descriptor_languages) languages = [[name, languages[name]] for name in languages] local_languages = self.get_local_languages() languages = [languages[i] for i in range(len(languages)) if os.path.basename(languages[i][1]) not in local_languages] self._languages = languages except URLError: self._languages = [] def get_local_languages(self): if not file_manager.folder_exists(resources.LANGS_DOWNLOAD): file_manager.create_tree_folders(resources.LANGS_DOWNLOAD) languages = os.listdir(resources.LANGS_DOWNLOAD) + \ os.listdir(resources.LANGS) languages = [s for s in languages if s.endswith('.qm')] return languages def _download_language_thread(self): for d in self.downloadItems: self.download(d[1], resources.LANGS_DOWNLOAD) def download(self, url, folder): fileName = os.path.join(folder, os.path.basename(url)) try: content = urlopen(url) f = open(fileName, 'wb') f.write(content.read()) f.close() except URLError: return class LanguageWidget(QWidget): def __init__(self, parent, languages): QWidget.__init__(self, parent) self._parent = parent self._languages = languages vbox = QVBoxLayout(self) self._table = QTableWidget(1, 2) self._table.removeRow(0) vbox.addWidget(self._table) ui_tools.load_table(self._table, [self.tr('Language'), self.tr('URL')], self._languages) btnUninstall = QPushButton(self.tr('Download')) btnUninstall.setMaximumWidth(100) vbox.addWidget(btnUninstall) self._table.setColumnWidth(0, 200) self.connect(btnUninstall, SIGNAL("clicked()"), self._download_language) def _download_language(self): languages = ui_tools.remove_get_selected_items(self._table, self._languages) self._parent.download_language(languages) ninja-ide-2.3/ninja_ide/gui/dialogs/plugins_manager.py000066400000000000000000000537301216641277400231110ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import webbrowser from copy import copy from distutils import version from PyQt4.QtGui import QWidget from PyQt4.QtGui import QFormLayout from PyQt4.QtGui import QFileDialog from PyQt4.QtGui import QDialog from PyQt4.QtGui import QLabel from PyQt4.QtGui import QTextBrowser from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QTableWidget from PyQt4.QtGui import QTabWidget from PyQt4.QtGui import QPlainTextEdit from PyQt4.QtGui import QLineEdit from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QSpacerItem from PyQt4.QtGui import QSizePolicy from PyQt4.QtGui import QMessageBox from PyQt4.QtGui import QIcon from PyQt4.QtCore import Qt from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import QThread from ninja_ide import resources from ninja_ide.core import plugin_manager from ninja_ide.core import file_manager from ninja_ide.tools import ui_tools from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.gui.dialogs.plugin_manager') HTML_STYLE = """

{name}

Version: {version}

{description}


Author: {author}

More info about the Plugin: Website

""" def _get_plugin(plugin_name, plugin_list): plugin = None for plug in plugin_list: if plug["name"] == plugin_name: plugin = plug break return plugin def _format_for_table(plugins): return [[data["name"], data["version"], data["description"], data["authors"], data["home"]] for data in plugins] class PluginsManagerWidget(QDialog): def __init__(self, parent): QDialog.__init__(self, parent, Qt.Dialog) self.setWindowTitle(self.tr("Plugins Manager")) self.resize(700, 600) vbox = QVBoxLayout(self) self._tabs = QTabWidget() vbox.addWidget(self._tabs) self._txt_data = QTextBrowser() self._txt_data.setOpenLinks(False) vbox.addWidget(QLabel(self.tr("Description:"))) vbox.addWidget(self._txt_data) # Footer hbox = QHBoxLayout() btn_close = QPushButton(self.tr('Close')) btnReload = QPushButton(self.tr("Reload")) hbox.addWidget(btn_close) hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding)) hbox.addWidget(btnReload) vbox.addLayout(hbox) self.overlay = ui_tools.Overlay(self) self.overlay.hide() self._oficial_available = [] self._community_available = [] self._locals = [] self._updates = [] self._loading = True self._requirements = {} self.connect(btnReload, SIGNAL("clicked()"), self._reload_plugins) self.thread = ThreadLoadPlugins(self) self.connect(self.thread, SIGNAL("finished()"), self._load_plugins_data) self.connect(self.thread, SIGNAL("plugin_downloaded(PyQt_PyObject)"), self._after_download_plugin) self.connect(self.thread, SIGNAL("plugin_manually_installed(PyQt_PyObject)"), self._after_manual_install_plugin) self.connect(self.thread, SIGNAL("plugin_uninstalled(PyQt_PyObject)"), self._after_uninstall_plugin) self.connect(self._txt_data, SIGNAL("anchorClicked(const QUrl&)"), self._open_link) self.connect(btn_close, SIGNAL('clicked()'), self.close) self.overlay.show() self._reload_plugins() def show_plugin_info(self, data): plugin_description = data[2].replace('\n', '
') html = HTML_STYLE.format(name=data[0], version=data[1], description=plugin_description, author=data[3], link=data[4]) self._txt_data.setHtml(html) def _open_link(self, url): link = url.toString() if link.startswith('/plugins/'): link = 'http://ninja-ide.org' + link webbrowser.open(link) def _reload_plugins(self): self.overlay.show() self._loading = True self.thread.runnable = self.thread.collect_data_thread self.thread.start() def _after_manual_install_plugin(self, plugin): data = {} data['name'] = plugin[0] data['version'] = plugin[1] data['description'] = '' data['authors'] = '' data['home'] = '' self._installedWidget.add_table_items([data]) def _after_download_plugin(self, plugin): oficial_plugin = _get_plugin(plugin[0], self._oficial_available) community_plugin = _get_plugin(plugin[0], self._community_available) if oficial_plugin: self._installedWidget.add_table_items([oficial_plugin]) self._availableOficialWidget.remove_item(plugin[0]) elif community_plugin: self._installedWidget.add_table_items([community_plugin]) self._availableCommunityWidget.remove_item(plugin[0]) def _after_uninstall_plugin(self, plugin): #make available the plugin corresponding to the type oficial_plugin = _get_plugin(plugin[0], self._oficial_available) community_plugin = _get_plugin(plugin[0], self._community_available) if oficial_plugin: self._availableOficialWidget.add_table_items([oficial_plugin]) self._installedWidget.remove_item(plugin[0]) elif community_plugin: self._availableCommunityWidget.add_table_items([community_plugin]) self._installedWidget.remove_item(plugin[0]) def _load_plugins_data(self): if self._loading: self._tabs.clear() self._updatesWidget = UpdatesWidget(self, copy(self._updates)) self._availableOficialWidget = AvailableWidget(self, copy(self._oficial_available)) self._availableCommunityWidget = AvailableWidget(self, copy(self._community_available)) self._installedWidget = InstalledWidget(self, copy(self._locals)) self._tabs.addTab(self._availableOficialWidget, self.tr("Official Available")) self._tabs.addTab(self._availableCommunityWidget, self.tr("Community Available")) self._tabs.addTab(self._updatesWidget, self.tr("Updates")) self._tabs.addTab(self._installedWidget, self.tr("Installed")) self._manualWidget = ManualInstallWidget(self) self._tabs.addTab(self._manualWidget, self.tr("Manual Install")) self._loading = False self.overlay.hide() self.thread.wait() def download_plugins(self, plugs): """ Install """ self.overlay.show() self.thread.plug = plugs #set the function to run in the thread self.thread.runnable = self.thread.download_plugins_thread self.thread.start() def install_plugins_manually(self, plug): """Install plugin from local zip.""" self.overlay.show() self.thread.plug = plug #set the function to run in the thread self.thread.runnable = self.thread.manual_install_plugins_thread self.thread.start() def mark_as_available(self, plugs): """ Uninstall """ self.overlay.show() self.thread.plug = plugs #set the function to run in the thread self.thread.runnable = self.thread.uninstall_plugins_thread self.thread.start() def update_plugin(self, plugs): """ Update """ self.overlay.show() self.thread.plug = plugs #set the function to run in the thread self.thread.runnable = self.thread.update_plugin_thread self.thread.start() def reset_installed_plugins(self): local_plugins = plugin_manager.local_plugins() plugins = _format_for_table(local_plugins) self._installedWidget.reset_table(plugins) def resizeEvent(self, event): self.overlay.resize(event.size()) event.accept() class UpdatesWidget(QWidget): """ This widget show the availables plugins to update """ def __init__(self, parent, updates): QWidget.__init__(self, parent) self._parent = parent self._updates = updates vbox = QVBoxLayout(self) self._table = QTableWidget(1, 2) self._table.removeRow(0) self._table.setSelectionMode(QTableWidget.SingleSelection) self._table.setColumnWidth(0, 500) vbox.addWidget(self._table) ui_tools.load_table(self._table, (self.tr('Name'), self.tr('Version')), _format_for_table(updates)) btnUpdate = QPushButton(self.tr("Update")) btnUpdate.setMaximumWidth(100) vbox.addWidget(btnUpdate) self.connect(btnUpdate, SIGNAL("clicked()"), self._update_plugins) self.connect(self._table, SIGNAL("itemSelectionChanged()"), self._show_item_description) def _show_item_description(self): item = self._table.currentItem() if item is not None: data = list(item.data(Qt.UserRole)) self._parent.show_plugin_info(data) def _update_plugins(self): data = _format_for_table(self._updates) plugins = ui_tools.remove_get_selected_items(self._table, data) #get the download link of each plugin for p_row in plugins: #search the plugin for p_dict in self._updates: if p_dict["name"] == p_row[0]: p_data = p_dict break #append the downlod link p_row.append(p_data["download"]) self._parent.update_plugin(plugins) class AvailableWidget(QWidget): def __init__(self, parent, available): QWidget.__init__(self, parent) self._parent = parent self._available = available vbox = QVBoxLayout(self) self._table = QTableWidget(1, 2) self._table.setSelectionMode(QTableWidget.SingleSelection) self._table.removeRow(0) vbox.addWidget(self._table) ui_tools.load_table(self._table, (self.tr('Name'), self.tr('Version')), _format_for_table(available)) self._table.setColumnWidth(0, 500) hbox = QHBoxLayout() btnInstall = QPushButton(self.tr('Install')) btnInstall.setMaximumWidth(100) hbox.addWidget(btnInstall) hbox.addWidget(QLabel(self.tr("NINJA needs to be restarted for " "changes to take effect."))) vbox.addLayout(hbox) self.connect(btnInstall, SIGNAL("clicked()"), self._install_plugins) self.connect(self._table, SIGNAL("itemSelectionChanged()"), self._show_item_description) def _show_item_description(self): item = self._table.currentItem() if item is not None: data = list(item.data(Qt.UserRole)) self._parent.show_plugin_info(data) def _install_plugins(self): data = _format_for_table(self._available) plugins = ui_tools.remove_get_selected_items(self._table, data) #get the download link of each plugin for p_row in plugins: #search the plugin for p_dict in self._available: if p_dict["name"] == p_row[0]: p_data = p_dict break #append the downlod link p_row.append(p_data["download"]) #download self._parent.download_plugins(plugins) def remove_item(self, plugin_name): plugin = _get_plugin(plugin_name, self._available) self._available.remove(plugin) def _install_external(self): if self._link.text().isEmpty(): QMessageBox.information(self, self.tr("External Plugins"), self.tr("URL from Plugin missing...")) return plug = [ file_manager.get_module_name(str(self._link.text())), 'External Plugin', '1.0', str(self._link.text())] self.parent().download_plugins(plug) self._link.setText('') def add_table_items(self, plugs): self._available += plugs data = _format_for_table(self._available) ui_tools.load_table(self._table, (self.tr('Name'), self.tr('Version')), data) class InstalledWidget(QWidget): """ This widget show the installed plugins """ def __init__(self, parent, installed): QWidget.__init__(self, parent) self._parent = parent self._installed = installed vbox = QVBoxLayout(self) self._table = QTableWidget(1, 2) self._table.setSelectionMode(QTableWidget.SingleSelection) self._table.removeRow(0) vbox.addWidget(self._table) ui_tools.load_table(self._table, (self.tr('Name'), self.tr('Version')), _format_for_table(installed)) self._table.setColumnWidth(0, 500) btnUninstall = QPushButton(self.tr("Uninstall")) btnUninstall.setMaximumWidth(100) vbox.addWidget(btnUninstall) self.connect(btnUninstall, SIGNAL("clicked()"), self._uninstall_plugins) self.connect(self._table, SIGNAL("itemSelectionChanged()"), self._show_item_description) def _show_item_description(self): item = self._table.currentItem() if item is not None: data = list(item.data(Qt.UserRole)) self._parent.show_plugin_info(data) def remove_item(self, plugin_name): plugin = _get_plugin(plugin_name, self._installed) self._installed.remove(plugin) def add_table_items(self, plugs): self._installed += plugs data = _format_for_table(self._installed) ui_tools.load_table(self._table, (self.tr('Name'), self.tr('Version')), data) def _uninstall_plugins(self): data = _format_for_table(self._installed) plugins = ui_tools.remove_get_selected_items(self._table, data) self._parent.mark_as_available(plugins) def reset_table(self, installed): self._installed = installed while self._table.rowCount() > 0: self._table.removeRow(0) ui_tools.load_table(self._table, (self.tr('Name'), self.tr('Version')), self._installed) class ManualInstallWidget(QWidget): def __init__(self, parent): super(ManualInstallWidget, self).__init__() self._parent = parent vbox = QVBoxLayout(self) form = QFormLayout() self._txtName = QLineEdit() self._txtVersion = QLineEdit() form.addRow(self.tr("Plugin Name:"), self._txtName) form.addRow(self.tr("Plugin Version:"), self._txtVersion) vbox.addLayout(form) hPath = QHBoxLayout() self._txtFilePath = QLineEdit() self._btnFilePath = QPushButton(QIcon(resources.IMAGES['open']), '') hPath.addWidget(QLabel(self.tr("Plugin File:"))) hPath.addWidget(self._txtFilePath) hPath.addWidget(self._btnFilePath) vbox.addLayout(hPath) vbox.addSpacerItem(QSpacerItem(0, 1, QSizePolicy.Expanding, QSizePolicy.Expanding)) hbox = QHBoxLayout() hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding)) self._btnInstall = QPushButton(self.tr("Install")) hbox.addWidget(self._btnInstall) vbox.addLayout(hbox) #Signals self.connect(self._btnFilePath, SIGNAL("clicked()"), self._load_plugin_path) self.connect(self._btnInstall, SIGNAL("clicked()"), self.install_plugin) def _load_plugin_path(self): path = QFileDialog.getOpenFileName(self, self.tr("Select Plugin Path")) if path: self._txtFilePath.setText(path) def install_plugin(self): if self._txtFilePath.text() and self._txtName.text(): plug = [] plug.append(self._txtName.text()) plug.append(self._txtVersion.text()) plug.append('') plug.append('') plug.append('') plug.append(self._txtFilePath.text()) self._parent.install_plugins_manually([plug]) class ThreadLoadPlugins(QThread): """ This thread makes the HEAVY work! """ def __init__(self, manager): super(ThreadLoadPlugins, self).__init__() self._manager = manager #runnable hold a function to call! self.runnable = self.collect_data_thread #this attribute contains the plugins to download/update self.plug = None def run(self): self.runnable() self.plug = None def collect_data_thread(self): """ Collects plugins info from NINJA-IDE webservice interface """ #get availables OFICIAL plugins oficial_available = plugin_manager.available_oficial_plugins() #get availables COMMUNITIES plugins community_available = plugin_manager.available_community_plugins() #get locals plugins local_plugins = plugin_manager.local_plugins() updates = [] #Check por update the already installed plugin for local_data in local_plugins: ava = None plug_oficial = _get_plugin(local_data["name"], oficial_available) plug_community = _get_plugin(local_data["name"], community_available) if plug_oficial: ava = plug_oficial oficial_available = [p for p in oficial_available if p["name"] != local_data["name"]] elif plug_community: ava = plug_community community_available = [p for p in community_available if p["name"] != local_data["name"]] #check versions if ava: available_version = version.LooseVersion(str(ava["version"])) else: available_version = version.LooseVersion('0.0') local_version = version.LooseVersion(str(local_data["version"])) if available_version > local_version: #this plugin has an update updates.append(ava) #set manager attributes self._manager._oficial_available = oficial_available self._manager._community_available = community_available self._manager._locals = local_plugins self._manager._updates = updates def download_plugins_thread(self): """ Downloads some plugins """ for p in self.plug: try: name = plugin_manager.download_plugin(p[5]) p.append(name) plugin_manager.update_local_plugin_descriptor((p, )) req_command = plugin_manager.has_dependencies(p) if req_command[0]: self._manager._requirements[p[0]] = req_command[1] self.emit(SIGNAL("plugin_downloaded(PyQt_PyObject)"), p) except Exception as e: logger.warning("Impossible to install (%s): %s", p[0], e) def manual_install_plugins_thread(self): """ Install a plugin from the a file. """ for p in self.plug: try: name = plugin_manager.manual_install(p[5]) p.append(name) plugin_manager.update_local_plugin_descriptor((p, )) req_command = plugin_manager.has_dependencies(p) if req_command[0]: self._manager._requirements[p[0]] = req_command[1] self.emit( SIGNAL("plugin_manually_installed(PyQt_PyObject)"), p) except Exception as e: logger.warning("Impossible to install (%s): %s", p[0], e) def uninstall_plugins_thread(self): for p in self.plug: try: plugin_manager.uninstall_plugin(p) self.emit(SIGNAL("plugin_uninstalled(PyQt_PyObject)"), p) except Exception as e: logger.warning("Impossible to uninstall (%s): %s", p[0], e) def update_plugin_thread(self): """ Updates some plugins """ for p in self.plug: try: plugin_manager.uninstall_plugin(p) name = plugin_manager.download_plugin(p[5]) p.append(name) plugin_manager.update_local_plugin_descriptor([p]) self._manager.reset_installed_plugins() except Exception as e: logger.warning("Impossible to update (%s): %s", p[0], e) class DependenciesHelpDialog(QDialog): def __init__(self, requirements_dict): super(DependenciesHelpDialog, self).__init__() self.setWindowTitle(self.tr("Plugin requirements")) self.resize(525, 400) vbox = QVBoxLayout(self) label = QLabel(self.tr("""It seems that some plugins needs some dependencies to be solved to work properly, you should install them as follows using a Terminal""")) vbox.addWidget(label) self._editor = QPlainTextEdit() self._editor.setReadOnly(True) vbox.addWidget(self._editor) hbox = QHBoxLayout() btnAccept = QPushButton(self.tr("Accept")) btnAccept.setMaximumWidth(100) hbox.addWidget(btnAccept) vbox.addLayout(hbox) #signals self.connect(btnAccept, SIGNAL("clicked()"), self.close) command_tmpl = "<%s>:\n%s\n" for name, description in list(requirements_dict.items()): self._editor.insertPlainText(command_tmpl % (name, description)) ninja-ide-2.3/ninja_ide/gui/dialogs/preferences.py000066400000000000000000002535431216641277400222430ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import os import copy import getpass from PyQt4.QtGui import QApplication from PyQt4.QtGui import QWidget from PyQt4.QtGui import QKeySequence from PyQt4.QtGui import QSpacerItem from PyQt4.QtGui import QColorDialog from PyQt4.QtGui import QColor from PyQt4.QtGui import QScrollArea from PyQt4.QtGui import QFrame from PyQt4.QtGui import QActionGroup from PyQt4.QtGui import QStyle from PyQt4.QtGui import QDialog from PyQt4.QtGui import QGroupBox from PyQt4.QtGui import QCheckBox from PyQt4.QtGui import QComboBox from PyQt4.QtGui import QSpinBox from PyQt4.QtGui import QIcon from PyQt4.QtGui import QLabel from PyQt4.QtGui import QLineEdit from PyQt4.QtGui import QToolBar from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QGridLayout from PyQt4.QtGui import QTabWidget from PyQt4.QtGui import QSizePolicy from PyQt4.QtGui import QListWidget from PyQt4.QtGui import QMessageBox from PyQt4.QtGui import QFileDialog from PyQt4.QtGui import QFontDialog from PyQt4.QtGui import QFont from PyQt4.QtCore import Qt from PyQt4.QtCore import QSize from PyQt4.QtCore import QSettings from PyQt4.QtCore import SIGNAL from ninja_ide import resources from ninja_ide.gui import central_widget from ninja_ide.gui import actions from ninja_ide.gui.editor import editor from ninja_ide.gui.misc import misc_container from ninja_ide.gui.misc import shortcut_manager from ninja_ide.gui.misc import plugin_preferences from ninja_ide.gui.main_panel import main_container from ninja_ide.gui.explorer import explorer_container from ninja_ide.core import settings from ninja_ide.core import file_manager from ninja_ide.tools import ui_tools from ninja_ide.tools import json_manager class PreferencesWidget(QDialog): def __init__(self, parent=None): QDialog.__init__(self, parent) self.setWindowTitle(self.tr("NINJA-IDE - Preferences")) self.setMaximumSize(QSize(0, 0)) self.overlay = ui_tools.Overlay(self) self.overlay.hide() #Tabs vbox = QVBoxLayout(self) self._tabs = QTabWidget() self._tabs.setTabPosition(QTabWidget.West) self._tabs.setMovable(False) self._general = GeneralTab(self) self._interface = InterfaceTab(self) self._editor = EditorTab() self._theme = ThemeTab() self._plugins = plugin_preferences.PluginPreferences() self._tabs.addTab(self._general, self.tr("General")) self._tabs.addTab(self._interface, self.tr("Interface")) self._tabs.addTab(self._editor, self.tr("Editor")) self._tabs.addTab(self._plugins, self.tr("Plugins")) self._tabs.addTab(self._theme, self.tr("Theme")) #Buttons (save-cancel) hbox = QHBoxLayout() self._btnSave = QPushButton(self.tr("Save")) self._btnCancel = QPushButton(self.tr("Cancel")) hbox = QHBoxLayout() hbox.addWidget(self._btnCancel) hbox.addWidget(self._btnSave) gridFooter = QGridLayout() gridFooter.addLayout(hbox, 0, 0, Qt.AlignRight) vbox.addWidget(self._tabs) vbox.addLayout(gridFooter) self.connect(self._btnSave, SIGNAL("clicked()"), self._save) self.connect(self._btnCancel, SIGNAL("clicked()"), self._cancel) def _cancel(self): editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget is not None: editorWidget.restyle(editorWidget.lang) self.close() def _save(self): for i in range(self._tabs.count()): self._tabs.widget(i).save() self.close() def resizeEvent(self, event): self.overlay.resize(event.size()) event.accept() class GeneralTab(QWidget): def __init__(self, parent): QWidget.__init__(self) vbox = QVBoxLayout(self) self._tabs = QTabWidget() self._generalConfiguration = GeneralConfiguration(parent) self._generalExecution = GeneralExecution() self._shortcutConfiguration = shortcut_manager.ShortcutConfiguration() self._tabs.addTab(self._generalConfiguration, self.tr("General")) self._tabs.addTab(self._generalExecution, self.tr("Execution")) self._tabs.addTab(self._shortcutConfiguration, self.tr("Shortcuts")) vbox.addWidget(self._tabs) def save(self): for i in range(self._tabs.count()): self._tabs.widget(i).save() class GeneralConfiguration(QWidget): def __init__(self, dialog): QWidget.__init__(self) self._dialog = dialog vbox = QVBoxLayout(self) groupBoxStart = QGroupBox(self.tr("On Start:")) groupBoxClose = QGroupBox(self.tr("On Close:")) groupBoxWorkspace = QGroupBox(self.tr("Workspace and Project:")) groupBoxReset = QGroupBox(self.tr("Reset NINJA-IDE Preferences:")) #Start vboxStart = QVBoxLayout(groupBoxStart) self._checkLastSession = QCheckBox( self.tr("Load files from last session")) self._checkActivatePlugins = QCheckBox(self.tr("Activate Plugins")) self._checkNotifyUpdates = QCheckBox( self.tr("Notify me of new updates.")) self._checkShowStartPage = QCheckBox(self.tr("Show Start Page")) vboxStart.addWidget(self._checkLastSession) vboxStart.addWidget(self._checkActivatePlugins) vboxStart.addWidget(self._checkNotifyUpdates) vboxStart.addWidget(self._checkShowStartPage) #Close vboxClose = QVBoxLayout(groupBoxClose) self._checkConfirmExit = QCheckBox(self.tr("Confirm Exit.")) vboxClose.addWidget(self._checkConfirmExit) #Workspace and Project gridWorkspace = QGridLayout(groupBoxWorkspace) self._txtWorkspace = QLineEdit() ui_tools.LineEditButton(self._txtWorkspace, self._txtWorkspace.clear, self.style().standardPixmap(self.style().SP_TrashIcon)) self._txtWorkspace.setReadOnly(True) self._btnWorkspace = QPushButton( QIcon(resources.IMAGES['openFolder']), '') gridWorkspace.addWidget( QLabel(self.tr("Workspace")), 0, 0, Qt.AlignRight) gridWorkspace.addWidget(self._txtWorkspace, 0, 1) gridWorkspace.addWidget(self._btnWorkspace, 0, 2) self._txtExtensions = QLineEdit() gridWorkspace.addWidget(QLabel( self.tr("Supported Extensions:")), 1, 0, Qt.AlignRight) gridWorkspace.addWidget(self._txtExtensions, 1, 1) # Resetting preferences vboxReset = QVBoxLayout(groupBoxReset) self._btnReset = QPushButton(self.tr("Reset preferences")) vboxReset.addWidget(self._btnReset, alignment=Qt.AlignLeft) #Settings qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.beginGroup('preferences') qsettings.beginGroup('general') self._checkLastSession.setChecked( qsettings.value('loadFiles', True, type=bool)) self._checkActivatePlugins.setChecked( qsettings.value('activatePlugins', defaultValue=True, type=bool)) self._checkNotifyUpdates.setChecked(settings.NOTIFY_UPDATES) self._checkShowStartPage.setChecked(settings.SHOW_START_PAGE) self._checkConfirmExit.setChecked(settings.CONFIRM_EXIT) self._txtWorkspace.setText(settings.WORKSPACE) extensions = ', '.join(settings.SUPPORTED_EXTENSIONS) self._txtExtensions.setText(extensions) qsettings.endGroup() qsettings.endGroup() vbox.addWidget(groupBoxStart) vbox.addWidget(groupBoxClose) vbox.addWidget(groupBoxWorkspace) vbox.addWidget(groupBoxReset) #Signals self.connect(self._btnWorkspace, SIGNAL("clicked()"), self._load_workspace) self.connect(self._btnReset, SIGNAL('clicked()'), self._reset_preferences) def _load_workspace(self): path = QFileDialog.getExistingDirectory( self, self.tr("Select Workspace")) self._txtWorkspace.setText(path) def _load_python_path(self): path = QFileDialog.getOpenFileName(self, self.tr("Select Python Path")) if path: self._txtPythonPath.setText(path) def save(self): qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.beginGroup('preferences') qsettings.beginGroup('general') qsettings.setValue('loadFiles', self._checkLastSession.isChecked()) qsettings.setValue('activatePlugins', self._checkActivatePlugins.isChecked()) qsettings.setValue('notifyUpdates', self._checkNotifyUpdates.isChecked()) qsettings.setValue('showStartPage', self._checkShowStartPage.isChecked()) settings.NOTIFY_UPDATES = self._checkNotifyUpdates.isChecked() qsettings.setValue('confirmExit', self._checkConfirmExit.isChecked()) settings.CONFIRM_EXIT = self._checkConfirmExit.isChecked() qsettings.setValue('workspace', self._txtWorkspace.text()) settings.WORKSPACE = self._txtWorkspace.text() extensions = str(self._txtExtensions.text()).split(',') extensions = [e.strip() for e in extensions] qsettings.setValue('supportedExtensions', extensions) settings.SUPPORTED_EXTENSIONS = list(extensions) qsettings.endGroup() qsettings.endGroup() def _reset_preferences(self): result = QMessageBox.question(self, self.tr("Reset preferences?"), self.tr("Are you sure you want to reset your preferences?"), buttons=QMessageBox.Yes | QMessageBox.No) if result == QMessageBox.Yes: QSettings(resources.SETTINGS_PATH, QSettings.IniFormat).clear() self._dialog.close() class GeneralExecution(QWidget): def __init__(self): QWidget.__init__(self) vbox = QVBoxLayout(self) groupExecution = QGroupBox(self.tr("Workspace and Project:")) grid = QVBoxLayout(groupExecution) #Python Path hPath = QHBoxLayout() self._txtPythonPath = QLineEdit() self._btnPythonPath = QPushButton(QIcon(resources.IMAGES['open']), '') hPath.addWidget(QLabel(self.tr("Python Path:"))) hPath.addWidget(self._txtPythonPath) hPath.addWidget(self._btnPythonPath) grid.addLayout(hPath) #Python Miscellaneous Execution options self.check_B = QCheckBox(self.tr( "-B: don't write .py[co] files on import")) self.check_d = QCheckBox(self.tr("-d: debug output from parser")) self.check_E = QCheckBox(self.tr( "-E: ignore PYTHON* environment variables (such as PYTHONPATH)")) self.check_O = QCheckBox( self.tr("-O: optimize generated bytecode slightly")) self.check_OO = QCheckBox(self.tr( "-OO: remove doc-strings in addition to the -O optimizations")) self.check_Q = QCheckBox(self.tr("-Q: division options:")) self.comboDivision = QComboBox() self.comboDivision.addItems(['old', 'new', 'warn', 'warnall']) self.check_s = QCheckBox(self.tr( "-s: don't add user site directory to sys.path")) self.check_S = QCheckBox(self.tr( "-S: don't imply 'import site' on initialization")) self.check_t = QCheckBox(self.tr( "-t: issue warnings about inconsistent tab usage")) self.check_tt = QCheckBox(self.tr( "-tt: issue errors about inconsistent tab usage")) self.check_v = QCheckBox(self.tr( "-v: verbose (trace import statements)")) self.check_W = QCheckBox(self.tr("-W: warning control:")) self.comboWarning = QComboBox() self.comboWarning.addItems( ['default', 'ignore', 'all', 'module', 'once', 'error']) self.check_x = QCheckBox(self.tr("-x: skip first line of source")) self.check_3 = QCheckBox(self.tr("-3: warn about Python 3.x " "incompatibilities that 2to3 cannot trivially fix")) grid.addWidget(self.check_B) grid.addWidget(self.check_d) grid.addWidget(self.check_E) grid.addWidget(self.check_O) grid.addWidget(self.check_OO) hDiv = QHBoxLayout() hDiv.addWidget(self.check_Q) hDiv.addWidget(self.comboDivision) grid.addLayout(hDiv) grid.addWidget(self.check_s) grid.addWidget(self.check_S) grid.addWidget(self.check_t) grid.addWidget(self.check_tt) grid.addWidget(self.check_v) hWarn = QHBoxLayout() hWarn.addWidget(self.check_W) hWarn.addWidget(self.comboWarning) grid.addLayout(hWarn) grid.addWidget(self.check_x) grid.addWidget(self.check_3) #Settings self._txtPythonPath.setText(settings.PYTHON_PATH) options = settings.EXECUTION_OPTIONS.split() if '-B' in options: self.check_B.setChecked(True) if '-d' in options: self.check_d.setChecked(True) if '-E' in options: self.check_E.setChecked(True) if '-O' in options: self.check_O.setChecked(True) if '-OO' in options: self.check_OO.setChecked(True) if settings.EXECUTION_OPTIONS.find('-Q') > -1: self.check_Q.setChecked(True) index = settings.EXECUTION_OPTIONS.find('-Q') opt = settings.EXECUTION_OPTIONS[index + 2:].split(' ', 1)[0] index = self.comboDivision.findText(opt) self.comboDivision.setCurrentIndex(index) if '-s' in options: self.check_s.setChecked(True) if '-S' in options: self.check_S.setChecked(True) if '-t' in options: self.check_t.setChecked(True) if '-tt' in options: self.check_tt.setChecked(True) if '-v' in options: self.check_v.setChecked(True) if settings.EXECUTION_OPTIONS.find('-W') > -1: self.check_W.setChecked(True) index = settings.EXECUTION_OPTIONS.find('-W') opt = settings.EXECUTION_OPTIONS[index + 2:].split(' ', 1)[0] index = self.comboWarning.findText(opt) self.comboWarning.setCurrentIndex(index) if '-x' in options: self.check_x.setChecked(True) if '-3' in options: self.check_3.setChecked(True) vbox.addWidget(groupExecution) #Signals self.connect(self._btnPythonPath, SIGNAL("clicked()"), self._load_python_path) def _load_python_path(self): path = QFileDialog.getOpenFileName(self, self.tr("Select Python Path")) if path: self._txtPythonPath.setText(path) def save(self): qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.beginGroup('preferences') qsettings.beginGroup('execution') qsettings.setValue('pythonPath', self._txtPythonPath.text()) settings.PYTHON_PATH = self._txtPythonPath.text() options = '' if self.check_B.isChecked(): options += ' -B' if self.check_d.isChecked(): options += ' -d' if self.check_E.isChecked(): options += ' -E' if self.check_O.isChecked(): options += ' -O' if self.check_OO.isChecked(): options += ' -OO' if self.check_Q.isChecked(): options += ' -Q' + self.comboDivision.currentText() if self.check_s.isChecked(): options += ' -s' if self.check_S.isChecked(): options += ' -S' if self.check_t.isChecked(): options += ' -t' if self.check_tt.isChecked(): options += ' -tt' if self.check_v.isChecked(): options += ' -v' if self.check_W.isChecked(): options += ' -W' + self.comboWarning.currentText() if self.check_x.isChecked(): options += ' -x' if self.check_3.isChecked(): options += ' -3' settings.EXECUTION_OPTIONS = options qsettings.setValue('executionOptions', options) qsettings.endGroup() qsettings.endGroup() class InterfaceTab(QWidget): def __init__(self, parent): QWidget.__init__(self, parent) vbox = QVBoxLayout(self) self._parent = parent self.toolbar_settings = settings.TOOLBAR_ITEMS groupBoxExplorer = QGroupBox(self.tr("Explorer Panel:")) groupBoxGui = QGroupBox(self.tr("GUI Customization:")) groupBoxToolbar = QGroupBox(self.tr("Tool Bar Customization:")) groupBoxLang = QGroupBox(self.tr("Language:")) #Explorer vboxExplorer = QVBoxLayout(groupBoxExplorer) self._checkProjectExplorer = QCheckBox( self.tr("Show Project Explorer.")) self._checkSymbols = QCheckBox(self.tr("Show Symbols List.")) self._checkWebInspetor = QCheckBox(self.tr("Show Web Inspector.")) self._checkFileErrors = QCheckBox(self.tr("Show File Errors.")) self._checkMigrationTips = QCheckBox(self.tr("Show Migration Tips.")) self._checkStatusBarNotifications = QCheckBox( self.tr("Show Status Bar Notifications.")) vboxExplorer.addWidget(self._checkProjectExplorer) vboxExplorer.addWidget(self._checkSymbols) vboxExplorer.addWidget(self._checkWebInspetor) vboxExplorer.addWidget(self._checkFileErrors) vboxExplorer.addWidget(self._checkMigrationTips) vboxExplorer.addWidget(self._checkStatusBarNotifications) #GUI self._btnCentralRotate = QPushButton( QIcon(resources.IMAGES['splitCPosition']), '') self._btnCentralRotate.setIconSize(QSize(64, 64)) self._btnCentralRotate.setCheckable(True) self._btnPanelsRotate = QPushButton( QIcon(resources.IMAGES['splitMPosition']), '') self._btnPanelsRotate.setIconSize(QSize(64, 64)) self._btnPanelsRotate.setCheckable(True) self._btnCentralOrientation = QPushButton( QIcon(resources.IMAGES['splitCRotate']), '') self._btnCentralOrientation.setIconSize(QSize(64, 64)) self._btnCentralOrientation.setCheckable(True) gridGuiConfig = QGridLayout(groupBoxGui) gridGuiConfig.addWidget(self._btnCentralRotate, 0, 0) gridGuiConfig.addWidget(self._btnPanelsRotate, 0, 1) gridGuiConfig.addWidget(self._btnCentralOrientation, 0, 2) gridGuiConfig.addWidget(QLabel( self.tr("Rotate Central")), 1, 0, Qt.AlignCenter) gridGuiConfig.addWidget(QLabel( self.tr("Rotate Lateral")), 1, 1, Qt.AlignCenter) gridGuiConfig.addWidget(QLabel( self.tr("Central Orientation")), 1, 2, Qt.AlignCenter) #GUI - Toolbar vbox_toolbar = QVBoxLayout(groupBoxToolbar) hbox_select_items = QHBoxLayout() label_toolbar = QLabel(self.tr("Toolbar Items:")) label_toolbar.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) hbox_select_items.addWidget(label_toolbar) self._comboToolbarItems = QComboBox() self._load_combo_data(self._comboToolbarItems) self._btnItemAdd = QPushButton( QIcon(resources.IMAGES['add']), '') self._btnItemAdd.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self._btnItemRemove = QPushButton( QIcon(resources.IMAGES['delete']), '') self._btnDefaultItems = QPushButton(self.tr("Default Items")) self._btnDefaultItems.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self._btnItemRemove.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) hbox_select_items.addWidget(self._comboToolbarItems) hbox_select_items.addWidget(self._btnItemAdd) hbox_select_items.addWidget(self._btnItemRemove) hbox_select_items.addWidget(self._btnDefaultItems) vbox_toolbar.addLayout(hbox_select_items) self._toolbar_items = QToolBar() self._toolbar_items.setObjectName("custom") self._toolbar_items.setToolButtonStyle(Qt.ToolButtonIconOnly) self._load_toolbar() vbox_toolbar.addWidget(self._toolbar_items) vbox_toolbar.addWidget(QLabel( self.tr("The New Item will be inserted after the item selected."))) #Language vboxLanguage = QVBoxLayout(groupBoxLang) vboxLanguage.addWidget(QLabel(self.tr("Select Language:"))) self._comboLang = QComboBox() self._comboLang.setEnabled(False) vboxLanguage.addWidget(self._comboLang) vboxLanguage.addWidget(QLabel(self.tr('Requires restart...'))) #Load Languages self._load_langs() #Settings self._checkProjectExplorer.setChecked( settings.SHOW_PROJECT_EXPLORER) self._checkSymbols.setChecked(settings.SHOW_SYMBOLS_LIST) self._checkWebInspetor.setChecked(settings.SHOW_WEB_INSPECTOR) self._checkFileErrors.setChecked(settings.SHOW_ERRORS_LIST) self._checkMigrationTips.setChecked(settings.SHOW_MIGRATION_LIST) self._checkStatusBarNotifications.setChecked( settings.SHOW_STATUS_NOTIFICATIONS) #ui layout self._btnCentralRotate.setChecked(bin(settings.UI_LAYOUT)[-1] == '1') self._btnPanelsRotate.setChecked(bin( settings.UI_LAYOUT >> 1)[-1] == '1') self._btnCentralOrientation.setChecked( bin(settings.UI_LAYOUT >> 2)[-1] == '1') vbox.addWidget(groupBoxExplorer) vbox.addWidget(groupBoxGui) vbox.addWidget(groupBoxToolbar) vbox.addWidget(groupBoxLang) #Signals self.connect(self._btnCentralRotate, SIGNAL('clicked()'), central_widget.CentralWidget().splitter_central_rotate) self.connect(self._btnPanelsRotate, SIGNAL('clicked()'), central_widget.CentralWidget().splitter_misc_rotate) self.connect(self._btnCentralOrientation, SIGNAL('clicked()'), central_widget.CentralWidget().splitter_central_orientation) self.connect(self._btnItemAdd, SIGNAL("clicked()"), self.toolbar_item_added) self.connect(self._btnItemRemove, SIGNAL("clicked()"), self.toolbar_item_removed) self.connect(self._btnDefaultItems, SIGNAL("clicked()"), self.toolbar_items_default) def toolbar_item_added(self): data = self._comboToolbarItems.itemData( self._comboToolbarItems.currentIndex()) if data not in self.toolbar_settings or data == 'separator': selected = self.actionGroup.checkedAction() if selected is None: self.toolbar_settings.append(data) else: dataAction = selected.data() self.toolbar_settings.insert( self.toolbar_settings.index(dataAction) + 1, data) self._load_toolbar() def toolbar_item_removed(self): data = self._comboToolbarItems.itemData( self._comboToolbarItems.currentIndex()) if data in self.toolbar_settings and data != 'separator': self.toolbar_settings.pop(self.toolbar_settings.index(data)) self._load_toolbar() elif data == 'separator': self.toolbar_settings.reverse() self.toolbar_settings.pop(self.toolbar_settings.index(data)) self.toolbar_settings.reverse() self._load_toolbar() def toolbar_items_default(self): self.toolbar_settings = settings.TOOLBAR_ITEMS_DEFAULT self._load_toolbar() def _load_combo_data(self, combo): self.toolbar_items = { 'separator': [QIcon(resources.IMAGES['separator']), 'Add Separtor'], 'new-file': [QIcon(resources.IMAGES['new']), self.tr('New File')], 'new-project': [QIcon(resources.IMAGES['newProj']), self.tr('New Project')], 'save-file': [QIcon(resources.IMAGES['save']), self.tr('Save File')], 'save-as': [QIcon(resources.IMAGES['saveAs']), self.tr('Save As')], 'save-all': [QIcon(resources.IMAGES['saveAll']), self.tr('Save All')], 'save-project': [QIcon(resources.IMAGES['saveAll']), self.tr('Save Project')], 'reload-file': [QIcon(resources.IMAGES['reload-file']), self.tr('Reload File')], 'open-file': [QIcon(resources.IMAGES['open']), self.tr('Open File')], 'open-project': [QIcon(resources.IMAGES['openProj']), self.tr('Open Project')], 'activate-profile': [QIcon(resources.IMAGES['activate-profile']), self.tr('Activate Profile')], 'deactivate-profile': [QIcon(resources.IMAGES['deactivate-profile']), self.tr('Deactivate Profile')], 'print-file': [QIcon(resources.IMAGES['print']), self.tr('Print File')], 'close-file': [self.style().standardIcon(QStyle.SP_DialogCloseButton), self.tr('Close File')], 'close-projects': [self.style().standardIcon(QStyle.SP_DialogCloseButton), self.tr('Close Projects')], 'undo': [QIcon(resources.IMAGES['undo']), self.tr('Undo')], 'redo': [QIcon(resources.IMAGES['redo']), self.tr('Redo')], 'cut': [QIcon(resources.IMAGES['cut']), self.tr('Cut')], 'copy': [QIcon(resources.IMAGES['copy']), self.tr('Copy')], 'paste': [QIcon(resources.IMAGES['paste']), self.tr('Paste')], 'find': [QIcon(resources.IMAGES['find']), self.tr('Find')], 'find-replace': [QIcon(resources.IMAGES['findReplace']), self.tr('Find/Replace')], 'find-files': [QIcon(resources.IMAGES['find']), self.tr('Find In files')], 'code-locator': [QIcon(resources.IMAGES['locator']), self.tr('Code Locator')], 'splith': [QIcon(resources.IMAGES['splitH']), self.tr('Split Horizontally')], 'splitv': [QIcon(resources.IMAGES['splitV']), self.tr('Split Vertically')], 'follow-mode': [QIcon(resources.IMAGES['follow']), self.tr('Follow Mode')], 'zoom-in': [QIcon(resources.IMAGES['zoom-in']), self.tr('Zoom In')], 'zoom-out': [QIcon(resources.IMAGES['zoom-out']), self.tr('Zoom Out')], 'indent-more': [QIcon(resources.IMAGES['indent-more']), self.tr('Indent More')], 'indent-less': [QIcon(resources.IMAGES['indent-less']), self.tr('Indent Less')], 'comment': [QIcon(resources.IMAGES['comment-code']), self.tr('Comment')], 'uncomment': [QIcon(resources.IMAGES['uncomment-code']), self.tr('Uncomment')], 'go-to-definition': [QIcon(resources.IMAGES['go-to-definition']), self.tr('Go To Definition')], 'insert-import': [QIcon(resources.IMAGES['insert-import']), self.tr('Insert Import')], 'run-project': [QIcon(resources.IMAGES['play']), 'Run Project'], 'run-file': [QIcon(resources.IMAGES['file-run']), 'Run File'], 'stop': [QIcon(resources.IMAGES['stop']), 'Stop'], 'preview-web': [QIcon(resources.IMAGES['preview-web']), self.tr('Preview Web')]} for item in self.toolbar_items: combo.addItem(self.toolbar_items[item][0], self.toolbar_items[item][1], item) combo.model().sort(0) def _load_toolbar(self): self._toolbar_items.clear() self.actionGroup = QActionGroup(self) self.actionGroup.setExclusive(True) for item in self.toolbar_settings: if item == 'separator': self._toolbar_items.addSeparator() else: action = self._toolbar_items.addAction( self.toolbar_items[item][0], self.toolbar_items[item][1]) action.setData(item) action.setCheckable(True) self.actionGroup.addAction(action) def _load_langs(self): langs = file_manager.get_files_from_folder( resources.LANGS, '.qm') langs_download = file_manager.get_files_from_folder( resources.LANGS_DOWNLOAD, '.qm') self._languages = ['English'] + \ [file_manager.get_module_name(lang) for lang in langs] + \ [file_manager.get_module_name(lang) for lang in langs_download] self._comboLang.addItems(self._languages) if(self._comboLang.count() > 1): self._comboLang.setEnabled(True) if settings.LANGUAGE: index = self._comboLang.findText(settings.LANGUAGE) else: index = 0 self._comboLang.setCurrentIndex(index) def save(self): settings.TOOLBAR_ITEMS = self.toolbar_settings lang = self._comboLang.currentText() qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.beginGroup('preferences') qsettings.beginGroup('interface') previous_state = (settings.SHOW_PROJECT_EXPLORER or settings.SHOW_SYMBOLS_LIST or settings.SHOW_MIGRATION_LIST or settings.SHOW_ERRORS_LIST or settings.SHOW_WEB_INSPECTOR) qsettings.setValue('showProjectExplorer', self._checkProjectExplorer.isChecked()) settings.SHOW_PROJECT_EXPLORER = self._checkProjectExplorer.isChecked() if settings.SHOW_PROJECT_EXPLORER: explorer_container.ExplorerContainer().add_tab_projects() else: explorer_container.ExplorerContainer().remove_tab_projects() qsettings.setValue('showSymbolsList', self._checkSymbols.isChecked()) settings.SHOW_SYMBOLS_LIST = self._checkSymbols.isChecked() if settings.SHOW_SYMBOLS_LIST: explorer_container.ExplorerContainer().add_tab_symbols() else: explorer_container.ExplorerContainer().remove_tab_symbols() qsettings.setValue('showWebInspector', self._checkWebInspetor.isChecked()) settings.SHOW_WEB_INSPECTOR = self._checkWebInspetor.isChecked() if settings.SHOW_WEB_INSPECTOR: explorer_container.ExplorerContainer().add_tab_inspector() else: explorer_container.ExplorerContainer().remove_tab_inspector() qsettings.setValue('showErrorsList', self._checkFileErrors.isChecked()) settings.SHOW_ERRORS_LIST = self._checkFileErrors.isChecked() if settings.SHOW_ERRORS_LIST: explorer_container.ExplorerContainer().add_tab_errors() else: explorer_container.ExplorerContainer().remove_tab_errors() qsettings.setValue('showMigrationList', self._checkMigrationTips.isChecked()) settings.SHOW_MIGRATION_LIST = self._checkMigrationTips.isChecked() if settings.SHOW_MIGRATION_LIST: explorer_container.ExplorerContainer().add_tab_migration() else: explorer_container.ExplorerContainer().remove_tab_migration() qsettings.setValue('showStatusNotifications', self._checkStatusBarNotifications.isChecked()) settings.SHOW_STATUS_NOTIFICATIONS = \ self._checkStatusBarNotifications.isChecked() #ui layout uiLayout = 1 if self._btnCentralRotate.isChecked() else 0 uiLayout += 2 if self._btnPanelsRotate.isChecked() else 0 uiLayout += 4 if self._btnCentralOrientation.isChecked() else 0 qsettings.setValue('uiLayout', uiLayout) qsettings.setValue('toolbar', settings.TOOLBAR_ITEMS) qsettings.setValue('language', lang) lang = lang + '.qm' settings.LANGUAGE = os.path.join(resources.LANGS, lang) qsettings.endGroup() qsettings.endGroup() actions.Actions().reload_toolbar() end_state = (settings.SHOW_PROJECT_EXPLORER or settings.SHOW_SYMBOLS_LIST or settings.SHOW_MIGRATION_LIST or settings.SHOW_ERRORS_LIST or settings.SHOW_WEB_INSPECTOR) if previous_state != end_state: actions.Actions().view_explorer_visibility() class EditorTab(QWidget): def __init__(self): QWidget.__init__(self) vbox = QVBoxLayout(self) self._tabs = QTabWidget() self._editorGeneral = EditorGeneral() self._editorConfiguration = EditorConfiguration() self._editorCompletion = EditorCompletion() self._editorSchemeDesigner = EditorSchemeDesigner(self) self._tabs.addTab(self._editorGeneral, self.tr("General")) self._tabs.addTab(self._editorConfiguration, self.tr("Configuration")) self._tabs.addTab(self._editorCompletion, self.tr("Completion")) self._tabs.addTab(self._editorSchemeDesigner, self.tr("Editor Scheme Designer")) vbox.addWidget(self._tabs) def save(self): for i in range(self._tabs.count()): self._tabs.widget(i).save() class EditorGeneral(QWidget): def __init__(self): QWidget.__init__(self) vbox = QVBoxLayout(self) self.original_style = copy.copy(resources.CUSTOM_SCHEME) self.current_scheme = 'default' groupBoxMini = QGroupBox(self.tr("MiniMap:")) groupBoxTypo = QGroupBox(self.tr("Typography:")) groupBoxScheme = QGroupBox(self.tr("Scheme Color:")) #Settings qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.beginGroup('preferences') qsettings.beginGroup('editor') #Minimap formMini = QGridLayout(groupBoxMini) self._checkShowMinimap = QCheckBox() self._checkShowMinimap.setChecked(settings.SHOW_MINIMAP) self._spinMaxOpacity = QSpinBox() self._spinMaxOpacity.setMaximum(100) self._spinMaxOpacity.setMinimum(0) self._spinMaxOpacity.setValue(settings.MINIMAP_MAX_OPACITY * 100) self._spinMinOpacity = QSpinBox() self._spinMinOpacity.setMaximum(100) self._spinMinOpacity.setMinimum(0) self._spinMinOpacity.setValue(settings.MINIMAP_MIN_OPACITY * 100) self._spinSize = QSpinBox() self._spinSize.setMaximum(100) self._spinSize.setMinimum(0) self._spinSize.setValue(settings.SIZE_PROPORTION * 100) formMini.addWidget(QLabel( self.tr("Enable/Disable MiniMap (Requires restart):\n" "(opacity not supported in MAC OS)")), 0, 0, Qt.AlignRight) formMini.addWidget(QLabel(self.tr("Max Opacity:")), 1, 0, Qt.AlignRight) formMini.addWidget(QLabel(self.tr("Min Opacity:")), 2, 0, Qt.AlignRight) formMini.addWidget(QLabel( self.tr("Size Area relative to the Editor:")), 3, 0, Qt.AlignRight) formMini.addWidget(self._checkShowMinimap, 0, 1) formMini.addWidget(self._spinMaxOpacity, 1, 1) formMini.addWidget(self._spinMinOpacity, 2, 1) formMini.addWidget(self._spinSize, 3, 1) #Typo gridTypo = QGridLayout(groupBoxTypo) self._btnEditorFont = QPushButton( ', '.join([settings.FONT_FAMILY, str(settings.FONT_SIZE)])) gridTypo.addWidget(QLabel( self.tr("Editor Font:")), 0, 0, Qt.AlignRight) gridTypo.addWidget(self._btnEditorFont, 0, 1) #Scheme hbox = QHBoxLayout(groupBoxScheme) self._listScheme = QListWidget() self._listScheme.addItem('default') self._schemes = json_manager.load_editor_skins() for item in self._schemes: self._listScheme.addItem(item) items = self._listScheme.findItems( qsettings.value('scheme', defaultValue='', type='QString'), Qt.MatchExactly) if items: self._listScheme.setCurrentItem(items[0]) else: self._listScheme.setCurrentRow(0) hbox.addWidget(self._listScheme) qsettings.endGroup() qsettings.endGroup() vbox.addWidget(groupBoxMini) vbox.addWidget(groupBoxTypo) vbox.addWidget(groupBoxScheme) #Signals self.connect(self._btnEditorFont, SIGNAL("clicked()"), self._load_editor_font) self.connect(self._listScheme, SIGNAL("itemSelectionChanged()"), self._preview_style) def showEvent(self, event): super(EditorGeneral, self).showEvent(event) self.thread_callback = ui_tools.ThreadExecution(self._get_editor_skins) self.connect(self.thread_callback, SIGNAL("finished()"), self._show_editor_skins) self.thread_callback.start() def _get_editor_skins(self): qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.beginGroup('preferences') qsettings.beginGroup('editor') self._schemes = json_manager.load_editor_skins() self._selected_scheme = qsettings.value('scheme', defaultValue='', type='QString') qsettings.endGroup() qsettings.endGroup() def _show_editor_skins(self): self._listScheme.clear() self._listScheme.addItem('default') for item in self._schemes: self._listScheme.addItem(item) items = self._listScheme.findItems( self._selected_scheme, Qt.MatchExactly) if items: self._listScheme.setCurrentItem(items[0]) else: self._listScheme.setCurrentRow(0) self.thread_callback.wait() def hideEvent(self, event): super(EditorGeneral, self).hideEvent(event) resources.CUSTOM_SCHEME = self.original_style editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget is not None: editorWidget.restyle(editorWidget.lang) editorWidget._sidebarWidget.repaint() def _preview_style(self): scheme = self._listScheme.currentItem().text() if scheme == self.current_scheme: return editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget is not None: resources.CUSTOM_SCHEME = self._schemes.get(scheme, resources.COLOR_SCHEME) editorWidget.restyle(editorWidget.lang) editorWidget._sidebarWidget.repaint() self.current_scheme = scheme def _load_editor_font(self): try: font = self._load_font( self._get_font_from_string(self._btnEditorFont.text()), self) self._btnEditorFont.setText(font) except: QMessageBox.warning(self, self.tr("Invalid Font"), self.tr("This font can not be used in the Editor.")) def _get_font_from_string(self, font): if not font: font = QFont(settings.FONT_FAMILY, settings.FONT_SIZE) else: listFont = font.split(',') font = QFont(listFont[0].strip(), int(listFont[1].strip())) return font def _load_font(self, initialFont, parent=0): font, ok = QFontDialog.getFont(initialFont, parent) if ok: newFont = font.toString().split(',') else: newFont = initialFont.toString().split(',') return newFont[0] + ', ' + newFont[1] def save(self): qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.beginGroup('preferences') qsettings.beginGroup('editor') settings.SHOW_MINIMAP = self._checkShowMinimap.isChecked() settings.MINIMAP_MAX_OPACITY = self._spinMaxOpacity.value() / 100.0 settings.MINIMAP_MIN_OPACITY = self._spinMinOpacity.value() / 100.0 settings.SIZE_PROPORTION = self._spinSize.value() / 100.0 qsettings.setValue('minimapShow', settings.SHOW_MINIMAP) qsettings.setValue('minimapMaxOpacity', settings.MINIMAP_MAX_OPACITY) qsettings.setValue('minimapMinOpacity', settings.MINIMAP_MIN_OPACITY) qsettings.setValue('minimapSizeProportion', settings.SIZE_PROPORTION) fontText = self._btnEditorFont.text().replace(' ', '') settings.FONT_FAMILY = fontText.split(',')[0] settings.FONT_SIZE = int(fontText.split(',')[1]) qsettings.setValue('fontFamily', settings.FONT_FAMILY) qsettings.setValue('fontSize', settings.FONT_SIZE) editorWidget = main_container.MainContainer().get_actual_editor() scheme = self._listScheme.currentItem().text() self.original_style = resources.CUSTOM_SCHEME if editorWidget is not None: editorWidget.set_font(settings.FONT_FAMILY, settings.FONT_SIZE) qsettings.setValue('scheme', scheme) resources.CUSTOM_SCHEME = self._schemes.get(scheme, resources.COLOR_SCHEME) qsettings.endGroup() qsettings.endGroup() main_container.MainContainer().apply_editor_theme(settings.FONT_FAMILY, settings.FONT_SIZE) misc = misc_container.MiscContainer() misc._console.restyle() misc._runWidget.set_font(settings.FONT_FAMILY, settings.FONT_SIZE) class EditorConfiguration(QWidget): def __init__(self): QWidget.__init__(self) vbox = QVBoxLayout(self) #Indentation groupBoxFeatures = QGroupBox(self.tr("Features:")) formFeatures = QGridLayout(groupBoxFeatures) formFeatures.addWidget(QLabel( self.tr("Indentation Length:")), 1, 0, Qt.AlignRight) self._spin = QSpinBox() self._spin.setAlignment(Qt.AlignRight) self._spin.setMinimum(1) self._spin.setValue(settings.INDENT) formFeatures.addWidget(self._spin, 1, 1, alignment=Qt.AlignTop) self._checkUseTabs = QCheckBox( self.tr("Use Tabs.")) self._checkUseTabs.setChecked(settings.USE_TABS) self.connect(self._checkUseTabs, SIGNAL("stateChanged(int)"), self._change_tab_spaces) formFeatures.addWidget(self._checkUseTabs, 1, 2, alignment=Qt.AlignTop) if settings.USE_TABS: self._spin.setSuffix(self.tr(" (tab size)")) else: self._spin.setSuffix(self.tr(" (spaces)")) #Margin Line formFeatures.addWidget(QLabel(self.tr("Margin Line:")), 2, 0, Qt.AlignRight) self._spinMargin = QSpinBox() self._spinMargin.setMaximum(200) self._spinMargin.setValue(settings.MARGIN_LINE) formFeatures.addWidget(self._spinMargin, 2, 1, alignment=Qt.AlignTop) self._checkShowMargin = QCheckBox(self.tr("Show Margin Line")) self._checkShowMargin.setChecked(settings.SHOW_MARGIN_LINE) formFeatures.addWidget(self._checkShowMargin, 2, 2, alignment=Qt.AlignTop) #End of line self._checkEndOfLine = QCheckBox(self.tr("Use Platform End of Line")) self._checkEndOfLine.setChecked(settings.USE_PLATFORM_END_OF_LINE) formFeatures.addWidget(self._checkEndOfLine, 3, 1, alignment=Qt.AlignTop) #Find Errors self._checkHighlightLine = QCheckBox( self.tr("Check: Highlight errors using Underline\n" "Uncheck: Highlight errors using Background")) self._checkHighlightLine.setChecked(settings.UNDERLINE_NOT_BACKGROUND) formFeatures.addWidget(self._checkHighlightLine, 4, 1, 1, 2, alignment=Qt.AlignTop) self._checkErrors = QCheckBox(self.tr("Find and Show Errors.")) self._checkErrors.setChecked(settings.FIND_ERRORS) formFeatures.addWidget(self._checkErrors, 5, 1, 1, 2, alignment=Qt.AlignTop) self.connect(self._checkErrors, SIGNAL("stateChanged(int)"), self._disable_show_errors) self._showErrorsOnLine = QCheckBox( self.tr("Show Tool tip information about the errors.")) self._showErrorsOnLine.setChecked(settings.ERRORS_HIGHLIGHT_LINE) self.connect(self._showErrorsOnLine, SIGNAL("stateChanged(int)"), self._enable_errors_inline) formFeatures.addWidget(self._showErrorsOnLine, 6, 2, 1, 1, Qt.AlignTop) #Find Check Style self._checkStyle = QCheckBox( self.tr("Find and Show Check Style errors.")) self._checkStyle.setChecked(settings.CHECK_STYLE) formFeatures.addWidget(self._checkStyle, 7, 1, 1, 2, alignment=Qt.AlignTop) self.connect(self._checkStyle, SIGNAL("stateChanged(int)"), self._disable_check_style) self._checkStyleOnLine = QCheckBox( self.tr("Show Tool tip information about the PEP8 errors.")) self._checkStyleOnLine.setChecked(settings.CHECK_HIGHLIGHT_LINE) self.connect(self._checkStyleOnLine, SIGNAL("stateChanged(int)"), self._enable_check_inline) formFeatures.addWidget(self._checkStyleOnLine, 8, 2, 1, 1, Qt.AlignTop) # Python3 Migration self._showMigrationTips = QCheckBox( self.tr("Show Python3 Migration Tips.")) self._showMigrationTips.setChecked(settings.SHOW_MIGRATION_TIPS) formFeatures.addWidget(self._showMigrationTips, 9, 1, 1, 2, Qt.AlignTop) #Center On Scroll self._checkCenterScroll = QCheckBox( self.tr("Center on Scroll.")) self._checkCenterScroll.setChecked(settings.CENTER_ON_SCROLL) formFeatures.addWidget(self._checkCenterScroll, 10, 1, 1, 2, alignment=Qt.AlignTop) #Remove Trailing Spaces add Last empty line automatically self._checkTrailing = QCheckBox(self.tr( "Remove Trailing Spaces and\nadd Last Line automatically.")) self._checkTrailing.setChecked(settings.REMOVE_TRAILING_SPACES) formFeatures.addWidget(self._checkTrailing, 11, 1, 1, 2, alignment=Qt.AlignTop) #Show Tabs and Spaces self._checkShowSpaces = QCheckBox(self.tr("Show Tabs and Spaces.")) self._checkShowSpaces.setChecked(settings.SHOW_TABS_AND_SPACES) formFeatures.addWidget(self._checkShowSpaces, 12, 1, 1, 2, alignment=Qt.AlignTop) self._allowWordWrap = QCheckBox(self.tr("Allow Word Wrap.")) self._allowWordWrap.setChecked(settings.ALLOW_WORD_WRAP) formFeatures.addWidget(self._allowWordWrap, 13, 1, 1, 2, alignment=Qt.AlignTop) self._checkForDocstrings = QCheckBox( self.tr("Check for Docstrings in Classes and Functions.")) self._checkForDocstrings.setChecked(settings.CHECK_FOR_DOCSTRINGS) formFeatures.addWidget(self._checkForDocstrings, 14, 1, 1, 2, alignment=Qt.AlignTop) vbox.addWidget(groupBoxFeatures) vbox.addItem(QSpacerItem(0, 10, QSizePolicy.Expanding, QSizePolicy.Expanding)) def _enable_check_inline(self, val): if val == Qt.Checked: self._checkStyle.setChecked(True) def _enable_errors_inline(self, val): if val == Qt.Checked: self._checkErrors.setChecked(True) def _disable_check_style(self, val): if val == Qt.Unchecked: self._checkStyleOnLine.setChecked(False) def _disable_show_errors(self, val): if val == Qt.Unchecked: self._showErrorsOnLine.setChecked(False) def _change_tab_spaces(self, val): if val == Qt.Unchecked: self._spin.setSuffix(self.tr(" (spaces)")) else: self._spin.setSuffix(self.tr(" (tab size)")) def save(self): qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.beginGroup('preferences') qsettings.beginGroup('editor') qsettings.setValue('indent', self._spin.value()) settings.INDENT = self._spin.value() endOfLine = self._checkEndOfLine.isChecked() qsettings.setValue('platformEndOfLine', endOfLine) settings.USE_PLATFORM_END_OF_LINE = endOfLine margin_line = self._spinMargin.value() qsettings.setValue('marginLine', margin_line) settings.MARGIN_LINE = margin_line settings.pep8mod_update_margin_line_length(margin_line) qsettings.setValue('showMarginLine', self._checkShowMargin.isChecked()) settings.SHOW_MARGIN_LINE = self._checkShowMargin.isChecked() settings.UNDERLINE_NOT_BACKGROUND = \ self._checkHighlightLine.isChecked() qsettings.setValue('errorsUnderlineBackground', settings.UNDERLINE_NOT_BACKGROUND) qsettings.setValue('errors', self._checkErrors.isChecked()) settings.FIND_ERRORS = self._checkErrors.isChecked() qsettings.setValue('errorsInLine', self._showErrorsOnLine.isChecked()) settings.ERRORS_HIGHLIGHT_LINE = self._showErrorsOnLine.isChecked() qsettings.setValue('checkStyle', self._checkStyle.isChecked()) settings.CHECK_STYLE = self._checkStyle.isChecked() qsettings.setValue('showMigrationTips', self._showMigrationTips.isChecked()) settings.SHOW_MIGRATION_TIPS = self._showMigrationTips.isChecked() qsettings.setValue('checkStyleInline', self._checkStyleOnLine.isChecked()) settings.CHECK_HIGHLIGHT_LINE = self._checkStyleOnLine.isChecked() qsettings.setValue('centerOnScroll', self._checkCenterScroll.isChecked()) settings.CENTER_ON_SCROLL = self._checkCenterScroll.isChecked() qsettings.setValue('removeTrailingSpaces', self._checkTrailing.isChecked()) settings.REMOVE_TRAILING_SPACES = self._checkTrailing.isChecked() qsettings.setValue('showTabsAndSpaces', self._checkShowSpaces.isChecked()) settings.SHOW_TABS_AND_SPACES = self._checkShowSpaces.isChecked() qsettings.setValue('useTabs', self._checkUseTabs.isChecked()) settings.USE_TABS = self._checkUseTabs.isChecked() qsettings.setValue('allowWordWrap', self._allowWordWrap.isChecked()) settings.ALLOW_WORD_WRAP = self._allowWordWrap.isChecked() qsettings.setValue('checkForDocstrings', self._checkForDocstrings.isChecked()) settings.CHECK_FOR_DOCSTRINGS = self._checkForDocstrings.isChecked() qsettings.endGroup() qsettings.endGroup() action = actions.Actions() action.reset_editor_flags() action.call_editors_function("set_tab_usage") if settings.USE_TABS: settings.pep8mod_add_ignore("W191") else: settings.pep8mod_remove_ignore("W191") settings.pep8mod_refresh_checks() main_container.MainContainer().update_editor_margin_line() class EditorCompletion(QWidget): def __init__(self): QWidget.__init__(self) vbox = QVBoxLayout(self) groupBoxClose = QGroupBox(self.tr("Complete:")) formClose = QGridLayout(groupBoxClose) self._checkParentheses = QCheckBox(self.tr("Parentheses: ()")) self._checkParentheses.setChecked('(' in settings.BRACES) self._checkKeys = QCheckBox(self.tr("Keys: {}")) self._checkKeys.setChecked('{' in settings.BRACES) self._checkBrackets = QCheckBox(self.tr("Brackets: []")) self._checkBrackets.setChecked('[' in settings.BRACES) self._checkSimpleQuotes = QCheckBox(self.tr("Simple Quotes: ''")) self._checkSimpleQuotes.setChecked("'" in settings.QUOTES) self._checkDoubleQuotes = QCheckBox(self.tr("Double Quotes: \"\"")) self._checkDoubleQuotes.setChecked('"' in settings.QUOTES) self._checkCompleteDeclarations = QCheckBox( self.tr("Complete Declarations\n" "(execute the opposite action with: %s).") % resources.get_shortcut("Complete-Declarations").toString( QKeySequence.NativeText)) self._checkCompleteDeclarations.setChecked( settings.COMPLETE_DECLARATIONS) formClose.addWidget(self._checkParentheses, 1, 1, alignment=Qt.AlignTop) formClose.addWidget(self._checkKeys, 1, 2, alignment=Qt.AlignTop) formClose.addWidget(self._checkBrackets, 2, 1, alignment=Qt.AlignTop) formClose.addWidget(self._checkSimpleQuotes, 2, 2, alignment=Qt.AlignTop) formClose.addWidget(self._checkDoubleQuotes, 3, 1, alignment=Qt.AlignTop) vbox.addWidget(groupBoxClose) groupBoxCode = QGroupBox(self.tr("Code Completion:")) formCode = QGridLayout(groupBoxCode) self._checkCodeDot = QCheckBox( self.tr("Activate Code Completion with: \".\"")) self._checkCodeDot.setChecked(settings.CODE_COMPLETION) formCode.addWidget(self._checkCompleteDeclarations, 5, 1, alignment=Qt.AlignTop) formCode.addWidget(self._checkCodeDot, 6, 1, alignment=Qt.AlignTop) vbox.addWidget(groupBoxCode) vbox.addItem(QSpacerItem(0, 10, QSizePolicy.Expanding, QSizePolicy.Expanding)) def save(self): qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.beginGroup('preferences') qsettings.beginGroup('editor') qsettings.setValue('parentheses', self._checkParentheses.isChecked()) if self._checkParentheses.isChecked(): settings.BRACES['('] = ')' elif ('(') in settings.BRACES: del settings.BRACES['('] qsettings.setValue('brackets', self._checkBrackets.isChecked()) if self._checkBrackets.isChecked(): settings.BRACES['['] = ']' elif ('[') in settings.BRACES: del settings.BRACES['['] qsettings.setValue('keys', self._checkKeys.isChecked()) if self._checkKeys.isChecked(): settings.BRACES['{'] = '}' elif ('{') in settings.BRACES: del settings.BRACES['{'] qsettings.setValue('simpleQuotes', self._checkSimpleQuotes.isChecked()) if self._checkSimpleQuotes.isChecked(): settings.QUOTES["'"] = "'" elif ("'") in settings.QUOTES: del settings.QUOTES["'"] qsettings.setValue('doubleQuotes', self._checkDoubleQuotes.isChecked()) if self._checkDoubleQuotes.isChecked(): settings.QUOTES['"'] = '"' elif ('"') in settings.QUOTES: del settings.QUOTES['"'] qsettings.setValue('codeCompletion', self._checkCodeDot .isChecked()) settings.CODE_COMPLETION = self._checkCodeDot.isChecked() settings.COMPLETE_DECLARATIONS = \ self._checkCompleteDeclarations.isChecked() qsettings.setValue("completeDeclarations", settings.COMPLETE_DECLARATIONS) qsettings.endGroup() qsettings.endGroup() class EditorSchemeDesigner(QWidget): def __init__(self, parent): super(EditorSchemeDesigner, self).__init__() self._parent = parent vbox = QVBoxLayout(self) scrollArea = QScrollArea() vbox.addWidget(scrollArea) self.original_style = copy.copy(resources.CUSTOM_SCHEME) self.txtKeyword = QLineEdit() btnKeyword = QPushButton(self.tr("Pick Color")) self.txtOperator = QLineEdit() btnOperator = QPushButton(self.tr("Pick Color")) self.txtBrace = QLineEdit() btnBrace = QPushButton(self.tr("Pick Color")) self.txtDefinition = QLineEdit() btnDefinition = QPushButton(self.tr("Pick Color")) self.txtString = QLineEdit() btnString = QPushButton(self.tr("Pick Color")) self.txtString2 = QLineEdit() btnString2 = QPushButton(self.tr("Pick Color")) self.txtComment = QLineEdit() btnComment = QPushButton(self.tr("Pick Color")) self.txtProperObject = QLineEdit() btnProperObject = QPushButton(self.tr("Pick Color")) self.txtNumbers = QLineEdit() btnNumbers = QPushButton(self.tr("Pick Color")) self.txtSpaces = QLineEdit() btnSpaces = QPushButton(self.tr("Pick Color")) self.txtExtras = QLineEdit() btnExtras = QPushButton(self.tr("Pick Color")) self.txtEditorText = QLineEdit() btnEditorText = QPushButton(self.tr("Pick Color")) self.txtEditorBackground = QLineEdit() btnEditorBackground = QPushButton(self.tr("Pick Color")) self.txtEditorSelectionColor = QLineEdit() btnEditorSelectionColor = QPushButton(self.tr("Pick Color")) self.txtEditorSelectionBackground = QLineEdit() btnEditorSelectionBackground = QPushButton(self.tr("Pick Color")) self.txtSelectedWord = QLineEdit() btnSelectedWord = QPushButton(self.tr("Pick Color")) self.txtCurrentLine = QLineEdit() btnCurrentLine = QPushButton(self.tr("Pick Color")) self.txtFoldArea = QLineEdit() btnFoldArea = QPushButton(self.tr("Pick Color")) self.txtFoldArrow = QLineEdit() btnFoldArrow = QPushButton(self.tr("Pick Color")) self.txtLinkNavigate = QLineEdit() btnLinkNavigate = QPushButton(self.tr("Pick Color")) self.txtBraceBackground = QLineEdit() btnBraceBackground = QPushButton(self.tr("Pick Color")) self.txtBraceForeground = QLineEdit() btnBraceForeground = QPushButton(self.tr("Pick Color")) self.txtErrorUnderline = QLineEdit() btnErrorUnderline = QPushButton(self.tr("Pick Color")) self.txtPep8Underline = QLineEdit() btnPep8Underline = QPushButton(self.tr("Pick Color")) self.txtSidebarBackground = QLineEdit() btnSidebarBackground = QPushButton(self.tr("Pick Color")) self.txtSidebarForeground = QLineEdit() btnSidebarForeground = QPushButton(self.tr("Pick Color")) hbox = QHBoxLayout() hbox.addWidget(QLabel(self.tr("New Theme Name:"))) self.line_name = QLineEdit() hbox.addWidget(self.line_name) btnSaveScheme = QPushButton(self.tr("Save Scheme!")) hbox.addWidget(btnSaveScheme) grid = QGridLayout() grid.addWidget(QLabel(self.tr("Keyword:")), 0, 0) grid.addWidget(self.txtKeyword, 0, 1) grid.addWidget(btnKeyword, 0, 2) grid.addWidget(QLabel(self.tr("Operator:")), 1, 0) grid.addWidget(self.txtOperator, 1, 1) grid.addWidget(btnOperator, 1, 2) grid.addWidget(QLabel(self.tr("Braces:")), 2, 0) grid.addWidget(self.txtBrace, 2, 1) grid.addWidget(btnBrace, 2, 2) grid.addWidget(QLabel(self.tr("Definition:")), 3, 0) grid.addWidget(self.txtDefinition, 3, 1) grid.addWidget(btnDefinition, 3, 2) grid.addWidget(QLabel(self.tr("String:")), 4, 0) grid.addWidget(self.txtString, 4, 1) grid.addWidget(btnString, 4, 2) grid.addWidget(QLabel(self.tr("String2:")), 5, 0) grid.addWidget(self.txtString2, 5, 1) grid.addWidget(btnString2, 5, 2) grid.addWidget(QLabel(self.tr("Comment:")), 6, 0) grid.addWidget(self.txtComment, 6, 1) grid.addWidget(btnComment, 6, 2) grid.addWidget(QLabel(self.tr("Proper Object:")), 7, 0) grid.addWidget(self.txtProperObject, 7, 1) grid.addWidget(btnProperObject, 7, 2) grid.addWidget(QLabel(self.tr("Numbers:")), 8, 0) grid.addWidget(self.txtNumbers, 8, 1) grid.addWidget(btnNumbers, 8, 2) grid.addWidget(QLabel(self.tr("Spaces:")), 9, 0) grid.addWidget(self.txtSpaces, 9, 1) grid.addWidget(btnSpaces, 9, 2) grid.addWidget(QLabel(self.tr("Extras:")), 10, 0) grid.addWidget(self.txtExtras, 10, 1) grid.addWidget(btnExtras, 10, 2) grid.addWidget(QLabel(self.tr("Editor Text:")), 11, 0) grid.addWidget(self.txtEditorText, 11, 1) grid.addWidget(btnEditorText, 11, 2) grid.addWidget(QLabel(self.tr("Editor Background:")), 12, 0) grid.addWidget(self.txtEditorBackground, 12, 1) grid.addWidget(btnEditorBackground, 12, 2) grid.addWidget(QLabel(self.tr("Editor Selection Color:")), 13, 0) grid.addWidget(self.txtEditorSelectionColor, 13, 1) grid.addWidget(btnEditorSelectionColor, 13, 2) grid.addWidget(QLabel(self.tr("Editor Selection Background:")), 14, 0) grid.addWidget(self.txtEditorSelectionBackground, 14, 1) grid.addWidget(btnEditorSelectionBackground, 14, 2) grid.addWidget(QLabel(self.tr("Editor Selected Word:")), 15, 0) grid.addWidget(self.txtSelectedWord, 15, 1) grid.addWidget(btnSelectedWord, 15, 2) grid.addWidget(QLabel(self.tr("Current Line:")), 16, 0) grid.addWidget(self.txtCurrentLine, 16, 1) grid.addWidget(btnCurrentLine, 16, 2) grid.addWidget(QLabel(self.tr("Fold Area:")), 17, 0) grid.addWidget(self.txtFoldArea, 17, 1) grid.addWidget(btnFoldArea, 17, 2) grid.addWidget(QLabel(self.tr("Fold Arrow:")), 18, 0) grid.addWidget(self.txtFoldArrow, 18, 1) grid.addWidget(btnFoldArrow, 18, 2) grid.addWidget(QLabel(self.tr("Link Navigate:")), 19, 0) grid.addWidget(self.txtLinkNavigate, 19, 1) grid.addWidget(btnLinkNavigate, 19, 2) grid.addWidget(QLabel(self.tr("Brace Background:")), 20, 0) grid.addWidget(self.txtBraceBackground, 20, 1) grid.addWidget(btnBraceBackground, 20, 2) grid.addWidget(QLabel(self.tr("Brace Foreground:")), 21, 0) grid.addWidget(self.txtBraceForeground, 21, 1) grid.addWidget(btnBraceForeground, 21, 2) grid.addWidget(QLabel(self.tr("Error Underline:")), 22, 0) grid.addWidget(self.txtErrorUnderline, 22, 1) grid.addWidget(btnErrorUnderline, 22, 2) grid.addWidget(QLabel(self.tr("PEP8 Underline:")), 23, 0) grid.addWidget(self.txtPep8Underline, 23, 1) grid.addWidget(btnPep8Underline, 23, 2) grid.addWidget(QLabel(self.tr("Sidebar Background:")), 24, 0) grid.addWidget(self.txtSidebarBackground, 24, 1) grid.addWidget(btnSidebarBackground, 24, 2) grid.addWidget(QLabel(self.tr("Sidebar Foreground:")), 25, 0) grid.addWidget(self.txtSidebarForeground, 25, 1) grid.addWidget(btnSidebarForeground, 25, 2) frame = QFrame() vbox = QVBoxLayout() vbox.addLayout(hbox) vbox.addLayout(grid) frame.setLayout(vbox) scrollArea.setWidget(frame) self.connect(self.txtKeyword, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style( btnKeyword, self.txtKeyword.text())) self.connect(self.txtOperator, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style( btnOperator, self.txtOperator.text())) self.connect(self.txtBrace, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style(btnBrace, self.txtBrace.text())) self.connect(self.txtDefinition, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style( btnDefinition, self.txtDefinition.text())) self.connect(self.txtString, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style(btnString, self.txtString.text())) self.connect(self.txtString2, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style( btnString2, self.txtString2.text())) self.connect(self.txtSpaces, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style(btnSpaces, self.txtSpaces.text())) self.connect(self.txtExtras, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style(btnExtras, self.txtExtras.text())) self.connect(self.txtComment, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style( btnComment, self.txtComment.text())) self.connect(self.txtProperObject, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style( btnProperObject, self.txtProperObject.text())) self.connect(self.txtNumbers, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style(btnNumbers, self.txtNumbers.text())) self.connect(self.txtEditorText, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style( btnEditorText, self.txtEditorText.text())) self.connect(self.txtEditorBackground, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style(btnEditorBackground, self.txtEditorBackground.text())) self.connect(self.txtEditorSelectionColor, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style(btnEditorSelectionColor, self.txtEditorSelectionColor.text())) self.connect(self.txtEditorSelectionBackground, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style(btnEditorSelectionBackground, self.txtEditorSelectionBackground.text())) self.connect(self.txtCurrentLine, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style( btnCurrentLine, self.txtCurrentLine.text())) self.connect(self.txtSelectedWord, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style( btnSelectedWord, self.txtSelectedWord.text())) self.connect(self.txtFoldArea, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style( btnFoldArea, self.txtFoldArea.text())) self.connect(self.txtFoldArrow, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style( btnFoldArrow, self.txtFoldArrow.text())) self.connect(self.txtLinkNavigate, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style( btnLinkNavigate, self.txtLinkNavigate.text())) self.connect(self.txtBraceBackground, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style(btnBraceBackground, self.txtBraceBackground.text())) self.connect(self.txtBraceForeground, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style(btnBraceForeground, self.txtBraceForeground.text())) self.connect(self.txtErrorUnderline, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style(btnErrorUnderline, self.txtErrorUnderline.text())) self.connect(self.txtPep8Underline, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style( btnPep8Underline, self.txtPep8Underline.text())) self.connect(self.txtSidebarBackground, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style(btnSidebarBackground, self.txtSidebarBackground.text())) self.connect(self.txtSidebarForeground, SIGNAL("textChanged(QString)"), lambda: self.apply_button_style(btnSidebarForeground, self.txtSidebarForeground.text())) self.connect(btnKeyword, SIGNAL("clicked()"), lambda: self._pick_color(self.txtKeyword, btnKeyword)) self.connect(btnOperator, SIGNAL("clicked()"), lambda: self._pick_color(self.txtOperator, btnOperator)) self.connect(btnBrace, SIGNAL("clicked()"), lambda: self._pick_color(self.txtBrace, btnBrace)) self.connect(btnDefinition, SIGNAL("clicked()"), lambda: self._pick_color(self.txtDefinition, btnDefinition)) self.connect(btnString, SIGNAL("clicked()"), lambda: self._pick_color(self.txtString, btnString)) self.connect(btnString2, SIGNAL("clicked()"), lambda: self._pick_color(self.txtString2, btnString2)) self.connect(btnSpaces, SIGNAL("clicked()"), lambda: self._pick_color(self.txtSpaces, btnSpaces)) self.connect(btnExtras, SIGNAL("clicked()"), lambda: self._pick_color(self.txtExtras, btnExtras)) self.connect(btnComment, SIGNAL("clicked()"), lambda: self._pick_color(self.txtComment, btnComment)) self.connect(btnProperObject, SIGNAL("clicked()"), lambda: self._pick_color(self.txtProperObject, btnProperObject)) self.connect(btnNumbers, SIGNAL("clicked()"), lambda: self._pick_color(self.txtNumbers, btnNumbers)) self.connect(btnEditorText, SIGNAL("clicked()"), lambda: self._pick_color(self.txtEditorText, btnEditorText)) self.connect(btnEditorBackground, SIGNAL("clicked()"), lambda: self._pick_color(self.txtEditorBackground, btnEditorBackground)) self.connect(btnEditorSelectionColor, SIGNAL("clicked()"), lambda: self._pick_color(self.txtEditorSelectionColor, btnEditorSelectionColor)) self.connect(btnEditorSelectionBackground, SIGNAL("clicked()"), lambda: self._pick_color(self.txtEditorSelectionBackground, btnEditorSelectionBackground)) self.connect(btnCurrentLine, SIGNAL("clicked()"), lambda: self._pick_color(self.txtCurrentLine, btnCurrentLine)) self.connect(btnSelectedWord, SIGNAL("clicked()"), lambda: self._pick_color(self.txtSelectedWord, btnSelectedWord)) self.connect(btnFoldArea, SIGNAL("clicked()"), lambda: self._pick_color(self.txtFoldArea, btnFoldArea)) self.connect(btnFoldArrow, SIGNAL("clicked()"), lambda: self._pick_color(self.txtFoldArrow, btnFoldArrow)) self.connect(btnLinkNavigate, SIGNAL("clicked()"), lambda: self._pick_color(self.txtLinkNavigate, btnLinkNavigate)) self.connect(btnBraceBackground, SIGNAL("clicked()"), lambda: self._pick_color(self.txtBraceBackground, btnBraceBackground)) self.connect(btnBraceForeground, SIGNAL("clicked()"), lambda: self._pick_color(self.txtBraceForeground, btnBraceForeground)) self.connect(btnErrorUnderline, SIGNAL("clicked()"), lambda: self._pick_color(self.txtErrorUnderline, btnErrorUnderline)) self.connect(btnPep8Underline, SIGNAL("clicked()"), lambda: self._pick_color(self.txtPep8Underline, btnPep8Underline)) self.connect(btnSidebarBackground, SIGNAL("clicked()"), lambda: self._pick_color(self.txtSidebarBackground, btnSidebarBackground)) self.connect(btnSidebarForeground, SIGNAL("clicked()"), lambda: self._pick_color(self.txtSidebarForeground, btnSidebarForeground)) # Connect Buttons for i in range(0, 26): item = grid.itemAtPosition(i, 1).widget() btn = grid.itemAtPosition(i, 2).widget() self.connect(item, SIGNAL("returnPressed()"), self._preview_style) self.apply_button_style(btn, item.text()) self.connect(btnSaveScheme, SIGNAL("clicked()"), self.save_scheme) def _apply_line_edit_style(self): self.txtKeyword.setText(resources.CUSTOM_SCHEME.get('keyword', resources.COLOR_SCHEME['keyword'])) self.txtOperator.setText(resources.CUSTOM_SCHEME.get('operator', resources.COLOR_SCHEME['operator'])) self.txtBrace.setText(resources.CUSTOM_SCHEME.get('brace', resources.COLOR_SCHEME['brace'])) self.txtDefinition.setText(resources.CUSTOM_SCHEME.get('definition', resources.COLOR_SCHEME['definition'])) self.txtString.setText(resources.CUSTOM_SCHEME.get('string', resources.COLOR_SCHEME['string'])) self.txtString2.setText(resources.CUSTOM_SCHEME.get('string2', resources.COLOR_SCHEME['string2'])) self.txtSpaces.setText(resources.CUSTOM_SCHEME.get('spaces', resources.COLOR_SCHEME['spaces'])) self.txtExtras.setText(resources.CUSTOM_SCHEME.get('extras', resources.COLOR_SCHEME['extras'])) self.txtComment.setText(resources.CUSTOM_SCHEME.get('comment', resources.COLOR_SCHEME['comment'])) self.txtProperObject.setText(resources.CUSTOM_SCHEME.get( 'properObject', resources.COLOR_SCHEME['properObject'])) self.txtNumbers.setText(resources.CUSTOM_SCHEME.get('numbers', resources.COLOR_SCHEME['numbers'])) self.txtEditorText.setText(resources.CUSTOM_SCHEME.get('editor-text', resources.COLOR_SCHEME['editor-text'])) self.txtEditorBackground.setText(resources.CUSTOM_SCHEME.get( 'editor-background', resources.COLOR_SCHEME['editor-background'])) self.txtEditorSelectionColor.setText(resources.CUSTOM_SCHEME.get( 'editor-selection-color', resources.COLOR_SCHEME['editor-selection-color'])) self.txtEditorSelectionBackground.setText(resources.CUSTOM_SCHEME.get( 'editor-selection-background', resources.COLOR_SCHEME['editor-selection-background'])) self.txtCurrentLine.setText(resources.CUSTOM_SCHEME.get('current-line', resources.COLOR_SCHEME['current-line'])) self.txtSelectedWord.setText(resources.CUSTOM_SCHEME.get( 'selected-word', resources.COLOR_SCHEME['selected-word'])) self.txtFoldArea.setText(resources.CUSTOM_SCHEME.get( 'fold-area', resources.COLOR_SCHEME['fold-area'])) self.txtFoldArrow.setText(resources.CUSTOM_SCHEME.get( 'fold-arrow', resources.COLOR_SCHEME['fold-arrow'])) self.txtLinkNavigate.setText(resources.CUSTOM_SCHEME.get( 'linkNavigate', resources.COLOR_SCHEME['linkNavigate'])) self.txtBraceBackground.setText(resources.CUSTOM_SCHEME.get( 'brace-background', resources.COLOR_SCHEME['brace-background'])) self.txtBraceForeground.setText(resources.CUSTOM_SCHEME.get( 'brace-foreground', resources.COLOR_SCHEME['brace-foreground'])) self.txtErrorUnderline.setText(resources.CUSTOM_SCHEME.get( 'error-underline', resources.COLOR_SCHEME['error-underline'])) self.txtPep8Underline.setText(resources.CUSTOM_SCHEME.get( 'pep8-underline', resources.COLOR_SCHEME['pep8-underline'])) self.txtSidebarBackground.setText(resources.CUSTOM_SCHEME.get( 'sidebar-background', resources.COLOR_SCHEME['sidebar-background'])) self.txtSidebarForeground.setText(resources.CUSTOM_SCHEME.get( 'sidebar-foreground', resources.COLOR_SCHEME['sidebar-foreground'])) def _pick_color(self, lineedit, btn): color = QColorDialog.getColor(QColor(lineedit.text()), self, self.tr("Choose Color for: ")) if color.isValid(): lineedit.setText(str(color.name())) self.apply_button_style(btn, color.name()) self._preview_style() def apply_button_style(self, btn, color_name): if QColor(color_name).isValid(): btn.setAutoFillBackground(True) style = ('background: %s; border-radius: 5px; ' 'padding: 5px;' % color_name) btn.setStyleSheet(style) def _preview_style(self): scheme = { "keyword": str(self.txtKeyword.text()), "operator": str(self.txtOperator.text()), "brace": str(self.txtBrace.text()), "definition": str(self.txtDefinition.text()), "string": str(self.txtString.text()), "string2": str(self.txtString2.text()), "comment": str(self.txtComment.text()), "properObject": str(self.txtProperObject.text()), "numbers": str(self.txtNumbers.text()), "spaces": str(self.txtSpaces.text()), "extras": str(self.txtExtras.text()), "editor-background": str(self.txtEditorBackground.text()), "editor-selection-color": str( self.txtEditorSelectionColor.text()), "editor-selection-background": str( self.txtEditorSelectionBackground.text()), "editor-text": str(self.txtEditorText.text()), "current-line": str(self.txtCurrentLine.text()), "selected-word": str(self.txtSelectedWord.text()), "fold-area": str(self.txtFoldArea.text()), "fold-arrow": str(self.txtFoldArrow.text()), "linkNavigate": str(self.txtLinkNavigate.text()), "brace-background": str(self.txtBraceBackground.text()), "brace-foreground": str(self.txtBraceForeground.text()), "error-underline": str(self.txtErrorUnderline.text()), "pep8-underline": str(self.txtPep8Underline.text()), "sidebar-background": str(self.txtSidebarBackground.text()), "sidebar-foreground": str(self.txtSidebarForeground.text())} resources.CUSTOM_SCHEME = scheme editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget is not None: editorWidget.restyle(editorWidget.lang) editorWidget.highlight_current_line() editorWidget._sidebarWidget.repaint() return scheme def hideEvent(self, event): super(EditorSchemeDesigner, self).hideEvent(event) resources.CUSTOM_SCHEME = self.original_style editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget is not None: editorWidget.restyle(editorWidget.lang) def showEvent(self, event): super(EditorSchemeDesigner, self).showEvent(event) schemes = self._parent._editorGeneral._schemes scheme = self._parent._editorGeneral.current_scheme resources.CUSTOM_SCHEME = schemes.get(scheme, resources.COLOR_SCHEME) editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget is not None: editorWidget.restyle(editorWidget.lang) editorWidget._sidebarWidget.repaint() self._apply_line_edit_style() if scheme != 'default': self.line_name.setText(scheme) def save(self): """All the widgets in preferences must contain a save method.""" pass @staticmethod def _is_valid_scheme_name(name): """Check if a given name is a valid name for an editor scheme. Params: name := the name to check Returns: True if and only if the name is okay to use for a scheme """ return not (name in ('', 'default')) def save_scheme(self): name = self.line_name.text().strip() if not self._is_valid_scheme_name(name): QMessageBox.information(self, self.tr("Invalid Scheme Name"), self.tr("The scheme name you have chosen is invalid.\nPlease " "pick a different name.")) return fileName = ('{0}.color'.format( file_manager.create_path(resources.EDITOR_SKINS, name))) answer = True if file_manager.file_exists(fileName): answer = QMessageBox.question(self, self.tr("Scheme already exists"), (self.tr("Do you want to override the file: %s?") % fileName), QMessageBox.Yes, QMessageBox.No) if name != '' and answer in (QMessageBox.Yes, True): scheme = self._preview_style() qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.beginGroup('preferences') qsettings.beginGroup('editor') if qsettings.value('scheme', defaultValue='', type='QString') == name and name != 'default': self.original_style = copy.copy(scheme) json_manager.save_editor_skins(fileName, scheme) QMessageBox.information(self, self.tr("Scheme Saved"), (self.tr("The scheme has been saved at: %s.") % fileName)) elif answer == QMessageBox.Yes: QMessageBox.information(self, self.tr("Scheme Not Saved"), self.tr("The name probably is invalid.")) class ThemeTab(QTabWidget): def __init__(self): super(ThemeTab, self).__init__() self.theme_chooser = ThemeChooser() self.theme_designer = ThemeDesigner(self) self.addTab(self.theme_chooser, self.tr("Theme Chooser")) self.addTab(self.theme_designer, self.tr("Theme Designer")) def save(self): self.theme_chooser.save() class ThemeChooser(QWidget): def __init__(self): super(ThemeChooser, self).__init__() vbox = QVBoxLayout(self) vbox.addWidget(QLabel(self.tr("Select Theme:"))) self.list_skins = QListWidget() self.list_skins.setSelectionMode(QListWidget.SingleSelection) vbox.addWidget(self.list_skins) self.btn_delete = QPushButton(self.tr("Delete Theme")) self.btn_preview = QPushButton(self.tr("Preview Theme")) hbox = QHBoxLayout() hbox.addWidget(self.btn_delete) hbox.addSpacerItem(QSpacerItem(10, 0, QSizePolicy.Expanding, QSizePolicy.Fixed)) hbox.addWidget(self.btn_preview) vbox.addLayout(hbox) self._refresh_list() self.connect(self.btn_preview, SIGNAL("clicked()"), self.preview_theme) self.connect(self.btn_delete, SIGNAL("clicked()"), self.delete_theme) def delete_theme(self): if self.list_skins.currentRow() != 0: file_name = ("%s.qss" % self.list_skins.currentItem().text()) qss_file = file_manager.create_path(resources.NINJA_THEME_DOWNLOAD, file_name) file_manager.delete_file(qss_file) self._refresh_list() def showEvent(self, event): self._refresh_list() super(ThemeChooser, self).showEvent(event) def _refresh_list(self): self.list_skins.clear() self.list_skins.addItem("Default") self.list_skins.addItem("Classic Theme") files = [file_manager.get_file_name(filename) for filename in file_manager.get_files_from_folder( resources.NINJA_THEME_DOWNLOAD, "qss")] files.sort() self.list_skins.addItems(files) if settings.NINJA_SKIN == 'Default': self.list_skins.setCurrentRow(0) elif settings.NINJA_SKIN == 'Classic Theme': self.list_skins.setCurrentRow(1) elif settings.NINJA_SKIN in files: index = files.index(settings.NINJA_SKIN) self.list_skins.setCurrentRow(index + 1) else: self.list_skins.setCurrentRow(0) def save(self): qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.beginGroup('preferences') qsettings.beginGroup('theme') settings.NINJA_SKIN = self.list_skins.currentItem().text() qsettings.setValue("skin", settings.NINJA_SKIN) qsettings.endGroup() qsettings.endGroup() self.preview_theme() def preview_theme(self): if self.list_skins.currentRow() == 0: qss_file = resources.NINJA_THEME elif self.list_skins.currentRow() == 1: qss_file = resources.NINJA__THEME_CLASSIC else: file_name = ("%s.qss" % self.list_skins.currentItem().text()) qss_file = file_manager.create_path(resources.NINJA_THEME_DOWNLOAD, file_name) with open(qss_file) as f: qss = f.read() QApplication.instance().setStyleSheet(qss) class ThemeDesigner(QWidget): def __init__(self, parent): super(ThemeDesigner, self).__init__() self._parent = parent vbox = QVBoxLayout(self) hbox = QHBoxLayout() hbox.addWidget(QLabel(self.tr("New Theme Name:"))) self.line_name = QLineEdit() hbox.addWidget(self.line_name) self.btn_save = QPushButton(self.tr("Save Theme")) hbox.addWidget(self.btn_save) self.edit_qss = editor.create_editor(fileName='qss.qss') if self.edit_qss._mini: self.edit_qss._mini.hide() self.edit_qss._mini = None self.btn_apply = QPushButton(self.tr("Apply Style Sheet")) hbox2 = QHBoxLayout() hbox2.addSpacerItem(QSpacerItem(10, 0, QSizePolicy.Expanding, QSizePolicy.Fixed)) hbox2.addWidget(self.btn_apply) hbox2.addSpacerItem(QSpacerItem(10, 0, QSizePolicy.Expanding, QSizePolicy.Fixed)) vbox.addLayout(hbox) vbox.addWidget(self.edit_qss) vbox.addLayout(hbox2) self.connect(self.btn_apply, SIGNAL("clicked()"), self.apply_stylesheet) self.connect(self.btn_save, SIGNAL("clicked()"), self.save_stylesheet) def showEvent(self, event): if not self.edit_qss.document().isModified(): if self._parent.theme_chooser.list_skins.currentRow() == 0: qss_file = resources.NINJA_THEME if self._parent.theme_chooser.list_skins.currentRow() == 1: qss_file = resources.NINJA__THEME_CLASSIC else: file_name = ("%s.qss" % self._parent.theme_chooser.list_skins.currentItem().text()) qss_file = file_manager.create_path( resources.NINJA_THEME_DOWNLOAD, file_name) with open(qss_file) as f: qss = f.read() self.edit_qss.setPlainText(qss) self.edit_qss.document().setModified(False) self.line_name.setText("%s_%s" % (self._parent.theme_chooser.list_skins.currentItem().text(), getpass.getuser())) super(ThemeDesigner, self).showEvent(event) def apply_stylesheet(self): qss = self.edit_qss.toPlainText() QApplication.instance().setStyleSheet(qss) def save_stylesheet(self): try: file_name = "%s.qss" % self.line_name.text() file_name = file_manager.create_path( resources.NINJA_THEME_DOWNLOAD, file_name) content = self.edit_qss.toPlainText() file_manager.store_file_content(file_name, content, newFile=True) QMessageBox.information(self, self.tr("Style Sheet Saved"), (self.tr("Theme saved at: '%s'." % file_name))) self.edit_qss.document().setModified(False) except file_manager.NinjaFileExistsException as ex: QMessageBox.information(self, self.tr("File Already Exists"), (self.tr("Invalid File Name: the file '%s' already exists.") % ex.filename)) ninja-ide-2.3/ninja_ide/gui/dialogs/project_properties_widget.py000066400000000000000000000410231216641277400252130ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import os import sys from PyQt4.QtGui import QWidget from PyQt4.QtGui import QSizePolicy from PyQt4.QtGui import QSpacerItem from PyQt4.QtGui import QTabWidget from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QDialog from PyQt4.QtGui import QGridLayout from PyQt4.QtGui import QLineEdit from PyQt4.QtGui import QLabel from PyQt4.QtGui import QCompleter from PyQt4.QtGui import QDirModel from PyQt4.QtGui import QPlainTextEdit from PyQt4.QtGui import QComboBox from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QIcon from PyQt4.QtGui import QFileDialog from PyQt4.QtGui import QMessageBox from PyQt4.QtGui import QSpinBox from PyQt4.QtGui import QCheckBox from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import Qt from ninja_ide import resources from ninja_ide.core import file_manager from ninja_ide.core import settings from ninja_ide.tools import json_manager from ninja_ide.tools import ui_tools from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.gui.dialogs.project_properties_widget') DEBUG = logger.debug class ProjectProperties(QDialog): def __init__(self, item, parent=None): QDialog.__init__(self, parent, Qt.Dialog) self._item = item self.setWindowTitle(self.tr("Project Properties")) vbox = QVBoxLayout(self) self.tab_widget = QTabWidget() self.projectData = ProjectData(self) self.projectExecution = ProjectExecution(self) self.projectMetadata = ProjectMetadata(self) self.tab_widget.addTab(self.projectData, self.tr("Project Data")) self.tab_widget.addTab(self.projectExecution, self.tr("Project Execution")) self.tab_widget.addTab(self.projectMetadata, self.tr("Project Metadata")) vbox.addWidget(self.tab_widget) self.btnSave = QPushButton(self.tr("Save")) self.btnCancel = QPushButton(self.tr("Cancel")) hbox = QHBoxLayout() hbox.addWidget(self.btnCancel) hbox.addWidget(self.btnSave) vbox.addLayout(hbox) self.connect(self.btnCancel, SIGNAL("clicked()"), self.close) self.connect(self.btnSave, SIGNAL("clicked()"), self.save_properties) def save_properties(self): if self.projectData.name.text().strip() == '': QMessageBox.critical(self, self.tr("Properties Invalid"), self.tr("The Project must have a name.")) return tempName = self._item.name self._item.name = self.projectData.name.text() self._item.description = self.projectData.description.toPlainText() self._item.license = self.projectData.cboLicense.currentText() self._item.mainFile = self.projectExecution.path.text() self._item.url = self.projectData.url.text() self._item.projectType = self.projectData.txtType.text() # FIXME self._item.pythonPath = self.projectExecution.txtPythonPath.text() self._item.PYTHONPATH = self.projectExecution.PYTHONPATH.toPlainText() self._item.additional_builtins = filter( lambda e: e, # remove empty names self.projectExecution.additional_builtins.text().split(' ')) self._item.preExecScript = self.projectExecution.txtPreExec.text() self._item.postExecScript = self.projectExecution.txtPostExec.text() self._item.programParams = self.projectExecution.txtParams.text() self._item.venv = self.projectExecution.txtVenvPath.text() extensions = self.projectData.txtExtensions.text().split(', ') self._item.extensions = tuple(extensions) self._item.indentation = self.projectData.spinIndentation.value() self._item.useTabs = self.projectData.checkUseTabs.isChecked() related = self.projectMetadata.txt_projects.toPlainText() related = [path for path in related.split('\n') if path != ''] self._item.related_projects = related #save project properties project = {} project['name'] = self._item.name project['description'] = self._item.description project['url'] = self._item.url project['license'] = self._item.license project['mainFile'] = self._item.mainFile project['project-type'] = self._item.projectType project['supported-extensions'] = self._item.extensions project['indentation'] = self._item.indentation project['use-tabs'] = self._item.useTabs project['pythonPath'] = self._item.pythonPath # FIXME project['PYTHONPATH'] = self._item.PYTHONPATH project['additional_builtins'] = self._item.additional_builtins project['preExecScript'] = self._item.preExecScript project['postExecScript'] = self._item.postExecScript project['venv'] = self._item.venv project['programParams'] = self._item.programParams project['relatedProjects'] = self._item.related_projects if tempName != self._item.name and \ file_manager.file_exists(self._item.path, tempName + '.nja'): file_manager.delete_file(self._item.path, tempName + '.nja') json_manager.create_ninja_project( self._item.path, self._item.name, project) self._item.setText(0, self._item.name) self._item.setToolTip(0, self._item.name) if self._item.extensions != settings.SUPPORTED_EXTENSIONS: self._item._parent._refresh_project(self._item) self._item.update_paths() self.close() class ProjectData(QWidget): def __init__(self, parent): super(ProjectData, self).__init__() self._parent = parent grid = QGridLayout(self) grid.addWidget(QLabel(self.tr("Name:")), 0, 0) self.name = QLineEdit() if self._parent._item.name == '': self.name.setText(file_manager.get_basename( self._parent._item.path)) else: self.name.setText(self._parent._item.name) grid.addWidget(self.name, 0, 1) grid.addWidget(QLabel(self.tr("Project Location:")), 1, 0) self.txtPath = QLineEdit() self.txtPath.setReadOnly(True) self.txtPath.setText(self._parent._item.path) grid.addWidget(self.txtPath, 1, 1) grid.addWidget(QLabel(self.tr("Project Type:")), 2, 0) self.txtType = QLineEdit() completer = QCompleter(sorted(settings.PROJECT_TYPES)) completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.txtType.setCompleter(completer) self.txtType.setText(self._parent._item.projectType) grid.addWidget(self.txtType, 2, 1) grid.addWidget(QLabel(self.tr("Description:")), 3, 0) self.description = QPlainTextEdit() self.description.setPlainText(self._parent._item.description) grid.addWidget(self.description, 3, 1) grid.addWidget(QLabel(self.tr("URL:")), 4, 0) self.url = QLineEdit() self.url.setText(self._parent._item.url) grid.addWidget(self.url, 4, 1) grid.addWidget(QLabel(self.tr("Licence:")), 5, 0) self.cboLicense = QComboBox() self.cboLicense.addItem('Apache License 2.0') self.cboLicense.addItem('Artistic License/GPL') self.cboLicense.addItem('Eclipse Public License 1.0') self.cboLicense.addItem('GNU General Public License v2') self.cboLicense.addItem('GNU General Public License v3') self.cboLicense.addItem('GNU Lesser General Public License') self.cboLicense.addItem('MIT License') self.cboLicense.addItem('Mozilla Public License 1.1') self.cboLicense.addItem('Mozilla Public License 2.0') self.cboLicense.addItem('New BSD License') self.cboLicense.addItem('Other Open Source') self.cboLicense.addItem('Other') self.cboLicense.setCurrentIndex(4) index = self.cboLicense.findText(self._parent._item.license) self.cboLicense.setCurrentIndex(index) grid.addWidget(self.cboLicense, 5, 1) self.txtExtensions = QLineEdit() self.txtExtensions.setText(', '.join(self._parent._item.extensions)) grid.addWidget(QLabel(self.tr("Supported Extensions:")), 6, 0) grid.addWidget(self.txtExtensions, 6, 1) grid.addWidget(QLabel(self.tr("Indentation: ")), 7, 0) self.spinIndentation = QSpinBox() self.spinIndentation.setValue(self._parent._item.indentation) self.spinIndentation.setMinimum(1) grid.addWidget(self.spinIndentation, 7, 1) self.checkUseTabs = QCheckBox(self.tr("Use Tabs.")) self.checkUseTabs.setChecked(self._parent._item.useTabs) grid.addWidget(self.checkUseTabs, 7, 2) class ProjectExecution(QWidget): def __init__(self, parent): super(ProjectExecution, self).__init__() self._parent = parent grid = QGridLayout(self) grid.addWidget(QLabel(self.tr("Main File:")), 0, 0) self.path = QLineEdit() ui_tools.LineEditButton(self.path, self.path.clear, self.style().standardPixmap(self.style().SP_TrashIcon)) self.path.setText(self._parent._item.mainFile) self.path.setReadOnly(True) self.btnBrowse = QPushButton(QIcon( self.style().standardPixmap(self.style().SP_FileIcon)), '') grid.addWidget(self.path, 0, 1) grid.addWidget(self.btnBrowse, 0, 2) # this should be changed, and ALL pythonPath names to # python_custom_interpreter or something like that. this is NOT the # PYTHONPATH self.txtPythonPath = QLineEdit() self.txtPythonPath.setText(self._parent._item.pythonPath) self.btnPythonPath = QPushButton(QIcon(resources.IMAGES['open']), '') grid.addWidget(QLabel(self.tr("Python Custom Interpreter:")), 1, 0) grid.addWidget(self.txtPythonPath, 1, 1) grid.addWidget(self.btnPythonPath, 1, 2) # THIS IS THE MODAFUCKA REAL PYTHONPATH BRO, YEAH !!! grid.addWidget(QLabel(self.tr("Custom PYTHONPATH:")), 2, 0) self.PYTHONPATH = QPlainTextEdit() # TODO : better widget self.PYTHONPATH.setPlainText(self._parent._item.PYTHONPATH) self.PYTHONPATH.setToolTip(self.tr("One path per line")) grid.addWidget(self.PYTHONPATH, 2, 1) # Additional builtins/globals for pyflakes grid.addWidget(QLabel(self.tr("Additional builtins/globals:")), 3, 0) self.additional_builtins = QLineEdit() self.additional_builtins.setText( ' '.join(self._parent._item.additional_builtins)) self.additional_builtins.setToolTip(self.tr( "Space-separated list of symbols that will be considered as " "builtin in every file")) grid.addWidget(self.additional_builtins, 3, 1) self.txtPreExec = QLineEdit() ui_tools.LineEditButton(self.txtPreExec, self.txtPreExec.clear, self.style().standardPixmap(self.style().SP_TrashIcon)) self.txtPreExec.setReadOnly(True) self.txtPreExec.setText(self._parent._item.preExecScript) self.btnPreExec = QPushButton(QIcon(resources.IMAGES['open']), '') grid.addWidget(QLabel(self.tr("Pre-exec Script:")), 4, 0) grid.addWidget(self.txtPreExec, 4, 1) grid.addWidget(self.btnPreExec, 4, 2) self.txtPostExec = QLineEdit() ui_tools.LineEditButton(self.txtPostExec, self.txtPostExec.clear, self.style().standardPixmap(self.style().SP_TrashIcon)) self.txtPostExec.setReadOnly(True) self.txtPostExec.setText(self._parent._item.postExecScript) self.btnPostExec = QPushButton(QIcon(resources.IMAGES['open']), '') grid.addWidget(QLabel(self.tr("Post-exec Script:")), 5, 0) grid.addWidget(self.txtPostExec, 5, 1) grid.addWidget(self.btnPostExec, 5, 2) grid.addItem(QSpacerItem(5, 10, QSizePolicy.Expanding, QSizePolicy.Expanding), 6, 0) # Properties grid.addWidget(QLabel(self.tr("Properties:")), 7, 0) self.txtParams = QLineEdit() self.txtParams.setToolTip( self.tr("Separate the params with commas (ie: help, verbose)")) self.txtParams.setText(self._parent._item.programParams) grid.addWidget(QLabel(self.tr("Params (comma separated):")), 8, 0) grid.addWidget(self.txtParams, 8, 1) #Widgets for virtualenv properties self.txtVenvPath = QLineEdit() ui_tools.LineEditButton(self.txtVenvPath, self.txtVenvPath.clear, self.style().standardPixmap(self.style().SP_TrashIcon)) self.txtVenvPath.setText(self._parent._item.venv) self._dir_completer = QCompleter() self._dir_completer.setModel(QDirModel(self._dir_completer)) self.txtVenvPath.setCompleter(self._dir_completer) self.btnVenvPath = QPushButton(QIcon(resources.IMAGES['open']), '') grid.addWidget(QLabel(self.tr("Virtualenv Folder:")), 9, 0) grid.addWidget(self.txtVenvPath, 9, 1) grid.addWidget(self.btnVenvPath, 9, 2) self.connect(self.btnBrowse, SIGNAL("clicked()"), self.select_file) self.connect(self.btnPythonPath, SIGNAL("clicked()"), self._load_python_path) self.connect(self.btnVenvPath, SIGNAL("clicked()"), self._load_python_venv) self.connect(self.btnPreExec, SIGNAL("clicked()"), self.select_pre_exec_script) self.connect(self.btnPostExec, SIGNAL("clicked()"), self.select_post_exec_script) def _load_python_path(self): path = QFileDialog.getOpenFileName( self, self.tr("Select Python Path")) self.txtPythonPath.setText(path) def _load_python_venv(self): venv = QFileDialog.getExistingDirectory( self, self.tr("Select Virtualenv Folder")) if sys.platform == 'win32': venv = os.path.join(venv, 'Scripts', 'python.exe') else: venv = os.path.join(venv, 'bin', 'python') #check if venv folder exists if not os.path.exists(venv): QMessageBox.information(self, self.tr("Virtualenv Folder"), self.tr("This is not a valid Virtualenv Folder")) self.txtVenvPath.setText("") else: self.txtVenvPath.setText(venv) def select_file(self): fileName = QFileDialog.getOpenFileName( self, self.tr("Select Main File"), self._parent._item.path, '(*.py);;(*.*)') if fileName != '': fileName = file_manager.convert_to_relative( self._parent._item.path, fileName) self.path.setText(fileName) def select_pre_exec_script(self): fileName = QFileDialog.getOpenFileName( self, self.tr("Select Pre Execution Script File"), self._parent._item.path, '(*.*)') if fileName != '': fileName = file_manager.convert_to_relative( self._parent._item.path, fileName) self.txtPreExec.setText(fileName) def select_post_exec_script(self): fileName = QFileDialog.getOpenFileName( self, self.tr("Select Post Execution Script File"), self._parent._item.path, '(*.*)') if fileName != '': fileName = file_manager.convert_to_relative( self._parent._item.path, fileName) self.txtPostExec.setText(fileName) class ProjectMetadata(QWidget): def __init__(self, parent): super(ProjectMetadata, self).__init__() self._parent = parent vbox = QVBoxLayout(self) vbox.addWidget(QLabel(self.tr( "Insert the path of Python Projects related" "to this one in order\nto improve Code Completion."))) self.txt_projects = QPlainTextEdit() vbox.addWidget(self.txt_projects) vbox.addWidget(QLabel( self.tr("Split your paths using newlines [ENTER]."))) paths = '\n'.join(self._parent._item.related_projects) self.txt_projects.setPlainText(paths) ninja-ide-2.3/ninja_ide/gui/dialogs/python_detect_dialog.py000066400000000000000000000041241216641277400241170ustar00rootroot00000000000000# -*- coding: utf-8 -*- from PyQt4.QtGui import QDialog from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QLabel from PyQt4.QtGui import QListWidget from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QSpacerItem from PyQt4.QtGui import QSizePolicy from PyQt4.QtCore import QSettings from PyQt4.QtCore import Qt from PyQt4.QtCore import QSize from PyQt4.QtCore import SIGNAL from ninja_ide import resources from ninja_ide.core import settings class PythonDetectDialog(QDialog): def __init__(self, suggested, parent=None): super(PythonDetectDialog, self).__init__(parent, Qt.Dialog) self.setMaximumSize(QSize(0, 0)) self.setWindowTitle("Configure Python Path") vbox = QVBoxLayout(self) lblMessage = QLabel(self.tr("We have detected that you are using " + "Windows,\nplease choose the proper " + "Python application for you:")) vbox.addWidget(lblMessage) self.listPaths = QListWidget() self.listPaths.setSelectionMode(QListWidget.SingleSelection) vbox.addWidget(self.listPaths) hbox = QHBoxLayout() hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding)) btnCancel = QPushButton(self.tr("Cancel")) btnAccept = QPushButton(self.tr("Accept")) hbox.addWidget(btnCancel) hbox.addWidget(btnAccept) vbox.addLayout(hbox) self.connect(btnAccept, SIGNAL("clicked()"), self._set_python_path) self.connect(btnCancel, SIGNAL("clicked()"), self.close) for path in suggested: self.listPaths.addItem(path) self.listPaths.setCurrentRow(0) def _set_python_path(self): python_path = self.listPaths.currentItem().text() qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) settings.PYTHON_PATH = python_path settings.PYTHON_PATH_CONFIGURED_BY_USER = True qsettings.setValue('preferences/execution/pythonPath', python_path) qsettings.setValue('preferences/execution/pythonPathConfigured', True) self.close()ninja-ide-2.3/ninja_ide/gui/dialogs/themes_manager.py000066400000000000000000000125021216641277400227050ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . import os #lint:disable try: from urllib.request import urlopen from urllib.error import URLError except ImportError: from urllib2 import urlopen from urllib2 import URLError #lint:enable from PyQt4.QtGui import QWidget from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QSpacerItem from PyQt4.QtGui import QSizePolicy from PyQt4.QtGui import QTabWidget from PyQt4.QtGui import QTableWidget from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QDialog from PyQt4.QtCore import Qt from PyQt4.QtCore import SIGNAL from ninja_ide import resources from ninja_ide.core import file_manager from ninja_ide.tools import ui_tools from ninja_ide.tools import json_manager class ThemesManagerWidget(QDialog): def __init__(self, parent): QDialog.__init__(self, parent, Qt.Dialog) self.setWindowTitle(self.tr("Themes Manager")) self.resize(700, 500) vbox = QVBoxLayout(self) self._tabs = QTabWidget() vbox.addWidget(self._tabs) # Footer hbox = QHBoxLayout() btn_close = QPushButton(self.tr('Close')) btnReload = QPushButton(self.tr("Reload")) hbox.addWidget(btn_close) hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding)) hbox.addWidget(btnReload) vbox.addLayout(hbox) self.overlay = ui_tools.Overlay(self) self.overlay.show() self._schemes = [] self._loading = True self.downloadItems = [] #Load Themes with Thread self.connect(btnReload, SIGNAL("clicked()"), self._reload_themes) self._thread = ui_tools.ThreadExecution(self.execute_thread) self.connect(self._thread, SIGNAL("finished()"), self.load_skins_data) self.connect(btn_close, SIGNAL('clicked()'), self.close) self._reload_themes() def _reload_themes(self): self.overlay.show() self._loading = True self._thread.execute = self.execute_thread self._thread.start() def load_skins_data(self): if self._loading: self._tabs.clear() self._schemeWidget = SchemeWidget(self, self._schemes) self._tabs.addTab(self._schemeWidget, self.tr("Editor Schemes")) self._loading = False self.overlay.hide() self._thread.wait() def download_scheme(self, scheme): self.overlay.show() self.downloadItems = scheme self._thread.execute = self._download_scheme_thread self._thread.start() def resizeEvent(self, event): self.overlay.resize(event.size()) event.accept() def execute_thread(self): try: descriptor_schemes = urlopen(resources.SCHEMES_URL) schemes = json_manager.parse(descriptor_schemes) schemes = [(d['name'], d['download']) for d in schemes] local_schemes = self.get_local_schemes() schemes = [schemes[i] for i in range(len(schemes)) if os.path.basename(schemes[i][1]) not in local_schemes] self._schemes = schemes except URLError: self._schemes = [] def get_local_schemes(self): if not file_manager.folder_exists(resources.EDITOR_SKINS): file_manager.create_tree_folders(resources.EDITOR_SKINS) schemes = os.listdir(resources.EDITOR_SKINS) schemes = [s for s in schemes if s.endswith('.color')] return schemes def _download_scheme_thread(self): for d in self.downloadItems: self.download(d[1], resources.EDITOR_SKINS) def download(self, url, folder): fileName = os.path.join(folder, os.path.basename(url)) try: content = urlopen(url) f = open(fileName, 'w') f.write(content.read()) f.close() except URLError: return class SchemeWidget(QWidget): def __init__(self, parent, schemes): QWidget.__init__(self, parent) self._parent = parent self._schemes = schemes vbox = QVBoxLayout(self) self._table = QTableWidget(1, 2) self._table.removeRow(0) vbox.addWidget(self._table) ui_tools.load_table(self._table, [self.tr('Name'), self.tr('URL')], self._schemes) btnUninstall = QPushButton(self.tr('Download')) btnUninstall.setMaximumWidth(100) vbox.addWidget(btnUninstall) self._table.setColumnWidth(0, 200) self.connect(btnUninstall, SIGNAL("clicked()"), self._download_scheme) def _download_scheme(self): schemes = ui_tools.remove_get_selected_items(self._table, self._schemes) self._parent.download_scheme(schemes) ninja-ide-2.3/ninja_ide/gui/dialogs/traceback_widget.py000066400000000000000000000044521216641277400232150ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QDialog from PyQt4.QtGui import QTabWidget from PyQt4.QtGui import QPlainTextEdit from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QWidget from PyQt4.QtGui import QLabel from PyQt4.QtGui import QPushButton from PyQt4.QtCore import SIGNAL class PluginErrorDialog(QDialog): """ Dialog with tabs each tab is a python traceback """ def __init__(self): QDialog.__init__(self) self.setWindowTitle(self.tr("Plugin error report")) self.resize(525, 400) vbox = QVBoxLayout(self) label = QLabel(self.tr('Some plugins have errors and were removed')) vbox.addWidget(label) self._tabs = QTabWidget() vbox.addWidget(self._tabs) hbox = QHBoxLayout() btnAccept = QPushButton(self.tr("Accept")) btnAccept.setMaximumWidth(100) hbox.addWidget(btnAccept) vbox.addLayout(hbox) #signals self.connect(btnAccept, SIGNAL("clicked()"), self.close) def add_traceback(self, plugin_name, traceback_msg): traceback_widget = TracebackWidget(traceback_msg) self._tabs.addTab(traceback_widget, plugin_name) class TracebackWidget(QWidget): """ Represents a python traceback """ def __init__(self, traceback_msg): QWidget.__init__(self) vbox = QVBoxLayout(self) self._editor = QPlainTextEdit() vbox.addWidget(QLabel(self.tr('Traceback'))) vbox.addWidget(self._editor) self._editor.setReadOnly(True) self._editor.insertPlainText(traceback_msg) ninja-ide-2.3/ninja_ide/gui/dialogs/wizard_new_project.py000066400000000000000000000323561216641277400236360ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import os import sys from PyQt4.QtGui import QWizard from PyQt4.QtGui import QWizardPage from PyQt4.QtGui import QLabel from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QComboBox from PyQt4.QtGui import QLineEdit from PyQt4.QtGui import QPlainTextEdit from PyQt4.QtGui import QGridLayout from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QMessageBox from PyQt4.QtGui import QFileDialog from PyQt4.QtGui import QListWidget from PyQt4.QtGui import QCompleter from PyQt4.QtGui import QDirModel from PyQt4.QtGui import QPixmap from PyQt4.QtCore import Qt from PyQt4.QtCore import SIGNAL from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.core import plugin_interfaces from ninja_ide.core import file_manager from ninja_ide.tools import json_manager from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.gui.dialogs.wizard_new_project') logger.info("loaded") DEBUG = logger.debug ############################################################################### # Wizard handler and Python Project handler ############################################################################### class WizardNewProject(QWizard): """ Wizard to create a new project (of any kind), it implements the base behavior. Also, it set two special projects type handler (PythonProjectHandler, ImportFromSourcesProjectHandler) """ def __init__(self, parent): QWizard.__init__(self, parent) self.__explorer = parent self.setWindowTitle(self.tr("NINJA - New Project Wizard")) self.setPixmap(QWizard.LogoPixmap, QPixmap(resources.IMAGES['icon'])) self.option = 'Python' #Add a project type handler for Python settings.set_project_type_handler(self.option, PythonProjectHandler(self.__explorer)) #Add a project type handler for Import from existing sources settings.set_project_type_handler('Import from sources', ImportFromSourcesProjectHandler(self.__explorer)) self.projectTypePage = PageProjectType(self) self.addPage(self.projectTypePage) self.addPage(PageProjectProperties()) self.setButtonLayout([ QWizard.BackButton, QWizard.Stretch, QWizard.NextButton, QWizard.FinishButton]) def add_project_pages(self, option='Python'): logger.debug("Add project pages") self.option = option pages = settings.get_project_type_handler(option).get_pages() listIds = self.pageIds() listIds.pop(listIds.index(0)) for page in pages: self.addPage(page) #this page is always needed by a project! self.addPage(PageProjectProperties()) for i in listIds: self.removePage(i) def done(self, result): logger.debug("Done") if result == 1: page = self.currentPage() if type(page) == PageProjectProperties: venv = page.vtxtPlace.text() if venv: if sys.platform == 'win32': venv = os.path.join(venv, 'Scripts', 'python.exe') else: venv = os.path.join(venv, 'bin', 'python') #check if venv folder exists if not os.path.exists(venv): btnPressed = QMessageBox.information(self, self.tr("Virtualenv Folder"), self.tr("Folder don't exists or this is not a " "valid Folder.\n If you want to set " "or modify, go to project properties"), self.tr("Back"), self.tr("Continue")) if btnPressed == QMessageBox.NoButton: return else: self.currentPage().vtxtPlace.setText("") page.vtxtPlace.setText(venv) settings.get_project_type_handler(self.option)\ .on_wizard_finish(self) super(WizardNewProject, self).done(result) def _load_project(self, path): """ Open Project based on path into Explorer """ self.__explorer.open_project_folder(path) def _load_project_with_extensions(self, path, extensions): """Open Project based on path into Explorer with extensions""" # self._main._properties._treeProjects.load_project( # self._main.open_project_with_extensions(path), path) pass ############################################################################### # Python Project Handler ############################################################################### class PythonProjectHandler(plugin_interfaces.IProjectTypeHandler): """ Handler for Python projects """ def __init__(self, explorer): self.__explorer = explorer def get_pages(self): """ Get extra pages for new Python projects """ return () def get_context_menus(self): """ Get a special menu for new Python projects """ return () def on_wizard_finish(self, wizard): """ Create the ninja_porject (.nja file), create the main __init__.py and open the project """ ids = wizard.pageIds() page = wizard.page(ids[1]) name = page.txtName.text() path = os.path.join(page.txtPlace.text(), name) if not path: QMessageBox.critical(self, self.tr("Incorrect Location"), self.tr("The project couldn\'t be create")) return if not os.path.exists(path): os.makedirs(path) project = {} project['name'] = name project['description'] = page.txtDescription.toPlainText() project['license'] = page.cboLicense.currentText() project['venv'] = page.vtxtPlace.text() json_manager.create_ninja_project(path, name, project) try: file_manager.create_init_file(path) except: logger.debug("The __init__ file already exists - Import Sources.") wizard._load_project(path) ############################################################################### # Import project from existing sources ############################################################################### class ImportFromSourcesProjectHandler(plugin_interfaces.IProjectTypeHandler): """ Handler for Import from existing sources project """ def __init__(self, explorer): self.__explorer = explorer def get_pages(self): """ Get extra pages for Import from sources projects """ return () def get_context_menus(self): """ Get a special menu for Import from sources projects """ return () def on_wizard_finish(self, wizard): """ Create the ninja_porject (.nja file) and open the project """ ids = wizard.pageIds() page = wizard.page(ids[1]) name = page.txtName.text() path = page.txtPlace.text() if not path: QMessageBox.critical(self, self.tr("Incorrect Location"), self.tr("The project couldn\'t be create")) return project = {} project['name'] = name project['description'] = page.txtDescription.toPlainText() project['license'] = page.cboLicense.currentText() project['venv'] = page.vtxtPlace.text() project['project-type'] = wizard.option json_manager.create_ninja_project(path, name, project) wizard._load_project(path) ############################################################################### # WIZARD FIRST PAGE ############################################################################### class PageProjectType(QWizardPage): def __init__(self, wizard): QWizardPage.__init__(self) self.setTitle(self.tr("Project Type")) self.setSubTitle(self.tr("Choose the Project Type")) self._wizard = wizard vbox = QVBoxLayout(self) self.listWidget = QListWidget() vbox.addWidget(self.listWidget) types = settings.get_all_project_types() types.sort() index = types.index('Python') types.insert(0, types.pop(index)) self.listWidget.addItems(types) self.listWidget.setCurrentRow(0) self.connect(self.listWidget, SIGNAL("itemClicked(QListWidgetItem *)"), self.load_pages) def validatePage(self): self._wizard.option = self.listWidget.currentItem().text() return True def load_pages(self): self.wizard().add_project_pages(self.listWidget.currentItem().text()) ############################################################################### # WIZARD LAST PAGE ############################################################################### class PageProjectProperties(QWizardPage): def __init__(self): QWizardPage.__init__(self) self.setTitle(self.tr("New Project Data")) self.setSubTitle(self.tr( "Complete the following fields to create the Project Structure")) gbox = QGridLayout(self) #Names of the fields to complete self.lblName = QLabel(self.tr("New Project Name (*):")) self.lblPlace = QLabel(self.tr("Create in (*):")) self.lblDescription = QLabel(self.tr("Project Description:")) self.lblLicense = QLabel(self.tr("Project License:")) self.lblVenvFolder = QLabel(self.tr("Virtualenv Folder:")) gbox.addWidget(self.lblName, 0, 0, Qt.AlignRight) gbox.addWidget(self.lblPlace, 1, 0, Qt.AlignRight) gbox.addWidget(self.lblDescription, 2, 0, Qt.AlignTop) gbox.addWidget(self.lblLicense, 3, 0, Qt.AlignRight) gbox.addWidget(self.lblVenvFolder, 4, 0, Qt.AlignRight) #Fields on de right of the grid #Name self.txtName = QLineEdit() #Location hPlace = QHBoxLayout() self.txtPlace = QLineEdit() self.txtPlace.setReadOnly(True) self.btnExamine = QPushButton(self.tr("Browse...")) hPlace.addWidget(self.txtPlace) hPlace.addWidget(self.btnExamine) #Virtualenv vPlace = QHBoxLayout() self.vtxtPlace = QLineEdit() self._dir_completer = QCompleter() self._dir_completer.setModel(QDirModel(self._dir_completer)) self.vtxtPlace.setCompleter(self._dir_completer) self.vbtnExamine = QPushButton(self.tr("Browse...")) vPlace.addWidget(self.vtxtPlace) vPlace.addWidget(self.vbtnExamine) #Project Description self.txtDescription = QPlainTextEdit() #Project License self.cboLicense = QComboBox() self.cboLicense.setFixedWidth(250) self.cboLicense.addItem('Apache License 2.0') self.cboLicense.addItem('Artistic License/GPL') self.cboLicense.addItem('Eclipse Public License 1.0') self.cboLicense.addItem('GNU General Public License v2') self.cboLicense.addItem('GNU General Public License v3') self.cboLicense.addItem('GNU Lesser General Public License') self.cboLicense.addItem('MIT License') self.cboLicense.addItem('Mozilla Public License 1.1') self.cboLicense.addItem('Mozilla Public License 2.0') self.cboLicense.addItem('New BSD License') self.cboLicense.addItem('Other Open Source') self.cboLicense.addItem('Other') self.cboLicense.setCurrentIndex(4) #Add to Grid gbox.addWidget(self.txtName, 0, 1) gbox.addLayout(hPlace, 1, 1) gbox.addWidget(self.txtDescription, 2, 1) gbox.addWidget(self.cboLicense, 3, 1) gbox.addLayout(vPlace, 4, 1) #Signal self.connect(self.btnExamine, SIGNAL('clicked()'), self.load_folder) self.connect(self.vbtnExamine, SIGNAL('clicked()'), self.load_folder_venv) self.connect(self.txtName, SIGNAL('textChanged(const QString&)'), lambda: self.emit(SIGNAL("completeChanged()"))) def isComplete(self): name = self.txtName.text().strip() place = self.txtPlace.text().strip() return (len(name) > 0) and (len(place) > 0) def load_folder(self): self.txtPlace.setText(QFileDialog.getExistingDirectory( self, self.tr("New Project Folder"))) self.emit(SIGNAL("completeChanged()")) def load_folder_venv(self): self.vtxtPlace.setText(QFileDialog.getExistingDirectory( self, self.tr("Select Virtualenv Folder"))) ninja-ide-2.3/ninja_ide/gui/editor/000077500000000000000000000000001216641277400172205ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/gui/editor/__init__.py000066400000000000000000000012641216641277400213340ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . ninja-ide-2.3/ninja_ide/gui/editor/editor.py000066400000000000000000001506401216641277400210660ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import re import sys from tokenize import generate_tokens, TokenError import token as tkn #lint:disable try: from StringIO import StringIO except: from io import StringIO #lint:enable from PyQt4.QtGui import QPlainTextEdit from PyQt4.QtGui import QFontMetricsF from PyQt4.QtGui import QToolTip from PyQt4.QtGui import QAction from PyQt4.QtGui import QTextOption from PyQt4.QtGui import QTextEdit from PyQt4.QtGui import QInputDialog from PyQt4.QtGui import QTextCursor from PyQt4.QtGui import QTextDocument from PyQt4.QtGui import QTextFormat from PyQt4.QtGui import QFont from PyQt4.QtGui import QMenu from PyQt4.QtGui import QPainter from PyQt4.QtGui import QColor from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import QMimeData from PyQt4.QtCore import Qt from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.core import file_manager from ninja_ide.tools.completion import completer_widget from ninja_ide.gui.main_panel import itab_item from ninja_ide.gui.editor import highlighter from ninja_ide.gui.editor import syntax_highlighter from ninja_ide.gui.editor import helpers from ninja_ide.gui.editor import minimap from ninja_ide.gui.editor import pep8_checker from ninja_ide.gui.editor import errors_checker from ninja_ide.gui.editor import migration_2to3 from ninja_ide.gui.editor import sidebar_widget from ninja_ide.gui.editor import python_syntax from ninja_ide.tools.logger import NinjaLogger BRACE_DICT = {')': '(', ']': '[', '}': '{', '(': ')', '[': ']', '{': '}'} logger = NinjaLogger('ninja_ide.gui.editor.editor') if sys.version_info.major == 3: python3 = True else: python3 = False class Editor(QPlainTextEdit, itab_item.ITabItem): ############################################################################### # EDITOR SIGNALS ############################################################################### """ modificationChanged(bool) fileSaved(QPlainTextEdit) locateFunction(QString, QString, bool) [functionName, filePath, isVariable] openDropFile(QString) addBackItemNavigation() warningsFound(QPlainTextEdit) errorsFound(QPlainTextEdit) cleanDocument(QPlainTextEdit) findOcurrences(QString) cursorPositionChange(int, int) #row, col migrationAnalyzed() """ ############################################################################### def __init__(self, filename, project, project_obj=None): QPlainTextEdit.__init__(self) itab_item.ITabItem.__init__(self) #Config Editor self.set_flags() self.__lines_count = None self._sidebarWidget = sidebar_widget.SidebarWidget(self) if filename in settings.BREAKPOINTS: self._sidebarWidget._breakpoints = settings.BREAKPOINTS[filename] if filename in settings.BOOKMARKS: self._sidebarWidget._bookmarks = settings.BOOKMARKS[filename] self.pep8 = pep8_checker.Pep8Checker(self) if project_obj is not None: additional_builtins = project_obj.additional_builtins else: additional_builtins = [] self.errors = errors_checker.ErrorsChecker(self, additional_builtins) self.migration = migration_2to3.MigrationTo3(self) self.textModified = False self.newDocument = True self.highlighter = None self.syncDocErrorsSignal = False self._selected_word = '' #Set editor style self.apply_editor_style() self.set_font(settings.FONT_FAMILY, settings.FONT_SIZE) #For Highlighting in document self.extraSelections = [] self.wordSelection = [] self._patIsWord = re.compile('\w+') #Brace matching self._braces = None self.__encoding = None #Completer self.completer = completer_widget.CodeCompletionWidget(self) #Flag to dont bug the user when answer *the modification dialog* self.ask_if_externally_modified = False self.just_saved = False #Dict functions for KeyPress self.preKeyPress = { Qt.Key_Tab: self.__insert_indentation, Qt.Key_Backspace: self.__backspace, Qt.Key_Home: self.__home_pressed, Qt.Key_Enter: self.__ignore_extended_line, Qt.Key_Return: self.__ignore_extended_line, Qt.Key_BracketRight: self.__brace_completion, Qt.Key_BraceRight: self.__brace_completion, Qt.Key_ParenRight: self.__brace_completion, Qt.Key_Apostrophe: self.__quot_completion, Qt.Key_QuoteDbl: self.__quot_completion} self.postKeyPress = { Qt.Key_Enter: self.__auto_indent, Qt.Key_Return: self.__auto_indent, Qt.Key_BracketLeft: self.__complete_braces, Qt.Key_BraceLeft: self.__complete_braces, Qt.Key_ParenLeft: self.__complete_braces, Qt.Key_Apostrophe: self.__complete_quotes, Qt.Key_QuoteDbl: self.__complete_quotes} self._line_colors = { 'current-line': QColor( resources.CUSTOM_SCHEME.get('current-line', resources.COLOR_SCHEME['current-line'])), 'error-line': QColor( resources.CUSTOM_SCHEME.get('error-underline', resources.COLOR_SCHEME['error-underline'])), 'pep8-line': QColor( resources.CUSTOM_SCHEME.get('pep8-underline', resources.COLOR_SCHEME['pep8-underline'])), 'migration-line': QColor( resources.CUSTOM_SCHEME.get('migration-underline', resources.COLOR_SCHEME['migration-underline'])), } self.connect(self, SIGNAL("updateRequest(const QRect&, int)"), self._sidebarWidget.update_area) self.connect(self, SIGNAL("undoAvailable(bool)"), self._file_saved) self.connect(self, SIGNAL("cursorPositionChanged()"), self.highlight_current_line) self.connect(self.pep8, SIGNAL("finished()"), self.show_pep8_errors) self.connect(self.migration, SIGNAL("finished()"), self.show_migration_info) self.connect(self.errors, SIGNAL("finished()"), self.show_static_errors) self.connect(self, SIGNAL("blockCountChanged(int)"), self._update_file_metadata) self._mini = None if settings.SHOW_MINIMAP: self._mini = minimap.MiniMap(self) self._mini.show() self.connect(self, SIGNAL("updateRequest(const QRect&, int)"), self._mini.update_visible_area) #Indentation self.set_project(project_obj) #Context Menu Options self.__actionFindOccurrences = QAction( self.tr("Find Usages"), self) self.connect(self.__actionFindOccurrences, SIGNAL("triggered()"), self._find_occurrences) def set_project(self, project): if project is not None: self.indent = project.indentation self.useTabs = project.useTabs #Set tab usage if self.useTabs: self.set_tab_usage() self.connect(project._parent, SIGNAL("projectPropertiesUpdated(QTreeWidgetItem)"), self.set_project) else: self.indent = settings.INDENT self.useTabs = settings.USE_TABS def __get_encoding(self): """Get the current encoding of 'utf-8' otherwise.""" if self.__encoding is not None: return self.__encoding return 'utf-8' def __set_encoding(self, encoding): """Set the current encoding.""" self.__encoding = encoding encoding = property(__get_encoding, __set_encoding) def set_flags(self): if settings.ALLOW_WORD_WRAP: self.setWordWrapMode(QTextOption.WrapAtWordBoundaryOrAnywhere) else: self.setWordWrapMode(QTextOption.NoWrap) self.setMouseTracking(True) doc = self.document() option = QTextOption() if settings.SHOW_TABS_AND_SPACES: option.setFlags(QTextOption.ShowTabsAndSpaces) doc.setDefaultTextOption(option) self.setDocument(doc) self.setCenterOnScroll(settings.CENTER_ON_SCROLL) def set_tab_usage(self): tab_size = self.pos_margin / settings.MARGIN_LINE * self.indent self.setTabStopWidth(tab_size) if self._mini: self._mini.setTabStopWidth(tab_size) def set_id(self, id_): super(Editor, self).set_id(id_) if self._mini: self._mini.set_code(self.toPlainText()) if settings.CHECK_STYLE: self.pep8.check_style() if settings.SHOW_MIGRATION_TIPS: self.migration.check_style() if not python3: if settings.FIND_ERRORS: self.errors.check_errors() def _add_line_increment(self, lines, blockModified, diference): def _inner_increment(line): if line < blockModified: return line return line + diference return list(map(_inner_increment, lines)) def _add_line_increment_for_dict(self, data, blockModified, diference): def _inner_increment(line): if line < blockModified: return line newLine = line + diference summary = data.pop(line) data[newLine] = summary return newLine list(map(_inner_increment, list(data.keys()))) return data def _update_file_metadata(self, val): """Update the info of bookmarks, breakpoint, pep8 and static errors.""" if (self.pep8.pep8checks or self.errors.errorsSummary or self.migration.migration_data or self._sidebarWidget._bookmarks or self._sidebarWidget._breakpoints or self._sidebarWidget._foldedBlocks): cursor = self.textCursor() if self.__lines_count: diference = val - self.__lines_count else: diference = 0 blockNumber = cursor.blockNumber() - abs(diference) if self.pep8.pep8checks: self.pep8.pep8checks = self._add_line_increment_for_dict( self.pep8.pep8checks, blockNumber, diference) self._sidebarWidget._pep8Lines = list( self.pep8.pep8checks.keys()) if self.migration.migration_data: self.migration.migration_data = \ self._add_line_increment_for_dict( self.migration.migration_data, blockNumber, diference) self._sidebarWidget._migrationLines = list( self.migration.migration_data.keys()) if self.errors.errorsSummary: self.errors.errorsSummary = self._add_line_increment_for_dict( self.errors.errorsSummary, blockNumber, diference) self._sidebarWidget._errorsLines = list( self.errors.errorsSummary.keys()) if self._sidebarWidget._breakpoints and self.ID: self._sidebarWidget._breakpoints = self._add_line_increment( self._sidebarWidget._breakpoints, blockNumber, diference) settings.BREAKPOINTS[self.ID] = \ self._sidebarWidget._breakpoints if self._sidebarWidget._bookmarks and self.ID: self._sidebarWidget._bookmarks = self._add_line_increment( self._sidebarWidget._bookmarks, blockNumber, diference) settings.BOOKMARKS[self.ID] = self._sidebarWidget._bookmarks if self._sidebarWidget._foldedBlocks and self.ID: self._sidebarWidget._foldedBlocks = self._add_line_increment( self._sidebarWidget._foldedBlocks, blockNumber - 1, diference) self.__lines_count = val self.highlight_current_line() def show_pep8_errors(self): self._sidebarWidget.pep8_check_lines(list(self.pep8.pep8checks.keys())) if self.syncDocErrorsSignal: self._sync_tab_icon_notification_signal() else: self.syncDocErrorsSignal = True self.pep8.wait() def show_migration_info(self): lines = list(self.migration.migration_data.keys()) self._sidebarWidget.migration_lines(lines) self.highlighter.rehighlight_lines(lines) self.emit(SIGNAL("migrationAnalyzed()")) self.migration.wait() def hide_pep8_errors(self): """Hide the pep8 errors from the sidebar and lines highlighted.""" self._sidebarWidget.pep8_check_lines([]) self.pep8.reset() self.highlighter.rehighlight_lines([]) self._sync_tab_icon_notification_signal() def show_static_errors(self): self._sidebarWidget.static_errors_lines( list(self.errors.errorsSummary.keys())) if self.syncDocErrorsSignal: self._sync_tab_icon_notification_signal() else: self.syncDocErrorsSignal = True self.errors.wait() def hide_lint_errors(self): """Hide the lint errors from the sidebar and lines highlighted.""" self._sidebarWidget.static_errors_lines([]) self.errors.reset() self.highlighter.rehighlight_lines([]) self._sync_tab_icon_notification_signal() def _sync_tab_icon_notification_signal(self): self.syncDocErrorsSignal = False if self.errors.errorsSummary: self.emit(SIGNAL("errorsFound(QPlainTextEdit)"), self) elif self.pep8.pep8checks: self.emit(SIGNAL("warningsFound(QPlainTextEdit)"), self) else: self.emit(SIGNAL("cleanDocument(QPlainTextEdit)"), self) if self.highlighter: lines = list(set(list(self.errors.errorsSummary.keys()) + list(self.pep8.pep8checks.keys()))) self.highlighter.rehighlight_lines(lines) self.highlight_current_line() def has_write_permission(self): if self.newDocument: return True return file_manager.has_write_permission(self.ID) def restyle(self, syntaxLang=None): self.apply_editor_style() if self.lang == 'python': parts_scanner, code_scanner, formats = \ syntax_highlighter.load_syntax(python_syntax.syntax) self.highlighter = syntax_highlighter.SyntaxHighlighter( self.document(), parts_scanner, code_scanner, formats, errors=self.errors, pep8=self.pep8, migration=self.migration) if self._mini: self._mini.highlighter = syntax_highlighter.SyntaxHighlighter( self._mini.document(), parts_scanner, code_scanner, formats) return if self.highlighter is None or isinstance(self.highlighter, highlighter.EmpyHighlighter): self.highlighter = highlighter.Highlighter(self.document(), None, resources.CUSTOM_SCHEME, self.errors, self.pep8, self.migration) if not syntaxLang: ext = file_manager.get_file_extension(self.ID) self.highlighter.apply_highlight( settings.EXTENSIONS.get(ext, 'python'), resources.CUSTOM_SCHEME) if self._mini: self._mini.highlighter.apply_highlight( settings.EXTENSIONS.get(ext, 'python'), resources.CUSTOM_SCHEME) else: self.highlighter.apply_highlight( syntaxLang, resources.CUSTOM_SCHEME) if self._mini: self._mini.highlighter.apply_highlight( syntaxLang, resources.CUSTOM_SCHEME) def apply_editor_style(self): css = 'QPlainTextEdit {color: %s; background-color: %s;' \ 'selection-color: %s; selection-background-color: %s;}' \ % (resources.CUSTOM_SCHEME.get('editor-text', resources.COLOR_SCHEME['editor-text']), resources.CUSTOM_SCHEME.get('editor-background', resources.COLOR_SCHEME['editor-background']), resources.CUSTOM_SCHEME.get('editor-selection-color', resources.COLOR_SCHEME['editor-selection-color']), resources.CUSTOM_SCHEME.get('editor-selection-background', resources.COLOR_SCHEME['editor-selection-background'])) self.setStyleSheet(css) def _file_saved(self, undoAvailable=False): if not undoAvailable: self.emit(SIGNAL("fileSaved(QPlainTextEdit)"), self) self.newDocument = False self.textModified = False self.document().setModified(self.textModified) def register_syntax(self, lang='', syntax=None): self.lang = settings.EXTENSIONS.get(lang, 'python') if self.lang == 'python': parts_scanner, code_scanner, formats = \ syntax_highlighter.load_syntax(python_syntax.syntax) self.highlighter = syntax_highlighter.SyntaxHighlighter( self.document(), parts_scanner, code_scanner, formats, errors=self.errors, pep8=self.pep8, migration=self.migration) if self._mini: self._mini.highlighter = syntax_highlighter.SyntaxHighlighter( self._mini.document(), parts_scanner, code_scanner, formats) elif lang in settings.EXTENSIONS: self.highlighter = highlighter.Highlighter(self.document(), self.lang, resources.CUSTOM_SCHEME, self.errors, self.pep8, self.migration) if self._mini: self._mini.highlighter = highlighter.Highlighter( self._mini.document(), self.lang, resources.CUSTOM_SCHEME) elif syntax is not None: self.highlighter = highlighter.Highlighter(self.document(), None, resources.CUSTOM_SCHEME) self.highlighter.apply_highlight(lang, resources.CUSTOM_SCHEME, syntax) if self._mini: self._mini.highlighter = highlighter.Highlighter( self.document(), None, resources.CUSTOM_SCHEME) self._mini.highlighter.apply_highlight(lang, resources.CUSTOM_SCHEME, syntax) else: self.highlighter = highlighter.EmpyHighlighter(self.document()) if self._mini: self._mini.highlighter = highlighter.EmpyHighlighter( self.document()) def get_text(self): """ Returns all the plain text of the editor """ return self.toPlainText() def get_lines_count(self): """ Returns the count of lines in the editor """ return self.textCursor().document().lineCount() def cursor_inside_string(self): inside = False cursor = self.textCursor() pos = cursor.positionInBlock() user_data = syntax_highlighter.get_user_data(cursor.block()) for vals in user_data.str_groups: if vals[0] < pos < vals[1]: inside = True break return inside def cursor_inside_comment(self): inside = False cursor = self.textCursor() pos = cursor.positionInBlock() user_data = syntax_highlighter.get_user_data(cursor.block()) if (user_data.comment_start != -1) and \ (pos > user_data.comment_start): inside = True return inside def set_font(self, family=settings.FONT_FAMILY, size=settings.FONT_SIZE): font = QFont(family, size) self.document().setDefaultFont(font) self._update_margin_line(font) def jump_to_line(self, lineno=None): """ Jump to a specific line number or ask to the user for the line """ if lineno is not None: self.emit(SIGNAL("addBackItemNavigation()")) self.go_to_line(lineno) return maximum = self.blockCount() line = QInputDialog.getInt(self, self.tr("Jump to Line"), self.tr("Line:"), 1, 1, maximum, 1) if line[1]: self.emit(SIGNAL("addBackItemNavigation()")) self.go_to_line(line[0] - 1) def _find_occurrences(self): if self.textCursor().hasSelection(): word = self.textCursor().selectedText() else: word = self._text_under_cursor() self.emit(SIGNAL("findOcurrences(QString)"), word) def _unfold_blocks_for_jump(self, lineno): """Unfold the blocks previous to the lineno.""" for line in self._sidebarWidget._foldedBlocks: if lineno >= line: self._sidebarWidget.code_folding_event(line + 1) else: break def go_to_line(self, lineno): """ Go to an specific line """ self._unfold_blocks_for_jump(lineno) if self.blockCount() >= lineno: cursor = self.textCursor() cursor.setPosition(self.document().findBlockByLineNumber( lineno).position()) self.setTextCursor(cursor) def zoom_in(self): font = self.document().defaultFont() size = font.pointSize() if size < settings.FONT_MAX_SIZE: size += 2 font.setPointSize(size) self.setFont(font) self._update_margin_line(font) def zoom_out(self): font = self.document().defaultFont() size = font.pointSize() if size > settings.FONT_MIN_SIZE: size -= 2 font.setPointSize(size) self.setFont(font) self._update_margin_line(font) def _update_margin_line(self, font=None): if not font: font = self.document().defaultFont() # Fix for older version of Qt which doens't has ForceIntegerMetrics if "ForceIntegerMetrics" in dir(QFont): self.document().defaultFont().setStyleStrategy( QFont.ForceIntegerMetrics) font_metrics = QFontMetricsF(self.document().defaultFont()) if (font_metrics.width("#") * settings.MARGIN_LINE) == \ (font_metrics.width(" ") * settings.MARGIN_LINE): self.pos_margin = font_metrics.width('#') * settings.MARGIN_LINE else: char_width = font_metrics.averageCharWidth() self.pos_margin = char_width * settings.MARGIN_LINE def get_parent_project(self): return '' def get_cursor_position(self): return self.textCursor().position() def set_cursor_position(self, pos): if self.document().characterCount() >= pos: cursor = self.textCursor() cursor.setPosition(pos) self.setTextCursor(cursor) def indent_more(self): #cursor is a COPY all changes do not affect the QPlainTextEdit's cursor cursor = self.textCursor() #line where indent_more should start and end block = self.document().findBlock( cursor.selectionStart()) end = self.document().findBlock(cursor.selectionEnd()).next() #Start a undo block cursor.beginEditBlock() #Move the COPY cursor cursor.setPosition(block.position()) while block != end: cursor.setPosition(block.position()) if self.useTabs: cursor.insertText('\t') else: cursor.insertText(' ' * self.indent) block = block.next() #End a undo block cursor.endEditBlock() def indent_less(self): #cursor is a COPY all changes do not affect the QPlainTextEdit's cursor cursor = self.textCursor() if not cursor.hasSelection(): cursor.movePosition(QTextCursor.EndOfLine) #line where indent_less should start and end block = self.document().findBlock( cursor.selectionStart()) end = self.document().findBlock(cursor.selectionEnd()).next() #Start a undo block cursor.beginEditBlock() cursor.setPosition(block.position()) while block != end: cursor.setPosition(block.position()) #Select Settings.indent chars from the current line if self.useTabs: cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor) else: cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, self.indent) text = cursor.selectedText() if not self.useTabs and text == ' ' * self.indent: cursor.removeSelectedText() elif self.useTabs and text == '\t': cursor.removeSelectedText() block = block.next() #End a undo block cursor.endEditBlock() def find_match(self, word, flags, findNext=False): flags = QTextDocument.FindFlags(flags) if findNext: self.moveCursor(QTextCursor.NoMove, QTextCursor.KeepAnchor) else: self.moveCursor(QTextCursor.StartOfWord, QTextCursor.KeepAnchor) found = self.find(word, flags) if not found: cursor = self.textCursor() self.moveCursor(QTextCursor.Start) found = self.find(word, flags) if not found: self.setTextCursor(cursor) if found: self.highlight_selected_word(word) def replace_match(self, wordOld, wordNew, flags, allwords=False, selection=False): """Find if searched text exists and replace it with new one. If there is a selection just do it inside it and exit. """ tc = self.textCursor() if selection and tc.hasSelection(): start, end = tc.selectionStart(), tc.selectionEnd() text = tc.selectedText() old_len = len(text) max_replace = -1 # all text = text.replace(wordOld, wordNew, max_replace) new_len = len(text) tc.insertText(text) offset = new_len - old_len self.__set_selection_from_pair(start, end + offset) return flags = QTextDocument.FindFlags(flags) self.moveCursor(QTextCursor.Start, QTextCursor.KeepAnchor) cursor = self.textCursor() cursor.beginEditBlock() replace = True while (replace or allwords): result = False result = self.find(wordOld, flags) if result: tc = self.textCursor() if tc.hasSelection(): tc.insertText(wordNew) else: break replace = False cursor.endEditBlock() def focusInEvent(self, event): super(Editor, self).focusInEvent(event) try: #use parent().parent() to Access QTabWidget #First parent() = QStackedWidget, Second parent() = TabWidget #Check for modifications self.parent().parent().focusInEvent(event) except RuntimeError: pass def focusOutEvent(self, event): """Hide Popup on focus lost.""" self.completer.hide_completer() super(Editor, self).focusOutEvent(event) def resizeEvent(self, event): QPlainTextEdit.resizeEvent(self, event) self._sidebarWidget.setFixedHeight(self.height()) if self._mini: self._mini.adjust_to_parent() def __insert_indentation(self, event): if self.textCursor().hasSelection(): self.indent_more() elif self.useTabs: return False else: self.textCursor().insertText(' ' * self.indent) return True def __backspace(self, event): if self.textCursor().hasSelection() or self.useTabs: return False cursor = self.textCursor() cursor.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor) text = cursor.selection().toPlainText() if (len(text) % self.indent == 0) and text.isspace(): cursor.movePosition(QTextCursor.StartOfLine) cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, self.indent) cursor.removeSelectedText() return True def __home_pressed(self, event): if event.modifiers() == Qt.ControlModifier: return False elif event.modifiers() == Qt.ShiftModifier: move = QTextCursor.KeepAnchor else: move = QTextCursor.MoveAnchor if self.textCursor().atBlockStart(): self.moveCursor(QTextCursor.WordRight, move) return True cursor = self.textCursor() position = cursor.position() self.moveCursor(QTextCursor.StartOfLine, move) self.moveCursor(QTextCursor.WordRight, move) if position != self.textCursor().position() and \ cursor.block().text().startswith(' '): return True def __ignore_extended_line(self, event): if event.modifiers() == Qt.ShiftModifier: return True def __set_selection_from_pair(self, begin, end): """Set the current editor cursor with a selection from a given pair of positions""" cursor = self.textCursor() cursor.setPosition(begin) cursor.setPosition(end, QTextCursor.KeepAnchor) self.setTextCursor(cursor) def __reverse_select_text_portion_from_offset(self, begin, end): """Backwards select text, go from current+begin to current - end possition, returns text""" cursor = self.textCursor() cursor_position = cursor.position() cursor.setPosition(cursor_position + begin) #QT silently fails on invalid position, ergo breaks when EOF < begin while (cursor.position() == cursor_position) and begin > 0: begin -= 1 cursor.setPosition(cursor_position + begin) cursor.setPosition(cursor_position - end, QTextCursor.KeepAnchor) selected_text = cursor.selectedText() return selected_text def __quot_completion(self, event): """Indicate if this is some sort of quote that needs to be completed This is a very simple boolean table, given that quotes are a simmetrical symbol, is a little more cumbersome guessing the completion table. """ text = event.text() pos = self.textCursor().position() next_char = self.get_selection(pos, pos + 1).strip() if self.cursor_inside_string() and text == next_char: self.moveCursor(QTextCursor.Right) return True PENTA_Q = 5 * text TETRA_Q = 4 * text TRIPLE_Q = 3 * text DOUBLE_Q = 2 * text supress_echo = False pre_context = self.__reverse_select_text_portion_from_offset(0, 3) pos_context = self.__reverse_select_text_portion_from_offset(3, 0) if pre_context == pos_context == TRIPLE_Q: supress_echo = True elif pos_context[:2] == DOUBLE_Q: pre_context = self.__reverse_select_text_portion_from_offset(0, 4) if pre_context == TETRA_Q: supress_echo = True elif pos_context[:1] == text: pre_context = self.__reverse_select_text_portion_from_offset(0, 5) if pre_context == PENTA_Q: supress_echo = True elif pre_context[-1] == text: supress_echo = True if supress_echo: self.moveCursor(QTextCursor.Right) return supress_echo def __brace_completion(self, event): """Indicate if this symbol is part of a given pair and needs to be completed. """ text = event.text() if text in list(settings.BRACES.values()): portion = self.__reverse_select_text_portion_from_offset(1, 1) brace_open = portion[0] brace_close = (len(portion) > 1) and portion[1] or None balance = BRACE_DICT.get(brace_open, None) == text == brace_close if balance: self.moveCursor(QTextCursor.Right) return True def __auto_indent(self, event): text = self.textCursor().block().previous().text() spaces = helpers.get_indentation(text, self.indent, self.useTabs) self.textCursor().insertText(spaces) if text != '' and text == ' ' * len(text): self.moveCursor(QTextCursor.Up) self.moveCursor(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) self.textCursor().removeSelectedText() self.moveCursor(QTextCursor.Down) elif settings.COMPLETE_DECLARATIONS: helpers.check_for_assistance_completion(self, text) cursor = self.textCursor() cursor.setPosition(cursor.position()) self.setTextCursor(cursor) def complete_declaration(self): settings.COMPLETE_DECLARATIONS = not settings.COMPLETE_DECLARATIONS self.insert_new_line() settings.COMPLETE_DECLARATIONS = not settings.COMPLETE_DECLARATIONS def insert_new_line(self): cursor = self.textCursor() at_block_end = cursor.atBlockEnd() cursor.movePosition(QTextCursor.EndOfLine) cursor.insertBlock() if not at_block_end: self.moveCursor(QTextCursor.Down) self.__auto_indent(None) def __complete_braces(self, event): """Complete () [] and {} using a mild inteligence to see if corresponds and also do some more magic such as complete in classes and functions. """ brace = event.text() if brace not in settings.BRACES: # Thou shalt not waste cpu cycles if this brace compleion dissabled return text = self.textCursor().block().text() complementary_brace = BRACE_DICT.get(brace) token_buffer = [] _, tokens = self.__tokenize_text(text) is_unbalance = 0 for tkn_type, tkn_rep, tkn_begin, tkn_end in tokens: if tkn_rep == brace: is_unbalance += 1 elif tkn_rep == complementary_brace: is_unbalance -= 1 if tkn_rep.strip() != "": token_buffer.append((tkn_rep, tkn_end[1])) is_unbalance = (is_unbalance >= 0) and is_unbalance or 0 if (self.lang == "python") and (len(token_buffer) == 3) and \ (token_buffer[2][0] == brace) and (token_buffer[0][0] in ("def", "class")): #are we in presence of a function? self.textCursor().insertText("):") self.__fancyMoveCursor(QTextCursor.Left, 2) self.textCursor().insertText(self.selected_text) elif token_buffer and (not is_unbalance) and \ self.selected_text: self.textCursor().insertText(self.selected_text) elif is_unbalance: pos = self.textCursor().position() next_char = self.get_selection(pos, pos + 1).strip() if self.selected_text or next_char == "": self.textCursor().insertText(complementary_brace) self.moveCursor(QTextCursor.Left) self.textCursor().insertText(self.selected_text) def __complete_quotes(self, event): """ Completion for single and double quotes, which since are simmetrical symbols used for different things can not be balanced as easily as braces or equivalent. """ cursor = self.textCursor() cursor.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor) symbol = event.text() if symbol in settings.QUOTES: pre_context = self.__reverse_select_text_portion_from_offset(0, 3) if pre_context == 3 * symbol: self.textCursor().insertText(3 * symbol) self.__fancyMoveCursor(QTextCursor.Left, 3) else: self.textCursor().insertText(symbol) self.moveCursor(QTextCursor.Left) self.textCursor().insertText(self.selected_text) def keyPressEvent(self, event): #Completer pre key event if self.completer.process_pre_key_event(event): return #On Return == True stop the execution of this method if self.preKeyPress.get(event.key(), lambda x: False)(event): #emit a signal then plugings can do something self.emit(SIGNAL("keyPressEvent(QEvent)"), event) return self.selected_text = self.textCursor().selectedText() QPlainTextEdit.keyPressEvent(self, event) self.postKeyPress.get(event.key(), lambda x: False)(event) #Completer post key event self.completer.process_post_key_event(event) #emit a signal then plugings can do something self.emit(SIGNAL("keyPressEvent(QEvent)"), event) def _text_under_cursor(self): tc = self.textCursor() tc.select(QTextCursor.WordUnderCursor) word = tc.selectedText() result = self._patIsWord.findall(word) word = result[0] if result else '' return word def paintEvent(self, event): super(Editor, self).paintEvent(event) if settings.SHOW_MARGIN_LINE: painter = QPainter() painter.begin(self.viewport()) painter.setPen(QColor('#FE9E9A')) offset = self.contentOffset() painter.drawLine(self.pos_margin + offset.x(), 0, self.pos_margin + offset.x(), self.viewport().height()) painter.end() def wheelEvent(self, event, forward=True): if event.modifiers() == Qt.ControlModifier: if event.delta() == 120: self.zoom_in() elif event.delta() == -120: self.zoom_out() event.ignore() QPlainTextEdit.wheelEvent(self, event) def contextMenuEvent(self, event): popup_menu = self.createStandardContextMenu() menu_lint = QMenu(self.tr("Ignore Lint")) ignoreLineAction = menu_lint.addAction( self.tr("Ignore This Line")) ignoreSelectedAction = menu_lint.addAction( self.tr("Ignore Selected Area")) self.connect(ignoreLineAction, SIGNAL("triggered()"), lambda: helpers.lint_ignore_line(self)) self.connect(ignoreSelectedAction, SIGNAL("triggered()"), lambda: helpers.lint_ignore_selection(self)) popup_menu.insertSeparator(popup_menu.actions()[0]) popup_menu.insertMenu(popup_menu.actions()[0], menu_lint) popup_menu.insertAction(popup_menu.actions()[0], self.__actionFindOccurrences) #add extra menus (from Plugins) lang = file_manager.get_file_extension(self.ID) extra_menus = self.EXTRA_MENU.get(lang, None) if extra_menus: popup_menu.addSeparator() for menu in extra_menus: popup_menu.addMenu(menu) #show menu popup_menu.exec_(event.globalPos()) def mouseMoveEvent(self, event): position = event.pos() cursor = self.cursorForPosition(position) block = cursor.block() if settings.ERRORS_HIGHLIGHT_LINE and \ (block.blockNumber()) in self.errors.errorsSummary: message = '\n'.join( self.errors.errorsSummary[block.blockNumber()]) QToolTip.showText(self.mapToGlobal(position), message, self) elif settings.SHOW_MIGRATION_TIPS and \ block.blockNumber() in self.migration.migration_data: message = self.migration.migration_data[block.blockNumber()][0] QToolTip.showText(self.mapToGlobal(position), message, self) elif settings.CHECK_HIGHLIGHT_LINE and \ (block.blockNumber()) in self.pep8.pep8checks: message = '\n'.join( self.pep8.pep8checks[block.blockNumber()]) QToolTip.showText(self.mapToGlobal(position), message, self) if event.modifiers() == Qt.ControlModifier: cursor.select(QTextCursor.WordUnderCursor) selection_start = cursor.selectionStart() selection_end = cursor.selectionEnd() cursor.setPosition(selection_start - 1) cursor.setPosition(selection_end + 1, QTextCursor.KeepAnchor) if cursor.selectedText()[-1:] in ('(', '.') or \ cursor.selectedText()[:1] in ('.', '@'): self.extraSelections = [] selection = QTextEdit.ExtraSelection() lineColor = QColor(resources.CUSTOM_SCHEME.get('linkNavigate', resources.COLOR_SCHEME['linkNavigate'])) selection.format.setForeground(lineColor) selection.format.setFontUnderline(True) selection.cursor = cursor self.extraSelections.append(selection) self.setExtraSelections(self.extraSelections) else: self.extraSelections = [] self.setExtraSelections(self.extraSelections) QPlainTextEdit.mouseMoveEvent(self, event) def mousePressEvent(self, event): if self.completer.isVisible(): self.completer.hide_completer() elif event.modifiers() == Qt.ControlModifier: cursor = self.cursorForPosition(event.pos()) self.setTextCursor(cursor) self.go_to_definition(cursor) elif event.button() == Qt.RightButton and \ not self.textCursor().hasSelection(): cursor = self.cursorForPosition(event.pos()) self.setTextCursor(cursor) QPlainTextEdit.mousePressEvent(self, event) def mouseReleaseEvent(self, event): QPlainTextEdit.mouseReleaseEvent(self, event) if event.button() == Qt.LeftButton: self.highlight_selected_word() def dropEvent(self, event): if len(event.mimeData().urls()) > 0: path = event.mimeData().urls()[0].path() self.emit(SIGNAL("openDropFile(QString)"), path) event.ignore() event.mimeData = QMimeData() QPlainTextEdit.dropEvent(self, event) self.undo() def go_to_definition(self, cursor=None): if not cursor: cursor = self.textCursor() cursor.select(QTextCursor.WordUnderCursor) selection_start = cursor.selectionStart() selection_end = cursor.selectionEnd() cursor.setPosition(selection_start - 1) cursor.setPosition(selection_end + 1, QTextCursor.KeepAnchor) if cursor.selectedText().endswith('(') or \ cursor.selectedText().startswith('@'): cursor.setPosition(selection_start) cursor.setPosition(selection_end, QTextCursor.KeepAnchor) self.emit(SIGNAL("locateFunction(QString, QString, bool)"), cursor.selectedText(), self.ID, False) elif cursor.selectedText().endswith('.') or \ cursor.selectedText().startswith('.'): cursor.setPosition(selection_start) cursor.setPosition(selection_end, QTextCursor.KeepAnchor) self.emit(SIGNAL("locateFunction(QString, QString, bool)"), cursor.selectedText(), self.ID, True) def get_selection(self, posStart, posEnd): cursor = self.textCursor() cursor.setPosition(posStart) cursor2 = self.textCursor() if posEnd == QTextCursor.End: cursor2.movePosition(posEnd) cursor.setPosition(cursor2.position(), QTextCursor.KeepAnchor) else: cursor.setPosition(posEnd, QTextCursor.KeepAnchor) return cursor.selection().toPlainText() def __get_abs_position_on_text(self, text, position): """tokens give us position of char in a given line, we need such position relative to the beginning of the text, also we need to add the number of lines, since our split removes the newlines which are counted as a character in the editor""" line, relative_position = position insplit_line = line - 1 full_lenght = 0 for each_line in text.splitlines()[:insplit_line]: full_lenght += len(each_line) return full_lenght + insplit_line + relative_position def __fancyMoveCursor(self, operation, repeat=1, moveMode=QTextCursor.MoveAnchor): """Move the cursor a given number of times (with or without anchoring), just a helper given the less than practical way qt has for such a common operation""" cursor = self.textCursor() cursor.movePosition(operation, moveMode, repeat) self.setTextCursor(cursor) def __tokenize_text(self, text): invalid_syntax = False token_buffer = [] try: for tkn_type, tkn_rep, tkn_begin, tkn_end, _ in \ generate_tokens(StringIO(text).readline): token_buffer.append((tkn_type, tkn_rep, tkn_begin, tkn_end)) except (TokenError, IndentationError, SyntaxError): invalid_syntax = True return (invalid_syntax, token_buffer) def _match_braces(self, position, brace, forward): """Return the position to hilight of the matching brace""" braceMatch = BRACE_DICT[brace] if forward: text = self.get_selection(position, QTextCursor.End) else: text = self.get_selection(QTextCursor.Start, position) brace_stack = [] brace_buffer = [] invalid_syntax, tokens = self.__tokenize_text(text) for tkn_type, tkn_rep, tkn_begin, tkn_end in tokens: if (tkn_type == tkn.OP) and (tkn_rep in BRACE_DICT): tkn_pos = forward and tkn_begin or tkn_end brace_buffer.append((tkn_rep, tkn_pos)) if not forward: brace_buffer.reverse() if forward and (not invalid_syntax): #Exclude the brace that triggered all this brace_buffer = brace_buffer[1:] for tkn_rep, tkn_position in brace_buffer: if (tkn_rep == braceMatch) and not brace_stack: hl_position = \ self.__get_abs_position_on_text(text, tkn_position) return forward and hl_position + position or hl_position elif brace_stack and \ (BRACE_DICT.get(tkn_rep, '') == brace_stack[-1]): brace_stack.pop(-1) else: brace_stack.append(tkn_rep) def highlight_current_line(self): self.emit(SIGNAL("cursorPositionChange(int, int)"), self.textCursor().blockNumber() + 1, self.textCursor().columnNumber()) self.extraSelections = [] if not self.isReadOnly(): block = self.textCursor() selection = QTextEdit.ExtraSelection() if block.blockNumber() in self.errors.errorsSummary: lineColor = self._line_colors['error-line'] lineColor.setAlpha(resources.CUSTOM_SCHEME.get( "error-background-opacity", resources.COLOR_SCHEME["error-background-opacity"])) elif block.blockNumber() in self.pep8.pep8checks: lineColor = self._line_colors['pep8-line'] lineColor.setAlpha(resources.CUSTOM_SCHEME.get( "error-background-opacity", resources.COLOR_SCHEME["error-background-opacity"])) elif block.blockNumber() in self.migration.migration_data: lineColor = self._line_colors['migration-line'] lineColor.setAlpha(resources.CUSTOM_SCHEME.get( "error-background-opacity", resources.COLOR_SCHEME["error-background-opacity"])) else: lineColor = self._line_colors['current-line'] lineColor.setAlpha(resources.CUSTOM_SCHEME.get( "current-line-opacity", resources.COLOR_SCHEME["current-line-opacity"])) selection.format.setBackground(lineColor) selection.format.setProperty(QTextFormat.FullWidthSelection, True) selection.cursor = self.textCursor() selection.cursor.clearSelection() self.extraSelections.append(selection) self.setExtraSelections(self.extraSelections) #Re-position tooltip to allow text editing in the line of the error if QToolTip.isVisible(): QToolTip.hideText() if self._braces is not None: self._braces = None cursor = self.textCursor() if cursor.position() == 0: self.setExtraSelections(self.extraSelections) return cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor) text = cursor.selectedText() pos1 = cursor.position() if text in (")", "]", "}"): pos2 = self._match_braces(pos1, text, forward=False) elif text in ("(", "[", "{"): pos2 = self._match_braces(pos1, text, forward=True) else: self.setExtraSelections(self.extraSelections) return if pos2 is not None: self._braces = (pos1, pos2) selection = QTextEdit.ExtraSelection() selection.format.setForeground(QColor( resources.CUSTOM_SCHEME.get('brace-foreground', resources.COLOR_SCHEME.get('brace-foreground')))) selection.cursor = cursor self.extraSelections.append(selection) selection = QTextEdit.ExtraSelection() selection.format.setForeground(QColor( resources.CUSTOM_SCHEME.get('brace-foreground', resources.COLOR_SCHEME.get('brace-foreground')))) selection.format.setBackground(QColor( resources.CUSTOM_SCHEME.get('brace-background', resources.COLOR_SCHEME.get('brace-background')))) selection.cursor = self.textCursor() selection.cursor.setPosition(pos2) selection.cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor) self.extraSelections.append(selection) else: self._braces = (pos1,) selection = QTextEdit.ExtraSelection() selection.format.setBackground(QColor( resources.CUSTOM_SCHEME.get('brace-background', resources.COLOR_SCHEME.get('brace-background')))) selection.format.setForeground(QColor( resources.CUSTOM_SCHEME.get('brace-foreground', resources.COLOR_SCHEME.get('brace-foreground')))) selection.cursor = cursor self.extraSelections.append(selection) self.setExtraSelections(self.extraSelections) def highlight_selected_word(self, word_find=None): #Highlight selected variable word = self._text_under_cursor() partial = False if word_find is not None: word = word_find if word != self._selected_word: self._selected_word = word if word_find: partial = True self.highlighter.set_selected_word(word, partial) elif (word == self._selected_word) and (word_find is None): self._selected_word = None self.highlighter.set_selected_word("", partial=True) elif (word == self._selected_word) and (word_find is not None): self.highlighter.set_selected_word(word_find, partial=True) def async_highlight(self): pass #self.highlighter.async_highlight() def create_editor(fileName='', project=None, syntax=None, use_open_highlight=False, project_obj=None): editor = Editor(fileName, project, project_obj=project_obj) #if syntax is specified, use it if syntax: editor.register_syntax(syntax) else: #try to set a syntax based on the file extension ext = file_manager.get_file_extension(fileName) if ext not in settings.EXTENSIONS and fileName == '': #by default use python syntax editor.register_syntax('py') else: editor.register_syntax(ext) return editor ninja-ide-2.3/ninja_ide/gui/editor/errors_checker.py000066400000000000000000000105031216641277400225710ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import re try: import compiler except ImportError: print('Errors checker not working in Python3') from PyQt4.QtCore import QThread from ninja_ide.core import file_manager from ninja_ide.core import settings try: from ninja_ide.dependencies.pyflakes_mod import checker except ImportError: print('Errors checker not working in Python3') class ErrorsChecker(QThread): pat_disable_lint = re.compile('(\s)*#lint:disable$') pat_enable_lint = re.compile('(\s)*#lint:enable$') pat_ignore_lint = re.compile('(.)+#lint:ok$|(.)+# lint:ok$') def __init__(self, editor, additional_builtins=[]): super(ErrorsChecker, self).__init__() self._editor = editor self._builtins = additional_builtins self._path = '' self._encoding = '' self.errorsSummary = {} def check_errors(self): if not self.isRunning(): self._path = self._editor.ID self._encoding = self._editor.encoding self.start() def reset(self): self.errorsSummary = {} def run(self): self.sleep(1) exts = settings.SYNTAX.get('python')['extension'] file_ext = file_manager.get_file_extension(self._path) if file_ext in exts: try: self.reset() source = self._editor.get_text() if self._encoding is not None: source = source.encode(self._encoding) parseResult = compiler.parse(source) lint_checker = checker.Checker(parseResult, self._path, builtins=self._builtins) for m in lint_checker.messages: lineno = m.lineno - 1 if lineno not in self.errorsSummary: message = [m.message % m.message_args] else: message = self.errorsSummary[lineno] message += [m.message % m.message_args] self.errorsSummary[lineno] = message except Exception as reason: message = '' if hasattr(reason, 'msg'): message = reason.msg else: message = reason.message if hasattr(reason, 'lineno') and reason.lineno: self.errorsSummary[reason.lineno - 1] = [message] else: self.errorsSummary[0] = [message] finally: ignored_range, ignored_lines = self._get_ignore_range() to_remove = [x for x in self.errorsSummary for r in ignored_range if r[0] < x < r[1]] to_remove += ignored_lines for line in to_remove: self.errorsSummary.pop(line, None) else: self.reset() def _get_ignore_range(self): ignored_range = [] ignored_lines = [] block = self._editor.document().begin() while block.isValid(): if self.pat_disable_lint.match(block.text()): start = block.blockNumber() while block.isValid(): block = block.next() if self.pat_enable_lint.match(block.text()): end = block.blockNumber() ignored_range.append((start, end)) break elif self.pat_ignore_lint.match(block.text()): ignored_lines.append(block.blockNumber()) block = block.next() return (ignored_range, ignored_lines) ninja-ide-2.3/ninja_ide/gui/editor/helpers.py000066400000000000000000000535251216641277400212460ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import re from PyQt4.QtGui import QTextCursor from PyQt4.QtGui import QInputDialog from ninja_ide.core import settings from ninja_ide.core import file_manager from ninja_ide.tools import introspection patIndent = re.compile('^\s+') patIsLocalFunction = re.compile('(\s)+self\.(\w)+\(\)') patClass = re.compile("(\\s)*class.+\\:$") endCharsForIndent = [':', '{', '(', '['] closeBraces = {'{': '}', '(': ')', '[': ']'} #Coding line by language CODING_LINE = { 'python': '# -*- coding: utf-8 -*-' } def get_leading_spaces(line): global patIndent space = patIndent.match(line) if space is not None: return space.group() return '' def get_indentation(line, indent=settings.INDENT, useTabs=settings.USE_TABS): global patIndent global endCharsForIndent indentation = '' if len(line) > 0 and line[-1] in endCharsForIndent: if useTabs: indentation = '\t' else: indentation = ' ' * indent elif len(line) > 0 and line[-1] == ',': count = [x for x in endCharsForIndent[1:] if (line.count(x) - line.count(closeBraces[x])) % 2 != 0] if count: if useTabs: indentation = '\t' else: indentation = ' ' * indent space = patIndent.match(line) if space is not None: return space.group() + indentation return indentation def get_start_end_selection(editorWidget, cursor): start = editorWidget.document().findBlock( cursor.selectionStart()).firstLineNumber() end = editorWidget.document().findBlock( cursor.selectionEnd()).firstLineNumber() if cursor.blockNumber() == end and cursor.atBlockStart(): end -= 1 return start, end def remove_trailing_spaces(editorWidget): cursor = editorWidget.textCursor() cursor.beginEditBlock() block = editorWidget.document().findBlockByLineNumber(0) while block.isValid(): text = block.text() if text.endswith(' '): cursor.setPosition(block.position()) cursor.select(QTextCursor.LineUnderCursor) cursor.insertText(text.rstrip()) block = block.next() cursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor) cursor.endEditBlock() def insert_horizontal_line(editorWidget): editorWidget.moveCursor(QTextCursor.StartOfLine, QTextCursor.KeepAnchor) text = editorWidget.textCursor().selection().toPlainText() editorWidget.moveCursor(QTextCursor.EndOfLine, QTextCursor.MoveAnchor) lang = file_manager.get_file_extension(editorWidget.ID) key = settings.EXTENSIONS.get(lang, 'python') comment_wildcard = settings.SYNTAX[key].get('comment', ['#'])[0] comment = comment_wildcard * ((79 - len(text)) / len(comment_wildcard)) editorWidget.textCursor().insertText(comment) def insert_title_comment(editorWidget): result = str(QInputDialog.getText(editorWidget, editorWidget.tr("Title Comment"), editorWidget.tr("Enter the Title Name:"))[0]) if result: editorWidget.textCursor().beginEditBlock() editorWidget.moveCursor(QTextCursor.StartOfLine, QTextCursor.MoveAnchor) lang = file_manager.get_file_extension(editorWidget.ID) key = settings.EXTENSIONS.get(lang, 'python') comment_wildcard = settings.SYNTAX[key].get('comment', ['#'])[0] comment = comment_wildcard * (79 / len(comment_wildcard)) editorWidget.textCursor().insertText(comment) editorWidget.textCursor().insertBlock() editorWidget.textCursor().insertText(comment_wildcard + ' ' + result) editorWidget.textCursor().insertBlock() editorWidget.textCursor().insertText(comment) editorWidget.textCursor().insertBlock() editorWidget.textCursor().endEditBlock() def insert_coding_line(editorWidget): lang = file_manager.get_file_extension(editorWidget.ID) key = settings.EXTENSIONS.get(lang) coding_line = CODING_LINE.get(key) if coding_line: editorWidget.textCursor().insertText("%s\n" % coding_line) def replace_tabs_with_spaces(editorWidget): text = editorWidget.toPlainText() text = text.replace('\t', ' ' * editorWidget.indent) editorWidget.setPlainText(text) def lint_ignore_line(editorWidget): cursor = editorWidget.textCursor() if not cursor.hasSelection(): cursor.movePosition(QTextCursor.EndOfLine) cursor.insertText(" # lint:ok") def lint_ignore_selection(editorWidget): cursor = editorWidget.textCursor() if cursor.hasSelection(): cursor.beginEditBlock() start, end = get_start_end_selection(editorWidget, cursor) position = editorWidget.document().findBlockByLineNumber( start).position() cursor.setPosition(position) indentation = get_indentation(cursor.block().text()) cursor.movePosition(QTextCursor.StartOfLine) cursor.insertText("%s#lint:disable\n" % indentation) position = editorWidget.document().findBlockByLineNumber( end + 2).position() cursor.setPosition(position) cursor.movePosition(QTextCursor.StartOfLine) cursor.insertText("%s#lint:enable\n" % indentation) cursor.endEditBlock() def insert_debugging_prints(editorWidget): cursor = editorWidget.textCursor() if cursor.hasSelection(): result = str(QInputDialog.getText(editorWidget, editorWidget.tr("Print Text"), editorWidget.tr("Insert a Text to use in the Print or " "leave empty to just print numbers:"))[0]) print_text = "" if result: print_text = "%s: " % result #begin Undo feature cursor.beginEditBlock() start, end = get_start_end_selection(editorWidget, cursor) lines = end - start for i in range(lines): position = editorWidget.document().findBlockByLineNumber( start + (i * 2)).position() cursor.setPosition(position) indentation = get_indentation(cursor.block().text()) cursor.movePosition(QTextCursor.EndOfLine) cursor.insertText("\n%sprint('%s%i')" % ( indentation, print_text, i)) #end Undo feature cursor.endEditBlock() def insert_pdb(editorWidget): """Insert a pdb statement into the current line to debug code.""" cursor = editorWidget.textCursor() indentation = get_indentation(cursor.block().text()) cursor.insertText("\n%simport pdb; pdb.set_trace()" % indentation) def move_up(editorWidget): cursor = editorWidget.textCursor() block_actual = cursor.block() if block_actual.blockNumber() > 0: #line where indent_more should start and end start, end = get_start_end_selection(editorWidget, cursor) if cursor.hasSelection() and (start != end): #get the position of the line startPosition = editorWidget.document().findBlockByLineNumber( start).position() #select the text to move cursor.setPosition(startPosition) cursor.movePosition(QTextCursor.StartOfLine) cursor.movePosition(QTextCursor.Down, QTextCursor.KeepAnchor, end - start) cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) text_to_move = cursor.selectedText() #begin Undo feature cursor.beginEditBlock() #Remove cursor.removeSelectedText() cursor.deleteChar() #Insert text and breakline cursor.movePosition(QTextCursor.Up, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.StartOfLine, QTextCursor.MoveAnchor) cursor.insertText(text_to_move + '\n') #end Undo feature cursor.endEditBlock() #Restore the user selection startPosition = editorWidget.document().findBlockByLineNumber( (start - 1)).position() cursor.setPosition(startPosition) cursor.movePosition(QTextCursor.StartOfLine) cursor.movePosition(QTextCursor.Down, QTextCursor.KeepAnchor, end - start) cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) editorWidget.setTextCursor(cursor) else: block_previous = block_actual.previous() tempLine = block_actual.text() cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor) #begin Undo feature cursor.beginEditBlock() cursor.insertText(block_previous.text()) cursor.movePosition(QTextCursor.Up, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor) cursor.insertText(tempLine) #end Undo feature cursor.endEditBlock() editorWidget.moveCursor(QTextCursor.Up, QTextCursor.MoveAnchor) def move_down(editorWidget): cursor = editorWidget.textCursor() block_actual = cursor.block() if block_actual.blockNumber() < (editorWidget.blockCount() - 1): start, end = get_start_end_selection(editorWidget, cursor) if cursor.hasSelection() and (start != end): #get the position of the line startPosition = editorWidget.document().findBlockByLineNumber( start).position() #select the text to move cursor.setPosition(startPosition) cursor.movePosition(QTextCursor.StartOfLine) cursor.movePosition(QTextCursor.Down, QTextCursor.KeepAnchor, end - start) cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) text_to_move = cursor.selectedText() #begin Undo feature cursor.beginEditBlock() #Remove cursor.removeSelectedText() cursor.deleteChar() #Insert text and breakline cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.MoveAnchor) cursor.insertText('\n' + text_to_move) #end Undo feature cursor.endEditBlock() #Restore the user selection startPosition = editorWidget.document().findBlockByLineNumber( (start + 1)).position() cursor.setPosition(startPosition) cursor.movePosition(QTextCursor.StartOfLine) cursor.movePosition(QTextCursor.Down, QTextCursor.KeepAnchor, end - start) cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) editorWidget.setTextCursor(cursor) else: block_next = block_actual.next() tempLine = block_actual.text() cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor) #begin Undo feature cursor.beginEditBlock() cursor.insertText(block_next.text()) cursor.movePosition(QTextCursor.Down, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor) cursor.insertText(tempLine) #end Undo feature cursor.endEditBlock() editorWidget.moveCursor(QTextCursor.Down, QTextCursor.MoveAnchor) def remove_line(editorWidget): cursor = editorWidget.textCursor() cursor.beginEditBlock() if cursor.hasSelection(): start, end = get_start_end_selection(editorWidget, cursor) if start == end: # same block selection cursor.movePosition(QTextCursor.StartOfLine) cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) cursor.removeSelectedText() cursor.deleteChar() else: # multiple blocks selection selection_start = cursor.selectionStart() selection_end = cursor.selectionEnd() cursor.setPosition(selection_end) if cursor.atBlockStart(): end = end - 1 cursor.setPosition(selection_start) cursor.movePosition(QTextCursor.StartOfLine) cursor.movePosition(QTextCursor.Down, QTextCursor.KeepAnchor, end - start) cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) cursor.removeSelectedText() cursor.deleteChar() else: cursor.select(QTextCursor.LineUnderCursor) cursor.removeSelectedText() cursor.deleteChar() cursor.endEditBlock() def duplicate(editorWidget): cursor = editorWidget.textCursor() cursor.beginEditBlock() if cursor.hasSelection(): start, end = get_start_end_selection(editorWidget, cursor) #get the position of the line startPosition = editorWidget.document().findBlockByLineNumber( start).position() #select the text to move cursor.setPosition(startPosition) cursor.movePosition(QTextCursor.StartOfLine) cursor.movePosition(QTextCursor.Down, QTextCursor.KeepAnchor, end - start) cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) text_to_move = cursor.selectedText() #Insert text and breakline cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.MoveAnchor) cursor.insertText('\n' + text_to_move) else: block = cursor.block() cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.MoveAnchor) cursor.insertBlock() cursor.insertText(block.text()) cursor.endEditBlock() def uncomment(editorWidget): #cursor is a COPY all changes do not affect the QPlainTextEdit's cursor!!! cursor = editorWidget.textCursor() block_start = editorWidget.document().findBlock( cursor.selectionStart()) block_end = editorWidget.document().findBlock( cursor.selectionEnd()).next() lang = file_manager.get_file_extension(editorWidget.ID) key = settings.EXTENSIONS.get(lang, 'python') same_line = (block_start == block_end.previous()) funcs = {'comment': uncomment_single_line, 'multiline_comment': uncomment_multiple_lines} comment_line_wildcard = settings.SYNTAX[key].get('comment', []) comment_multi_wildcard = settings.SYNTAX[key].get('multiline_comment', {}) option = 'multiline_comment' comment_wildcard = comment_multi_wildcard if ((same_line and comment_line_wildcard) or not (same_line or comment_multi_wildcard)): option = 'comment' comment_wildcard = comment_line_wildcard f = funcs[option] f(cursor, block_start, block_end, comment_wildcard) def uncomment_single_line(cursor, block_start, block_end, comment_wildcard): """Uncoment one or more lines when one line symbol is supported""" comment_wildcard = comment_wildcard[0] # Start block undo cursor.beginEditBlock() while (block_start != block_end): # Find the position of the comment in the line comment_position = block_start.text().find( comment_wildcard[0]) if block_start.text().startswith( " " * comment_position + comment_wildcard[0]) or ( settings.USE_TABS and block_start.text().startswith( "\t" * comment_position + comment_wildcard[0])): cursor.setPosition(block_start.position() + comment_position) cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, len(comment_wildcard)) cursor.removeSelectedText() block_start = block_start.next() cursor.endEditBlock() def uncomment_multiple_lines(cursor, block_start, block_end, comment_wildcard): """Uncomment one or more lines when multiple lines symbols is supported""" #begin Undo feature cursor.beginEditBlock() #Remove start symbol comment if correspond if block_start.previous().text().startswith(comment_wildcard['open']): block_start = block_start.previous() delete_lines_selected(cursor, block_start) if block_start.text().startswith(comment_wildcard['open']): delete_lines_selected(cursor, block_start) #Remove end symbol comment if correspond if block_end.previous().text().startswith(comment_wildcard['close']): block_end = block_end.previous() delete_lines_selected(cursor, block_end) if block_end.text().startswith(comment_wildcard['close']): delete_lines_selected(cursor, block_end) cursor.endEditBlock() def delete_lines_selected(cursor, block_actual): cursor.setPosition(block_actual.position()) cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) cursor.removeSelectedText() cursor.deleteChar() def comment(editorWidget): """ This method comment one or more lines of code """ #cursor is a COPY all changes do not affect the QPlainTextEdit's cursor!!! cursor = editorWidget.textCursor() block_start = editorWidget.document().findBlock( cursor.selectionStart()) block_end = editorWidget.document().findBlock( cursor.selectionEnd()).next() lang = file_manager.get_file_extension(editorWidget.ID) key = settings.EXTENSIONS.get(lang, 'python') same_line = (block_start == block_end.previous()) funcs = {'comment': comment_single_line, 'multiline_comment': comment_multiple_lines} comment_line_wildcard = settings.SYNTAX[key].get('comment', []) comment_multi_wildcard = settings.SYNTAX[key].get('multiline_comment', {}) option = 'multiline_comment' comment_wildcard = comment_multi_wildcard if ((same_line and comment_line_wildcard) or not (same_line or comment_multi_wildcard)): option = 'comment' comment_wildcard = comment_line_wildcard f = funcs[option] f(cursor, block_start, block_end, comment_wildcard) def comment_single_line(cursor, block_start, block_end, comment_wildcard): """Comment one or more lines with single comment symbol""" #Start a undo block cursor.beginEditBlock() #Move the COPY cursor while block_start != block_end: cursor.setPosition(block_start.position()) block_number = block_start.blockNumber() cursor.select(QTextCursor.WordUnderCursor) word = cursor.selectedText() cursor.movePosition(QTextCursor.StartOfBlock) if not word: cursor.movePosition(QTextCursor.WordRight) if block_number == cursor.blockNumber(): cursor.insertText(comment_wildcard[0]) block_start = block_start.next() #End a undo block cursor.endEditBlock() def comment_multiple_lines(cursor, block_start, block_end, comment_wildcard): """Comment one or more lines with multiple comment symbol""" #select the text to comment cursor.setPosition(block_start.position()) cursor.movePosition(QTextCursor.StartOfLine) cursor.movePosition(QTextCursor.Down, QTextCursor.KeepAnchor, block_end.previous().firstLineNumber() - block_start.firstLineNumber()) cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) text_to_comment = cursor.selectedText() #begin Undo feature cursor.beginEditBlock() #Remove cursor.removeSelectedText() cursor.insertText(comment_wildcard['open']) cursor.insertText('\n' + text_to_comment) cursor.insertText('\n' + comment_wildcard['close']) #End a undo block cursor.endEditBlock() def check_for_assistance_completion(editorWidget, line): #This will be possible when code completion is working global patClass if patClass.match(line) and editorWidget.lang == 'python': source = editorWidget.toPlainText() source = source.encode(editorWidget.encoding) symbols = introspection.obtain_symbols(source) clazzName = [name for name in re.split("(\\s)*class(\\s)+|:|\(", line) if name is not None and name.strip()][0] clazz_key = [item for item in symbols.get('classes', []) if item.startswith(clazzName)] if clazz_key: clazz = symbols['classes'][clazz_key['lineno']] if [init for init in clazz['members']['functions'] if init.startswith('__init__')]: return editorWidget.textCursor().insertText('\n') indent = get_indentation( line, editorWidget.indent, editorWidget.useTabs) editorWidget.textCursor().insertText(indent + 'def __init__(self):\n') if editorWidget.useTabs: indent += '\t' else: indent += ' ' * editorWidget.indent if line.find('(') != -1: classes = line.split('(') parents = [] if len(classes) > 1: parents += classes[1].split(',') if len(parents) > 0 and 'object):' not in parents: editorWidget.textCursor().insertText( indent + "super({0}, self).__init__()\n".format(clazzName)) editorWidget.textCursor().insertText(indent) else: editorWidget.textCursor().insertText(indent) else: editorWidget.textCursor().insertText(indent) ninja-ide-2.3/ninja_ide/gui/editor/highlighter.py000066400000000000000000000567641216641277400221120ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . # based on Python Syntax highlighting from: # http://diotavelli.net/PyQtWiki/Python%20syntax%20highlighting from __future__ import absolute_import from __future__ import unicode_literals import re from PyQt4.QtGui import QColor from PyQt4.QtGui import QTextCharFormat from PyQt4.QtGui import QFont from PyQt4.QtGui import QSyntaxHighlighter from PyQt4.QtCore import QThread from PyQt4.QtCore import QRegExp from PyQt4.QtCore import SIGNAL from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.gui.editor import syntax_highlighter def format(color, style=''): """Return a QTextCharFormat with the given attributes.""" _color = QColor() _color.setNamedColor(color) _format = QTextCharFormat() _format.setFontFamily(settings.FONT_FAMILY) _format.setForeground(_color) if 'bold' in style: _format.setFontWeight(QFont.Bold) if 'italic' in style: _format.setFontItalic(True) return _format # Syntax styles that can be shared by all languages STYLES = {} SDEFAULTS = ( ("keyword", "keyword", "bold"), ("operator", "operator", None), ("brace", "brace", None), ("definition", "definition", "bold"), ("string", "string", None), ("string2", "string2", None), ("comment", "comment", "italic"), ("properObject", "properObject", None), ("numbers", "numbers", None), ("spaces", "spaces", None), ("extras", "extras", "bold"), ("selectedWord", "selected-word", None), ) def restyle(scheme): """Reset the style for each highlighting item when the scheme change.""" rescs = resources.COLOR_SCHEME global STYLES for stkw, srkw, default in SDEFAULTS: if default: STYLES[stkw] = format(scheme.get(srkw, rescs[srkw]), default) else: STYLES[stkw] = format(scheme.get(srkw, rescs[srkw])) class Highlighter(QSyntaxHighlighter): """Syntax Highlighter for NINJA-IDE.""" # braces braces = ['\\(', '\\)', '\\{', '\\}', '\\[', '\\]'] def __init__(self, document, lang=None, scheme=None, errors=None, pep8=None, migration=None): QSyntaxHighlighter.__init__(self, document) self.highlight_function = self.realtime_highlight self.errors = errors self.pep8 = pep8 self.migration = migration self._old_search = None self.selected_word_lines = [] self.visible_limits = (0, 50) self._styles = {} if lang is not None: self.apply_highlight(lang, scheme) def sanitize(self, word): """Sanitize the string to avoid problems with the regex.""" return word.replace('\\', '\\\\') def apply_highlight(self, lang, scheme=None, syntax=None): """Set the rules that will decide what to highlight and how.""" if syntax is None: langSyntax = settings.SYNTAX.get(lang, {}) else: langSyntax = syntax if scheme is not None: restyle(scheme) keywords = langSyntax.get('keywords', []) operators = langSyntax.get('operators', []) extras = langSyntax.get('extras', []) rules = [] # Keyword, operator, brace and extras rules keyword_pattern = '(^|[^\w\.]{1})(%s)([^\w]{1}|$)' rules += [(keyword_pattern % w, 2, STYLES['keyword']) for w in keywords] rules += [(r'%s' % o, 0, STYLES['operator']) for o in operators] rules += [(r'%s' % b, 0, STYLES['brace']) for b in Highlighter.braces] rules += [(keyword_pattern % e, 2, STYLES['extras']) for e in extras] # All other rules proper = langSyntax.get('properObject', None) if proper is not None: proper = r'\b%s\b' % str(proper[0]) rules += [(proper, 0, STYLES['properObject'])] rules.append((r'__\w+__', 0, STYLES['properObject'])) # Classes and functions definition = langSyntax.get('definition', []) for de in definition: expr = r'\b%s\b\s*(\w+)' % de rules.append((expr, 1, STYLES['definition'])) # Numeric literals rules += [ (r'\b[+-]?[0-9]+[lL]?\b', 0, STYLES['numbers']), (r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, STYLES['numbers']), (r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0, STYLES['numbers']), ] # Regular expressions regex = langSyntax.get('regex', []) for reg in regex: expr = reg[0] color = resources.COLOR_SCHEME['extras'] style = '' if len(reg) > 1: if reg[1] in resources.CUSTOM_SCHEME: color = resources.CUSTOM_SCHEME[reg[1]] elif reg[1] in resources.COLOR_SCHEME: color = resources.COLOR_SCHEME[reg[1]] if len(reg) > 2: style = reg[2] rules.append((expr, 0, format(color, style))) # Strings stringChar = langSyntax.get('string', []) for sc in stringChar: expr = r'"[^"\\]*(\\.[^"\\]*)*"' if sc == '"' \ else r"'[^'\\]*(\\.[^'\\]*)*'" rules.append((expr, 0, STYLES['string'])) # Comments comments = langSyntax.get('comment', []) for co in comments: expr = co + '[^\\n]*' rules.append((expr, 0, STYLES['comment'])) # Multi-line strings (expression, flag, style) # FIXME: The triple-quotes in these two lines will mess up the # syntax highlighting from this point onward self.tri_single = (QRegExp("'''"), 1, STYLES["string2"]) self.tri_double = (QRegExp('"""'), 2, STYLES['string2']) multi = langSyntax.get('multiline_comment', []) if multi: self.multi_start = (QRegExp( re.escape(multi['open'])), STYLES['comment']) self.multi_end = (QRegExp( re.escape(multi['close'])), STYLES['comment']) else: self.multi_start = None # Build a QRegExp for each pattern self.rules = [(QRegExp(pat), index, fmt) for (pat, index, fmt) in rules] self.selected_word_pattern = None #Apply Highlight to the document... (when colors change) self.rehighlight() def set_selected_word(self, word, partial=True): """Set the word to highlight.""" # partial = True for new highlighter compatibility hl_worthy = len(word) > 2 if hl_worthy: self.selected_word_pattern = QRegExp( r'\b%s\b' % self.sanitize(word)) else: self.selected_word_pattern = None suffix = "(?![A-Za-z_\d])" prefix = "(?= 0: # We actually want the index of the nth match index = expression.pos(nth) length = len(expression.cap(nth)) char_format = highlight_errors(char_format, user_data) if (self.format(index) != STYLES['string']): self.setFormat(index, length, char_format) if char_format == STYLES['string']: hls.append((index, index + length)) user_data.add_str_group(index, index + length) elif char_format == STYLES['comment']: user_data.comment_start_at(index) index = expression.indexIn(text, index + length) self.setCurrentBlockState(0) if not self.multi_start: # Do multi-line strings in_multiline = self.match_multiline(text, *self.tri_single, hls=hls, highlight_errors=highlight_errors, user_data=user_data) if not in_multiline: in_multiline = self.match_multiline(text, *self.tri_double, hls=hls, highlight_errors=highlight_errors, user_data=user_data) else: # Do multi-line comment self.comment_multiline(text, self.multi_end[0], *self.multi_start) #Highlight selected word if self.selected_word_pattern is not None: index = self.selected_word_pattern.indexIn(text, 0) while index >= 0: index = self.selected_word_pattern.pos(0) length = len(self.selected_word_pattern.cap(0)) char_format = self.format(index) color = STYLES['selectedWord'].foreground().color() color.setAlpha(100) char_format.setBackground(color) self.setFormat(index, length, char_format) index = self.selected_word_pattern.indexIn( text, index + length) #Spaces expression = QRegExp('\s+') index = expression.indexIn(text, 0) while index >= 0: index = expression.pos(0) length = len(expression.cap(0)) char_format = STYLES['spaces'] char_format = highlight_errors(char_format, user_data) self.setFormat(index, length, char_format) index = expression.indexIn(text, index + length) block.setUserData(user_data) def _rehighlight_lines(self, lines): """If the document is valid, highlight the list of lines received.""" if self.document() is None: return for line in lines: block = self.document().findBlockByNumber(line) self.rehighlightBlock(block) def _get_errors_lines(self): """Return the number of lines that contains errors to highlight.""" errors_lines = [] block = self.document().begin() while block.isValid(): user_data = syntax_highlighter.get_user_data(block) if user_data.error: errors_lines.append(block.blockNumber()) block = block.next() return errors_lines def rehighlight_lines(self, lines, errors=True): """Rehighlight the lines for errors or selected words.""" if errors: errors_lines = self._get_errors_lines() refresh_lines = set(lines + errors_lines) else: refresh_lines = set(lines + self.selected_word_lines) self.selected_word_lines = lines self._rehighlight_lines(refresh_lines) def match_multiline(self, text, delimiter, in_state, style, hls=[], highlight_errors=lambda x: x, user_data=None): """Do highlighting of multi-line strings. ``delimiter`` should be a ``QRegExp`` for triple-single-quotes or triple-double-quotes, and ``in_state`` should be a unique integer to represent the corresponding state changes when inside those strings. Returns True if we're still inside a multi-line string when this function is finished. """ # If inside triple-single quotes, start at 0 if self.previousBlockState() == in_state: start = 0 add = 0 # Otherwise, look for the delimiter on this line else: start = delimiter.indexIn(text) # Move past this match add = delimiter.matchedLength() # As long as there's a delimiter match on this line... while start >= 0: # Look for the ending delimiter end = delimiter.indexIn(text, start + add) # Ending delimiter on this line? if end >= add: length = end - start + add + delimiter.matchedLength() self.setCurrentBlockState(0) # No; multi-line string else: self.setCurrentBlockState(in_state) length = len(text) - start + add st_fmt = self.format(start) start_collides = [pos for pos in hls if pos[0] < start < pos[1]] # Apply formatting if ((st_fmt != STYLES['comment']) or ((st_fmt == STYLES['comment']) and (self.previousBlockState() != 0))) and \ (len(start_collides) == 0): style = highlight_errors(style, user_data) self.setFormat(start, length, style) else: self.setCurrentBlockState(0) # Look for the next match start = delimiter.indexIn(text, start + length) # Return True if still inside a multi-line string, False otherwise if self.currentBlockState() == in_state: return True else: return False def comment_multiline(self, text, delimiter_end, delimiter_start, style): """Process the beggining and end of a multiline comment.""" startIndex = 0 if self.previousBlockState() != 1: startIndex = delimiter_start.indexIn(text) while startIndex >= 0: endIndex = delimiter_end.indexIn(text, startIndex) commentLength = 0 if endIndex == -1: self.setCurrentBlockState(1) commentLength = len(text) - startIndex else: commentLength = endIndex - startIndex + \ delimiter_end.matchedLength() self.setFormat(startIndex, commentLength, style) startIndex = delimiter_start.indexIn(text, startIndex + commentLength) class HighlightParserThread(QThread): """Thread that collect the highlighting info to the current file.""" def __init__(self, highlighter): super(HighlightParserThread, self).__init__() self._highlighter = highlighter def run(self): """Execute this rules in another thread to avoid blocking the ui.""" styles = {} self.msleep(300) block = self._highlighter.document().begin() while block.blockNumber() != -1: text = block.text() formats = [] for expression, nth, char_format in self._highlighter.rules: index = expression.indexIn(text, 0) while index >= 0: # We actually want the index of the nth match index = expression.pos(nth) length = len(expression.cap(nth)) formats.append((index, length, char_format)) index = expression.indexIn(text, index + length) #Spaces expression = QRegExp('\s+') index = expression.indexIn(text, 0) while index >= 0: index = expression.pos(0) length = len(expression.cap(0)) formats.append((index, length, STYLES['spaces'])) index = expression.indexIn(text, index + length) styles[block.blockNumber()] = formats block = block.next() self.emit(SIGNAL("highlightingDetected(PyQt_PyObject)"), styles) class EmpyHighlighter(QSyntaxHighlighter): """Dummy highlighter to be used when the current file is not recognized.""" def __init__(self, document): super(EmpyHighlighter, self).__init__(document) self.highlight_function = lambda x: None def apply_highlight(self, *args, **kwargs): pass def set_selected_word(self, *args, **kwargs): pass def realtime_highlight(self, *args, **kwargs): pass def set_open_visible_area(self, *args, **kwargs): pass def open_highlight(self, *args, **kwargs): pass def async_highlight(self, *args, **kwargs): pass def highlightBlock(self, text): pass def rehighlight_lines(self, lines, errors=True): pass ninja-ide-2.3/ninja_ide/gui/editor/migration_2to3.py000066400000000000000000000064661216641277400224460ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import os import subprocess from PyQt4.QtCore import QThread from ninja_ide.core import file_manager from ninja_ide.core import settings class MigrationTo3(QThread): def __init__(self, editor): QThread.__init__(self) self._editor = editor self._path = '' self.migration_data = {} if settings.IS_WINDOWS and settings.PYTHON_PATH_CONFIGURED_BY_USER: tool_path = os.path.join(os.path.dirname(settings.PYTHON_PATH), 'Tools', 'Scripts', '2to3.py') self._command = [settings.PYTHON_PATH, tool_path] else: self._command = ['2to3'] def check_style(self): if not self.isRunning() and settings.VALID_2TO3: self._path = self._editor.ID self.start() def run(self): self.sleep(1) exts = settings.SYNTAX.get('python')['extension'] file_ext = file_manager.get_file_extension(self._path) if file_ext in exts: self.migration_data = {} lineno = 0 lines_to_remove = [] lines_to_add = [] parsing_adds = False try: output = subprocess.check_output(self._command + [self._path]) output = output.split('\n') except OSError: settings.VALID_2TO3 = False return for line in output[2:]: if line.startswith('+'): lines_to_add.append(line) parsing_adds = True continue if parsing_adds: # Add in migration removes = '\n'.join([liner for _, liner in lines_to_remove]) adds = '\n'.join(lines_to_add) message = self.tr( 'The actual code look like this:\n%s\n\n' 'For Python3 support should look like:\n%s' % (removes, adds)) lineno = -1 for nro, _ in lines_to_remove: if lineno == -1: lineno = nro self.migration_data[nro] = (message, lineno) parsing_adds = False lines_to_add = [] lines_to_remove = [] if line.startswith('-'): lines_to_remove.append((lineno, line)) lineno += 1 if line.startswith('@@'): lineno = int(line[line.index('-') + 1:line.index(',')]) - 1 ninja-ide-2.3/ninja_ide/gui/editor/minimap.py000066400000000000000000000163521216641277400212330ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . import sys from PyQt4.QtGui import QFrame from PyQt4.QtGui import QPlainTextEdit from PyQt4.QtGui import QTextOption from PyQt4.QtGui import QGraphicsOpacityEffect from PyQt4.QtGui import QFontMetrics from PyQt4.QtGui import QPainter from PyQt4.QtGui import QColor from PyQt4.QtGui import QPen from PyQt4.QtGui import QBrush from PyQt4.QtCore import Qt from PyQt4.QtCore import QPropertyAnimation from ninja_ide import resources from ninja_ide.core import settings #QGraphicsOpacityEffect doesn't work in mac cause a Qt Issue: QTBUG-15367 ACTIVATE_OPACITY = True if sys.platform != 'darwin' else False class MiniMap(QPlainTextEdit): def __init__(self, parent): super(MiniMap, self).__init__(parent) self.setWordWrapMode(QTextOption.NoWrap) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setReadOnly(True) self.setCenterOnScroll(True) self.setMouseTracking(True) self.viewport().setCursor(Qt.PointingHandCursor) self.setTextInteractionFlags(Qt.NoTextInteraction) self._parent = parent self.highlighter = None self.lines_count = 0 if ACTIVATE_OPACITY: self.goe = QGraphicsOpacityEffect() self.setGraphicsEffect(self.goe) self.goe.setOpacity(settings.MINIMAP_MIN_OPACITY) self.animation = QPropertyAnimation(self.goe, "opacity") self.slider = SliderArea(self) self.slider.show() def __calculate_max(self): line_height = self._parent.cursorRect().height() if line_height > 0: self.lines_count = self._parent.viewport().height() / line_height self.slider.update_position() self.update_visible_area() def set_code(self, source): self.setPlainText(source) self.__calculate_max() def adjust_to_parent(self): self.setFixedHeight(self._parent.height()) self.setFixedWidth(self._parent.width() * settings.SIZE_PROPORTION) x = self._parent.width() - self.width() self.move(x, 0) fontsize = int(self.width() / settings.MARGIN_LINE) if fontsize < 1: fontsize = 1 font = self.document().defaultFont() font.setPointSize(fontsize) self.setFont(font) self.__calculate_max() def update_visible_area(self): if not self.slider.pressed: line_number = self._parent.firstVisibleBlock().blockNumber() block = self.document().findBlockByLineNumber(line_number) cursor = self.textCursor() cursor.setPosition(block.position()) rect = self.cursorRect(cursor) self.setTextCursor(cursor) self.slider.move_slider(rect.y()) def enterEvent(self, event): if ACTIVATE_OPACITY: self.animation.setDuration(300) self.animation.setStartValue(settings.MINIMAP_MIN_OPACITY) self.animation.setEndValue(settings.MINIMAP_MAX_OPACITY) self.animation.start() def leaveEvent(self, event): if ACTIVATE_OPACITY: self.animation.setDuration(300) self.animation.setStartValue(settings.MINIMAP_MAX_OPACITY) self.animation.setEndValue(settings.MINIMAP_MIN_OPACITY) self.animation.start() def mousePressEvent(self, event): super(MiniMap, self).mousePressEvent(event) cursor = self.cursorForPosition(event.pos()) self._parent.jump_to_line(cursor.blockNumber()) def resizeEvent(self, event): super(MiniMap, self).resizeEvent(event) self.slider.update_position() def scroll_area(self, pos_parent, pos_slider): pos_parent.setY(pos_parent.y() - pos_slider.y()) cursor = self.cursorForPosition(pos_parent) self._parent.verticalScrollBar().setValue(cursor.blockNumber()) def wheelEvent(self, event): super(MiniMap, self).wheelEvent(event) self._parent.wheelEvent(event) class SliderArea(QFrame): def __init__(self, parent): super(SliderArea, self).__init__(parent) self._parent = parent self.setMouseTracking(True) self.setCursor(Qt.OpenHandCursor) color = resources.CUSTOM_SCHEME.get('current-line', resources.COLOR_SCHEME['current-line']) if ACTIVATE_OPACITY: self.setStyleSheet("background: %s;" % color) self.goe = QGraphicsOpacityEffect() self.setGraphicsEffect(self.goe) self.goe.setOpacity(settings.MINIMAP_MAX_OPACITY / 2) else: self.setStyleSheet("background: transparent;") self.pressed = False self.__scroll_margins = None def paintEvent(self, event): """Paint over the widget to overlay its content.""" if not ACTIVATE_OPACITY: painter = QPainter() painter.begin(self) painter.setRenderHint(QPainter.TextAntialiasing, True) painter.setRenderHint(QPainter.Antialiasing, True) painter.fillRect(event.rect(), QBrush( QColor(255, 255, 255, 80))) painter.setPen(QPen(Qt.NoPen)) painter.end() super(SliderArea, self).paintEvent(event) def update_position(self): font_size = QFontMetrics(self._parent.font()).height() height = self._parent.lines_count * font_size self.setFixedHeight(height) self.setFixedWidth(self._parent.width()) self.__scroll_margins = (height, self._parent.height() - height) def move_slider(self, y): self.move(0, y) def mousePressEvent(self, event): super(SliderArea, self).mousePressEvent(event) self.pressed = True self.setCursor(Qt.ClosedHandCursor) def mouseReleaseEvent(self, event): super(SliderArea, self).mouseReleaseEvent(event) self.pressed = False self.setCursor(Qt.OpenHandCursor) def mouseMoveEvent(self, event): super(SliderArea, self).mouseMoveEvent(event) if self.pressed: pos = self.mapToParent(event.pos()) y = pos.y() - (self.height() / 2) if y < 0: y = 0 if y < self.__scroll_margins[0]: self._parent.verticalScrollBar().setSliderPosition( self._parent.verticalScrollBar().sliderPosition() - 2) elif y > self.__scroll_margins[1]: self._parent.verticalScrollBar().setSliderPosition( self._parent.verticalScrollBar().sliderPosition() + 2) self.move(0, y) self._parent.scroll_area(pos, event.pos()) ninja-ide-2.3/ninja_ide/gui/editor/pep8_checker.py000066400000000000000000000052621216641277400221370ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals from PyQt4.QtCore import QThread from ninja_ide.core import file_manager from ninja_ide.core import settings from ninja_ide.dependencies import pep8mod class Pep8Checker(QThread): def __init__(self, editor): QThread.__init__(self) self._editor = editor self._path = '' self._encoding = '' self.pep8checks = {} def check_style(self): if not self.isRunning(): self._path = self._editor.ID self._encoding = self._editor.encoding self.start() def reset(self): self.pep8checks = {} def run(self): self.sleep(1) exts = settings.SYNTAX.get('python')['extension'] file_ext = file_manager.get_file_extension(self._path) if file_ext in exts: self.reset() source = self._editor.get_text() tempData = pep8mod.run_check(self._path, source) i = 0 while i < len(tempData): lineno = -1 try: offset = 2 + len(file_ext) startPos = tempData[i].find('.%s:' % file_ext) + offset endPos = tempData[i].find(':', startPos) lineno = int(tempData[i][startPos:endPos]) - 1 error = tempData[i][tempData[i].find( ':', endPos + 1) + 2:] line = '\n'.join( [error, tempData[i + 1], tempData[i + 2]]) except Exception: line = '' finally: i += 3 if line and lineno > -1: if lineno not in self.pep8checks: self.pep8checks[lineno] = [line] else: message = self.pep8checks[lineno] message += [line] self.pep8checks[lineno] = message else: self.reset() ninja-ide-2.3/ninja_ide/gui/editor/python_syntax.py000066400000000000000000000171461216641277400225320ustar00rootroot00000000000000syntax = {'formats': {'builtin': '%(syntax_builtin)s', 'comment': '%(syntax_comment)s', 'hexnumber': '%(syntax_number)s', 'keyword': '%(syntax_keyword)s', 'number': '%(syntax_number)s', 'proper_object': '%(syntax_proper_object)s', 'operators': '%(syntax_operators)s', 'pending': '%(syntax_pending)s', 'braces': '%(syntax_braces)s', 'definition': '%(syntax_definition)s', 'highlight_word': '%(syntax_highlight_word)s', 'string': '%(syntax_string)s'}, 'partitions': [('pending', "#FIXME", "\n"), ('pending', "#TODO", "\n"), ('pending', "#WTF", "\n"), ('comment', '#', '\n'), ('string', "[bruBRU]?'''", "(?', ]), ('keyword', ['def', 'class', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield'], '(^|[^\\.\\w])??(?. from __future__ import absolute_import import math import re from PyQt4.QtGui import QWidget from PyQt4.QtGui import QBrush from PyQt4.QtGui import QLinearGradient from PyQt4.QtGui import QPixmap from PyQt4.QtGui import QColor from PyQt4.QtGui import QPolygonF from PyQt4.QtGui import QFontMetrics from PyQt4.QtGui import QPainter from PyQt4.QtCore import Qt from PyQt4.QtCore import QPointF from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.gui.editor import helpers #based on: http://john.nachtimwald.com/2009/08/15/qtextedit-with-line-numbers/ #(MIT license) class SidebarWidget(QWidget): def __init__(self, editor): QWidget.__init__(self, editor) self.edit = editor self.highest_line = 0 self.foldArea = 15 self.rightArrowIcon = QPixmap() self.downArrowIcon = QPixmap() self.pat = re.compile( "(\s)*def |(\s)*class |(\s)*if |(\s)*while |" "(\s)*else:|(\s)*elif |(\s)*for |" "(\s)*try:|(\s)*except:|(\s)*except |(\s)*#begin-fold:") self.patNotPython = re.compile('(\s)*#begin-fold:|(.)*{') self._foldedBlocks = [] self._breakpoints = [] self._bookmarks = [] self._pep8Lines = [] self._errorsLines = [] self._migrationLines = [] def update_area(self): maxLine = math.ceil(math.log10(self.edit.blockCount())) width = QFontMetrics( self.edit.document().defaultFont()).width('0' * int(maxLine)) \ + 10 + self.foldArea if self.width() != width: self.setFixedWidth(width) self.edit.setViewportMargins(width, 0, 0, 0) self.update() def update(self, *args): QWidget.update(self, *args) def pep8_check_lines(self, lines): self._pep8Lines = lines def static_errors_lines(self, lines): self._errorsLines = lines def migration_lines(self, lines): self._migrationLines = lines def code_folding_event(self, lineNumber): if self._is_folded(lineNumber): self._fold(lineNumber) else: self._unfold(lineNumber) self.edit.update() self.update() def _fold(self, lineNumber): startBlock = self.edit.document().findBlockByNumber(lineNumber - 1) endPos = self._find_fold_closing(startBlock) endBlock = self.edit.document().findBlockByNumber(endPos) block = startBlock.next() while block.isValid() and block != endBlock: block.setVisible(False) block.setLineCount(0) block = block.next() self._foldedBlocks.append(startBlock.blockNumber()) self.edit.document().markContentsDirty(startBlock.position(), endPos) def _unfold(self, lineNumber): startBlock = self.edit.document().findBlockByNumber(lineNumber - 1) endPos = self._find_fold_closing(startBlock) endBlock = self.edit.document().findBlockByNumber(endPos) block = startBlock.next() while block.isValid() and block != endBlock: block.setVisible(True) block.setLineCount(block.layout().lineCount()) endPos = block.position() + block.length() if block.blockNumber() in self._foldedBlocks: close = self._find_fold_closing(block) block = self.edit.document().findBlockByNumber(close) else: block = block.next() self._foldedBlocks.remove(startBlock.blockNumber()) self.edit.document().markContentsDirty(startBlock.position(), endPos) def _is_folded(self, line): block = self.edit.document().findBlockByNumber(line) if not block.isValid(): return False return block.isVisible() def _find_fold_closing(self, block): text = block.text() pat = re.compile('(\s)*#begin-fold:') patBrace = re.compile('(.)*{$') if pat.match(text): return self._find_fold_closing_label(block) elif patBrace.match(text): return self._find_fold_closing_brace(block) spaces = helpers.get_leading_spaces(text) pat = re.compile('^\s*$|^\s*#') block = block.next() while block.isValid(): text2 = block.text() if not pat.match(text2): spacesEnd = helpers.get_leading_spaces(text2) if len(spacesEnd) <= len(spaces): if pat.match(block.previous().text()): return block.previous().blockNumber() else: return block.blockNumber() block = block.next() return block.previous().blockNumber() def _find_fold_closing_label(self, block): text = block.text() label = text.split(':')[1] block = block.next() pat = re.compile('\s*#end-fold:' + label) while block.isValid(): if pat.match(block.text()): return block.blockNumber() + 1 block = block.next() return block.blockNumber() def _find_fold_closing_brace(self, block): block = block.next() openBrace = 1 while block.isValid(): openBrace += block.text().count('{') openBrace -= block.text().count('}') if openBrace == 0: return block.blockNumber() + 1 elif openBrace < 0: return block.blockNumber() block = block.next() return block.blockNumber() def paintEvent(self, event): page_bottom = self.edit.viewport().height() font_metrics = QFontMetrics(self.edit.document().defaultFont()) current_block = self.edit.document().findBlock( self.edit.textCursor().position()) pattern = self.pat if self.edit.lang == "python" else self.patNotPython painter = QPainter(self) background = resources.CUSTOM_SCHEME.get('sidebar-background', resources.COLOR_SCHEME['sidebar-background']) foreground = resources.CUSTOM_SCHEME.get('sidebar-foreground', resources.COLOR_SCHEME['sidebar-foreground']) pep8color = resources.CUSTOM_SCHEME.get('pep8-underline', resources.COLOR_SCHEME['pep8-underline']) errorcolor = resources.CUSTOM_SCHEME.get('error-underline', resources.COLOR_SCHEME['error-underline']) migrationcolor = resources.CUSTOM_SCHEME.get('migration-underline', resources.COLOR_SCHEME['migration-underline']) painter.fillRect(self.rect(), QColor(background)) block = self.edit.firstVisibleBlock() viewport_offset = self.edit.contentOffset() line_count = block.blockNumber() painter.setFont(self.edit.document().defaultFont()) while block.isValid(): line_count += 1 # The top left position of the block in the document position = self.edit.blockBoundingGeometry(block).topLeft() + \ viewport_offset # Check if the position of the block is outside of the visible area if position.y() > page_bottom: break # Set the Painter Pen depending on special lines error = False if settings.CHECK_STYLE and \ ((line_count - 1) in self._pep8Lines): painter.setPen(QColor(pep8color)) font = painter.font() font.setItalic(True) font.setUnderline(True) painter.setFont(font) error = True elif settings.FIND_ERRORS and \ ((line_count - 1) in self._errorsLines): painter.setPen(QColor(errorcolor)) font = painter.font() font.setItalic(True) font.setUnderline(True) painter.setFont(font) error = True elif settings.SHOW_MIGRATION_TIPS and \ ((line_count - 1) in self._migrationLines): painter.setPen(QColor(migrationcolor)) font = painter.font() font.setItalic(True) font.setUnderline(True) painter.setFont(font) error = True else: painter.setPen(QColor(foreground)) # We want the line number for the selected line to be bold. bold = False if block == current_block: bold = True font = painter.font() font.setBold(True) painter.setFont(font) # Draw the line number right justified at the y position of the # line. 3 is a magic padding number. drawText(x, y, text). if block.isVisible(): painter.drawText(self.width() - self.foldArea - font_metrics.width(str(line_count)) - 3, round(position.y()) + font_metrics.ascent() + font_metrics.descent() - 1, str(line_count)) # Remove the bold style if it was set previously. if bold: font = painter.font() font.setBold(False) painter.setFont(font) if error: font = painter.font() font.setItalic(False) font.setUnderline(False) painter.setFont(font) block = block.next() self.highest_line = line_count #Code Folding xofs = self.width() - self.foldArea painter.fillRect(xofs, 0, self.foldArea, self.height(), QColor(resources.CUSTOM_SCHEME.get('fold-area', resources.COLOR_SCHEME['fold-area']))) if self.foldArea != self.rightArrowIcon.width(): polygon = QPolygonF() self.rightArrowIcon = QPixmap(self.foldArea, self.foldArea) self.rightArrowIcon.fill(Qt.transparent) self.downArrowIcon = QPixmap(self.foldArea, self.foldArea) self.downArrowIcon.fill(Qt.transparent) polygon.append(QPointF(self.foldArea * 0.4, self.foldArea * 0.25)) polygon.append(QPointF(self.foldArea * 0.4, self.foldArea * 0.75)) polygon.append(QPointF(self.foldArea * 0.8, self.foldArea * 0.5)) iconPainter = QPainter(self.rightArrowIcon) iconPainter.setRenderHint(QPainter.Antialiasing) iconPainter.setPen(Qt.NoPen) iconPainter.setBrush(QColor( resources.CUSTOM_SCHEME.get('fold-arrow', resources.COLOR_SCHEME['fold-arrow']))) iconPainter.drawPolygon(polygon) polygon.clear() polygon.append(QPointF(self.foldArea * 0.25, self.foldArea * 0.4)) polygon.append(QPointF(self.foldArea * 0.75, self.foldArea * 0.4)) polygon.append(QPointF(self.foldArea * 0.5, self.foldArea * 0.8)) iconPainter = QPainter(self.downArrowIcon) iconPainter.setRenderHint(QPainter.Antialiasing) iconPainter.setPen(Qt.NoPen) iconPainter.setBrush(QColor( resources.CUSTOM_SCHEME.get('fold-arrow', resources.COLOR_SCHEME['fold-arrow']))) iconPainter.drawPolygon(polygon) block = self.edit.firstVisibleBlock() while block.isValid(): position = self.edit.blockBoundingGeometry( block).topLeft() + viewport_offset #Check if the position of the block is outside of the visible area if position.y() > page_bottom: break if pattern.match(block.text()) and block.isVisible(): if block.blockNumber() in self._foldedBlocks: painter.drawPixmap(xofs, round(position.y()), self.rightArrowIcon) else: painter.drawPixmap(xofs, round(position.y()), self.downArrowIcon) #Add Bookmarks and Breakpoint elif block.blockNumber() in self._breakpoints: linear_gradient = QLinearGradient( xofs, round(position.y()), xofs + self.foldArea, round(position.y()) + self.foldArea) linear_gradient.setColorAt(0, QColor(255, 11, 11)) linear_gradient.setColorAt(1, QColor(147, 9, 9)) painter.setRenderHints(QPainter.Antialiasing, True) painter.setPen(Qt.NoPen) painter.setBrush(QBrush(linear_gradient)) painter.drawEllipse( xofs + 1, round(position.y()) + 6, self.foldArea - 1, self.foldArea - 1) elif block.blockNumber() in self._bookmarks: linear_gradient = QLinearGradient( xofs, round(position.y()), xofs + self.foldArea, round(position.y()) + self.foldArea) linear_gradient.setColorAt(0, QColor(13, 62, 243)) linear_gradient.setColorAt(1, QColor(5, 27, 106)) painter.setRenderHints(QPainter.Antialiasing, True) painter.setPen(Qt.NoPen) painter.setBrush(QBrush(linear_gradient)) painter.drawRoundedRect( xofs + 1, round(position.y()) + 6, self.foldArea - 2, self.foldArea - 1, 3, 3) block = block.next() painter.end() QWidget.paintEvent(self, event) def mousePressEvent(self, event): if self.foldArea > 0: xofs = self.width() - self.foldArea font_metrics = QFontMetrics(self.edit.document().defaultFont()) fh = font_metrics.lineSpacing() ys = event.posF().y() lineNumber = 0 if event.pos().x() > xofs: pattern = self.pat if self.edit.lang != "python": pattern = self.patNotPython block = self.edit.firstVisibleBlock() viewport_offset = self.edit.contentOffset() page_bottom = self.edit.viewport().height() while block.isValid(): position = self.edit.blockBoundingGeometry( block).topLeft() + viewport_offset if position.y() > page_bottom: break if position.y() < ys and (position.y() + fh) > ys and \ pattern.match(str(block.text())): lineNumber = block.blockNumber() + 1 break elif position.y() < ys and (position.y() + fh) > ys and \ event.button() == Qt.LeftButton: line = block.blockNumber() if line in self._breakpoints: self._breakpoints.remove(line) else: self._breakpoints.append(line) self.update() break elif position.y() < ys and (position.y() + fh) > ys and \ event.button() == Qt.RightButton: line = block.blockNumber() if line in self._bookmarks: self._bookmarks.remove(line) else: self._bookmarks.append(line) self.update() break block = block.next() self._save_breakpoints_bookmarks() if lineNumber > 0: self.code_folding_event(lineNumber) def _save_breakpoints_bookmarks(self): if self._bookmarks and self.edit.ID != "": settings.BOOKMARKS[self.edit.ID] = self._bookmarks elif self.edit.ID in settings.BOOKMARKS: settings.BOOKMARKS.pop(self.edit.ID) if self._breakpoints and self.edit.ID != "": settings.BREAKPOINTS[self.edit.ID] = self._breakpoints elif self.edit.ID in settings.BREAKPOINTS: settings.BREAKPOINTS.pop(self.edit.ID) def set_breakpoint(self, lineno): if lineno in self._breakpoints: self._breakpoints.remove(lineno) else: self._breakpoints.append(lineno) self.update() self._save_breakpoints_bookmarks() def set_bookmark(self, lineno): if lineno in self._bookmarks: self._bookmarks.remove(lineno) else: self._bookmarks.append(lineno) self.update() self._save_breakpoints_bookmarks() ninja-ide-2.3/ninja_ide/gui/editor/syntax_highlighter.py000066400000000000000000000462441216641277400235100ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . # Based on: https://bitbucket.org/henning/syntaxhighlighter """ Partition-based syntax highlighter """ from __future__ import absolute_import from __future__ import unicode_literals import re from PyQt4.QtGui import ( QSyntaxHighlighter, QColor, QTextCharFormat, QTextBlockUserData, QFont, QBrush, QTextFormat) from ninja_ide import resources from ninja_ide.core import settings try: unicode except NameError: # Python 3 basestring = unicode = str # lint:ok def get_user_data(block): user_data = block.userData() if user_data is None or not isinstance(user_data, SyntaxUserData): user_data = SyntaxUserData() return user_data class SyntaxUserData(QTextBlockUserData): """Store the information of the errors, str and comments for each block.""" def __init__(self, error=False): super(SyntaxUserData, self).__init__() self.error = error self.str_groups = [] self.comment_start = -1 def clear_data(self): """Clear the data stored for the current block.""" self.error = False self.str_groups = [] self.comment_start = -1 def add_str_group(self, start, end): """Add a pair of values setting the beggining and end of a string.""" self.str_groups.append((start, end + 1)) def comment_start_at(self, pos): """Set the position in the line where the comment starts.""" self.comment_start = pos class TextCharFormat(QTextCharFormat): NAME = QTextFormat.UserProperty + 1 class Format(object): __slots__ = ("name", "tcf") def __init__(self, name, color=None, bold=None, italic=None, base_format=None, background=None): self.name = name tcf = TextCharFormat() if base_format is not None: if isinstance(base_format, Format): base_format = base_format.tcf tcf.merge(base_format) tcf.setFont(base_format.font()) if color is not None: if not isinstance(color, QColor): color = QColor(color) tcf.setForeground(QBrush(color)) if bold is not None: if bold: tcf.setFontWeight(QFont.Bold) else: tcf.setFontWeight(QFont.Normal) if italic is not None: tcf.setFontItalic(italic) if background is not None: color = QColor(background) tcf.setBackground(color) tcf.setProperty(TextCharFormat.NAME, name) self.tcf = tcf class HighlighterError(Exception): pass class Partition(object): # every partition maps to a specific state in QSyntaxHighlighter __slots__ = ("name", "start", "end", "is_multiline", "search_end") def __init__(self, name, start, end, is_multiline=False): self.name = name self.start = start self.end = end self.is_multiline = is_multiline try: self.search_end = re.compile(end, re.M | re.S).search except Exception as exc: raise HighlighterError("%s: %s %s" % (exc, name, end)) class PartitionScanner(object): # The idea to partition the source into different contexts comes # from Eclipse. # http://wiki.eclipse.org/FAQ_What_is_a_document_partition%3F def __init__(self, partitions): start_groups = [] self.partitions = [] for i, p in enumerate(partitions): if isinstance(p, (tuple, list)): p = Partition(*p) elif isinstance(p, dict): p = Partition(**p) else: assert isinstance(p, Partition), \ "Partition expected, got %r" % p self.partitions.append(p) start_groups.append("(?P%s)" % (i, p.name, p.start)) start_pat = "|".join(start_groups) try: self.search_start = re.compile(start_pat, re.M | re.S).search except Exception as exc: raise HighlighterError("%s: %s" % (exc, start_pat)) def scan(self, current_state, text): last_pos = 0 length = len(text) parts = self.partitions search_start = self.search_start # loop yields (start, end, partition, new_state, is_inside) while last_pos < length: if current_state == -1: found = search_start(text, last_pos) if found: start, end = found.span() yield last_pos, start, None, -1, True current_state = found.lastindex - 1 p = parts[current_state] yield start, end, p.name, current_state, False last_pos = end else: current_state = -1 yield last_pos, length, None, -1, True break else: p = parts[current_state] found = p.search_end(text, last_pos) if found: start, end = found.span() yield last_pos, start, p.name, current_state, True yield start, end, p.name, current_state, False last_pos = end current_state = -1 else: yield last_pos, length, p.name, current_state, True break if current_state != -1: p = parts[current_state] if not p.is_multiline: current_state = -1 yield length, length, None, current_state, False class Token(object): __slots__ = ("name", "pattern", "prefix", "suffix") def __init__(self, name, pattern, prefix="", suffix=""): self.name = name if isinstance(pattern, list): pattern = "|".join(pattern) self.pattern = pattern self.prefix = prefix self.suffix = suffix class Scanner(object): __slots__ = ("groups", "tokens", "search") def __init__(self, tokens): self.tokens = [] self.groups = [] for t in tokens: if isinstance(t, (list, tuple)): t = Token(*t) elif isinstance(t, dict): t = Token(**t) else: assert isinstance(t, Token), "Token expected, got %r" % t gdef = "?P<%s>" % t.name if gdef in t.pattern: p = t.pattern else: p = ("(%s%s)" % (gdef, t.pattern)) p = t.prefix + p + t.suffix self.groups.append(p) self.tokens.append(t) pat = "|".join(self.groups) self.search = re.compile(pat).search def add_token(self, token): self.__clean_highlight() tpattern = token[1] if tpattern != "": t = Token(*token) gdef = "?P<%s>" % t.name if gdef in t.pattern: p = t.pattern else: p = ("(%s%s)" % (gdef, t.pattern)) p = t.prefix + p + t.suffix self.groups.append(p) self.tokens.append(t) pat = "|".join(self.groups) self.search = re.compile(pat).search def __clean_highlight(self): for group in self.groups: if group.startswith('(?P'): self.groups.remove(group) break for token in self.tokens: if token.name == "highlight_word": self.tokens.remove(token) break def scan(self, s): search = self.search #length = len(s) last_pos = 0 # loop yields (token, start_pos, end_pos) while 1: found = search(s, last_pos) if found: lg = found.lastgroup start, last_pos = found.span(lg) yield lg, start, last_pos else: break class SyntaxHighlighter(QSyntaxHighlighter): def __init__(self, parent, partition_scanner, scanner, formats, errors=None, pep8=None, migration=None): """ :param parent: QDocument or QTextEdit/QPlainTextEdit instance 'partition_scanner: PartitionScanner instance :param scanner: dictionary of token scanners for each partition The key is the name of the partition, the value is a Scanner instance The default scanner has the key None :formats: list of tuples consisting of a name and a format definition The name is the name of a partition or token """ super(SyntaxHighlighter, self).__init__(parent) self.errors = errors self.pep8 = pep8 self.migration = migration self._old_search = None if isinstance(partition_scanner, (list, tuple)): partition_scanner = PartitionScanner(partition_scanner) else: assert isinstance(partition_scanner, PartitionScanner), \ "PartitionScanner expected, got %r" % partition_scanner self.partition_scanner = partition_scanner self.scanner = scanner for inside_part, inside_scanner in list(scanner.items()): if isinstance(inside_scanner, (list, tuple)): inside_scanner = Scanner(inside_scanner) self.scanner[inside_part] = inside_scanner else: assert isinstance(inside_scanner, Scanner), \ "Scanner expected" self.formats = {} for f in formats: if isinstance(f, tuple): fname, f = f else: assert isinstance(f, (Format, dict)), \ "Format expected, got %r" % f if isinstance(f, basestring): f = (f,) # only color specified if isinstance(f, (tuple, list)): f = Format(*((fname,) + f)) elif isinstance(f, dict): f = Format(**dict(name=fname, **f)) else: assert isinstance(f, Format), "Format expected, got %r" % f f.tcf.setFontFamily(parent.defaultFont().family()) self.formats[f.name] = f.tcf # reduce name look-ups for better speed scan_inside = {} for inside_part, inside_scanner in list(self.scanner.items()): scan_inside[inside_part] = inside_scanner.scan self.get_scanner = scan_inside.get self.scan_partitions = partition_scanner.scan self.get_format = self.formats.get def __apply_proper_style(self, char_format, color): if settings.UNDERLINE_NOT_BACKGROUND: char_format.setUnderlineColor(color) char_format.setUnderlineStyle(QTextCharFormat.WaveUnderline) else: color.setAlpha(60) char_format.setBackground(color) def __highlight_pep8(self, char_format, user_data=None): """Highlight the lines with pep8 errors.""" user_data.error = True color = QColor( resources.CUSTOM_SCHEME.get('pep8-underline', resources.COLOR_SCHEME['pep8-underline'])) self.__apply_proper_style(char_format, color) return char_format def __highlight_lint(self, char_format, user_data): """Highlight the lines with lint errors.""" user_data.error = True color = QColor( resources.CUSTOM_SCHEME.get('error-underline', resources.COLOR_SCHEME['error-underline'])) self.__apply_proper_style(char_format, color) return char_format def __highlight_migration(self, char_format, user_data): """Highlight the lines with lint errors.""" user_data.error = True color = QColor( resources.CUSTOM_SCHEME.get('migration-underline', resources.COLOR_SCHEME['migration-underline'])) self.__apply_proper_style(char_format, color) return char_format def __remove_error_highlight(self, char_format, user_data): user_data.error = False char_format.setUnderlineStyle(QTextCharFormat.NoUnderline) char_format.clearBackground() return char_format def highlightBlock(self, text): """automatically called by Qt""" text += "\n" previous_state = self.previousBlockState() new_state = previous_state # User data and errors block = self.currentBlock() user_data = get_user_data(block) user_data.clear_data() valid_error_line, highlight_errors = self.get_error_highlighter(block) # speed-up name-lookups get_format = self.get_format set_format = self.setFormat get_scanner = self.get_scanner for start, end, partition, new_state, is_inside in \ self.scan_partitions(previous_state, text): f = get_format(partition, None) if f: f = highlight_errors(f, user_data) set_format(start, end - start, f) elif valid_error_line: f = TextCharFormat() f = highlight_errors(f, user_data) set_format(start, end - start, f) if is_inside: scan = get_scanner(partition) if scan: for token, token_pos, token_end in scan(text[start:end]): f = get_format(token) if f: f = highlight_errors(f, user_data) set_format(start + token_pos, token_end - token_pos, f) if partition and partition == "string": user_data.add_str_group(start, end) block.setUserData(user_data) self.setCurrentBlockState(new_state) def get_error_highlighter(self, block): block_number = block.blockNumber() highlight_errors = self.__remove_error_highlight valid_error_line = False if self.errors and (block_number in self.errors.errorsSummary): highlight_errors = self.__highlight_lint valid_error_line = True elif self.pep8 and (block_number in self.pep8.pep8checks): highlight_errors = self.__highlight_pep8 valid_error_line = True elif self.migration and ( block_number in self.migration.migration_data): highlight_errors = self.__highlight_migration valid_error_line = True return valid_error_line, highlight_errors def set_selected_word(self, word, partial=False): """Set the word to highlight.""" hl_worthy = len(word) > 2 if hl_worthy: suffix = "(?![A-Za-z_\d])" prefix = "(?. ninja-ide-2.3/ninja_ide/gui/explorer/errors_lists.py000066400000000000000000000135351216641277400227050ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QWidget from PyQt4.QtGui import QListWidget from PyQt4.QtGui import QListWidgetItem from PyQt4.QtGui import QLabel from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QSpacerItem from PyQt4.QtGui import QSizePolicy from PyQt4.QtCore import Qt from PyQt4.QtCore import SIGNAL from ninja_ide.core import settings from ninja_ide.gui.main_panel import main_container class ErrorsWidget(QWidget): ############################################################################### # ERRORS WIDGET SIGNALS ############################################################################### """ pep8Activated(bool) lintActivated(bool) """ ############################################################################### def __init__(self): QWidget.__init__(self) self.pep8 = None self._outRefresh = True vbox = QVBoxLayout(self) self.listErrors = QListWidget() self.listErrors.setSortingEnabled(True) self.listPep8 = QListWidget() self.listPep8.setSortingEnabled(True) hbox_lint = QHBoxLayout() if settings.FIND_ERRORS: self.btn_lint_activate = QPushButton(self.tr("Lint: ON")) else: self.btn_lint_activate = QPushButton(self.tr("Lint: OFF")) self.errorsLabel = QLabel(self.tr("Static Errors: %s") % 0) hbox_lint.addWidget(self.errorsLabel) hbox_lint.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding)) hbox_lint.addWidget(self.btn_lint_activate) vbox.addLayout(hbox_lint) vbox.addWidget(self.listErrors) hbox_pep8 = QHBoxLayout() if settings.CHECK_STYLE: self.btn_pep8_activate = QPushButton(self.tr("PEP8: ON")) else: self.btn_pep8_activate = QPushButton(self.tr("PEP8: OFF")) self.pep8Label = QLabel(self.tr("PEP8 Errors: %s") % 0) hbox_pep8.addWidget(self.pep8Label) hbox_pep8.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding)) hbox_pep8.addWidget(self.btn_pep8_activate) vbox.addLayout(hbox_pep8) vbox.addWidget(self.listPep8) self.connect(self.listErrors, SIGNAL("itemSelectionChanged()"), self.errors_selected) self.connect(self.listPep8, SIGNAL("itemSelectionChanged()"), self.pep8_selected) self.connect(self.btn_lint_activate, SIGNAL("clicked()"), self._turn_on_off_lint) self.connect(self.btn_pep8_activate, SIGNAL("clicked()"), self._turn_on_off_pep8) def _turn_on_off_lint(self): """Change the status of the lint checker state.""" settings.FIND_ERRORS = not settings.FIND_ERRORS if settings.FIND_ERRORS: self.btn_lint_activate.setText(self.tr("Lint: ON")) else: self.btn_lint_activate.setText(self.tr("Lint: OFF")) self.emit(SIGNAL("lintActivated(bool)"), settings.FIND_ERRORS) def _turn_on_off_pep8(self): """Change the status of the lint checker state.""" settings.CHECK_STYLE = not settings.CHECK_STYLE if settings.CHECK_STYLE: self.btn_pep8_activate.setText(self.tr("PEP8: ON")) else: self.btn_pep8_activate.setText(self.tr("PEP8: OFF")) self.emit(SIGNAL("pep8Activated(bool)"), settings.CHECK_STYLE) def errors_selected(self): editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget and self._outRefresh: lineno = int(self.listErrors.currentItem().data(Qt.UserRole)) editorWidget.jump_to_line(lineno) editorWidget.setFocus() def pep8_selected(self): editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget and self._outRefresh: lineno = int(self.listPep8.currentItem().data(Qt.UserRole)) editorWidget.jump_to_line(lineno) editorWidget.setFocus() def refresh_lists(self, errors, pep8): self._outRefresh = False self.listErrors.clear() self.listPep8.clear() for lineno in errors.errorsSummary: linenostr = 'L%s\t' % str(lineno + 1) for data in errors.errorsSummary[lineno]: item = QListWidgetItem(linenostr + data) item.setToolTip(linenostr + data) item.setData(Qt.UserRole, lineno) self.listErrors.addItem(item) self.errorsLabel.setText(self.tr("Static Errors: %s") % len(errors.errorsSummary)) for lineno in pep8.pep8checks: linenostr = 'L%s\t' % str(lineno + 1) for data in pep8.pep8checks[lineno]: item = QListWidgetItem(linenostr + data.split('\n')[0]) item.setToolTip(linenostr + data.split('\n')[0]) item.setData(Qt.UserRole, lineno) self.listPep8.addItem(item) self.pep8Label.setText(self.tr("PEP8 Errors: %s") % len(pep8.pep8checks)) self._outRefresh = True def clear(self): """ Clear the widget """ self.listErrors.clear() self.listPep8.clear() ninja-ide-2.3/ninja_ide/gui/explorer/explorer_container.py000066400000000000000000000412161216641277400240520ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import os from PyQt4.QtGui import QWidget from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QTabWidget from PyQt4.QtGui import QFileDialog from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QMessageBox from PyQt4.QtGui import QIcon from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import QSettings from PyQt4.QtCore import QDateTime from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.core import file_manager from ninja_ide.gui.explorer import tree_projects_widget from ninja_ide.gui.explorer import tree_symbols_widget from ninja_ide.gui.explorer import errors_lists from ninja_ide.gui.explorer import migration_lists from ninja_ide.gui.main_panel import main_container from ninja_ide.gui.dialogs import wizard_new_project from ninja_ide.tools import json_manager from ninja_ide.tools import ui_tools try: from PyQt4.QtWebKit import QWebInspector except: settings.WEBINSPECTOR_SUPPORTED = False from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.gui.explorer.explorer_container') __explorerContainerInstance = None def ExplorerContainer(*args, **kw): global __explorerContainerInstance if __explorerContainerInstance is None: __explorerContainerInstance = __ExplorerContainer(*args, **kw) return __explorerContainerInstance class __ExplorerContainer(QTabWidget): ############################################################################### # ExplorerContainer SIGNALS ############################################################################### """ updateLocator() goToDefinition(int) projectOpened(QString) projectClosed(QString) """ ############################################################################### def __init__(self, parent=None): QTabWidget.__init__(self, parent) self.setTabPosition(QTabWidget.East) self.__ide = parent self._thread_execution = {} #Searching the Preferences self._treeProjects = None if settings.SHOW_PROJECT_EXPLORER: self.add_tab_projects() self._treeSymbols = None if settings.SHOW_SYMBOLS_LIST: self.add_tab_symbols() self._inspector = None if settings.SHOW_WEB_INSPECTOR and settings.WEBINSPECTOR_SUPPORTED: self.add_tab_inspector() self._listErrors = None if settings.SHOW_ERRORS_LIST: self.add_tab_errors() self._listMigration = None if settings.SHOW_MIGRATION_LIST: self.add_tab_migration() def update_symbols(self, symbols, fileName): if self._treeSymbols: self._treeSymbols.update_symbols_tree(symbols, filename=fileName) def update_errors(self, errors, pep8): if self._listErrors: self._listErrors.refresh_lists(errors, pep8) def update_migration(self, migration): if self._listMigration: self._listMigration.refresh_lists(migration) def addTab(self, tab, title): QTabWidget.addTab(self, tab, title) def add_tab_migration(self): if not self._listMigration: self._listMigration = migration_lists.MigrationWidget() self.addTab(self._listMigration, self.tr("Migration 2to3")) def add_tab_projects(self): if not self._treeProjects: self._treeProjects = tree_projects_widget.TreeProjectsWidget() self.addTab(self._treeProjects, self.tr('Projects')) self.connect(self._treeProjects, SIGNAL("runProject()"), self.__ide.actions.execute_project) self.connect(self.__ide, SIGNAL("goingDown()"), self._treeProjects.shutdown) self.connect(self._treeProjects, SIGNAL("addProjectToConsole(QString)"), self.__ide.actions.add_project_to_console) self.connect(self._treeProjects, SIGNAL("removeProjectFromConsole(QString)"), self.__ide.actions.remove_project_from_console) def close_project_signal(): self.emit(SIGNAL("updateLocator()")) def close_files_related_to_closed_project(project): if project: self.emit(SIGNAL("projectClosed(QString)"), project) self.connect(self._treeProjects, SIGNAL("closeProject(QString)"), close_project_signal) self.connect(self._treeProjects, SIGNAL("refreshProject()"), close_project_signal) self.connect(self._treeProjects, SIGNAL("closeFilesFromProjectClosed(QString)"), close_files_related_to_closed_project) def add_tab_symbols(self): if not self._treeSymbols: self._treeSymbols = tree_symbols_widget.TreeSymbolsWidget() self.addTab(self._treeSymbols, self.tr('Symbols')) def _go_to_definition(lineno): self.emit(SIGNAL("goToDefinition(int)"), lineno) self.connect(self._treeSymbols, SIGNAL("goToDefinition(int)"), _go_to_definition) def update_current_symbol(self, line, col): """Select the proper item in the symbols list.""" if self._treeSymbols is not None: self._treeSymbols.select_current_item(line, col) def add_tab_inspector(self): if not settings.WEBINSPECTOR_SUPPORTED: QMessageBox.information(self, self.tr("Web Inspector not Supported"), self.tr("Your Qt version doesn't support the Web Inspector")) if not self._inspector: self._inspector = WebInspector(self) self.addTab(self._inspector, self.tr("Web Inspector")) self.connect(self._inspector.btnDock, SIGNAL("clicked()"), self._dock_inspector) def add_tab_errors(self): if not self._listErrors: self._listErrors = errors_lists.ErrorsWidget() self.addTab(self._listErrors, self.tr("Errors")) self.connect(self._listErrors, SIGNAL("pep8Activated(bool)"), self.__ide.mainContainer.reset_pep8_warnings) self.connect(self._listErrors, SIGNAL("lintActivated(bool)"), self.__ide.mainContainer.reset_lint_warnings) def remove_tab_migration(self): if self._listMigration: self.removeTab(self.indexOf(self._listMigration)) self._listMigration = None def remove_tab_errors(self): if self._listErrors: self.removeTab(self.indexOf(self._listErrors)) self._listErrors = None def remove_tab_projects(self): if self._treeProjects: self.removeTab(self.indexOf(self._treeProjects)) self._treeProjects = None def remove_tab_symbols(self): if self._treeSymbols: self.removeTab(self.indexOf(self._treeSymbols)) self._treeSymbols = None def remove_tab_inspector(self): if self._inspector: self.removeTab(self.indexOf(self._inspector)) self._inspector = None def _dock_inspector(self): if self._inspector.parent(): self._inspector.btnDock.setText(self.tr("Dock")) self._inspector.setParent(None) self._inspector.resize(500, 500) self._inspector.show() else: self.addTab(self._inspector, self.tr("Web Inspector")) self._inspector.btnDock.setText(self.tr("Undock")) def add_tab(self, widget, name, icon): self.addTab(widget, QIcon(icon), name) def rotate_tab_position(self): if self.tabPosition() == QTabWidget.East: self.setTabPosition(QTabWidget.West) else: self.setTabPosition(QTabWidget.East) def show_project_tree(self): if self._treeProjects: self.setCurrentWidget(self._treeProjects) def show_symbols_tree(self): if self._treeSymbols: self.setCurrentWidget(self._treeSymbols) def show_web_inspector(self): if self._inspector: self.setCurrentWidget(self._inspector) def refresh_inspector(self): if self._inspector: self._inspector._webInspector.hide() self._inspector._webInspector.show() def set_inspection_page(self, page): if self._inspector: self._inspector._webInspector.setPage(page) self._inspector._webInspector.setVisible(True) def open_project_folder(self, folderName='', notIDEStart=True): if not self._treeProjects and notIDEStart: QMessageBox.information(self, self.tr("Projects Disabled"), self.tr("Project support has been disabled from Preferences")) return if not folderName: if settings.WORKSPACE: directory = settings.WORKSPACE else: directory = os.path.expanduser("~") current_project = self.get_actual_project() mainContainer = main_container.MainContainer() editorWidget = mainContainer.get_actual_editor() if current_project is not None: directory = current_project elif editorWidget is not None and editorWidget.ID: directory = file_manager.get_folder(editorWidget.ID) folderName = QFileDialog.getExistingDirectory(self, self.tr("Open Project Directory"), directory) try: if not folderName: return if not self._treeProjects.is_open(folderName): self._treeProjects.mute_signals = True self._treeProjects.loading_project(folderName) thread = ui_tools.ThreadProjectExplore() self._thread_execution[folderName] = thread self.connect(thread, SIGNAL("folderDataAcquired(PyQt_PyObject)"), self._callback_open_project) self.connect(thread, SIGNAL("finished()"), self._unmute_tree_signals_clean_threads) thread.open_folder(folderName) else: self._treeProjects._set_current_project(folderName) except Exception as reason: logger.error('open_project_folder: %s', reason) if not notIDEStart: QMessageBox.information(self, self.tr("Incorrect Project"), self.tr("The project could not be loaded!")) def _unmute_tree_signals_clean_threads(self): paths_to_delete = [] for path in self._thread_execution: thread = self._thread_execution.get(path, None) if thread and not thread.isRunning(): paths_to_delete.append(path) for path in paths_to_delete: thread = self._thread_execution.pop(path, None) if thread: thread.wait() if len(self._thread_execution) == 0: self._treeProjects.mute_signals = False def _callback_open_project(self, value): path, structure = value if structure is None: self._treeProjects.remove_loading_icon(path) return self._treeProjects.load_project(structure, path) self.save_recent_projects(path) self.emit(SIGNAL("projectOpened(QString)"), path) self.emit(SIGNAL("updateLocator()")) def create_new_project(self): if not self._treeProjects: QMessageBox.information(self, self.tr("Projects Disabled"), self.tr("Project support has been disabled from Preferences")) return wizard = wizard_new_project.WizardNewProject(self) wizard.show() def add_existing_file(self, path): if self._treeProjects: self._treeProjects.add_existing_file(path) def get_actual_project(self): if self._treeProjects: return self._treeProjects.get_selected_project_path() return None def get_project_main_file(self): if self._treeProjects: return self._treeProjects.get_project_main_file() return '' def get_project_given_filename(self, filename): projects = self.get_opened_projects() for project in projects: if filename.startswith(project.path): return project return None def get_opened_projects(self): if self._treeProjects: return self._treeProjects.get_open_projects() return [] def open_session_projects(self, projects, notIDEStart=True): if not self._treeProjects: return for project in projects: if file_manager.folder_exists(project): self.open_project_folder(project, notIDEStart) def open_project_properties(self): if self._treeProjects: self._treeProjects.open_project_properties() def close_opened_projects(self): if self._treeProjects: self._treeProjects._close_open_projects() def save_recent_projects(self, folder): recent_project_list = QSettings( resources.SETTINGS_PATH, QSettings.IniFormat).value('recentProjects', {}) #if already exist on the list update the date time projectProperties = json_manager.read_ninja_project(folder) name = projectProperties.get('name', '') description = projectProperties.get('description', '') if name == '': name = file_manager.get_basename(folder) if description == '': description = self.tr('no description available') if folder in recent_project_list: properties = recent_project_list[folder] properties["lastopen"] = QDateTime.currentDateTime() properties["name"] = name properties["description"] = description recent_project_list[folder] = properties else: recent_project_list[folder] = { "name": name, "description": description, "isFavorite": False, "lastopen": QDateTime.currentDateTime()} #if the length of the project list it's high that 10 then delete #the most old #TODO: add the length of available projects to setting if len(recent_project_list) > 10: del recent_project_list[self.find_most_old_open()] QSettings(resources.SETTINGS_PATH, QSettings.IniFormat).setValue( 'recentProjects', recent_project_list) def find_most_old_open(self): recent_project_list = QSettings( resources.SETTINGS_PATH, QSettings.IniFormat).value('recentProjects', {}) listFounder = [] for recent_project_path, content in list(recent_project_list.items()): listFounder.append((recent_project_path, int( content["lastopen"].toString("yyyyMMddHHmmzzz")))) listFounder = sorted(listFounder, key=lambda date: listFounder[1], reverse=True) # sort by date last used return listFounder[0][0] def get_project_name(self, path): if self._treeProjects: item = self._treeProjects._projects.get(path, None) if item is not None: return item.name def cleanup_tabs(self): """ Cleans depending on what objects are visible """ # Clean up tabs if self._treeSymbols: self._treeSymbols.clean() if self._listErrors: self._listErrors.clear() if self._listMigration: self._listMigration.clear() class WebInspector(QWidget): def __init__(self, parent): QWidget.__init__(self, parent) vbox = QVBoxLayout(self) self._webInspector = QWebInspector(self) vbox.addWidget(self._webInspector) self.btnDock = QPushButton(self.tr("Undock")) vbox.addWidget(self.btnDock) ninja-ide-2.3/ninja_ide/gui/explorer/migration_lists.py000066400000000000000000000107121216641277400233540ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QWidget from PyQt4.QtGui import QListWidget from PyQt4.QtGui import QListWidgetItem from PyQt4.QtGui import QLabel from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QSpacerItem from PyQt4.QtGui import QSizePolicy from PyQt4.QtGui import QPlainTextEdit from PyQt4.QtGui import QTextCursor from PyQt4.QtCore import Qt from PyQt4.QtCore import SIGNAL from ninja_ide.gui.main_panel import main_container class MigrationWidget(QWidget): def __init__(self): super(MigrationWidget, self).__init__() self._migration = {} vbox = QVBoxLayout(self) lbl_title = QLabel(self.tr("Current code:")) self.current_list = QListWidget() lbl_suggestion = QLabel(self.tr("Suggested changes:")) self.suggestion = QPlainTextEdit() self.suggestion.setReadOnly(True) self.btn_apply = QPushButton(self.tr("Apply change!")) hbox = QHBoxLayout() hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding)) hbox.addWidget(self.btn_apply) vbox.addWidget(lbl_title) vbox.addWidget(self.current_list) vbox.addWidget(lbl_suggestion) vbox.addWidget(self.suggestion) vbox.addLayout(hbox) self.connect(self.current_list, SIGNAL("itemClicked(QListWidgetItem*)"), self.load_suggestion) self.connect(self.btn_apply, SIGNAL("clicked()"), self.apply_changes) def apply_changes(self): lineno = int(self.current_list.currentItem().data(Qt.UserRole)) lines = self._migration.migration_data[lineno][0].split('\n') remove = -1 code = '' for line in lines: if line.startswith('-'): remove += 1 elif line.startswith('+'): code += '%s\n' % line[1:] editorWidget = main_container.MainContainer().get_actual_editor() block_start = editorWidget.document().findBlockByLineNumber(lineno) block_end = editorWidget.document().findBlockByLineNumber( lineno + remove) cursor = editorWidget.textCursor() cursor.setPosition(block_start.position()) cursor.setPosition(block_end.position(), QTextCursor.KeepAnchor) cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) cursor.insertText(code[:-1]) def load_suggestion(self, item): lineno = int(item.data(Qt.UserRole)) lines = self._migration.migration_data[lineno][0].split('\n') code = '' for line in lines: if line.startswith('+'): code += '%s\n' % line[1:] self.suggestion.setPlainText(code) editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget: editorWidget.jump_to_line(lineno) editorWidget.setFocus() def refresh_lists(self, migration): self._migration = migration self.current_list.clear() base_lineno = -1 for lineno in sorted(migration.migration_data.keys()): linenostr = 'L%s\n' % str(lineno + 1) data = migration.migration_data[lineno] lines = data[0].split('\n') if base_lineno == data[1]: continue base_lineno = data[1] message = '' for line in lines: if line.startswith('-'): message += '%s\n' % line item = QListWidgetItem(linenostr + message) item.setToolTip(linenostr + message) item.setData(Qt.UserRole, lineno) self.current_list.addItem(item) def clear(self): """ Clear the widget """ self.current_list.clear() self.suggestion.clear() ninja-ide-2.3/ninja_ide/gui/explorer/tree_projects_widget.py000066400000000000000000001075461216641277400243740ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import os from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.gui.explorer.tree_projects_widget') DEBUG = logger.debug from PyQt4.QtGui import QTreeWidget from PyQt4.QtGui import QTreeWidgetItem from PyQt4.QtGui import QAbstractItemView from PyQt4.QtGui import QHeaderView from PyQt4.QtGui import QInputDialog from PyQt4.QtGui import QMessageBox from PyQt4.QtGui import QColor from PyQt4.QtGui import QBrush from PyQt4.QtGui import QMenu from PyQt4.QtGui import QIcon from PyQt4.QtGui import QStyle from PyQt4.QtGui import QCursor from PyQt4.QtCore import Qt from PyQt4.QtCore import QTimer from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import QUrl from PyQt4.QtGui import QDesktopServices from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.core import file_manager from ninja_ide.core.filesystem_notifications import NinjaFileSystemWatcher from ninja_ide.core.filesystem_notifications.base_watcher import ADDED,\ DELETED, REMOVE, RENAME from ninja_ide.tools import json_manager from ninja_ide.tools import ui_tools from ninja_ide.gui.main_panel import main_container from ninja_ide.gui.dialogs import project_properties_widget from ninja_ide.tools.completion import completion_daemon class TreeProjectsWidget(QTreeWidget): ############################################################################### # TreeProjectsWidget SIGNALS ############################################################################### """ runProject() closeProject(QString) closeFilesFromProjectClosed(QString) addProjectToConsole(QString) removeProjectFromConsole(QString) projectPropertiesUpdated(QTreeWidgetItem) """ ############################################################################### #Extra context menu 'all' indicate a menu for ALL LANGUAGES! extra_menus = {'all': []} #Extra context menu by scope all is for ALL the TREE ITEMS! extra_menus_by_scope = {'project': [], 'folder': [], 'file': []} images = { 'py': resources.IMAGES['tree-python'], 'jpg': resources.IMAGES['tree-image'], 'png': resources.IMAGES['tree-image'], 'html': resources.IMAGES['tree-html'], 'css': resources.IMAGES['tree-css'], 'ui': resources.IMAGES['designer']} def __init__(self, state_index=list()): QTreeWidget.__init__(self) self.header().setHidden(True) self.setSelectionMode(QTreeWidget.SingleSelection) self.setAnimated(True) self._actualProject = None #self._projects -> key: [Item, folderStructure] self._projects = {} self._loading_items = {} self._thread_execution = {} self.__enableCloseNotification = True self._fileWatcher = NinjaFileSystemWatcher self._refresh_projects_queue = [] self._timer_running = False self.header().setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) self.header().setResizeMode(0, QHeaderView.ResizeToContents) self.header().setStretchLastSection(False) self.setContextMenuPolicy(Qt.CustomContextMenu) self.connect(self, SIGNAL( "customContextMenuRequested(const QPoint &)"), self._menu_context_tree) signal_name = "itemClicked(QTreeWidgetItem *, int)" # For windows double click instead of single click if settings.IS_WINDOWS: signal_name = "itemDoubleClicked(QTreeWidgetItem *, int)" self.connect(self, SIGNAL(signal_name), self._open_file) self.connect(self._fileWatcher, SIGNAL("fileChanged(int, QString)"), self._refresh_project_by_path) self.itemExpanded.connect(self._item_expanded) self.itemCollapsed.connect(self._item_collapsed) self.mute_signals = False self.state_index = list() self._folding_menu = FoldingContextMenu(self) def _item_collapsed(self, tree_item): """Store status of item when collapsed""" if not self.mute_signals: path = tree_item.get_full_path() if path in self.state_index: path_index = self.state_index.index(path) self.state_index.pop(path_index) def _item_expanded(self, tree_item): """Store status of item when expanded""" if not self.mute_signals: path = tree_item.get_full_path() if path not in self.state_index: self.state_index.append(path) def shutdown(self): self._fileWatcher.shutdown_notification() def add_extra_menu(self, menu, lang='all'): ''' Add an extra menu for the given language @lang: string with the form 'py', 'php', 'json', etc ''' #remove blanks and replace dots Example(.py => py) lang = lang.strip().replace('.', '') self.extra_menus.setdefault(lang, []) self.extra_menus[lang].append(menu) def add_extra_menu_by_scope(self, menu, scope): ''' Add an extra menu for the given language @scope: string with the menu scope (all, project, folder, item) ''' if scope.project: self.extra_menus_by_scope['project'].append(menu) if scope.folder: self.extra_menus_by_scope['folder'].append(menu) if scope.file: self.extra_menus_by_scope['file'].append(menu) def _menu_context_tree(self, point): index = self.indexAt(point) if not index.isValid(): return item = self.itemAt(point) handler = None menu = QMenu(self) if item.isFolder or item.parent() is None: self._add_context_menu_for_folders(menu, item) elif not item.isFolder: self._add_context_menu_for_files(menu, item) if item.parent() is None: #get the extra context menu for this projectType if isinstance(item, ProjectItem): return handler = settings.get_project_type_handler(item.projectType) self._add_context_menu_for_root(menu, item) menu.addMenu(self._folding_menu) #menu for all items (legacy API)! extra_menus = self.extra_menus.get('all', ()) #menu for all items! for m in extra_menus: if isinstance(m, QMenu): menu.addSeparator() menu.addMenu(m) #menu for the Project Type(if present) if handler: for m in handler.get_context_menus(): if isinstance(m, QMenu): menu.addSeparator() menu.addMenu(m) #show the menu! menu.exec_(QCursor.pos()) def _add_context_menu_for_root(self, menu, item): menu.addSeparator() actionRunProject = menu.addAction(QIcon( resources.IMAGES['play']), self.tr("Run Project")) self.connect(actionRunProject, SIGNAL("triggered()"), SIGNAL("runProject()")) actionMainProject = menu.addAction(self.tr("Set as Main Project")) self.connect(actionMainProject, SIGNAL("triggered()"), lambda: self.set_default_project(item)) if item.addedToConsole: actionRemoveFromConsole = menu.addAction( self.tr("Remove this Project from the Python Console")) self.connect(actionRemoveFromConsole, SIGNAL("triggered()"), self._remove_project_from_console) else: actionAdd2Console = menu.addAction( self.tr("Add this Project to the Python Console")) self.connect(actionAdd2Console, SIGNAL("triggered()"), self._add_project_to_console) actionProperties = menu.addAction(QIcon(resources.IMAGES['pref']), self.tr("Project Properties")) self.connect(actionProperties, SIGNAL("triggered()"), self.open_project_properties) menu.addSeparator() action_refresh = menu.addAction( self.style().standardIcon(QStyle.SP_BrowserReload), self.tr("Refresh Project")) self.connect(action_refresh, SIGNAL("triggered()"), self._refresh_project) action_close = menu.addAction( self.style().standardIcon(QStyle.SP_DialogCloseButton), self.tr("Close Project")) self.connect(action_close, SIGNAL("triggered()"), self._close_project) #menu for the project for m in self.extra_menus_by_scope['project']: if isinstance(m, QMenu): menu.addSeparator() menu.addMenu(m) def _add_context_menu_for_folders(self, menu, item): action_add_file = menu.addAction(QIcon(resources.IMAGES['new']), self.tr("Add New File")) self.connect(action_add_file, SIGNAL("triggered()"), self._add_new_file) action_add_folder = menu.addAction(QIcon( resources.IMAGES['openProj']), self.tr("Add New Folder")) self.connect(action_add_folder, SIGNAL("triggered()"), self._add_new_folder) action_create_init = menu.addAction( self.tr("Create '__init__' Complete")) self.connect(action_create_init, SIGNAL("triggered()"), self._create_init) if item.isFolder and (item.parent() is not None): action_remove_folder = menu.addAction(self.tr("Remove Folder")) self.connect(action_remove_folder, SIGNAL("triggered()"), self._delete_folder) #Folders but not the root if item.isFolder and item.parent() is not None: for m in self.extra_menus_by_scope['folder']: if isinstance(m, QMenu): menu.addSeparator() menu.addMenu(m) def _add_context_menu_for_files(self, menu, item): action_rename_file = menu.addAction(self.tr("Rename File")) action_move_file = menu.addAction(self.tr("Move File")) action_copy_file = menu.addAction(self.tr("Copy File")) action_remove_file = menu.addAction( self.style().standardIcon(QStyle.SP_DialogCloseButton), self.tr("Delete File")) self.connect(action_remove_file, SIGNAL("triggered()"), self._delete_file) self.connect(action_rename_file, SIGNAL("triggered()"), self._rename_file) self.connect(action_copy_file, SIGNAL("triggered()"), self._copy_file) self.connect(action_move_file, SIGNAL("triggered()"), self._move_file) #Allow to edit Qt UI files with the appropiate program if item.lang() == 'ui': action_edit_ui_file = menu.addAction(self.tr("Edit UI File")) self.connect(action_edit_ui_file, SIGNAL("triggered()"), self._edit_ui_file) #menu per file language (legacy plugin API)! for m in self.extra_menus.get(item.lang(), ()): if isinstance(m, QMenu): menu.addSeparator() menu.addMenu(m) #menu for files for m in self.extra_menus_by_scope['file']: if isinstance(m, QMenu): menu.addSeparator() menu.addMenu(m) def _add_project_to_console(self): item = self.currentItem() if isinstance(item, ProjectTree): self.emit(SIGNAL("addProjectToConsole(QString)"), item.path) item.addedToConsole = True def _remove_project_from_console(self): item = self.currentItem() if isinstance(item, ProjectTree): self.emit(SIGNAL("removeProjectFromConsole(QString)"), item.path) item.addedToConsole = False def _open_file(self, item, column): if item.childCount() == 0 and not item.isFolder: fileName = os.path.join(item.path, item.text(column)) main_container.MainContainer().open_file(fileName) def _get_project_root(self, item=None): if item is None: item = self.currentItem() while item is not None and item.parent() is not None: item = item.parent() return item def set_default_project(self, item): item.setForeground(0, QBrush(QColor(0, 204, 82))) if self._actualProject: item.setForeground(0, QBrush(QColor(255, 165, 0))) self._actualProject = item def open_project_properties(self): item = self._get_project_root() proj = project_properties_widget.ProjectProperties(item, self) proj.show() def _timeout(self): projects = set(self._refresh_projects_queue) self._refresh_projects_queue = [] self._timer_running = False for prefresh in projects: self._refresh_project(prefresh) def _refresh_project_by_path(self, event, folder): if event not in (DELETED, ADDED, REMOVE, RENAME): return oprojects = self.get_open_projects() for each_project in oprojects: p_path = each_project.path if file_manager.belongs_to_folder(p_path, folder) and \ file_manager.is_supported_extension(folder, each_project.extensions) \ and folder[:1] != '.': self._refresh_projects_queue.append(each_project) break if not self._timer_running: self._timeout() QTimer.singleShot(3000, self._timeout) self._timer_running = True def _refresh_project(self, item=None): if item is None: item = self.currentItem() parentItem = self._get_project_root(item) if parentItem is None: return if item.parent() is None: path = item.path else: path = file_manager.create_path(item.path, item.text(0)) thread = ui_tools.ThreadProjectExplore() self._thread_execution[path] = thread self.connect(thread, SIGNAL("folderDataRefreshed(PyQt_PyObject)"), self._callback_refresh_project) self.connect(thread, SIGNAL("finished()"), self._clean_threads) thread.refresh_project(path, item, parentItem.extensions) def _clean_threads(self): paths_to_delete = [] for path in self._thread_execution: thread = self._thread_execution.get(path, None) if thread and not thread.isRunning(): paths_to_delete.append(path) for path in paths_to_delete: thread = self._thread_execution.pop(path, None) if thread: thread.wait() def _callback_refresh_project(self, value): path, item, structure = value item.takeChildren() self._load_folder(structure, path, item) #todo: refresh completion item.setExpanded(True) if isinstance(item, ProjectTree): self.emit(SIGNAL("projectPropertiesUpdated(QTreeWidgetItem)"), item) def _close_project(self): item = self.currentItem() index = self.indexOfTopLevelItem(item) pathKey = item.path self._fileWatcher.remove_watch(pathKey) self.takeTopLevelItem(index) self._projects.pop(pathKey, None) if self.__enableCloseNotification: self.emit(SIGNAL("closeProject(QString)"), pathKey) self.emit(SIGNAL("closeFilesFromProjectClosed(QString)"), pathKey) item = self.currentItem() if item: self.set_default_project(item) self._actualProject = item else: self._actualProject = None def _create_init(self): item = self.currentItem() if item.parent() is None: pathFolder = item.path else: pathFolder = os.path.join(item.path, str(item.text(0))) try: file_manager.create_init_file_complete(pathFolder) except file_manager.NinjaFileExistsException as ex: QMessageBox.information(self, self.tr("Create INIT fail"), ex.message) self._refresh_project(item) def _add_new_file(self): item = self.currentItem() if item.parent() is None: pathForFile = item.path else: pathForFile = os.path.join(item.path, item.text(0)) result = QInputDialog.getText(self, self.tr("New File"), self.tr("Enter the File Name:")) fileName = result[0] if result[1] and fileName.strip() != '': try: fileName = os.path.join(pathForFile, fileName) fileName = file_manager.store_file_content( fileName, '', newFile=True) name = file_manager.get_basename(fileName) subitem = ProjectItem(item, name, pathForFile) subitem.setToolTip(0, name) subitem.setIcon(0, self._get_file_icon(name)) mainContainer = main_container.MainContainer() mainContainer.open_file(fileName) except file_manager.NinjaFileExistsException as ex: QMessageBox.information(self, self.tr("File Already Exists"), (self.tr("Invalid Path: the file '%s' already exists.") % ex.filename)) def add_existing_file(self, path): relative = file_manager.convert_to_relative( self._actualProject.path, path) paths = relative.split(os.sep)[:-1] itemParent = self._actualProject for p in paths: for i in range(itemParent.childCount()): item = itemParent.child(i) if item.text(0) == p: itemParent = item break itemParent.setSelected(True) name = file_manager.get_basename(path) subitem = ProjectItem(itemParent, name, file_manager.get_folder(path)) subitem.setToolTip(0, name) subitem.setIcon(0, self._get_file_icon(name)) itemParent.setExpanded(True) def _add_new_folder(self): item = self.currentItem() if item.parent() is None: pathForFolder = item.path else: pathForFolder = os.path.join(item.path, item.text(0)) result = QInputDialog.getText(self, self.tr("New Folder"), self.tr("Enter the Folder Name:")) folderName = result[0] if result[1] and folderName.strip() != '': folderName = os.path.join(pathForFolder, folderName) file_manager.create_folder(folderName) name = file_manager.get_basename(folderName) subitem = ProjectItem(item, name, pathForFolder) subitem.setToolTip(0, name) subitem.setIcon(0, QIcon(resources.IMAGES['tree-folder'])) self._refresh_project(item) def _delete_file(self): item = self.currentItem() val = QMessageBox.question(self, self.tr("Delete File"), self.tr("Do you want to delete the following file: ") + os.path.join(item.path, item.text(0)), QMessageBox.Yes, QMessageBox.No) if val == QMessageBox.Yes: path = file_manager.create_path(item.path, item.text(0)) file_manager.delete_file(item.path, item.text(0)) index = item.parent().indexOfChild(item) item.parent().takeChild(index) mainContainer = main_container.MainContainer() if mainContainer.is_open(path): mainContainer.close_deleted_file(path) def _delete_folder(self): item = self.currentItem() val = QMessageBox.question(self, self.tr("Delete Folder"), self.tr("Do you want to delete the following folder: ") + os.path.join(item.path, item.text(0)), QMessageBox.Yes, QMessageBox.No) if val == QMessageBox.Yes: file_manager.delete_folder(item.path, item.text(0)) index = item.parent().indexOfChild(item) item.parent().takeChild(index) def _rename_file(self): item = self.currentItem() if item.parent() is None: pathForFile = item.path else: pathForFile = os.path.join(item.path, item.text(0)) result = QInputDialog.getText(self, self.tr("Rename File"), self.tr("Enter New File Name:"), text=item.text(0)) fileName = result[0] if result[1] and fileName.strip() != '': fileName = os.path.join( file_manager.get_folder(pathForFile), fileName) if pathForFile == fileName: return try: fileName = file_manager.rename_file(pathForFile, fileName) name = file_manager.get_basename(fileName) mainContainer = main_container.MainContainer() if mainContainer.is_open(pathForFile): mainContainer.change_open_tab_name(pathForFile, fileName) subitem = ProjectItem(item.parent(), name, file_manager.get_folder(fileName)) subitem.setToolTip(0, name) subitem.setIcon(0, self._get_file_icon(name)) index = item.parent().indexOfChild(item) subitem.parent().takeChild(index) except file_manager.NinjaFileExistsException as ex: QMessageBox.information(self, self.tr("File Already Exists"), (self.tr("Invalid Path: the file '%s' already exists.") % ex.filename)) def _copy_file(self): #get the selected QTreeWidgetItem item = self.currentItem() if item.parent() is None: pathForFile = item.path else: pathForFile = os.path.join(item.path, item.text(0)) pathProjects = [p.path for p in self.get_open_projects()] addToProject = ui_tools.AddToProject(pathProjects, self) addToProject.setWindowTitle(self.tr("Copy File to")) addToProject.exec_() if not addToProject.pathSelected: return name = QInputDialog.getText(self, self.tr("Copy File"), self.tr("File Name:"), text=item.text(0))[0] if not name: QMessageBox.information(self, self.tr("Invalid Name"), self.tr("The file name is empty, please enter a name")) return path = file_manager.create_path(addToProject.pathSelected, name) try: content = file_manager.read_file_content(pathForFile) path = file_manager.store_file_content(path, content, newFile=True) self.add_existing_file(path) except file_manager.NinjaFileExistsException as ex: QMessageBox.information(self, self.tr("File Already Exists"), (self.tr("Invalid Path: the file '%s' already exists.") % ex.filename)) def _move_file(self): item = self.currentItem() if item.parent() is None: pathForFile = item.path else: pathForFile = os.path.join(item.path, item.text(0)) pathProjects = [p.path for p in self.get_open_projects()] addToProject = ui_tools.AddToProject(pathProjects, self) addToProject.setWindowTitle(self.tr("Copy File to")) addToProject.exec_() if not addToProject.pathSelected: return name = file_manager.get_basename(pathForFile) path = file_manager.create_path(addToProject.pathSelected, name) try: content = file_manager.read_file_content(pathForFile) path = file_manager.store_file_content(path, content, newFile=True) file_manager.delete_file(pathForFile) index = item.parent().indexOfChild(item) item.parent().takeChild(index) self.add_existing_file(path) # Update path of opened file main = main_container.MainContainer() if main.is_open(pathForFile): widget = main.get_widget_for_path(pathForFile) if widget: widget.ID = path except file_manager.NinjaFileExistsException as ex: QMessageBox.information(self, self.tr("File Already Exists"), (self.tr("Invalid Path: the file '%s' already exists.") % ex.filename)) def _edit_ui_file(self): item = self.currentItem() if item.parent() is None: pathForFile = item.path else: pathForFile = os.path.join(item.path, item.text(0)) pathForFile = "file://%s" % pathForFile #open the correct program to edit Qt UI files! QDesktopServices.openUrl(QUrl(pathForFile, QUrl.TolerantMode)) def loading_project(self, folder, reload_item=None): loadingItem = ui_tools.LoadingItem() if reload_item is None: parent = self else: parent = reload_item.parent() item = loadingItem.add_item_to_tree(folder, self, ProjectItem, parent) self._loading_items[folder] = item def remove_loading_icon(self, folder): item = self._loading_items.pop(folder, None) if item is not None: index = self.indexOfTopLevelItem(item) self.takeTopLevelItem(index) def load_project(self, folderStructure, folder): if not folder: return self.remove_loading_icon(folder) name = file_manager.get_basename(folder) item = ProjectTree(self, name, folder) item.isFolder = True item.setToolTip(0, folder) item.setIcon(0, QIcon(resources.IMAGES['tree-app'])) self._projects[folder] = item if folderStructure[folder][1] is not None: folderStructure[folder][1].sort() self._load_folder(folderStructure, folder, item) item.setExpanded(True) if len(self._projects) == 1: self.set_default_project(item) if self.currentItem() is None: item.setSelected(True) self.setCurrentItem(item) self._fileWatcher.add_watch(folder) completion_daemon.add_project_folder(folder) self.sortItems(0, Qt.AscendingOrder) def _load_folder(self, folderStructure, folder, parentItem): """Load the Tree Project structure recursively.""" # Avoid failing if for some reason folder is not found # Might be the case of special files, as links or versioning files files, folders = folderStructure.get(folder, [None, None]) if files is not None: files.sort() for i in files: subitem = ProjectItem(parentItem, i, folder) subitem.setToolTip(0, i) subitem.setIcon(0, self._get_file_icon(i)) if folders is not None: folders.sort() for _folder in folders: if _folder.startswith('.'): continue subfolder = ProjectItem(parentItem, _folder, folder, True) subfolder.setToolTip(0, _folder) subfolder.setIcon(0, QIcon(resources.IMAGES['tree-folder'])) subFolderPath = os.path.join(folder, _folder) if subFolderPath in self.state_index: subfolder.setExpanded(True) self._load_folder(folderStructure, subFolderPath, subfolder) def _get_file_icon(self, fileName): return QIcon(self.images.get(file_manager.get_file_extension(fileName), resources.IMAGES['tree-generic'])) def get_item_for_path(self, path): items = self.findItems(file_manager.get_basename(path), Qt.MatchRecursive, 0) folder = file_manager.get_folder(path) for item in items: if file_manager.belongs_to_folder(folder, item.path): return item def get_project_by_name(self, projectName): """Return the name of the project item based on the project name.""" # Return the item or None if it's not found for item in list(self._projects.values()): if item.name == projectName: return item def get_selected_project_path(self): if self._actualProject: return self._actualProject.path return None def get_project_main_file(self): if self._actualProject: return self._actualProject.mainFile return '' def get_selected_project_type(self): rootItem = self._get_project_root() return rootItem.projectType def get_selected_project_lang(self): rootItem = self._get_project_root() return rootItem.lang() def get_open_projects(self): return list(self._projects.values()) def is_open(self, path): return len([True for item in list(self._projects.values()) if item.path == path]) != 0 def _set_current_project(self, path): for item in list(self._projects.values()): if item.path == path: self.set_default_project(item) break def _close_open_projects(self): self.__enableCloseNotification = False for i in range(self.topLevelItemCount()): self.setCurrentItem(self.topLevelItem(0)) self._close_project() self.__enableCloseNotification = True self._projects = {} def keyPressEvent(self, event): item = self.currentItem() if event.key() in (Qt.Key_Return, Qt.Key_Enter): self._open_file(item, 0) elif event.key() in (Qt.Key_Space, Qt.Key_Slash) and item.isFolder: expand = not item.isExpanded() item.setExpanded(expand) elif event.key() == Qt.Key_Left and not item.isExpanded(): parent = item.parent() if parent: parent.setExpanded(False) self.setCurrentItem(parent) return super(TreeProjectsWidget, self).keyPressEvent(event) class ProjectItem(QTreeWidgetItem): def __init__(self, parent, name, path, isFolder=False): QTreeWidgetItem.__init__(self, parent) self.setText(0, name) self.path = path self.isFolder = isFolder @property def isProject(self): #flag to check if the item is a project ALWAYS FALSE return False def lang(self): return file_manager.get_file_extension(self.text(0)) def get_full_path(self): ''' Returns the full path of the file ''' return os.path.join(self.path, self.text(0)) def set_item_icon(self, icon): self.setIcon(0, icon) def __lt__(self, otherItem): column = self.treeWidget().sortColumn() my_text = ('1%s' % self.text(column).lower() if self.isFolder else '0%s' % self.text(column).lower()) other_text = ('1%s' % otherItem.text(column).lower() if otherItem.isFolder else '0%s' % otherItem.text(column).lower()) return my_text < other_text class ProjectTree(QTreeWidgetItem): def __init__(self, parent, _name, path): QTreeWidgetItem.__init__(self, parent) self._parent = parent self.setText(0, _name) self.path = path self.isFolder = True self.setForeground(0, QBrush(QColor(255, 165, 0))) project = json_manager.read_ninja_project(path) self.name = project.get('name', '') if self.name == '': self.name = _name self.setText(0, self.name) self.projectType = project.get('project-type', '') self.description = project.get('description', '') self.url = project.get('url', '') self.license = project.get('license', '') self.mainFile = project.get('mainFile', '') self.preExecScript = project.get('preExecScript', '') self.postExecScript = project.get('postExecScript', '') self.indentation = project.get('indentation', settings.INDENT) self.useTabs = project.get('use-tabs', settings.USE_TABS) self.extensions = project.get('supported-extensions', settings.SUPPORTED_EXTENSIONS) self.pythonPath = project.get('pythonPath', settings.PYTHON_PATH) self.PYTHONPATH = project.get('PYTHONPATH', '') self.additional_builtins = project.get('additional_builtins', []) self.programParams = project.get('programParams', '') self.venv = project.get('venv', '') self.related_projects = project.get('relatedProjects', []) self.update_paths() self.addedToConsole = False def update_paths(self): for path in self.related_projects: completion_daemon.add_project_folder(path) @property def isProject(self): #flag to check if the item is a project ALWAYS TRUE return True def lang(self): if self.mainFile != '': return file_manager.get_file_extension(self.mainFile) return 'py' def get_full_path(self): ''' Returns the full path of the project ''' project_file = json_manager.get_ninja_project_file(self.path) if not project_file: # FIXME: If we dont have a project file project_file = '' # we should do SOMETHING! like kill zombies! return os.path.join(self.path, project_file) class FoldingContextMenu(QMenu): """ This class represents a menu for Folding/Unfolding task """ def __init__(self, tree): super(FoldingContextMenu, self).__init__() self.setTitle(self.tr("Fold/Unfold")) self._tree = tree fold_project = self.addAction(self.tr("Fold the project")) unfold_project = self.addAction(self.tr("Unfold the project")) self.addSeparator() fold_all_projects = self.addAction(self.tr("Fold all projects")) unfold_all_projects = self.addAction(self.tr("Unfold all projects")) self.connect(fold_project, SIGNAL("triggered()"), lambda: self._fold_unfold_project(False)) self.connect(unfold_project, SIGNAL("triggered()"), lambda: self._fold_unfold_project(True)) self.connect(fold_all_projects, SIGNAL("triggered()"), self._fold_all_projects) self.connect(unfold_all_projects, SIGNAL("triggered()"), self._unfold_all_projects) def _recursive_fold_unfold(self, item, expand): if item.isFolder: item.setExpanded(expand) for index in range(item.childCount()): child = item.child(index) self._recursive_fold_unfold(child, expand) def _fold_unfold_project(self, expand): """ Fold the current project """ root = self._tree._get_project_root(item=self._tree.currentItem()) childs = root.childCount() if childs: root.setExpanded(expand) for index in range(childs): item = root.child(index) self._recursive_fold_unfold(item, expand) def _fold_all_projects(self): """ Fold all projects """ self._tree.collapseAll() def _unfold_all_projects(self): """ Unfold all project """ self._tree.expandAll() ninja-ide-2.3/ninja_ide/gui/explorer/tree_symbols_widget.py000066400000000000000000000257101216641277400242230ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QTreeWidget from PyQt4.QtGui import QTreeWidgetItem from PyQt4.QtGui import QIcon from PyQt4.QtGui import QAbstractItemView from PyQt4.QtGui import QHeaderView from PyQt4.QtGui import QCursor from PyQt4.QtGui import QMenu from PyQt4.QtCore import Qt from PyQt4.QtCore import SIGNAL from ninja_ide import resources class TreeSymbolsWidget(QTreeWidget): ############################################################################### # TreeSymbolsWidget SIGNALS ############################################################################### """ goToDefinition(int) """ ############################################################################### def __init__(self): QTreeWidget.__init__(self) self.header().setHidden(True) self.setSelectionMode(self.SingleSelection) self.setAnimated(True) self.header().setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) self.header().setResizeMode(0, QHeaderView.ResizeToContents) self.header().setStretchLastSection(False) self.actualSymbols = ('', {}) self.docstrings = {} self.collapsedItems = {} self.connect(self, SIGNAL("itemClicked(QTreeWidgetItem *, int)"), self._go_to_definition) self.connect(self, SIGNAL("itemActivated(QTreeWidgetItem *, int)"), self._go_to_definition) self.setContextMenuPolicy(Qt.CustomContextMenu) self.connect(self, SIGNAL("customContextMenuRequested(const QPoint &)"), self._menu_context_tree) self.connect(self, SIGNAL("itemCollapsed(QTreeWidgetItem *)"), self._item_collapsed) self.connect(self, SIGNAL("itemExpanded(QTreeWidgetItem *)"), self._item_expanded) def select_current_item(self, line, col): #TODO #print line, col pass def _menu_context_tree(self, point): index = self.indexAt(point) if not index.isValid(): return menu = QMenu(self) f_all = menu.addAction(self.tr("Fold all")) u_all = menu.addAction(self.tr("Unfold all")) menu.addSeparator() u_class = menu.addAction(self.tr("Unfold classes")) u_class_method = menu.addAction(self.tr("Unfold classes and methods")) u_class_attr = menu.addAction(self.tr("Unfold classes and attributes")) menu.addSeparator() #save_state = menu.addAction(self.tr("Save State")) self.connect(f_all, SIGNAL("triggered()"), lambda: self.collapseAll()) self.connect(u_all, SIGNAL("triggered()"), lambda: self.expandAll()) self.connect(u_class, SIGNAL("triggered()"), self._unfold_class) self.connect(u_class_method, SIGNAL("triggered()"), self._unfold_class_method) self.connect(u_class_attr, SIGNAL("triggered()"), self._unfold_class_attribute) #self.connect(save_state, SIGNAL("triggered()"), #self._save_symbols_state) menu.exec_(QCursor.pos()) def _get_classes_root(self): class_root = None for i in range(self.topLevelItemCount()): item = self.topLevelItem(i) if item.isClass and not item.isClickable: class_root = item break return class_root def _unfold_class(self): self.collapseAll() classes_root = self._get_classes_root() if not classes_root: return classes_root.setExpanded(True) def _unfold_class_method(self): self.expandAll() classes_root = self._get_classes_root() if not classes_root: return #for each class! for i in range(classes_root.childCount()): class_item = classes_root.child(i) #for each attribute or functions for j in range(class_item.childCount()): item = class_item.child(j) #METHODS ROOT!! if not item.isMethod and not item.isClickable: item.setExpanded(False) break def _unfold_class_attribute(self): self.expandAll() classes_root = self._get_classes_root() if not classes_root: return #for each class! for i in range(classes_root.childCount()): class_item = classes_root.child(i) #for each attribute or functions for j in range(class_item.childCount()): item = class_item.child(j) #ATTRIBUTES ROOT!! if not item.isAttribute and not item.isClickable: item.setExpanded(False) break def _save_symbols_state(self): #filename = self.actualSymbols[0] #TODO: persist self.collapsedItems[filename] in QSettings pass def _get_expand(self, item): """ Returns True or False to be used as setExpanded() with the items It method is based on the click that the user made in the tree """ name = self._get_unique_name(item) filename = self.actualSymbols[0] collapsed_items = self.collapsedItems.get(filename, []) can_check = (not item.isClickable) or item.isClass or item.isMethod if can_check and name in collapsed_items: return False return True @staticmethod def _get_unique_name(item): """ Returns a string used as unique name """ # className_Attributes/className_Functions parent = item.parent() if parent: return "%s_%s" % (parent.text(0), item.text(0)) return "_%s" % item.text(0) def update_symbols_tree(self, symbols, filename='', parent=None): if not parent: if filename == self.actualSymbols[0] and \ self.actualSymbols[1] and not symbols: return if symbols == self.actualSymbols[1]: # Nothing new then return return # we have new symbols refresh it self.clear() self.actualSymbols = (filename, symbols) self.docstrings = symbols.get('docstrings', {}) parent = self if 'attributes' in symbols: globalAttribute = ItemTree(parent, [self.tr("Attributes")]) globalAttribute.isClickable = False globalAttribute.isAttribute = True globalAttribute.setExpanded(self._get_expand(globalAttribute)) for glob in sorted(symbols['attributes']): globItem = ItemTree(globalAttribute, [glob], lineno=symbols['attributes'][glob]) globItem.isAttribute = True globItem.setIcon(0, QIcon(resources.IMAGES['attribute'])) globItem.setExpanded(self._get_expand(globItem)) if 'functions' in symbols and symbols['functions']: functionsItem = ItemTree(parent, [self.tr("Functions")]) functionsItem.isClickable = False functionsItem.isMethod = True functionsItem.setExpanded(self._get_expand(functionsItem)) for func in sorted(symbols['functions']): item = ItemTree(functionsItem, [func], lineno=symbols['functions'][func]['lineno']) tooltip = self.create_tooltip( func, symbols['functions'][func]['lineno']) item.isMethod = True item.setIcon(0, QIcon(resources.IMAGES['function'])) item.setToolTip(0, tooltip) item.setExpanded(self._get_expand(item)) self.update_symbols_tree( symbols['functions'][func]['functions'], parent=item) if 'classes' in symbols and symbols['classes']: classItem = ItemTree(parent, [self.tr("Classes")]) classItem.isClickable = False classItem.isClass = True classItem.setExpanded(self._get_expand(classItem)) for claz in sorted(symbols['classes']): line_number = symbols['classes'][claz]['lineno'] item = ItemTree(classItem, [claz], lineno=line_number) item.isClass = True tooltip = self.create_tooltip(claz, line_number) item.setToolTip(0, tooltip) item.setIcon(0, QIcon(resources.IMAGES['class'])) item.setExpanded(self._get_expand(item)) self.update_symbols_tree(symbols['classes'][claz]['members'], parent=item) def _go_to_definition(self, item): if item.isClickable: self.emit(SIGNAL("goToDefinition(int)"), item.lineno - 1) def create_tooltip(self, name, lineno): doc = self.docstrings.get(lineno, None) if doc is None: doc = '' else: doc = '\n' + doc tooltip = name + doc return tooltip def _item_collapsed(self, item): super(TreeSymbolsWidget, self).collapseItem(item) can_check = (not item.isClickable) or item.isClass or item.isMethod if can_check: n = self._get_unique_name(item) filename = self.actualSymbols[0] self.collapsedItems.setdefault(filename, []) if not n in self.collapsedItems[filename]: self.collapsedItems[filename].append(n) def _item_expanded(self, item): super(TreeSymbolsWidget, self).expandItem(item) n = self._get_unique_name(item) filename = self.actualSymbols[0] if n in self.collapsedItems.get(filename, []): self.collapsedItems[filename].remove(n) if not len(self.collapsedItems[filename]): # no more items, free space del self.collapsedItems[filename] def clean(self): """ Reset the tree and reset attributes """ self.clear() self.collapsedItems = {} class ItemTree(QTreeWidgetItem): def __init__(self, parent, name, lineno=None): QTreeWidgetItem.__init__(self, parent, name) self.lineno = lineno self.isClickable = True self.isAttribute = False self.isClass = False self.isMethod = False ninja-ide-2.3/ninja_ide/gui/ide.py000066400000000000000000000644431216641277400170600ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import sys from PyQt4.QtGui import QApplication from PyQt4.QtGui import QMainWindow from PyQt4.QtGui import QMessageBox from PyQt4.QtGui import QSplashScreen from PyQt4.QtGui import QIcon from PyQt4.QtGui import QPixmap from PyQt4.QtGui import QToolBar from PyQt4.QtGui import QToolTip from PyQt4.QtGui import QFont from PyQt4.QtCore import Qt from PyQt4.QtCore import QLibraryInfo from PyQt4.QtCore import QLocale from PyQt4.QtCore import QSettings from PyQt4.QtCore import QCoreApplication from PyQt4.QtCore import QTranslator from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import QTextCodec from PyQt4.QtCore import QSizeF from PyQt4.QtCore import QPointF from PyQt4.QtNetwork import QLocalServer from ninja_ide import resources from ninja_ide.core import plugin_manager from ninja_ide.core import plugin_services from ninja_ide.core import settings from ninja_ide.core import file_manager from ninja_ide.core import ipc from ninja_ide.gui import updates from ninja_ide.gui import actions from ninja_ide.gui.dialogs import preferences from ninja_ide.gui.dialogs import traceback_widget from ninja_ide.gui.dialogs import python_detect_dialog from ninja_ide.tools import json_manager from ninja_ide.tools.completion import completion_daemon #NINJA-IDE Containers from ninja_ide.gui import central_widget from ninja_ide.gui.main_panel import main_container from ninja_ide.gui.explorer import explorer_container from ninja_ide.gui.misc import misc_container from ninja_ide.gui import status_bar #NINJA-IDE Menus from ninja_ide.gui.menus import menu_about from ninja_ide.gui.menus import menu_file from ninja_ide.gui.menus import menu_edit from ninja_ide.gui.menus import menu_view from ninja_ide.gui.menus import menu_plugins from ninja_ide.gui.menus import menu_project from ninja_ide.gui.menus import menu_source ############################################################################### # LOGGER INITIALIZE ############################################################################### from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.gui.ide') ############################################################################### # IDE: MAIN CONTAINER ############################################################################### __ideInstance = None def IDE(*args, **kw): global __ideInstance if __ideInstance is None: __ideInstance = __IDE(*args, **kw) return __ideInstance class __IDE(QMainWindow): ############################################################################### # SIGNALS # # goingDown() ############################################################################### def __init__(self, start_server=False): QMainWindow.__init__(self) self.setWindowTitle('NINJA-IDE {Ninja-IDE Is Not Just Another IDE}') self.setMinimumSize(700, 500) #Load the size and the position of the main window self.load_window_geometry() self.__project_to_open = 0 #Start server if needed self.s_listener = None if start_server: self.s_listener = QLocalServer() self.s_listener.listen("ninja_ide") self.connect(self.s_listener, SIGNAL("newConnection()"), self._process_connection) #Profile handler self.profile = None #Opacity self.opacity = settings.MAX_OPACITY #Define Actions object before the UI self.actions = actions.Actions() #StatusBar self.status = status_bar.StatusBar(self) self.status.hide() self.setStatusBar(self.status) #Main Widget - Create first than everything else self.central = central_widget.CentralWidget(self) self.load_ui(self.central) self.setCentralWidget(self.central) #ToolBar self.toolbar = QToolBar(self) self.toolbar.setToolTip(self.tr("Press and Drag to Move")) self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly) self.addToolBar(settings.TOOLBAR_AREA, self.toolbar) if settings.HIDE_TOOLBAR: self.toolbar.hide() #Install Shortcuts after the UI has been initialized self.actions.install_shortcuts(self) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.actions.update_explorer) #Menu menubar = self.menuBar() file_ = menubar.addMenu(self.tr("&File")) edit = menubar.addMenu(self.tr("&Edit")) view = menubar.addMenu(self.tr("&View")) source = menubar.addMenu(self.tr("&Source")) project = menubar.addMenu(self.tr("&Project")) self.pluginsMenu = menubar.addMenu(self.tr("&Addins")) about = menubar.addMenu(self.tr("Abou&t")) #The order of the icons in the toolbar is defined by this calls self._menuFile = menu_file.MenuFile(file_, self.toolbar, self) self._menuView = menu_view.MenuView(view, self.toolbar, self) self._menuEdit = menu_edit.MenuEdit(edit, self.toolbar) self._menuSource = menu_source.MenuSource(source) self._menuProject = menu_project.MenuProject(project, self.toolbar) self._menuPlugins = menu_plugins.MenuPlugins(self.pluginsMenu) self._menuAbout = menu_about.MenuAbout(about) self.load_toolbar() #Plugin Manager services = { 'editor': plugin_services.MainService(), 'toolbar': plugin_services.ToolbarService(self.toolbar), 'menuApp': plugin_services.MenuAppService(self.pluginsMenu), 'explorer': plugin_services.ExplorerService(), 'misc': plugin_services.MiscContainerService(self.misc)} serviceLocator = plugin_manager.ServiceLocator(services) self.plugin_manager = plugin_manager.PluginManager(resources.PLUGINS, serviceLocator) self.plugin_manager.discover() #load all plugins! self.plugin_manager.load_all() #Tray Icon self.trayIcon = updates.TrayIconUpdates(self) self.trayIcon.show() self.connect(self._menuFile, SIGNAL("openFile(QString)"), self.mainContainer.open_file) self.connect(self.mainContainer, SIGNAL("fileSaved(QString)"), self.show_status_message) self.connect(self.mainContainer, SIGNAL("recentTabsModified(QStringList)"), self._menuFile.update_recent_files) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.actions.update_migration_tips) self.connect(self.mainContainer, SIGNAL("updateFileMetadata(QString)"), self.actions.update_migration_tips) self.connect(self.mainContainer, SIGNAL("migrationAnalyzed()"), self.actions.update_migration_tips) def _process_connection(self): connection = self.s_listener.nextPendingConnection() connection.waitForReadyRead() data = connection.readAll() connection.close() if data: files, projects = str(data).split(ipc.project_delimiter, 1) files = [(x.split(':')[0], int(x.split(':')[1])) for x in files.split(ipc.file_delimiter)] projects = projects.split(ipc.project_delimiter) self.load_session_files_projects(files, [], projects, None) def load_toolbar(self): self.toolbar.clear() toolbar_items = {} toolbar_items.update(self._menuFile.toolbar_items) toolbar_items.update(self._menuView.toolbar_items) toolbar_items.update(self._menuEdit.toolbar_items) toolbar_items.update(self._menuSource.toolbar_items) toolbar_items.update(self._menuProject.toolbar_items) for item in settings.TOOLBAR_ITEMS: if item == 'separator': self.toolbar.addSeparator() else: tool_item = toolbar_items.get(item, None) if tool_item is not None: self.toolbar.addAction(tool_item) #load action added by plugins, This is a special case when reload #the toolbar after save the preferences widget for toolbar_action in settings.get_toolbar_item_for_plugins(): self.toolbar.addAction(toolbar_action) def load_external_plugins(self, paths): for path in paths: self.plugin_manager.add_plugin_dir(path) #load all plugins! self.plugin_manager.discover() self.plugin_manager.load_all() def show_status_message(self, message): self.status.showMessage(message, 2000) def load_ui(self, centralWidget): #Set Application Font for ToolTips QToolTip.setFont(QFont(settings.FONT_FAMILY, 10)) #Create Main Container to manage Tabs self.mainContainer = main_container.MainContainer(self) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.change_window_title) self.connect(self.mainContainer, SIGNAL("locateFunction(QString, QString, bool)"), self.actions.locate_function) self.connect(self.mainContainer, SIGNAL("navigateCode(bool, int)"), self.actions.navigate_code_history) self.connect(self.mainContainer, SIGNAL("addBackItemNavigation()"), self.actions.add_back_item_navigation) self.connect(self.mainContainer, SIGNAL("updateFileMetadata()"), self.actions.update_explorer) self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"), self.actions.update_explorer) self.connect(self.mainContainer, SIGNAL("openPreferences()"), self._show_preferences) self.connect(self.mainContainer, SIGNAL("dontOpenStartPage()"), self._dont_show_start_page_again) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.status.handle_tab_changed) # When close the last tab cleanup self.connect(self.mainContainer, SIGNAL("allTabsClosed()"), self._last_tab_closed) # Update symbols self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"), self.status.explore_file_code) #Create Explorer Panel self.explorer = explorer_container.ExplorerContainer(self) self.connect(self.central, SIGNAL("splitterCentralRotated()"), self.explorer.rotate_tab_position) self.connect(self.explorer, SIGNAL("updateLocator()"), self.status.explore_code) self.connect(self.explorer, SIGNAL("goToDefinition(int)"), self.actions.editor_go_to_line) self.connect(self.explorer, SIGNAL("projectClosed(QString)"), self.actions.close_files_from_project) #Create Misc Bottom Container self.misc = misc_container.MiscContainer(self) self.connect(self.mainContainer, SIGNAL("findOcurrences(QString)"), self.misc.show_find_occurrences) centralWidget.insert_central_container(self.mainContainer) centralWidget.insert_lateral_container(self.explorer) centralWidget.insert_bottom_container(self.misc) if self.explorer.count() == 0: centralWidget.change_explorer_visibility(force_hide=True) self.connect(self.mainContainer, SIGNAL("cursorPositionChange(int, int)"), self.central.lateralPanel.update_line_col) # TODO: Change current symbol on move #self.connect(self.mainContainer, #SIGNAL("cursorPositionChange(int, int)"), #self.explorer.update_current_symbol) self.connect(self.mainContainer, SIGNAL("enabledFollowMode(bool)"), self.central.enable_follow_mode_scrollbar) if settings.SHOW_START_PAGE: self.mainContainer.show_start_page() def _last_tab_closed(self): """ Called when the last tasb is closed """ self.explorer.cleanup_tabs() def _show_preferences(self): pref = preferences.PreferencesWidget(self.mainContainer) pref.show() def _dont_show_start_page_again(self): settings.SHOW_START_PAGE = False qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.beginGroup('preferences') qsettings.beginGroup('general') qsettings.setValue('showStartPage', settings.SHOW_START_PAGE) qsettings.endGroup() qsettings.endGroup() self.mainContainer.actualTab.close_tab() def load_session_files_projects(self, filesTab1, filesTab2, projects, current_file, recent_files=None): self.__project_to_open = len(projects) self.connect(self.explorer, SIGNAL("projectOpened(QString)"), self._set_editors_project_data) self.explorer.open_session_projects(projects, notIDEStart=False) self.mainContainer.open_files(filesTab1, notIDEStart=False) self.mainContainer.open_files(filesTab2, mainTab=False, notIDEStart=False) if current_file: self.mainContainer.open_file(current_file, notStart=False) if recent_files is not None: self._menuFile.update_recent_files(recent_files) def _set_editors_project_data(self): self.__project_to_open -= 1 if self.__project_to_open == 0: self.disconnect(self.explorer, SIGNAL("projectOpened(QString)"), self._set_editors_project_data) self.mainContainer.update_editor_project() def open_file(self, filename): if filename: self.mainContainer.open_file(filename) def open_project(self, project): if project: self.actions.open_project(project) def __get_profile(self): return self.profile def __set_profile(self, profileName): self.profile = profileName if self.profile is not None: self.setWindowTitle('NINJA-IDE (PROFILE: %s)' % self.profile) else: self.setWindowTitle( 'NINJA-IDE {Ninja-IDE Is Not Just Another IDE}') Profile = property(__get_profile, __set_profile) def change_window_title(self, title): if self.profile is None: self.setWindowTitle('NINJA-IDE - %s' % title) else: self.setWindowTitle('NINJA-IDE (PROFILE: %s) - %s' % ( self.profile, title)) currentEditor = self.mainContainer.get_actual_editor() if currentEditor is not None: line = currentEditor.textCursor().blockNumber() + 1 col = currentEditor.textCursor().columnNumber() self.central.lateralPanel.update_line_col(line, col) def wheelEvent(self, event): if event.modifiers() == Qt.ShiftModifier: if event.delta() == 120 and self.opacity < settings.MAX_OPACITY: self.opacity += 0.1 elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY: self.opacity -= 0.1 self.setWindowOpacity(self.opacity) event.ignore() else: QMainWindow.wheelEvent(self, event) def save_settings(self): """Save the settings before the application is closed with QSettings. Info saved: Tabs and projects opened, windows state(size and position). """ qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) editor_widget = self.mainContainer.get_actual_editor() current_file = '' if editor_widget is not None: current_file = editor_widget.ID if qsettings.value('preferences/general/loadFiles', True, type=bool): openedFiles = self.mainContainer.get_opened_documents() projects_obj = self.explorer.get_opened_projects() projects = [p.path for p in projects_obj] qsettings.setValue('openFiles/projects', projects) if len(openedFiles) > 0: qsettings.setValue('openFiles/mainTab', openedFiles[0]) if len(openedFiles) == 2: qsettings.setValue('openFiles/secondaryTab', openedFiles[1]) qsettings.setValue('openFiles/currentFile', current_file) qsettings.setValue('openFiles/recentFiles', self.mainContainer._tabMain.get_recent_files_list()) qsettings.setValue('preferences/editor/bookmarks', settings.BOOKMARKS) qsettings.setValue('preferences/editor/breakpoints', settings.BREAKPOINTS) qsettings.setValue('preferences/general/toolbarArea', self.toolBarArea(self.toolbar)) #Save if the windows state is maximixed if(self.isMaximized()): qsettings.setValue("window/maximized", True) else: qsettings.setValue("window/maximized", False) #Save the size and position of the mainwindow qsettings.setValue("window/size", self.size()) qsettings.setValue("window/pos", self.pos()) #Save the size of de splitters qsettings.setValue("window/central/areaSize", self.central.get_area_sizes()) qsettings.setValue("window/central/mainSize", self.central.get_main_sizes()) #Save the toolbar visibility if not self.toolbar.isVisible() and self.menuBar().isVisible(): qsettings.setValue("window/hide_toolbar", True) else: qsettings.setValue("window/hide_toolbar", False) #Save Misc state qsettings.setValue("window/show_misc", self.misc.isVisible()) #Save Profiles if self.profile is not None: self.actions.save_profile(self.profile) else: qsettings.setValue('ide/profiles', settings.PROFILES) def load_window_geometry(self): """Load from QSettings the window size of de Ninja IDE""" qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) if qsettings.value("window/maximized", True, type=bool): self.setWindowState(Qt.WindowMaximized) else: self.resize(qsettings.value("window/size", QSizeF(800, 600).toSize(), type='QSize')) self.move(qsettings.value("window/pos", QPointF(100, 100).toPoint(), type='QPoint')) def closeEvent(self, event): if self.s_listener: self.s_listener.close() if (settings.CONFIRM_EXIT and self.mainContainer.check_for_unsaved_tabs()): unsaved_files = self.mainContainer.get_unsaved_files() txt = '\n'.join(unsaved_files) val = QMessageBox.question(self, self.tr("Some changes were not saved"), (self.tr("%s\n\nDo you want to save them?") % txt), QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel) if val == QMessageBox.Yes: #Saves all open files self.mainContainer.save_all() if val == QMessageBox.Cancel: event.ignore() self.emit(SIGNAL("goingDown()")) self.save_settings() completion_daemon.shutdown_daemon() #close python documentation server (if running) self.mainContainer.close_python_doc() #Shutdown PluginManager self.plugin_manager.shutdown() def notify_plugin_errors(self): errors = self.plugin_manager.errors if errors: plugin_error_dialog = traceback_widget.PluginErrorDialog() for err_tuple in errors: plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1]) #show the dialog plugin_error_dialog.exec_() def show_python_detection(self): suggested = settings.detect_python_path() if suggested: dialog = python_detect_dialog.PythonDetectDialog(suggested, self) dialog.show() ############################################################################### # START NINJA-IDE ############################################################################### def start(filenames=None, projects_path=None, extra_plugins=None, linenos=None): app = QApplication(sys.argv) QCoreApplication.setOrganizationName('NINJA-IDE') QCoreApplication.setOrganizationDomain('NINJA-IDE') QCoreApplication.setApplicationName('NINJA-IDE') app.setWindowIcon(QIcon(resources.IMAGES['icon'])) # Check if there is another session of ninja-ide opened # and in that case send the filenames and projects to that session running = ipc.is_running() start_server = not running[0] if running[0] and (filenames or projects_path): sended = ipc.send_data(running[1], filenames, projects_path, linenos) running[1].close() if sended: sys.exit() else: running[1].close() # Create and display the splash screen splash_pix = QPixmap(resources.IMAGES['splash']) splash = QSplashScreen(splash_pix, Qt.WindowStaysOnTopHint) splash.setMask(splash_pix.mask()) splash.show() app.processEvents() # Set the cursor to unblinking if sys.platform != 'win32': app.setCursorFlashTime(0) #Set the codec for strings (QString) QTextCodec.setCodecForCStrings(QTextCodec.codecForName('utf-8')) #Translator #qsettings = QSettings() qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) language = QLocale.system().name() lang = qsettings.value('preferences/interface/language', defaultValue=language, type='QString') + '.qm' lang_path = file_manager.create_path(resources.LANGS, lang) if file_manager.file_exists(lang_path): settings.LANGUAGE = lang_path elif file_manager.file_exists(file_manager.create_path( resources.LANGS_DOWNLOAD, lang)): settings.LANGUAGE = file_manager.create_path( resources.LANGS_DOWNLOAD, lang) translator = QTranslator() if settings.LANGUAGE: translator.load(settings.LANGUAGE) app.installTranslator(translator) qtTranslator = QTranslator() qtTranslator.load("qt_" + language, QLibraryInfo.location(QLibraryInfo.TranslationsPath)) app.installTranslator(qtTranslator) #Loading Syntax splash.showMessage("Loading Syntax", Qt.AlignRight | Qt.AlignTop, Qt.black) json_manager.load_syntax() #Read Settings splash.showMessage("Loading Settings", Qt.AlignRight | Qt.AlignTop, Qt.black) settings.load_settings() #Set Stylesheet style_applied = False if settings.NINJA_SKIN not in ('Default', 'Classic Theme'): file_name = ("%s.qss" % settings.NINJA_SKIN) qss_file = file_manager.create_path(resources.NINJA_THEME_DOWNLOAD, file_name) if file_manager.file_exists(qss_file): with open(qss_file) as f: qss = f.read() app.setStyleSheet(qss) style_applied = True if not style_applied: if settings.NINJA_SKIN == 'Default': with open(resources.NINJA_THEME) as f: qss = f.read() else: with open(resources.NINJA__THEME_CLASSIC) as f: qss = f.read() app.setStyleSheet(qss) #Loading Schemes splash.showMessage("Loading Schemes", Qt.AlignRight | Qt.AlignTop, Qt.black) scheme = qsettings.value('preferences/editor/scheme', "default", type='QString') if scheme != 'default': scheme = file_manager.create_path(resources.EDITOR_SKINS, scheme + '.color') if file_manager.file_exists(scheme): resources.CUSTOM_SCHEME = json_manager.parse(open(scheme)) #Loading Shortcuts resources.load_shortcuts() #Loading GUI splash.showMessage("Loading GUI", Qt.AlignRight | Qt.AlignTop, Qt.black) ide = IDE(start_server) #Showing GUI ide.show() #Loading Session Files splash.showMessage("Loading Files and Projects", Qt.AlignRight | Qt.AlignTop, Qt.black) #Files in Main Tab main_files = qsettings.value('openFiles/mainTab', []) if main_files is not None: mainFiles = list(main_files) else: mainFiles = list() tempFiles = [] for file_ in mainFiles: fileData = list(file_) if fileData: lineno = fileData[1] tempFiles.append((fileData[0], lineno)) mainFiles = tempFiles #Files in Secondary Tab sec_files = qsettings.value('openFiles/secondaryTab', []) if sec_files is not None: secondaryFiles = list(sec_files) else: secondaryFiles = list() tempFiles = [] for file_ in secondaryFiles: fileData = list(file_) lineno = fileData[1] tempFiles.append((fileData[0], lineno)) secondaryFiles = tempFiles # Recent Files recent = qsettings.value('openFiles/recentFiles', []) if recent is not None: recent_files = list(recent) else: recent_files = list() recent_files = [file_ for file_ in recent_files] #Current File current_file = qsettings.value('openFiles/currentFile', '', type='QString') #Projects projects_list = qsettings.value('openFiles/projects', []) if projects_list is not None: projects = list(projects_list) else: projects = list() projects = [project for project in projects] #Include files received from console args file_with_nro = list([(f[0], f[1] - 1) for f in zip(filenames, linenos)]) file_without_nro = list([(f, 0) for f in filenames[len(linenos):]]) mainFiles += file_with_nro + file_without_nro #Include projects received from console args if projects_path: projects += projects_path ide.load_session_files_projects(mainFiles, secondaryFiles, projects, current_file, recent_files) #Load external plugins if extra_plugins: ide.load_external_plugins(extra_plugins) splash.finish(ide) ide.notify_plugin_errors() ide.show_python_detection() sys.exit(app.exec_()) ninja-ide-2.3/ninja_ide/gui/main_panel/000077500000000000000000000000001216641277400200355ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/gui/main_panel/__init__.py000066400000000000000000000012641216641277400221510ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . ninja-ide-2.3/ninja_ide/gui/main_panel/browser_widget.py000066400000000000000000000047771216641277400234540ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import time from PyQt4.QtGui import QWidget from PyQt4.QtGui import QVBoxLayout from PyQt4.QtCore import Qt from PyQt4.QtCore import QUrl from PyQt4.QtCore import SIGNAL from PyQt4.QtWebKit import QWebView from ninja_ide.core import file_manager from ninja_ide.gui.main_panel import itab_item class BrowserWidget(QWidget, itab_item.ITabItem): ############################################################################### # RecentProjectItem SIGNALS ############################################################################### """ openProject(QString) openPreferences() dontOpenStartPage() """ ############################################################################### def __init__(self, url, process=None, parent=None): QWidget.__init__(self, parent) itab_item.ITabItem.__init__(self) self._id = url self._process = process vbox = QVBoxLayout(self) #Web Frame self.webFrame = QWebView(self) self.webFrame.setAcceptDrops(False) self.webFrame.load(QUrl(url)) vbox.addWidget(self.webFrame) if process is not None: time.sleep(0.5) self.webFrame.load(QUrl(url)) self.webFrame.page().currentFrame().setScrollBarPolicy( Qt.Vertical, Qt.ScrollBarAsNeeded) self.webFrame.page().currentFrame().setScrollBarPolicy( Qt.Horizontal, Qt.ScrollBarAsNeeded) def start_page_operations(self, url): opt = file_manager.get_basename(url.toString()) self.emit(SIGNAL(opt)) def shutdown_pydoc(self): if self._process is not None: self._process.kill() def find_match(self, word, back=False, sensitive=False, whole=False): self.webFrame.page().findText(word)ninja-ide-2.3/ninja_ide/gui/main_panel/class_diagram.py000066400000000000000000000207551216641277400232110ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from PyQt4.QtCore import Qt from PyQt4.QtCore import QRectF from PyQt4.QtGui import QGraphicsItem from PyQt4.QtGui import QRadialGradient from PyQt4.QtGui import QGraphicsTextItem from PyQt4.QtGui import QStyle from PyQt4.QtGui import QColor from PyQt4.QtGui import QPen from PyQt4.QtGui import QWidget from PyQt4.QtGui import QGraphicsView from PyQt4.QtGui import QGraphicsScene from PyQt4.QtGui import QVBoxLayout from ninja_ide.gui.main_panel import itab_item from ninja_ide.tools import introspection from ninja_ide.core import file_manager class ClassDiagram(QWidget, itab_item.ITabItem): def __init__(self, actions, parent=None): QWidget.__init__(self, parent) itab_item.ITabItem.__init__(self) self.actions = actions self.graphicView = QGraphicsView(self) self.scene = QGraphicsScene() self.graphicView.setScene(self.scene) self.graphicView.setViewportUpdateMode( QGraphicsView.BoundingRectViewportUpdate) vLayout = QVBoxLayout(self) self.setLayout(vLayout) vLayout.addWidget(self.graphicView) self.scene.setItemIndexMethod(QGraphicsScene.NoIndex) self.scene.setSceneRect(-200, -200, 400, 400) self.graphicView.setMinimumSize(400, 400) actualProject = self.actions.ide.explorer.get_actual_project() arrClasses = self.actions._locator.get_classes_from_project( actualProject) #FIXME:dirty need to fix self.mX = -400 self.mY = -320 self.hightestY = self.mY filesList = [] for elem in arrClasses: #loking for paths filesList.append(elem[2]) for path in set(filesList): self.create_class(path) def create_class(self, path): content = file_manager.read_file_content(path) items = introspection.obtain_symbols(content) mYPadding = 10 mXPadding = 10 for classname, classdetail in list(items["classes"].items()): cl = ClassModel(self.graphicView, self.scene) cl.set_class_name(classname) self.fill_clases(cl, classdetail[1]) self.scene.addItem(cl) cl.setPos(self.mX, self.mY) self.mX += cl._get_width() + mXPadding if self.hightestY < self.mY + cl.get_height(): self.hightestY = self.mY + cl.get_height() if self.mX > 2000: self.mX = -400 self.mY += self.hightestY + mYPadding def fill_clases(self, classComponent, classContent): funct = classContent['functions'] classComponent.set_functions_list(funct) attr = classContent['attributes'] classComponent.set_attributes_list(attr) def scale_view(self, scaleFactor): factor = self.graphicView.transform().scale( scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width() if factor > 0.05 and factor < 15: self.graphicView.scale(scaleFactor, scaleFactor) def keyPressEvent(self, event): taskList = { Qt.Key_Plus: lambda: self.scaleView(1.2), Qt.Key_Minus: lambda: self.scaleView(1 / 1.2)} if(event.key() in taskList): taskList[event.key()]() else: QWidget.keyPressEvent(self, event) class ClassModel(QGraphicsItem): def __init__(self, parent=None, graphicView=None, graphicScene=None): QGraphicsItem.__init__(self) self.set_default_data() self.className = QGraphicsTextItem(self) self.functionsItem = FunctionsContainerModel(self) self.className.setPlainText(self.defaultClassName) self.setFlag(self.ItemIsMovable) self.setFlag(self.ItemSendsGeometryChanges) self.functionsItem.setPos(0, self.__get_title_height()) self.attributesItem = FunctionsContainerModel(self) self.attributesItem.setPos(0, self.functionsItem.get_height()) def set_default_data(self): self.maxWidth = 100 self.defaultClassNameHeight = 30 self.defaultClassName = "No name" def set_functions_list(self, functionsList): self.functionsItem.set_functions_list(functionsList, "*", "()") self.update_positions() def set_attributes_list(self, attributesList): self.attributesItem.set_functions_list(attributesList) self.update_positions() def set_class_name(self, className): self.className.setPlainText(className) def _get_width(self): self.__calc_max_width() return self.maxWidth def __get_title_height(self): titleHeight = self.defaultClassNameHeight if titleHeight == self.className.document().size().height(): titleHeight = self.className.document().size().height() return titleHeight def get_height(self): summary = self.defaultClassNameHeight summary += self.functionsItem.get_height() summary += self.attributesItem.get_height() return summary def __calc_max_width(self): if self.maxWidth < self.className.document().size().width(): self.maxWidth = self.className.document().size().width() if hasattr(self, "functionsItem"): if self.maxWidth < self.functionsItem.get_width(): self.maxWidth = self.functionsItem.get_width() if hasattr(self, "attributesItem"): if self.maxWidth < self.attributesItem.get_width(): self.maxWidth = self.attributesItem.get_width() def set_bg_color(self, qColor): self.backgroundColor = qColor def set_method_list(self, itemList): self.methodList = itemList def update_positions(self): self.functionsItem.setPos(0, self.__get_title_height()) self.attributesItem.setPos( 0, self.functionsItem.y() + self.functionsItem.get_height()) def paint(self, painter, option, widget): gradient = QRadialGradient(-3, -3, 10) if option.state & QStyle.State_Sunken: gradient.setCenter(3, 3) gradient.setFocalPoint(3, 3) gradient.setColorAt(0, QColor(Qt.yellow).light(120)) else: gradient.setColorAt(0, QColor(Qt.yellow).light(120)) painter.setBrush(gradient) painter.setPen(QPen(Qt.black, 0)) painter.drawRoundedRect(self.boundingRect(), 3, 3) def boundingRect(self): return QRectF(0, 0, self._get_width(), self.get_height()) def add_edge(self, edge): self.myEdge = edge edge.adjust() class FunctionsContainerModel(QGraphicsItem): def __init__(self, parent=None): QGraphicsItem.__init__(self, parent) self.parent = parent self.maxWidth = self.parent._get_width() self.maxHeight = 0 def paint(self, painter, option, widget): painter.drawLine( self.boundingRect().topLeft(), self.boundingRect().topRight()) def set_functions_list(self, functionsList, prefix="", sufix=""): self.funtionsList = functionsList self.funtionsListItems = [] self.maxHeight = 0 tempHeight = 0 for element in functionsList: tempElement = QGraphicsTextItem(self) tempElement.setPlainText(prefix + element + sufix) tempElement.setPos(0, tempHeight) tempHeight += tempElement.document().size().height() if self.maxWidth < tempElement.document().size().width(): self.maxWidth = tempElement.document().size().width() self.funtionsListItems.append(tempElement) self.maxHeight = tempHeight def get_height(self): return self.maxHeight def get_width(self): if self.parent.maxWidth < self.maxWidth: return self.maxWidth else: return self.parent.maxWidth def boundingRect(self): return QRectF(0, 0, self.get_width(), self.get_height()) ninja-ide-2.3/ninja_ide/gui/main_panel/image_viewer.py000066400000000000000000000022731216641277400230560ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QScrollArea from PyQt4.QtGui import QLabel from PyQt4.QtGui import QPixmap from ninja_ide.gui.main_panel import itab_item class ImageViewer(QScrollArea, itab_item.ITabItem): def __init__(self, image): super(ImageViewer, self).__init__() itab_item.ITabItem.__init__(self) self._id = image self.label = QLabel() pixmap = QPixmap(image) self.label.setPixmap(pixmap) self.setWidget(self.label) ninja-ide-2.3/ninja_ide/gui/main_panel/itab_item.py000066400000000000000000000027061216641277400223510ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import class ITabItem(object): EXTRA_MENU = {} def __init__(self): self._id = "" # Should be unique self.wasModified = False self._parentTab = None def get_id(self): return self._id def set_id(self, id_): self._id = id_ if id_: self.newDocument = False ID = property(lambda self: self.get_id(), lambda self, fileName: self.set_id(fileName)) def __eq__(self, path): """Compares if the path (str) received is equal to the id""" return self._id == path @classmethod def add_extra_menu(cls, menu, lang="py"): if not lang in cls.EXTRA_MENU: cls.EXTRA_MENU[lang] = [] cls.EXTRA_MENU[lang].append(menu) ninja-ide-2.3/ninja_ide/gui/main_panel/main_container.py000066400000000000000000001137371216641277400234110ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import sys import os from PyQt4 import uic from PyQt4.QtGui import QSplitter from PyQt4.QtGui import QStyle from PyQt4.QtGui import QMessageBox from PyQt4.QtGui import QFileDialog from PyQt4.QtGui import QIcon from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import Qt from PyQt4.QtCore import QDir from ninja_ide import resources from ninja_ide.core import file_manager from ninja_ide.core import settings from ninja_ide.core.filesystem_notifications import NinjaFileSystemWatcher from ninja_ide.gui.main_panel import tab_widget from ninja_ide.gui.editor import editor from ninja_ide.gui.editor import highlighter from ninja_ide.gui.editor import helpers from ninja_ide.gui.main_panel import browser_widget from ninja_ide.gui.main_panel import start_page from ninja_ide.gui.main_panel import image_viewer from ninja_ide.tools import runner from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.gui.main_panel.main_container') __mainContainerInstance = None def MainContainer(*args, **kw): global __mainContainerInstance if __mainContainerInstance is None: __mainContainerInstance = __MainContainer(*args, **kw) return __mainContainerInstance class __MainContainer(QSplitter): ############################################################################### # MainContainer SIGNALS ############################################################################### """ beforeFileSaved(QString) fileSaved(QString) currentTabChanged(QString) locateFunction(QString, QString, bool) [functionName, filePath, isVariable] openProject(QString) openPreferences() dontOpenStartPage() navigateCode(bool, int) addBackItemNavigation() updateLocator(QString) updateFileMetadata() findOcurrences(QString) cursorPositionChange(int, int) #row, col fileOpened(QString) newFileOpened(QString) enabledFollowMode(bool) recentTabsModified(QStringList) migrationAnalyzed() allTabClosed() """ ############################################################################### def __init__(self, parent=None): QSplitter.__init__(self, parent) self._parent = parent self._tabMain = tab_widget.TabWidget(self) self._tabSecondary = tab_widget.TabWidget(self) self.setAcceptDrops(True) self.addWidget(self._tabMain) self.addWidget(self._tabSecondary) self.setSizes([1, 1]) self._tabSecondary.hide() self.actualTab = self._tabMain self._followMode = False self.splitted = False highlighter.restyle(resources.CUSTOM_SCHEME) #documentation browser self.docPage = None # File Watcher self._file_watcher = NinjaFileSystemWatcher self._watched_simple_files = [] self.connect(self._tabMain, SIGNAL("currentChanged(int)"), self._current_tab_changed) self.connect(self._tabSecondary, SIGNAL("currentChanged(int)"), self._current_tab_changed) self.connect(self._tabMain, SIGNAL("currentChanged(int)"), self._exit_follow_mode) self.connect(self._tabMain, SIGNAL("changeActualTab(QTabWidget)"), self._change_actual) self.connect(self._tabSecondary, SIGNAL("changeActualTab(QTabWidget)"), self._change_actual) self.connect(self._tabMain, SIGNAL("splitTab(QTabWidget, int, bool)"), self._split_this_tab) self.connect(self._tabSecondary, SIGNAL("splitTab(QTabWidget, int, bool)"), self._split_this_tab) self.connect(self._tabMain, SIGNAL("reopenTab(QTabWidget, QString)"), self._reopen_last_tab) self.connect(self._tabSecondary, SIGNAL("reopenTab(QTabWidget, QString)"), self._reopen_last_tab) self.connect(self._tabMain, SIGNAL("syntaxChanged(QWidget, QString)"), self._specify_syntax) self.connect(self._tabSecondary, SIGNAL("syntaxChanged(QWidget, QString)"), self._specify_syntax) self.connect(self._tabMain, SIGNAL("allTabsClosed()"), self._main_without_tabs) self.connect(self._tabSecondary, SIGNAL("allTabsClosed()"), self._secondary_without_tabs) #reload file self.connect(self._tabMain, SIGNAL("reloadFile(QWidget)"), self.reload_file) self.connect(self._tabSecondary, SIGNAL("reloadFile(QWidget)"), self.reload_file) #for Save on Close operation self.connect(self._tabMain, SIGNAL("saveActualEditor()"), self.save_file) self.connect(self._tabSecondary, SIGNAL("saveActualEditor()"), self.save_file) #Navigate Code self.connect(self._tabMain, SIGNAL("navigateCode(bool, int)"), self._navigate_code) self.connect(self._tabSecondary, SIGNAL("navigateCode(bool, int)"), self._navigate_code) # Refresh recent tabs self.connect(self._tabMain, SIGNAL("recentTabsModified(QStringList)"), self._recent_files_changed) def _recent_files_changed(self, files): self.emit(SIGNAL("recentTabsModified(QStringList)"), files) def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.accept() else: event.ignore() def dropEvent(self, event): file_path = event.mimeData().urls()[0].toLocalFile() self.open_file(file_path) def _navigate_code(self, val, op): self.emit(SIGNAL("navigateCode(bool, int)"), val, op) def _main_without_tabs(self): if self._followMode: # if we were in follow mode, close the duplicated editor. self._exit_follow_mode() elif self._tabSecondary.isVisible(): self.show_split(self.orientation()) self.emit(SIGNAL("allTabsClosed()")) def _secondary_without_tabs(self): self.show_split(self.orientation()) def _reopen_last_tab(self, tab, path): self.actualTab = tab self.open_file(path) def _change_actual(self, tabWidget): if not self._followMode: self.actualTab = tabWidget def _current_tab_changed(self, index): if self.actualTab.widget(index): self.emit(SIGNAL("currentTabChanged(QString)"), self.actualTab.widget(index)._id) def split_tab(self, orientationHorizontal): """Split the main container in 2 areas. We are inverting the horizontal and vertical property here, because Qt see it Horizontal as side by side, but is confusing for the user.""" if orientationHorizontal: self.show_split(Qt.Vertical) else: self.show_split(Qt.Horizontal) def _split_this_tab(self, tab, index, orientationHorizontal): tab.setCurrentIndex(index) if orientationHorizontal: self.show_split(Qt.Horizontal) else: self.show_split(Qt.Vertical) def change_tabs_visibility(self): if self._tabMain.tabBar().isVisible(): self._tabMain.tabBar().hide() self._tabSecondary.tabBar().hide() else: self._tabMain.tabBar().show() self._tabSecondary.tabBar().show() def show_split(self, orientation): closingFollowMode = self._followMode if self._followMode: self._exit_follow_mode() if self._tabSecondary.isVisible() and \ orientation == self.orientation(): self._tabSecondary.hide() self.splitted = False for i in range(self._tabSecondary.count()): widget = self._tabSecondary.widget(0) name = self._tabSecondary.tabText(0) self._tabMain.add_tab(widget, name) if name in self._tabSecondary.titles: self._tabSecondary.titles.remove(name) if type(widget) is editor.Editor and widget.textModified: self._tabMain.tab_was_modified(True) self.actualTab = self._tabMain elif not self._tabSecondary.isVisible() and not closingFollowMode: widget = self.get_actual_widget() name = self._tabMain.tabText(self._tabMain.currentIndex()) self._tabSecondary.add_tab(widget, name) if name in self._tabMain.titles: self._tabMain.titles.remove(name) if type(widget) is editor.Editor and widget.textModified: self._tabSecondary.tab_was_modified(True) self._tabSecondary.show() self.splitted = True self.setSizes([1, 1]) self.actualTab = self._tabSecondary self.emit(SIGNAL("currentTabChanged(QString)"), widget.ID) self.setOrientation(orientation) def move_tab_to_next_split(self, tab_from): if self._followMode: return if tab_from == self._tabSecondary: tab_to = self._tabMain else: tab_to = self._tabSecondary widget = tab_from.currentWidget() name = tab_from.tabText(tab_from.currentIndex()) tab_from.remove_title(tab_from.currentIndex()) tab_to.add_tab(widget, name) if widget is editor.Editor and widget.textModified: tab_to.tab_was_saved(widget) tab_from.update_current_widget() def add_editor(self, fileName="", project=None, tabIndex=None, syntax=None, use_open_highlight=False): project_obj = self._parent.explorer.get_project_given_filename( fileName) editorWidget = editor.create_editor(fileName=fileName, project=project, syntax=syntax, use_open_highlight=use_open_highlight, project_obj=project_obj) if not fileName: tabName = "New Document" else: tabName = file_manager.get_basename(fileName) #add the tab inserted_index = self.add_tab(editorWidget, tabName, tabIndex=tabIndex) self.actualTab.setTabToolTip(inserted_index, QDir.toNativeSeparators(fileName)) #Connect signals self.connect(editorWidget, SIGNAL("modificationChanged(bool)"), self._editor_tab_was_modified) self.connect(editorWidget, SIGNAL("fileSaved(QPlainTextEdit)"), self._editor_tab_was_saved) self.connect(editorWidget, SIGNAL("openDropFile(QString)"), self.open_file) self.connect(editorWidget, SIGNAL("addBackItemNavigation()"), lambda: self.emit(SIGNAL("addBackItemNavigation()"))) self.connect(editorWidget, SIGNAL("locateFunction(QString, QString, bool)"), self._editor_locate_function) self.connect(editorWidget, SIGNAL("warningsFound(QPlainTextEdit)"), self._show_warning_tab_indicator) self.connect(editorWidget, SIGNAL("errorsFound(QPlainTextEdit)"), self._show_error_tab_indicator) self.connect(editorWidget, SIGNAL("cleanDocument(QPlainTextEdit)"), self._hide_icon_tab_indicator) self.connect(editorWidget, SIGNAL("findOcurrences(QString)"), self._find_occurrences) self.connect(editorWidget, SIGNAL("migrationAnalyzed()"), lambda: self.emit(SIGNAL("migrationAnalyzed()"))) #Cursor position changed self.connect(editorWidget, SIGNAL("cursorPositionChange(int, int)"), self._cursor_position_changed) #keyPressEventSignal for plugins self.connect(editorWidget, SIGNAL("keyPressEvent(QEvent)"), self._editor_keyPressEvent) #emit a signal about the file open self.emit(SIGNAL("fileOpened(QString)"), fileName) return editorWidget def update_editor_project(self): for i in range(self._tabMain.count()): widget = self._tabMain.widget(i) if type(widget) is editor.Editor: project = self._parent.explorer.get_project_given_filename( widget.ID) widget.set_project(project) for i in range(self._tabSecondary.count()): widget = self._tabSecondary.widget(i) if type(widget) is editor.Editor: project = self._parent.explorer.get_project_given_filename( widget.ID) widget.set_project(project) def reset_pep8_warnings(self, value): for i in range(self._tabMain.count()): widget = self._tabMain.widget(i) if type(widget) is editor.Editor: if value: widget.syncDocErrorsSignal = True widget.pep8.check_style() else: widget.hide_pep8_errors() for i in range(self._tabSecondary.count()): widget = self._tabSecondary.widget(i) if type(widget) is editor.Editor: if value: widget.syncDocErrorsSignal = True widget.pep8.check_style() else: widget.hide_pep8_errors() def reset_lint_warnings(self, value): for i in range(self._tabMain.count()): widget = self._tabMain.widget(i) if type(widget) is editor.Editor: if value: widget.syncDocErrorsSignal = True widget.errors.check_errors() else: widget.hide_lint_errors() for i in range(self._tabSecondary.count()): widget = self._tabSecondary.widget(i) if type(widget) is editor.Editor: if value: widget.syncDocErrorsSignal = True widget.errors.check_errors() else: widget.hide_lint_errors() def _cursor_position_changed(self, row, col): self.emit(SIGNAL("cursorPositionChange(int, int)"), row, col) def _find_occurrences(self, word): self.emit(SIGNAL("findOcurrences(QString)"), word) def _show_warning_tab_indicator(self, editorWidget): index = self.actualTab.indexOf(editorWidget) self.emit(SIGNAL("updateFileMetadata()")) if index >= 0: self.actualTab.setTabIcon(index, QIcon(self.style().standardIcon(QStyle.SP_MessageBoxWarning))) def _show_error_tab_indicator(self, editorWidget): index = self.actualTab.indexOf(editorWidget) self.emit(SIGNAL("updateFileMetadata()")) if index >= 0: self.actualTab.setTabIcon(index, QIcon(resources.IMAGES['bug'])) def _hide_icon_tab_indicator(self, editorWidget): index = self.actualTab.indexOf(editorWidget) self.emit(SIGNAL("updateFileMetadata()")) if index >= 0: self.actualTab.setTabIcon(index, QIcon()) def _editor_keyPressEvent(self, event): self.emit(SIGNAL("editorKeyPressEvent(QEvent)"), event) def _editor_locate_function(self, function, filePath, isVariable): self.emit(SIGNAL("locateFunction(QString, QString, bool)"), function, filePath, isVariable) def _editor_tab_was_modified(self, val=True): self.actualTab.tab_was_modified(val) def _editor_tab_was_saved(self, editorWidget=None): self.actualTab.tab_was_saved(editorWidget) self.emit(SIGNAL("updateLocator(QString)"), editorWidget.ID) def add_tab(self, widget, tabName, tabIndex=None): return self.actualTab.add_tab(widget, tabName, index=tabIndex) def get_actual_widget(self): return self.actualTab.currentWidget() def get_actual_editor(self): """Return the Actual Editor or None Return an instance of Editor if the Current Tab contains an Editor or None if it is not an instance of Editor""" widget = self.actualTab.currentWidget() if type(widget) is editor.Editor: return widget return None def reload_file(self, editorWidget=None): if editorWidget is None: editorWidget = self.get_actual_editor() if editorWidget is not None and editorWidget.ID: fileName = editorWidget.ID self._file_watcher.allow_kill = False old_cursor_position = editorWidget.textCursor().position() old_widget_index = self.actualTab.indexOf(editorWidget) self.actualTab.removeTab(old_widget_index) #open the file in the same tab as before self.open_file(fileName, tabIndex=old_widget_index) #get the new editor and set the old cursor position editorWidget = self.get_actual_editor() cursor = editorWidget.textCursor() cursor.setPosition(old_cursor_position) editorWidget.setTextCursor(cursor) self._file_watcher.allow_kill = True def open_image(self, fileName): try: if not self.is_open(fileName): viewer = image_viewer.ImageViewer(fileName) self.add_tab(viewer, file_manager.get_basename(fileName)) viewer.id = fileName else: self.move_to_open(fileName) except Exception as reason: logger.error('open_image: %s', reason) QMessageBox.information(self, self.tr("Incorrect File"), self.tr("The image couldn\'t be open")) def open_file(self, filename='', cursorPosition=-1, tabIndex=None, positionIsLineNumber=False, notStart=True): if not filename: if settings.WORKSPACE: directory = settings.WORKSPACE else: directory = os.path.expanduser("~") editorWidget = self.get_actual_editor() pexplorer = self._parent.explorer current_project = pexplorer and pexplorer.get_actual_project() if current_project is not None: directory = current_project elif editorWidget is not None and editorWidget.ID: directory = file_manager.get_folder(editorWidget.ID) extensions = ';;'.join( ['(*%s)' % e for e in settings.SUPPORTED_EXTENSIONS + ['.*', '']]) fileNames = list(QFileDialog.getOpenFileNames(self, self.tr("Open File"), directory, extensions)) else: fileNames = [filename] if not fileNames: return for filename in fileNames: if file_manager.get_file_extension(filename) in ('jpg', 'png'): self.open_image(filename) elif file_manager.get_file_extension(filename).endswith('ui'): self.w = uic.loadUi(filename) self.w.show() else: self.__open_file(filename, cursorPosition, tabIndex, positionIsLineNumber, notStart) def __open_file(self, fileName='', cursorPosition=-1, tabIndex=None, positionIsLineNumber=False, notStart=True): try: if not self.is_open(fileName): self.actualTab.notOpening = False content = file_manager.read_file_content(fileName) editorWidget = self.add_editor(fileName, tabIndex=tabIndex, use_open_highlight=True) #Add content #we HAVE to add the editor's content before set the ID #because of the minimap logic editorWidget.setPlainText(content) editorWidget.ID = fileName editorWidget.async_highlight() encoding = file_manager.get_file_encoding(content) editorWidget.encoding = encoding if cursorPosition == -1: cursorPosition = 0 if not positionIsLineNumber: editorWidget.set_cursor_position(cursorPosition) else: editorWidget.go_to_line(cursorPosition) self.add_standalone_watcher(editorWidget.ID, notStart) #New file then try to add a coding line if not content: helpers.insert_coding_line(editorWidget) self.save_file(editorWidget=editorWidget) if not editorWidget.has_write_permission(): fileName += self.tr(" (Read-Only)") index = self.actualTab.currentIndex() self.actualTab.setTabText(index, fileName) else: self.move_to_open(fileName) editorWidget = self.get_actual_editor() if editorWidget and notStart and cursorPosition != -1: if positionIsLineNumber: editorWidget.go_to_line(cursorPosition) else: editorWidget.set_cursor_position(cursorPosition) self.emit(SIGNAL("currentTabChanged(QString)"), fileName) except file_manager.NinjaIOException as reason: if notStart: QMessageBox.information(self, self.tr("The file couldn't be open"), str(reason)) except Exception as reason: logger.error('open_file: %s', reason) self.actualTab.notOpening = True def add_standalone_watcher(self, filename, not_start=True): # Add File Watcher if needed opened_projects = self._parent.explorer.get_opened_projects() opened_projects = [p.path for p in opened_projects] #alone = not_start #for folder in opened_projects: #if file_manager.belongs_to_folder(folder, filename): #alone = False #if alone or sys.platform == 'darwin': #self._file_watcher.add_file_watch(filename) #self._watched_simple_files.append(filename) def remove_standalone_watcher(self, filename): if filename in self._watched_simple_files: self._file_watcher.remove_file_watch(filename) self._watched_simple_files.remove(filename) def is_open(self, filename): return self._tabMain.is_open(filename) != -1 or \ self._tabSecondary.is_open(filename) != -1 def move_to_open(self, filename): if self._tabMain.is_open(filename) != -1: self._tabMain.move_to_open(filename) self.actualTab = self._tabMain elif self._tabSecondary.is_open(filename) != -1: self._tabSecondary.move_to_open(filename) self.actualTab = self._tabSecondary self.actualTab.currentWidget().setFocus() self.emit(SIGNAL("currentTabChanged(QString)"), filename) def get_widget_for_path(self, filename): if self._tabMain.is_open(filename) != -1: index = self._tabMain.search_for_identifier_index(filename) return self._tabMain.widget(index) elif self._tabSecondary.is_open(filename) != -1: index = self._tabSecondary.search_for_identifier_index(filename) return self._tabSecondary.widget(index) return None def change_open_tab_name(self, id, newId): """Search for the Tab with id, and set the newId to that Tab.""" index = self._tabMain.is_open(id) if index != -1: widget = self._tabMain.widget(index) tabContainer = self._tabMain elif self._tabSecondary.is_open(id): # tabSecondaryIndex is recalculated because there is a really # small chance that the tab is there, so there is no need to # calculate this value by default index = self._tabSecondary.is_open(id) widget = self._tabSecondary.widget(index) tabContainer = self._tabSecondary tabName = file_manager.get_basename(newId) tabContainer.change_open_tab_name(index, tabName) widget.ID = newId def close_deleted_file(self, id): """Search for the Tab with id, and ask the user if should be closed.""" index = self._tabMain.is_open(id) if index != -1: tabContainer = self._tabMain elif self._tabSecondary.is_open(id): # tabSecondaryIndex is recalculated because there is a really # small chance that the tab is there, so there is no need to # calculate this value by default index = self._tabSecondary.is_open(id) tabContainer = self._tabSecondary result = QMessageBox.question(self, self.tr("Close Deleted File"), self.tr("Are you sure you want to close the deleted file?\n" "The content will be completely deleted."), buttons=QMessageBox.Yes | QMessageBox.No) if result == QMessageBox.Yes: tabContainer.removeTab(index) def save_file(self, editorWidget=None): if not editorWidget: editorWidget = self.get_actual_editor() if not editorWidget: return False try: editorWidget.just_saved = True if editorWidget.newDocument or \ not file_manager.has_write_permission(editorWidget.ID): return self.save_file_as() fileName = editorWidget.ID self.emit(SIGNAL("beforeFileSaved(QString)"), fileName) if settings.REMOVE_TRAILING_SPACES: helpers.remove_trailing_spaces(editorWidget) content = editorWidget.get_text() file_manager.store_file_content( fileName, content, addExtension=False) self._file_watcher.allow_kill = False if editorWidget.ID != fileName: self.remove_standalone_watcher(editorWidget.ID) self.add_standalone_watcher(fileName) self._file_watcher.allow_kill = True editorWidget.ID = fileName encoding = file_manager.get_file_encoding(content) editorWidget.encoding = encoding self.emit(SIGNAL("fileSaved(QString)"), (self.tr("File Saved: %s") % fileName)) editorWidget._file_saved() return True except Exception as reason: editorWidget.just_saved = False logger.error('save_file: %s', reason) QMessageBox.information(self, self.tr("Save Error"), self.tr("The file couldn't be saved!")) return False def save_file_as(self): editorWidget = self.get_actual_editor() if not editorWidget: return False try: editorWidget.just_saved = True filters = '(*.py);;(*.*)' if editorWidget.ID: ext = file_manager.get_file_extension(editorWidget.ID) if ext != 'py': filters = '(*.%s);;(*.py);;(*.*)' % ext save_folder = self._get_save_folder(editorWidget.ID) fileName = QFileDialog.getSaveFileName( self._parent, self.tr("Save File"), save_folder, filters) if not fileName: return False if settings.REMOVE_TRAILING_SPACES: helpers.remove_trailing_spaces(editorWidget) newFile = file_manager.get_file_extension(fileName) == '' fileName = file_manager.store_file_content( fileName, editorWidget.get_text(), addExtension=True, newFile=newFile) self.actualTab.setTabText(self.actualTab.currentIndex(), file_manager.get_basename(fileName)) editorWidget.register_syntax( file_manager.get_file_extension(fileName)) self._file_watcher.allow_kill = False if editorWidget.ID != fileName: self.remove_standalone_watcher(editorWidget.ID) editorWidget.ID = fileName self.emit(SIGNAL("fileSaved(QString)"), (self.tr("File Saved: %s") % fileName)) self.emit(SIGNAL("currentTabChanged(QString)"), fileName) editorWidget._file_saved() self.add_standalone_watcher(fileName) self._file_watcher.allow_kill = True return True except file_manager.NinjaFileExistsException as ex: editorWidget.just_saved = False QMessageBox.information(self, self.tr("File Already Exists"), (self.tr("Invalid Path: the file '%s' already exists.") % ex.filename)) except Exception as reason: editorWidget.just_saved = False logger.error('save_file_as: %s', reason) QMessageBox.information(self, self.tr("Save Error"), self.tr("The file couldn't be saved!")) self.actualTab.setTabText(self.actualTab.currentIndex(), self.tr("New Document")) return False def _get_save_folder(self, fileName): """ Returns the root directory of the 'Main Project' or the home folder """ actual_project = self._parent.explorer.get_actual_project() if actual_project: return actual_project return os.path.expanduser("~") def save_project(self, projectFolder): for i in range(self._tabMain.count()): editorWidget = self._tabMain.widget(i) if type(editorWidget) is editor.Editor and \ file_manager.belongs_to_folder(projectFolder, editorWidget.ID): reloaded = self._tabMain.check_for_external_modifications( editorWidget) if not reloaded: self.save_file(editorWidget) for i in range(self._tabSecondary.count()): editorWidget = self._tabSecondary.widget(i) if type(editorWidget) is editor.Editor and \ file_manager.belongs_to_folder(projectFolder, editorWidget.ID): reloaded = self._tabSecondary.check_for_external_modifications( editorWidget) if not reloaded: self.save_file(editorWidget) def save_all(self): for i in range(self._tabMain.count()): editorWidget = self._tabMain.widget(i) if type(editorWidget) is editor.Editor: reloaded = self._tabMain.check_for_external_modifications( editorWidget) if not reloaded: self.save_file(editorWidget) for i in range(self._tabSecondary.count()): editorWidget = self._tabSecondary.widget(i) self._tabSecondary.check_for_external_modifications(editorWidget) if type(editorWidget) is editor.Editor: reloaded = self._tabSecondary.check_for_external_modifications( editorWidget) if not reloaded: self.save_file(editorWidget) def call_editors_function(self, call_function, *arguments): args = arguments[0] kwargs = arguments[1] for i in range(self._tabMain.count()): editorWidget = self._tabMain.widget(i) if type(editorWidget) is editor.Editor: function = getattr(editorWidget, call_function) function(*args, **kwargs) for i in range(self._tabSecondary.count()): editorWidget = self._tabSecondary.widget(i) self._tabSecondary.check_for_external_modifications(editorWidget) if type(editorWidget) is editor.Editor: function = getattr(editorWidget, call_function) function(*args, **kwargs) def show_start_page(self): if not self.is_open("Start Page"): startPage = start_page.StartPage(parent=self) self.connect(startPage, SIGNAL("openProject(QString)"), self.open_project) self.connect(startPage, SIGNAL("openPreferences()"), lambda: self.emit(SIGNAL("openPreferences()"))) self.add_tab(startPage, 'Start Page') else: self.move_to_open("Start Page") def show_python_doc(self): if sys.platform == 'win32': self.docPage = browser_widget.BrowserWidget( 'http://docs.python.org/') self.add_tab(self.docPage, self.tr("Python Documentation")) else: process = runner.start_pydoc() self.docPage = browser_widget.BrowserWidget(process[1], process[0]) self.add_tab(self.docPage, self.tr("Python Documentation")) def editor_jump_to_line(self, lineno=None): """Jump to line *lineno* if it is not None otherwise ask to the user the line number to jump """ editorWidget = self.get_actual_editor() if editorWidget: editorWidget.jump_to_line(lineno=lineno) def show_follow_mode(self): tempTab = self.actualTab self.actualTab = self._tabMain editorWidget = self.get_actual_editor() if not editorWidget: return if self._tabSecondary.isVisible() and not self._followMode: self.show_split(self.orientation()) if self._followMode: self._exit_follow_mode() else: self._followMode = True self.setOrientation(Qt.Horizontal) name = self._tabMain.tabText(self._tabMain.currentIndex()) editor2 = editor.create_editor() editor2.setDocument(editorWidget.document()) self._tabSecondary.add_tab(editor2, name) if editorWidget.textModified: self._tabSecondary.tab_was_modified(True) self._tabSecondary.show() editor2.verticalScrollBar().setRange( editorWidget._sidebarWidget.highest_line - 2, 0) self._tabSecondary.setTabsClosable(False) self._tabSecondary.follow_mode = True self.setSizes([1, 1]) self.emit(SIGNAL("enabledFollowMode(bool)"), self._followMode) self.actualTab = tempTab def _exit_follow_mode(self): if self._followMode: self._followMode = False self._tabSecondary.close_tab() self._tabSecondary.hide() self._tabSecondary.follow_mode = False self._tabSecondary.setTabsClosable(True) self.emit(SIGNAL("enabledFollowMode(bool)"), self._followMode) def get_opened_documents(self): if self._followMode: return [self._tabMain.get_documents_data(), []] return [self._tabMain.get_documents_data(), self._tabSecondary.get_documents_data()] def open_files(self, files, mainTab=True, notIDEStart=True): if mainTab: self.actualTab = self._tabMain else: self.actualTab = self._tabSecondary if files: self._tabSecondary.show() self.splitted = True for fileData in files: if file_manager.file_exists(fileData[0]): self.open_file(fileData[0], fileData[1], notStart=notIDEStart) self.actualTab = self._tabMain def check_for_unsaved_tabs(self): return self._tabMain._check_unsaved_tabs() or \ self._tabSecondary._check_unsaved_tabs() def get_unsaved_files(self): return self._tabMain.get_unsaved_files() or \ self._tabSecondary.get_unsaved_files() def reset_editor_flags(self): for i in range(self._tabMain.count()): widget = self._tabMain.widget(i) if type(widget) is editor.Editor: widget.set_flags() for i in range(self._tabSecondary.count()): widget = self._tabSecondary.widget(i) if type(widget) is editor.Editor: widget.set_flags() def _specify_syntax(self, widget, syntaxLang): if type(widget) is editor.Editor: widget.restyle(syntaxLang) def apply_editor_theme(self, family, size): for i in range(self._tabMain.count()): widget = self._tabMain.widget(i) if type(widget) is editor.Editor: widget.restyle() widget.set_font(family, size) for i in range(self._tabSecondary.count()): widget = self._tabSecondary.widget(i) if type(widget) is editor.Editor: widget.restyle() widget.set_font(family, size) def update_editor_margin_line(self): for i in range(self._tabMain.count()): widget = self._tabMain.widget(i) if type(widget) is editor.Editor: widget._update_margin_line() for i in range(self._tabSecondary.count()): widget = self._tabSecondary.widget(i) if type(widget) is editor.Editor: widget._update_margin_line() def open_project(self, path): self.emit(SIGNAL("openProject(QString)"), path) def close_python_doc(self): #close the python document server (if running) if self.docPage: index = self.actualTab.indexOf(self.docPage) self.actualTab.removeTab(index) #assign None to the browser self.docPage = None def close_tab(self): """Close the current tab in the current TabWidget.""" self.actualTab.close_tab() def change_tab(self): """Change the tab in the current TabWidget.""" self.actualTab.change_tab() def change_tab_reverse(self): """Change the tab in the current TabWidget backwards.""" self.actualTab.change_tab_reverse() def show_navigation_buttons(self): self.actualTab.navigator.show_menu_navigation() def change_split_focus(self): if self.actualTab == self._tabMain and self._tabSecondary.isVisible(): self.actualTab = self._tabSecondary else: self.actualTab = self._tabMain widget = self.actualTab.currentWidget() if widget is not None: widget.setFocus() ninja-ide-2.3/ninja_ide/gui/main_panel/start_page.py000066400000000000000000000071541216641277400225470ustar00rootroot00000000000000# -*- coding: utf-8 -*- import os from urlparse import urlparse, urlunparse from PyQt4.QtGui import QWidget from PyQt4.QtGui import QVBoxLayout from PyQt4.QtCore import QUrl from PyQt4.QtCore import QDir from PyQt4.QtCore import QSettings from PyQt4.QtCore import SIGNAL from PyQt4.QtDeclarative import QDeclarativeView from ninja_ide import resources from ninja_ide.gui.main_panel import itab_item class StartPage(QWidget, itab_item.ITabItem): def __init__(self, parent=None): super(StartPage, self).__init__(parent) self._id = "Start Page" vbox = QVBoxLayout(self) self.view = QDeclarativeView() self.view.setMinimumWidth(400) self.view.setResizeMode(QDeclarativeView.SizeRootObjectToView) path_qml = QDir.fromNativeSeparators( os.path.join(resources.QML_FILES, "StartPage.qml")) path_qml = urlunparse(urlparse(path_qml)._replace(scheme='file')) self.view.setSource(QUrl(path_qml)) self.root = self.view.rootObject() vbox.addWidget(self.view) self.load_items() self.connect(self.root, SIGNAL("openProject(QString)"), self._open_project) self.connect(self.root, SIGNAL("removeProject(QString)"), self._on_click_on_delete) self.connect(self.root, SIGNAL("markAsFavorite(QString, bool)"), self._on_click_on_favorite) self.connect(self.root, SIGNAL("openPreferences()"), lambda: self.emit(SIGNAL("openPreferences()"))) def _open_project(self, path): self.emit(SIGNAL("openProject(QString)"), path) def _on_click_on_delete(self, path): settings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) recent_projects = settings.value("recentProjects") if path in recent_projects: del recent_projects[path] settings.setValue("recentProjects", recent_projects) def _on_click_on_favorite(self, path, value): settings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) recent_projects = settings.value("recentProjects") properties = recent_projects[path] properties["isFavorite"] = value recent_projects[path] = properties settings.setValue("recentProjects", recent_projects) def load_items(self): settings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) listByFavorites = [] listNoneFavorites = [] recent_projects_dict = dict(settings.value('recentProjects', {})) #Filter for favorites for recent_project_path, content in list(recent_projects_dict.items()): if bool(dict(content)["isFavorite"]): listByFavorites.append((recent_project_path, content["lastopen"])) else: listNoneFavorites.append((recent_project_path, content["lastopen"])) if len(listByFavorites) > 1: # sort by date favorites listByFavorites = sorted(listByFavorites, key=lambda date: listByFavorites[1]) if len(listNoneFavorites) > 1: #sort by date last used listNoneFavorites = sorted(listNoneFavorites, key=lambda date: listNoneFavorites[1]) for recent_project_path in listByFavorites: path = recent_project_path[0] name = recent_projects_dict[path]['name'] self.root.add_project(name, path, True) for recent_project_path in listNoneFavorites: path = recent_project_path[0] name = recent_projects_dict[path]['name'] self.root.add_project(name, path, False)ninja-ide-2.3/ninja_ide/gui/main_panel/tab_group.py000066400000000000000000000055471216641277400224040ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QWidget from PyQt4.QtCore import SIGNAL from PyQt4.QtGui import QSizePolicy from PyQt4.QtGui import QSpacerItem from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QListWidget from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QHBoxLayout from ninja_ide.core import file_manager from ninja_ide.gui.main_panel import itab_item class TabGroup(QWidget, itab_item.ITabItem): def __init__(self, project, name, actions): QWidget.__init__(self) itab_item.ITabItem.__init__(self) vbox = QVBoxLayout(self) self.actions = actions self.project = project self.ID = self.project self.name = name self.tabs = [] self.listWidget = QListWidget() hbox = QHBoxLayout() btnExpand = QPushButton(self.tr("Expand this Files")) btnExpandAll = QPushButton(self.tr("Expand all Groups")) hbox.addWidget(btnExpandAll) hbox.addSpacerItem(QSpacerItem(20, 20, QSizePolicy.Expanding)) hbox.addWidget(btnExpand) vbox.addLayout(hbox) vbox.addWidget(self.listWidget) self.connect(btnExpand, SIGNAL("clicked()"), self.expand_this) self.connect(btnExpandAll, SIGNAL("clicked()"), self.actions.deactivate_tabs_groups) def add_widget(self, widget): self.tabs.append(widget) self.listWidget.addItem(widget.ID) def expand_this(self): self.actions.group_tabs_together() for tab in self.tabs: tabName = file_manager.get_basename(tab.ID) self.actions.ide.mainContainer.add_tab(tab, tabName) index = self.actions.ide.mainContainer._tabMain.indexOf(self) self.actions.ide.mainContainer._tabMain.removeTab(index) self.tabs = [] self.listWidget.clear() def only_expand(self): for tab in self.tabs: tabName = file_manager.get_basename(tab.ID) self.actions.ide.mainContainer.add_tab(tab, tabName) index = self.actions.ide.mainContainer._tabMain.indexOf(self) self.actions.ide.mainContainer._tabMain.removeTab(index) self.tabs = [] self.listWidget.clear() ninja-ide-2.3/ninja_ide/gui/main_panel/tab_widget.py000066400000000000000000000540501216641277400225240ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QTabWidget from PyQt4.QtGui import QCursor from PyQt4.QtGui import QIcon from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QWidget from PyQt4.QtGui import QMessageBox from PyQt4.QtGui import QMenu from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QApplication from PyQt4.QtGui import QClipboard from PyQt4.QtCore import Qt from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import QDir from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.core import file_manager from ninja_ide.core.filesystem_notifications.base_watcher import MODIFIED, \ DELETED from ninja_ide.core.filesystem_notifications import NinjaFileSystemWatcher from ninja_ide.gui.editor import editor from ninja_ide.gui.main_panel import browser_widget from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.gui.main_panel.tab_widget') DEBUG = logger.debug class TabWidget(QTabWidget): ############################################################################### # TabWidget SIGNALS ############################################################################### """ tabCloseRequested(int) dropTab(QTabWidget) saveActualEditor() allTabsClosed() changeActualTab(QTabWidget) splitTab(QTabWidget, int, bool) reopenTab(QTabWidget, QString) runFile() addToProject(QString) syntaxChanged(QWidget, QString) reloadFile(QWidget) navigateCode(bool, int) recentTabsModified(QStringList) """ ############################################################################### def __init__(self, parent): QTabWidget.__init__(self, parent) self.setTabsClosable(True) self.setMovable(True) self.setAcceptDrops(True) self.notOpening = True self.__lastOpened = [] self._resyntax = [] self.navigator = TabNavigator() self.setCornerWidget(self.navigator, Qt.TopRightCorner) self._parent = parent self.follow_mode = False self._change_map = {} #On some platforms there are problem with focusInEvent self.question_already_open = False #Keep track of the tab titles self.titles = [] self.dontLoopInExpandTitle = False self.connect(NinjaFileSystemWatcher, SIGNAL("fileChanged(int, QString)"), self._file_changed) self.connect(self, SIGNAL("tabCloseRequested(int)"), self.removeTab) self.connect(self.navigator.btnPrevious, SIGNAL("clicked()"), lambda: self._navigate_code(False)) self.connect(self.navigator.btnNext, SIGNAL("clicked()"), lambda: self._navigate_code(True)) def get_recent_files_list(self): return self.__lastOpened def _navigate_code(self, val): op = self.navigator.operation self.emit(SIGNAL("navigateCode(bool, int)"), val, op) def _add_to_last_opened(self, path): if path not in self.__lastOpened: self.__lastOpened.append(path) if len(self.__lastOpened) > settings.MAX_REMEMBER_TABS: self.__lastOpened = self.__lastOpened[1:] self.emit(SIGNAL("recentTabsModified(QStringList)"), self.__lastOpened) def add_tab(self, widget, title, index=None): try: if index is not None: inserted_index = self.insertTab(index, widget, title) else: inserted_index = self.addTab(widget, title) self.setCurrentIndex(inserted_index) self.expand_tab_name(title) widget.setFocus() return inserted_index except AttributeError as reason: msg = "Widget couldn't be added, doesn't inherit from ITabWidget" logger.error('add_tab: %s', reason) logger.error(msg) def expand_tab_name(self, title): """Expand the tab title to differentiate files with the same name. The way it is currently implemented, it will only change the first conflicting title passed in, because it only searches until the new title isn't in the tab titles. """ if title == 'New Document': return elif title not in self.titles: self.titles.append(title) return indexes = [i for i in range(self.count()) if type(self.widget(i)) is editor.Editor and self.tabText(i) == title and self.widget(i).ID] # self.widget.ID returns the basename self.dontLoopInExpandTitle = True for i in indexes: newName = file_manager.create_path( file_manager.get_basename( file_manager.get_folder(self.widget(i).ID)), title) while newName in self.titles: # Keep prepending the folder name onto the title until it # does not conflict. path = self.widget(i).ID tempDir = path[:path.rfind(newName)] newName = file_manager.create_path( file_manager.get_basename( file_manager.get_folder(tempDir)), '..', title) self.titles.append(newName) self.setTabText(i, newName) self.dontLoopInExpandTitle = False def tab_was_modified(self, val): ed = self.currentWidget() text = self.tabBar().tabText(self.currentIndex()) if type(ed) is editor.Editor and self.notOpening and val and \ not text.startswith('(*) '): ed.textModified = True text = '(*) %s' % self.tabBar().tabText(self.currentIndex()) self.tabBar().setTabText(self.currentIndex(), text) def focusInEvent(self, event): QTabWidget.focusInEvent(self, event) self.emit(SIGNAL("changeActualTab(QTabWidget)"), self) #custom behavior after the default editorWidget = self.currentWidget() if not editorWidget: return #Check never saved if editorWidget.newDocument: return #Check external modifications! self.check_for_external_modifications(editorWidget) #we can ask again self.question_already_open = False def _prompt_reload(self, editorWidget, change): self.question_already_open = True if change == MODIFIED: if editorWidget.just_saved: editorWidget.just_saved = False return val = QMessageBox.question(self, 'The file has changed on disk!', (self.tr("%s\nDo you want to reload it?") % editorWidget.ID), QMessageBox.Yes, QMessageBox.No) if val == QMessageBox.Yes: self.emit(SIGNAL("reloadFile(QWidget)"), editorWidget) else: #dont ask again while the current file is open editorWidget.ask_if_externally_modified = False elif change == DELETED: val = QMessageBox.information(self, 'The file has been deleted from disk!', (self.tr("%s\n") % editorWidget.ID), QMessageBox.Yes) self.question_already_open = False def _file_changed(self, change_type, file_path): file_path = QDir.toNativeSeparators(file_path) editorWidget = self.currentWidget() current_open = QDir.toNativeSeparators(editorWidget and editorWidget.ID or "") opened = [path for path, _ in self.get_documents_data()] if (file_path in opened) and \ ((not editorWidget) or (current_open != file_path)) and \ (change_type in (MODIFIED, DELETED)): self._change_map.setdefault(file_path, []).append(change_type) elif not editorWidget: return elif (current_open == file_path) and \ (not self.question_already_open): #dont ask again if you are already asking! self._prompt_reload(editorWidget, change_type) def check_for_external_modifications(self, editorWidget): e_path = editorWidget.ID if e_path in self._change_map: if DELETED in self._change_map[e_path]: self._prompt_reload(editorWidget, DELETED) else: self._prompt_reload(editorWidget, MODIFIED) self._change_map.pop(e_path) def tab_was_saved(self, ed): index = self.indexOf(ed) text = self.tabBar().tabText(index) if text.startswith('(*) '): text = text[4:] self.tabBar().setTabText(index, text) def is_open(self, identifier): """Check if a Tab with id = identifier is open""" for i in range(self.count()): if self.widget(i) == identifier: return i return -1 def move_to_open(self, identifier): """Set the selected Tab for the widget with id = identifier""" for i in range(self.count()): if self.widget(i) == identifier: self.setCurrentIndex(i) return def search_for_identifier_index(self, identifier): for i in range(self.count()): if self.widget(i) == identifier: return i def remove_title(self, index): """Looks for the title of the tab at index and removes it from self.titles, if it's there.'""" if self.tabText(index) in self.titles: self.titles.remove(self.tabText(index)) def update_current_widget(self): """Sets the focus to the current widget. If this is the last tab in the current split, the allTabsClosed() signal is emitted.'""" if self.currentWidget() is not None: self.currentWidget().setFocus() else: self.emit(SIGNAL("allTabsClosed()")) def removeTab(self, index): """Remove the Tab at the selected index and check if the widget was modified and need to execute any saving""" if index != -1: self.setCurrentIndex(index) widget = self.currentWidget() if type(widget) is editor.Editor: val = QMessageBox.No if widget.textModified and not self.follow_mode: fileName = self.tabBar().tabText(self.currentIndex()) val = QMessageBox.question( self, (self.tr('The file %s was not saved') % fileName), self.tr("Do you want to save before closing?"), QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) if val == QMessageBox.Cancel: return elif val == QMessageBox.Yes: self.emit(SIGNAL("saveActualEditor()")) if widget.textModified: return if type(widget) == browser_widget.BrowserWidget: widget.shutdown_pydoc() elif type(widget) is editor.Editor and widget.ID: self._add_to_last_opened(widget.ID) self._parent.remove_standalone_watcher(widget.ID) widget.completer.cc.unload_module() self.remove_title(index) super(TabWidget, self).removeTab(index) del widget self.update_current_widget() def setTabText(self, index, text): QTabWidget.setTabText(self, index, text) if text in self.titles and not self.dontLoopInExpandTitle: self.expand_tab_name(text) def close_tab(self): self.removeTab(self.currentIndex()) def get_documents_data(self): """Return Editors: path, project, cursor position""" files = [] for i in range(self.count()): if (type(self.widget(i)) is editor.Editor) \ and self.widget(i).ID != '': files.append([self.widget(i).ID, self.widget(i).get_cursor_position()]) self.widget(i)._sidebarWidget._save_breakpoints_bookmarks() return files def mousePressEvent(self, event): QTabWidget.mousePressEvent(self, event) if self.follow_mode: return if event.button() == Qt.RightButton: index = self.tabBar().tabAt(event.pos()) self.setCurrentIndex(index) widget = self.widget(index) if type(widget) is editor.Editor: #show menu menu = QMenu() actionAdd = menu.addAction(self.tr("Add to Project...")) actionRun = menu.addAction(self.tr("Run this File!")) menuSyntax = menu.addMenu(self.tr("Change Syntax")) self._create_menu_syntax(menuSyntax) menu.addSeparator() actionClose = menu.addAction(self.tr("Close This Tab")) actionCloseAll = menu.addAction(self.tr("Close All Tabs")) actionCloseAllNotThis = menu.addAction( self.tr("Close Other Tabs")) menu.addSeparator() if self._parent.splitted: actionMoveSplit = menu.addAction( self.tr("Move this Tab to the other Split")) actionCloseSplit = menu.addAction( self.tr("Close Split")) #Connect split actions self.connect(actionMoveSplit, SIGNAL("triggered()"), lambda: self._parent.move_tab_to_next_split(self)) self.connect(actionCloseSplit, SIGNAL("triggered()"), lambda: self._parent.split_tab( self._parent.orientation() == Qt.Horizontal)) else: actionSplitH = menu.addAction( self.tr("Split this Tab (Vertically)")) actionSplitV = menu.addAction( self.tr("Split this Tab (Horizontally)")) #Connect split actions self.connect(actionSplitH, SIGNAL("triggered()"), lambda: self._split_this_tab(True)) self.connect(actionSplitV, SIGNAL("triggered()"), lambda: self._split_this_tab(False)) menu.addSeparator() actionCopyPath = menu.addAction( self.tr("Copy file location to Clipboard")) actionReopen = menu.addAction( self.tr("Reopen last closed File")) if len(self.__lastOpened) == 0: actionReopen.setEnabled(False) #Connect actions self.connect(actionRun, SIGNAL("triggered()"), self._run_this_file) self.connect(actionAdd, SIGNAL("triggered()"), self._add_to_project) self.connect(actionClose, SIGNAL("triggered()"), lambda: self.removeTab(index)) self.connect(actionCloseAllNotThis, SIGNAL("triggered()"), self._close_all_tabs_except_this) self.connect(actionCloseAll, SIGNAL("triggered()"), self._close_all_tabs) self.connect(actionCopyPath, SIGNAL("triggered()"), self._copy_file_location) self.connect(actionReopen, SIGNAL("triggered()"), self._reopen_last_tab) menu.exec_(event.globalPos()) if event.button() == Qt.MidButton: index = self.tabBar().tabAt(event.pos()) self.removeTab(index) def _create_menu_syntax(self, menuSyntax): syntax = list(settings.SYNTAX.keys()) syntax.sort() for syn in syntax: menuSyntax.addAction(syn) self.connect(menuSyntax, SIGNAL("triggered(QAction*)"), self._reapply_syntax) def _reapply_syntax(self, syntaxAction): if [self.currentIndex(), syntaxAction] != self._resyntax: self._resyntax = [self.currentIndex(), syntaxAction] self.emit(SIGNAL("syntaxChanged(QWidget, QString)"), self.currentWidget(), syntaxAction.text()) def _run_this_file(self): self.emit(SIGNAL("runFile()")) def _add_to_project(self): self.emit(SIGNAL("changeActualTab(QTabWidget)"), self) widget = self.currentWidget() if type(widget) is editor.Editor: self.emit(SIGNAL("addToProject(QString)"), widget.ID) def _reopen_last_tab(self): self.emit(SIGNAL("reopenTab(QTabWidget, QString)"), self, self.__lastOpened.pop()) self.emit(SIGNAL("recentTabsModified(QStringList)"), self.__lastOpened) def _split_this_tab(self, orientation): self.emit(SIGNAL("splitTab(QTabWidget, int, bool)"), self, self.currentIndex(), orientation) def _copy_file_location(self): widget = self.currentWidget() QApplication.clipboard().setText(widget.ID, QClipboard.Clipboard) def _close_all_tabs(self): for i in range(self.count()): self.removeTab(0) def _close_all_tabs_except_this(self): self.tabBar().moveTab(self.currentIndex(), 0) for i in range(self.count()): if self.count() > 1: self.removeTab(1) def _check_unsaved_tabs(self): """ Check if are there any unsaved tab Returns True or False """ val = False for i in range(self.count()): if type(self.widget(i)) is editor.Editor: val = val or self.widget(i).textModified return val def get_unsaved_files(self): """ Returns a list with the tabText of the unsaved files """ files = [] for i in range(self.count()): widget = self.widget(i) if type(widget) is editor.Editor and widget.textModified: files.append(self.tabText(i)) return files def change_tab(self): if self.currentIndex() < (self.count() - 1): self.setCurrentIndex(self.currentIndex() + 1) else: self.setCurrentIndex(0) def change_tab_reverse(self): if self.currentIndex() > 0: self.setCurrentIndex(self.currentIndex() - 1) else: self.setCurrentIndex(self.count() - 1) def change_open_tab_name(self, index, newName): """Change the name of the tab at index, for the newName.""" self.remove_title(index) self.setTabText(index, newName) self.titles.append(newName) class TabNavigator(QWidget): def __init__(self): QWidget.__init__(self) self.setContextMenuPolicy(Qt.DefaultContextMenu) self.setMinimumHeight(38) hbox = QHBoxLayout(self) self.btnPrevious = QPushButton( QIcon(resources.IMAGES['nav-code-left']), '') self.btnPrevious.setObjectName('navigation_button') self.btnPrevious.setToolTip( self.tr("Right click to change navigation options")) self.btnNext = QPushButton( QIcon(resources.IMAGES['nav-code-right']), '') self.btnNext.setObjectName('navigation_button') self.btnNext.setToolTip( self.tr("Right click to change navigation options")) hbox.addWidget(self.btnPrevious) hbox.addWidget(self.btnNext) self.setContentsMargins(0, 0, 0, 0) self.menuNavigate = QMenu(self.tr("Navigate")) self.codeAction = self.menuNavigate.addAction( self.tr("Code Jumps")) self.codeAction.setCheckable(True) self.codeAction.setChecked(True) self.bookmarksAction = self.menuNavigate.addAction( self.tr("Bookmarks")) self.bookmarksAction.setCheckable(True) self.breakpointsAction = self.menuNavigate.addAction( self.tr("Breakpoints")) self.breakpointsAction.setCheckable(True) # 0 = Code Jumps # 1 = Bookmarks # 2 = Breakpoints self.operation = 0 self.connect(self.codeAction, SIGNAL("triggered()"), self._show_code_nav) self.connect(self.breakpointsAction, SIGNAL("triggered()"), self._show_breakpoints) self.connect(self.bookmarksAction, SIGNAL("triggered()"), self._show_bookmarks) def contextMenuEvent(self, event): self.show_menu_navigation() def show_menu_navigation(self): self.menuNavigate.exec_(QCursor.pos()) def _show_bookmarks(self): self.btnPrevious.setIcon(QIcon(resources.IMAGES['book-left'])) self.btnNext.setIcon(QIcon(resources.IMAGES['book-right'])) self.bookmarksAction.setChecked(True) self.breakpointsAction.setChecked(False) self.codeAction.setChecked(False) self.operation = 1 def _show_breakpoints(self): self.btnPrevious.setIcon(QIcon(resources.IMAGES['break-left'])) self.btnNext.setIcon(QIcon(resources.IMAGES['break-right'])) self.bookmarksAction.setChecked(False) self.breakpointsAction.setChecked(True) self.codeAction.setChecked(False) self.operation = 2 def _show_code_nav(self): self.btnPrevious.setIcon(QIcon(resources.IMAGES['nav-code-left'])) self.btnNext.setIcon(QIcon(resources.IMAGES['nav-code-right'])) self.bookmarksAction.setChecked(False) self.breakpointsAction.setChecked(False) self.codeAction.setChecked(True) self.operation = 0 ninja-ide-2.3/ninja_ide/gui/menus/000077500000000000000000000000001216641277400170615ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/gui/menus/__init__.py000066400000000000000000000012641216641277400211750ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . ninja-ide-2.3/ninja_ide/gui/menus/menu_about.py000066400000000000000000000060371216641277400215770ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import webbrowser from PyQt4.QtGui import QMessageBox from PyQt4.QtGui import QKeySequence from PyQt4.QtCore import QObject from PyQt4.QtCore import SIGNAL from ninja_ide import resources from ninja_ide.gui.main_panel import main_container from ninja_ide.gui.main_panel import browser_widget from ninja_ide.gui.dialogs import about_ninja class MenuAbout(QObject): def __init__(self, menuAbout): QObject.__init__(self) startPageAction = menuAbout.addAction(self.tr("Show Start Page")) helpAction = menuAbout.addAction(self.tr("Python Help (%s)") % resources.get_shortcut("Help").toString(QKeySequence.NativeText)) menuAbout.addSeparator() reportBugAction = menuAbout.addAction(self.tr("Report Bugs!")) pluginsDocAction = menuAbout.addAction( self.tr("Plugins Documentation")) menuAbout.addSeparator() aboutNinjaAction = menuAbout.addAction(self.tr("About NINJA-IDE")) aboutQtAction = menuAbout.addAction(self.tr("About Qt")) #Connect Action SIGNALs to proper functions self.connect(startPageAction, SIGNAL("triggered()"), main_container.MainContainer().show_start_page) self.connect(reportBugAction, SIGNAL("triggered()"), self.show_report_bugs) self.connect(aboutQtAction, SIGNAL("triggered()"), self._show_about_qt) self.connect(helpAction, SIGNAL("triggered()"), main_container.MainContainer().show_python_doc) self.connect(aboutNinjaAction, SIGNAL("triggered()"), self._show_about_ninja) self.connect(pluginsDocAction, SIGNAL("triggered()"), self.show_plugins_doc) def show_report_bugs(self): webbrowser.open(resources.BUGS_PAGE) def show_plugins_doc(self): bugsPage = browser_widget.BrowserWidget(resources.PLUGINS_DOC, parent=main_container.MainContainer()) main_container.MainContainer().add_tab( bugsPage, self.tr("How to Write NINJA-IDE plugins")) def _show_about_qt(self): QMessageBox.aboutQt(main_container.MainContainer(), self.tr("About Qt")) def _show_about_ninja(self): self.about = about_ninja.AboutNinja(main_container.MainContainer()) self.about.show() ninja-ide-2.3/ninja_ide/gui/menus/menu_edit.py000066400000000000000000000202001216641277400213760ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QIcon from PyQt4.QtGui import QTextCursor from PyQt4.QtGui import QKeySequence from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import QObject from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.gui.main_panel import main_container from ninja_ide.gui.misc import misc_container from ninja_ide.gui import status_bar from ninja_ide.gui.dialogs import preferences class MenuEdit(QObject): def __init__(self, menuEdit, toolbar): QObject.__init__(self) undoAction = menuEdit.addAction(QIcon(resources.IMAGES['undo']), (self.trUtf8("Undo (%s+Z)") % settings.OS_KEY)) redoAction = menuEdit.addAction(QIcon(resources.IMAGES['redo']), (self.trUtf8("Redo (%s)") % resources.get_shortcut("Redo").toString( QKeySequence.NativeText))) cutAction = menuEdit.addAction(QIcon(resources.IMAGES['cut']), (self.trUtf8("&Cut (%s+X)") % settings.OS_KEY)) copyAction = menuEdit.addAction(QIcon(resources.IMAGES['copy']), (self.trUtf8("&Copy (%s+C)") % settings.OS_KEY)) pasteAction = menuEdit.addAction(QIcon(resources.IMAGES['paste']), (self.trUtf8("&Paste (%s+V)") % settings.OS_KEY)) menuEdit.addSeparator() findAction = menuEdit.addAction(QIcon(resources.IMAGES['find']), (self.trUtf8("Find (%s)") % resources.get_shortcut("Find").toString( QKeySequence.NativeText))) findReplaceAction = menuEdit.addAction( QIcon(resources.IMAGES['findReplace']), (self.trUtf8("Find/Replace (%s)") % resources.get_shortcut("Find-replace").toString( QKeySequence.NativeText))) findWithWordAction = menuEdit.addAction( (self.trUtf8("Find using word under cursor (%s)") % resources.get_shortcut("Find-with-word").toString( QKeySequence.NativeText))) findInFilesAction = menuEdit.addAction(QIcon(resources.IMAGES['find']), (self.trUtf8("Find in Files (%s)") % resources.get_shortcut("Find-in-files").toString( QKeySequence.NativeText))) locatorAction = menuEdit.addAction(QIcon(resources.IMAGES['locator']), (self.trUtf8("Code Locator (%s)") % resources.get_shortcut("Code-locator").toString( QKeySequence.NativeText))) menuEdit.addSeparator() upperAction = menuEdit.addAction( self.trUtf8("Convert selected Text to: UPPER")) lowerAction = menuEdit.addAction( self.trUtf8("Convert selected Text to: lower")) titleAction = menuEdit.addAction( self.trUtf8("Convert selected Text to: Title Word")) menuEdit.addSeparator() prefAction = menuEdit.addAction(QIcon(resources.IMAGES['pref']), self.trUtf8("Preference&s")) self.toolbar_items = { 'undo': undoAction, 'redo': redoAction, 'cut': cutAction, 'copy': copyAction, 'paste': pasteAction, 'find': findAction, 'find-replace': findReplaceAction, 'find-files': findInFilesAction, 'code-locator': locatorAction} self.connect(cutAction, SIGNAL("triggered()"), self._editor_cut) self.connect(copyAction, SIGNAL("triggered()"), self._editor_copy) self.connect(pasteAction, SIGNAL("triggered()"), self._editor_paste) self.connect(redoAction, SIGNAL("triggered()"), self._editor_redo) self.connect(undoAction, SIGNAL("triggered()"), self._editor_undo) self.connect(upperAction, SIGNAL("triggered()"), self._editor_upper) self.connect(lowerAction, SIGNAL("triggered()"), self._editor_lower) self.connect(titleAction, SIGNAL("triggered()"), self._editor_title) self.connect(findAction, SIGNAL("triggered()"), status_bar.StatusBar().show) self.connect(findWithWordAction, SIGNAL("triggered()"), status_bar.StatusBar().show_with_word) self.connect(findReplaceAction, SIGNAL("triggered()"), status_bar.StatusBar().show_replace) self.connect(findInFilesAction, SIGNAL("triggered()"), self._show_find_in_files) self.connect(locatorAction, SIGNAL("triggered()"), status_bar.StatusBar().show_locator) self.connect(prefAction, SIGNAL("triggered()"), self._show_preferences) def _editor_upper(self): editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget: editorWidget.textCursor().beginEditBlock() if editorWidget.textCursor().hasSelection(): text = editorWidget.textCursor().selectedText().upper() else: text = editorWidget._text_under_cursor().upper() editorWidget.moveCursor(QTextCursor.StartOfWord) editorWidget.moveCursor(QTextCursor.EndOfWord, QTextCursor.KeepAnchor) editorWidget.textCursor().insertText(text) editorWidget.textCursor().endEditBlock() def _editor_lower(self): editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget: editorWidget.textCursor().beginEditBlock() if editorWidget.textCursor().hasSelection(): text = editorWidget.textCursor().selectedText().lower() else: text = editorWidget._text_under_cursor().lower() editorWidget.moveCursor(QTextCursor.StartOfWord) editorWidget.moveCursor(QTextCursor.EndOfWord, QTextCursor.KeepAnchor) editorWidget.textCursor().insertText(text) editorWidget.textCursor().endEditBlock() def _editor_title(self): editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget: editorWidget.textCursor().beginEditBlock() if editorWidget.textCursor().hasSelection(): text = editorWidget.textCursor().selectedText().title() else: text = editorWidget._text_under_cursor().title() editorWidget.moveCursor(QTextCursor.StartOfWord) editorWidget.moveCursor(QTextCursor.EndOfWord, QTextCursor.KeepAnchor) editorWidget.textCursor().insertText(text) editorWidget.textCursor().endEditBlock() def _editor_cut(self): editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget: editorWidget.cut() def _editor_copy(self): editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget: editorWidget.copy() def _editor_paste(self): editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget: editorWidget.paste() def _editor_redo(self): editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget: editorWidget.redo() def _editor_undo(self): editorWidget = main_container.MainContainer().get_actual_editor() if editorWidget: editorWidget.undo() def _show_preferences(self): pref = preferences.PreferencesWidget(main_container.MainContainer()) pref.show() def _show_find_in_files(self): misc_container.MiscContainer().show_find_in_files_widget() ninja-ide-2.3/ninja_ide/gui/menus/menu_file.py000066400000000000000000000161611216641277400214030ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QIcon from PyQt4.QtGui import QKeySequence from PyQt4.QtGui import QStyle from PyQt4.QtCore import QObject from PyQt4.QtCore import SIGNAL from ninja_ide import resources class MenuFile(QObject): ############################################################################### # MENU FILE SIGNALS ############################################################################### """ openFile(QString) """ ############################################################################### def __init__(self, menuFile, toolbar, ide): QObject.__init__(self) newAction = menuFile.addAction(QIcon(resources.IMAGES['new']), (self.trUtf8("&New File (%s)") % resources.get_shortcut("New-file").toString( QKeySequence.NativeText))) newProjectAction = menuFile.addAction( QIcon(resources.IMAGES['newProj']), (self.trUtf8("New Pro&ject (%s)") % resources.get_shortcut("New-project").toString( QKeySequence.NativeText))) menuFile.addSeparator() saveAction = menuFile.addAction(QIcon(resources.IMAGES['save']), (self.trUtf8("&Save (%s)") % resources.get_shortcut("Save-file").toString( QKeySequence.NativeText))) saveAsAction = menuFile.addAction(QIcon(resources.IMAGES['saveAs']), self.trUtf8("Save &As")) saveAllAction = menuFile.addAction(QIcon(resources.IMAGES['saveAll']), self.trUtf8("Save All")) saveProjectAction = menuFile.addAction(QIcon( resources.IMAGES['saveAll']), (self.trUtf8("Save Pro&ject (%s)") % resources.get_shortcut("Save-project").toString( QKeySequence.NativeText))) menuFile.addSeparator() reloadFileAction = menuFile.addAction( QIcon(resources.IMAGES['reload-file']), (self.trUtf8("Reload File (%s)") % resources.get_shortcut("Reload-file").toString( QKeySequence.NativeText))) menuFile.addSeparator() openAction = menuFile.addAction(QIcon(resources.IMAGES['open']), (self.trUtf8("&Open (%s)") % resources.get_shortcut("Open-file").toString( QKeySequence.NativeText))) openProjectAction = menuFile.addAction( QIcon(resources.IMAGES['openProj']), (self.trUtf8("Open &Project (%s)") % resources.get_shortcut("Open-project").toString( QKeySequence.NativeText))) self.recent_files = menuFile.addMenu(self.trUtf8('Open Recent Files')) menuFile.addSeparator() activateProfileAction = menuFile.addAction( QIcon(resources.IMAGES['activate-profile']), self.trUtf8("Activate Profile")) deactivateProfileAction = menuFile.addAction( QIcon(resources.IMAGES['deactivate-profile']), self.trUtf8("Deactivate Profile")) menuFile.addSeparator() printFile = menuFile.addAction(QIcon(resources.IMAGES['print']), (self.trUtf8("Pr&int File (%s)") % resources.get_shortcut("Print-file").toString( QKeySequence.NativeText))) closeAction = menuFile.addAction( ide.style().standardIcon(QStyle.SP_DialogCloseButton), (self.trUtf8("&Close Tab (%s)") % resources.get_shortcut("Close-tab").toString( QKeySequence.NativeText))) closeProjectsAction = menuFile.addAction( ide.style().standardIcon(QStyle.SP_DialogCloseButton), self.trUtf8("&Close All Projects")) menuFile.addSeparator() exitAction = menuFile.addAction( ide.style().standardIcon(QStyle.SP_DialogCloseButton), self.trUtf8("&Exit")) self.toolbar_items = { 'new-file': newAction, 'new-project': newProjectAction, 'save-file': saveAction, 'save-as': saveAsAction, 'save-all': saveAllAction, 'save-project': saveProjectAction, 'reload-file': reloadFileAction, 'open-file': openAction, 'open-project': openProjectAction, 'activate-profile': activateProfileAction, 'deactivate-profile': deactivateProfileAction, 'print-file': printFile, 'close-file': closeAction, 'close-projects': closeProjectsAction} self.connect(newAction, SIGNAL("triggered()"), ide.mainContainer.add_editor) self.connect(newProjectAction, SIGNAL("triggered()"), ide.explorer.create_new_project) self.connect(openAction, SIGNAL("triggered()"), ide.mainContainer.open_file) self.connect(saveAction, SIGNAL("triggered()"), ide.mainContainer.save_file) self.connect(saveAsAction, SIGNAL("triggered()"), ide.mainContainer.save_file_as) self.connect(saveAllAction, SIGNAL("triggered()"), ide.actions.save_all) self.connect(saveProjectAction, SIGNAL("triggered()"), ide.actions.save_project) self.connect(openProjectAction, SIGNAL("triggered()"), ide.explorer.open_project_folder) self.connect(closeAction, SIGNAL("triggered()"), ide.mainContainer.actualTab.close_tab) self.connect(exitAction, SIGNAL("triggered()"), ide.close) QObject.connect(reloadFileAction, SIGNAL("triggered()"), ide.mainContainer.reload_file) self.connect(printFile, SIGNAL("triggered()"), ide.actions.print_file) self.connect(closeProjectsAction, SIGNAL("triggered()"), ide.explorer.close_opened_projects) self.connect(deactivateProfileAction, SIGNAL("triggered()"), ide.actions.deactivate_profile) self.connect(activateProfileAction, SIGNAL("triggered()"), ide.actions.activate_profile) self.connect(self.recent_files, SIGNAL("triggered(QAction*)"), self._open_file) def update_recent_files(self, files): """Recreate the recent files menu.""" self.recent_files.clear() for file_ in files: self.recent_files.addAction(file_) def _open_file(self, action): """Open the file selected in the recent files menu.""" path = action.text() self.emit(SIGNAL("openFile(QString)"), path) ninja-ide-2.3/ninja_ide/gui/menus/menu_plugins.py000066400000000000000000000042171216641277400221440ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtCore import QObject from PyQt4.QtCore import SIGNAL from ninja_ide.gui import central_widget from ninja_ide.gui.dialogs import plugins_manager from ninja_ide.gui.dialogs import themes_manager from ninja_ide.gui.dialogs import language_manager class MenuPlugins(QObject): def __init__(self, menuPlugins): QObject.__init__(self) manageAction = menuPlugins.addAction(self.tr("Manage Plugins")) skinsAction = menuPlugins.addAction(self.tr("Editor Schemes")) languagesAction = menuPlugins.addAction(self.tr("Languages Manager")) menuPlugins.addSeparator() self.connect(manageAction, SIGNAL("triggered()"), self._show_manager) self.connect(skinsAction, SIGNAL("triggered()"), self._show_themes) self.connect(languagesAction, SIGNAL("triggered()"), self._show_languages) def _show_manager(self): manager = plugins_manager.PluginsManagerWidget( central_widget.CentralWidget()) manager.exec_() if manager._requirements: d = plugins_manager.DependenciesHelpDialog(manager._requirements) d.exec_() def _show_languages(self): manager = language_manager.LanguagesManagerWidget( central_widget.CentralWidget()) manager.show() def _show_themes(self): manager = themes_manager.ThemesManagerWidget( central_widget.CentralWidget()) manager.show() ninja-ide-2.3/ninja_ide/gui/menus/menu_project.py000066400000000000000000000064201216641277400221270ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QIcon from PyQt4.QtGui import QKeySequence from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import QObject from ninja_ide import resources from ninja_ide.gui import actions class MenuProject(QObject): def __init__(self, menuProject, toolbar): QObject.__init__(self) runAction = menuProject.addAction(QIcon(resources.IMAGES['play']), (self.trUtf8("Run Project (%s)") % resources.get_shortcut("Run-project").toString( QKeySequence.NativeText))) # debugAction = menuProject.addAction( # QIcon(resources.IMAGES['debug']), # self.trUtf8("Debug Project (%s)" % # resources.get_shortcut("Debug").toString( # QKeySequence.NativeText))) runFileAction = menuProject.addAction( QIcon(resources.IMAGES['file-run']), (self.trUtf8("Run File (%s)") % resources.get_shortcut("Run-file").toString( QKeySequence.NativeText))) stopAction = menuProject.addAction(QIcon(resources.IMAGES['stop']), (self.trUtf8("Stop (%s)") % resources.get_shortcut("Stop-execution").toString( QKeySequence.NativeText))) menuProject.addSeparator() projectPropertiesAction = menuProject.addAction( self.trUtf8("Open Project Properties")) menuProject.addSeparator() previewAction = menuProject.addAction( QIcon(resources.IMAGES['preview-web']), self.trUtf8("Preview Web in Default Browser")) # diagramView = menuProject.addAction(self.trUtf8("Diagram View")) self.toolbar_items = { 'run-project': runAction, 'run-file': runFileAction, 'stop': stopAction, 'preview-web': previewAction} self.connect(runAction, SIGNAL("triggered()"), actions.Actions().execute_project) self.connect(runFileAction, SIGNAL("triggered()"), actions.Actions().execute_file) self.connect(stopAction, SIGNAL("triggered()"), actions.Actions().kill_execution) self.connect(previewAction, SIGNAL("triggered()"), actions.Actions().preview_in_browser) self.connect(projectPropertiesAction, SIGNAL("triggered()"), actions.Actions().open_project_properties) # self.connect(debugAction, SIGNAL("triggered()"), # actions.Actions().debug_file) # self.connect(diagramView, SIGNAL("triggered()"), # actions.Actions().open_class_diagram) ninja-ide-2.3/ninja_ide/gui/menus/menu_source.py000066400000000000000000000172141216641277400217640ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QIcon from PyQt4.QtGui import QKeySequence from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import QObject from PyQt4.QtCore import Qt from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.gui import actions class MenuSource(QObject): def __init__(self, menuSource): QObject.__init__(self) indentMoreAction = menuSource.addAction( QIcon(resources.IMAGES['indent-more']), (self.trUtf8("Indent More (%s)") % QKeySequence(Qt.Key_Tab).toString(QKeySequence.NativeText))) indentLessAction = menuSource.addAction( QIcon(resources.IMAGES['indent-less']), (self.trUtf8("Indent Less (%s)") % resources.get_shortcut("Indent-less").toString( QKeySequence.NativeText))) menuSource.addSeparator() commentAction = menuSource.addAction( QIcon(resources.IMAGES['comment-code']), (self.trUtf8("Comment (%s)") % resources.get_shortcut("Comment").toString( QKeySequence.NativeText))) unCommentAction = menuSource.addAction( QIcon(resources.IMAGES['uncomment-code']), (self.trUtf8("Uncomment (%s)") % resources.get_shortcut("Uncomment").toString( QKeySequence.NativeText))) horizontalLineAction = menuSource.addAction( (self.trUtf8("Insert Horizontal Line (%s)") % resources.get_shortcut("Horizontal-line").toString( QKeySequence.NativeText))) titleCommentAction = menuSource.addAction( (self.trUtf8("Insert Title Comment (%s)") % resources.get_shortcut("Title-comment").toString( QKeySequence.NativeText))) countCodeLinesAction = menuSource.addAction( self.trUtf8("Count Code Lines")) menuSource.addSeparator() # tellTaleAction = menuSource.addAction( # self.trUtf8("Tell me a Tale of Code")) # tellTaleAction.setEnabled(False) goToDefinitionAction = menuSource.addAction( QIcon(resources.IMAGES['go-to-definition']), (self.trUtf8("Go To Definition (%s or %s+Click)") % (resources.get_shortcut("Go-to-definition").toString( QKeySequence.NativeText), settings.OS_KEY))) insertImport = menuSource.addAction( QIcon(resources.IMAGES['insert-import']), (self.trUtf8("Insert &Import (%s)") % resources.get_shortcut("Import").toString( QKeySequence.NativeText))) menu_debugging = menuSource.addMenu(self.trUtf8("Debugging Tricks")) insertPrints = menu_debugging.addAction( self.trUtf8("Insert Prints per selected line.")) insertPdb = menu_debugging.addAction( self.trUtf8("Insert pdb.set_trace()")) # organizeImportsAction = menuSource.addAction( # self.trUtf8("&Organize Imports")) # removeUnusedImportsAction = menuSource.addAction( # self.trUtf8("Remove Unused &Imports")) # extractMethodAction = menuSource.addAction( # self.trUtf8("Extract selected &code as Method")) menuSource.addSeparator() removeTrailingSpaces = menuSource.addAction( self.trUtf8("&Remove Trailing Spaces")) replaceTabsSpaces = menuSource.addAction( self.trUtf8("Replace Tabs With &Spaces")) moveUp = menuSource.addAction((self.trUtf8("Move &Up (%s)") % resources.get_shortcut("Move-up").toString( QKeySequence.NativeText))) moveDown = menuSource.addAction((self.trUtf8("Move &Down (%s)") % resources.get_shortcut("Move-down").toString( QKeySequence.NativeText))) duplicate = menuSource.addAction( (self.trUtf8("Duplica&te (%s)") % resources.get_shortcut("Duplicate").toString( QKeySequence.NativeText))) remove = menuSource.addAction( (self.trUtf8("&Remove Line (%s)") % resources.get_shortcut("Remove-line").toString( QKeySequence.NativeText))) self.toolbar_items = { 'indent-more': indentMoreAction, 'indent-less': indentLessAction, 'comment': commentAction, 'uncomment': unCommentAction, 'go-to-definition': goToDefinitionAction, 'insert-import': insertImport} self.connect(goToDefinitionAction, SIGNAL("triggered()"), actions.Actions().editor_go_to_definition) self.connect(countCodeLinesAction, SIGNAL("triggered()"), actions.Actions().count_file_code_lines) self.connect(insertImport, SIGNAL("triggered()"), actions.Actions().import_from_everywhere) self.connect(indentMoreAction, SIGNAL("triggered()"), actions.Actions().editor_indent_more) self.connect(indentLessAction, SIGNAL("triggered()"), actions.Actions().editor_indent_less) self.connect(commentAction, SIGNAL("triggered()"), actions.Actions().editor_comment) self.connect(unCommentAction, SIGNAL("triggered()"), actions.Actions().editor_uncomment) self.connect(horizontalLineAction, SIGNAL("triggered()"), actions.Actions().editor_insert_horizontal_line) self.connect(titleCommentAction, SIGNAL("triggered()"), actions.Actions().editor_insert_title_comment) # QObject.connect(removeUnusedImportsAction, SIGNAL("triggered()"), # lambda: self._main._central.obtain_editor().remove_unused_imports()) ## QObject.connect(addMissingImportsAction, SIGNAL("triggered()"), # lambda: self._main._central.obtain_editor().add_missing_imports()) # QObject.connect(organizeImportsAction, SIGNAL("triggered()"), # lambda: self._main._central.obtain_editor().organize_imports()) # QObject.connect(extractMethodAction, SIGNAL("triggered()"), # lambda: self._main._central.obtain_editor().extract_method()) self.connect(moveUp, SIGNAL("triggered()"), actions.Actions().editor_move_up) self.connect(moveDown, SIGNAL("triggered()"), actions.Actions().editor_move_down) self.connect(duplicate, SIGNAL("triggered()"), actions.Actions().editor_duplicate) self.connect(replaceTabsSpaces, SIGNAL("triggered()"), actions.Actions().editor_replace_tabs_with_spaces) self.connect(removeTrailingSpaces, SIGNAL("triggered()"), actions.Actions().editor_remove_trailing_spaces) self.connect(remove, SIGNAL("triggered()"), actions.Actions().editor_remove_line) self.connect(insertPrints, SIGNAL("triggered()"), actions.Actions().editor_insert_debugging_prints) self.connect(insertPdb, SIGNAL("triggered()"), actions.Actions().editor_insert_pdb) ninja-ide-2.3/ninja_ide/gui/menus/menu_view.py000066400000000000000000000155561216641277400214450ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QIcon from PyQt4.QtGui import QKeySequence from PyQt4.QtGui import QWheelEvent from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import QObject from PyQt4.QtCore import QPoint from PyQt4.QtCore import Qt from ninja_ide import resources class MenuView(QObject): def __init__(self, menuView, toolbar, ide): QObject.__init__(self) self.__ide = ide self.hideConsoleAction = menuView.addAction( (self.trUtf8("Show/Hide &Console (%s)") % resources.get_shortcut("Hide-misc").toString( QKeySequence.NativeText))) self.hideConsoleAction.setCheckable(True) self.hideEditorAction = menuView.addAction( (self.trUtf8("Show/Hide &Editor (%s)") % resources.get_shortcut("Hide-editor").toString( QKeySequence.NativeText))) self.hideEditorAction.setCheckable(True) self.hideAllAction = menuView.addAction( (self.trUtf8("Show/Hide &All (%s)") % resources.get_shortcut("Hide-all").toString( QKeySequence.NativeText))) self.hideAllAction.setCheckable(True) self.hideExplorerAction = menuView.addAction( (self.trUtf8("Show/Hide &Explorer (%s)") % resources.get_shortcut("Hide-explorer").toString( QKeySequence.NativeText))) self.hideExplorerAction.setCheckable(True) self.hideToolbarAction = menuView.addAction( self.trUtf8("Show/Hide &Toolbar")) self.hideToolbarAction.setCheckable(True) self.fullscreenAction = menuView.addAction( QIcon(resources.IMAGES['fullscreen']), (self.trUtf8("Full Screen &Mode (%s)") % resources.get_shortcut("Full-screen").toString( QKeySequence.NativeText))) menuView.addSeparator() splitTabHAction = menuView.addAction( QIcon(resources.IMAGES['splitH']), (self.trUtf8("Split Tabs Horizontally (%s)") % resources.get_shortcut("Split-horizontal").toString( QKeySequence.NativeText))) splitTabVAction = menuView.addAction( QIcon(resources.IMAGES['splitV']), (self.trUtf8("Split Tabs Vertically (%s)") % resources.get_shortcut("Split-vertical").toString( QKeySequence.NativeText))) followModeAction = menuView.addAction( QIcon(resources.IMAGES['follow']), (self.trUtf8("Follow Mode (%s)") % resources.get_shortcut("Follow-mode").toString( QKeySequence.NativeText))) groupTabsAction = menuView.addAction( self.trUtf8("Group Tabs by Project")) deactivateGroupTabsAction = menuView.addAction( self.trUtf8("Deactivate Group Tabs")) menuView.addSeparator() #Zoom zoomInAction = menuView.addAction(QIcon(resources.IMAGES['zoom-in']), self.trUtf8("Zoom &In (Ctrl+Wheel-Up)")) zoomOutAction = menuView.addAction(QIcon(resources.IMAGES['zoom-out']), self.trUtf8("Zoom &Out (Ctrl+Wheel-Down)")) menuView.addSeparator() fadeInAction = menuView.addAction( self.trUtf8("Fade In (Alt+Wheel-Up)")) fadeOutAction = menuView.addAction( self.trUtf8("Fade Out (Alt+Wheel-Down)")) self.toolbar_items = { 'splitv': splitTabVAction, 'splith': splitTabHAction, 'follow-mode': followModeAction, 'zoom-in': zoomInAction, 'zoom-out': zoomOutAction} self.connect(self.hideConsoleAction, SIGNAL("triggered()"), self.__ide.actions.view_misc_visibility) self.connect(self.hideEditorAction, SIGNAL("triggered()"), self.__ide.actions.view_main_visibility) self.connect(self.hideExplorerAction, SIGNAL("triggered()"), self.__ide.actions.view_explorer_visibility) self.connect(self.hideAllAction, SIGNAL("triggered()"), self.__ide.actions.hide_all) self.connect(self.fullscreenAction, SIGNAL("triggered()"), self.__ide.actions.fullscreen_mode) self.connect(splitTabHAction, SIGNAL("triggered()"), lambda: self.__ide.mainContainer.split_tab(True)) self.connect(splitTabVAction, SIGNAL("triggered()"), lambda: self.__ide.mainContainer.split_tab(False)) QObject.connect(followModeAction, SIGNAL("triggered()"), self.__ide.mainContainer.show_follow_mode) self.connect(zoomInAction, SIGNAL("triggered()"), self.zoom_in_editor) self.connect(zoomOutAction, SIGNAL("triggered()"), self.zoom_out_editor) self.connect(fadeInAction, SIGNAL("triggered()"), self._fade_in) self.connect(fadeOutAction, SIGNAL("triggered()"), self._fade_out) self.connect(self.hideToolbarAction, SIGNAL("triggered()"), self._hide_show_toolbar) self.connect(groupTabsAction, SIGNAL("triggered()"), self.__ide.actions.group_tabs_together) self.connect(deactivateGroupTabsAction, SIGNAL("triggered()"), self.__ide.actions.deactivate_tabs_groups) #Set proper MenuView checks values: self.hideAllAction.setChecked(True) self.hideConsoleAction.setChecked(False) self.hideEditorAction.setChecked(True) self.hideExplorerAction.setChecked(True) self.hideToolbarAction.setChecked(True) def _hide_show_toolbar(self): if self.__ide.toolbar.isVisible(): self.__ide.toolbar.hide() else: self.__ide.toolbar.show() def zoom_in_editor(self): editor = self.__ide.mainContainer.get_actual_editor() if editor: editor.zoom_in() def zoom_out_editor(self): editor = self.__ide.mainContainer.get_actual_editor() if editor: editor.zoom_out() def _fade_in(self): event = QWheelEvent(QPoint(), 120, Qt.NoButton, Qt.AltModifier) self.__ide.wheelEvent(event) def _fade_out(self): event = QWheelEvent(QPoint(), -120, Qt.NoButton, Qt.AltModifier) self.__ide.wheelEvent(event) ninja-ide-2.3/ninja_ide/gui/misc/000077500000000000000000000000001216641277400166655ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/gui/misc/__init__.py000066400000000000000000000012641216641277400210010ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . ninja-ide-2.3/ninja_ide/gui/misc/console_widget.py000066400000000000000000000526211216641277400222520ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import re from PyQt4.QtGui import QApplication from PyQt4.QtGui import QPlainTextEdit from PyQt4.QtGui import QTextCursor from PyQt4.QtGui import QTextFormat from PyQt4.QtGui import QTextEdit from PyQt4.QtGui import QColor from PyQt4.QtGui import QFont from PyQt4.QtGui import QKeyEvent from PyQt4.QtCore import Qt from PyQt4.QtCore import QEvent from PyQt4.QtCore import QProcess from PyQt4.QtCore import QRegExp from PyQt4.QtCore import SIGNAL from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.tools import console from ninja_ide.gui.editor import syntax_highlighter from ninja_ide.gui.editor import python_syntax from ninja_ide.tools.completion import completer from ninja_ide.tools.completion import completer_widget from ninja_ide.tools.logger import NinjaLogger try: # For Python2 str = unicode # lint:ok except NameError: # We are in Python3 pass logger = NinjaLogger('ninja_ide.gui.misc.console_widget') BRACES = {"'": "'", '"': '"', '{': '}', '[': ']', '(': ')'} class ConsoleWidget(QPlainTextEdit): def __init__(self): QPlainTextEdit.__init__(self, '>>> ') self.setUndoRedoEnabled(False) self.apply_editor_style() self.setToolTip(self.tr("Show/Hide (F4)")) self.moveCursor(QTextCursor.EndOfLine) self._patIsWord = re.compile('\w+') self.prompt = '>>> ' self._console = console.Console() self._history = [] self.history_index = 0 self._current_command = '' self._braces = None self.imports = ['import __builtin__'] self.patFrom = re.compile('^(\\s)*from ((\\w)+(\\.)*(\\w)*)+ import') self.patImport = re.compile('^(\\s)*import (\\w)+') self.patObject = re.compile('[^a-zA-Z0-9_\\.]') self.completer = completer_widget.CompleterWidget(self) self.okPrefix = QRegExp('[.)}:,\]]') self._pre_key_press = { Qt.Key_Enter: self._enter_pressed, Qt.Key_Return: self._enter_pressed, Qt.Key_Tab: self._tab_pressed, Qt.Key_Home: self._home_pressed, Qt.Key_PageUp: lambda x: True, Qt.Key_PageDown: lambda x: True, Qt.Key_Left: self._left_pressed, Qt.Key_Up: self._up_pressed, Qt.Key_Down: self._down_pressed, Qt.Key_Backspace: self._backspace, } #Create Context Menu self._create_context_menu() #Set Font self.set_font() #Create Highlighter parts_scanner, code_scanner, formats = \ syntax_highlighter.load_syntax(python_syntax.syntax) self.highlighter = syntax_highlighter.SyntaxHighlighter( self.document(), parts_scanner, code_scanner, formats) self.connect(self, SIGNAL("cursorPositionChanged()"), self.highlight_current_line) self.highlight_current_line() self._proc = QProcess(self) self.connect(self._proc, SIGNAL("readyReadStandardOutput()"), self._python_path_detected) self.connect(self._proc, SIGNAL("error(QProcess::ProcessError)"), self.process_error) self._add_system_path_for_frozen() def _add_system_path_for_frozen(self): try: self._proc.start(settings.PYTHON_PATH, [resources.GET_SYSTEM_PATH]) except Exception as reason: logger.warning('Could not get system path, error: %r' % reason) def _python_path_detected(self): paths = self._proc.readAllStandardOutput().data().decode('utf8') add_system_path = ('import sys; ' 'sys.path = list(set(sys.path + %s))' % paths) self._write(add_system_path) self._proc.deleteLater() def process_error(self, error): message = '' if error == 0: message = 'Failed to start' else: message = 'Error during execution, QProcess error: %d' % error logger.warning('Could not get system path, error: %r' % message) def set_font(self, family=settings.FONT_FAMILY, size=settings.FONT_SIZE): font = QFont(family, size) self.document().setDefaultFont(font) def _create_context_menu(self): self.popup_menu = self.createStandardContextMenu() self.popup_menu.clear() actionCut = self.popup_menu.addAction(self.tr("Cut")) actionCopy = self.popup_menu.addAction(self.tr("Copy")) actionPaste = self.popup_menu.addAction(self.tr("Paste")) actionClean = self.popup_menu.addAction(self.tr("Clean Console")) actionCopyHistory = self.popup_menu.addAction(self.tr("Copy History")) actionCopyConsoleContent = self.popup_menu.addAction( self.tr("Copy Console Content")) self.popup_menu.addAction(actionCut) self.popup_menu.addAction(actionCopy) self.popup_menu.addAction(actionPaste) self.popup_menu.addSeparator() self.popup_menu.addAction(actionClean) self.popup_menu.addSeparator() self.popup_menu.addAction(actionCopyHistory) self.popup_menu.addAction(actionCopyConsoleContent) self.connect(actionCut, SIGNAL("triggered()"), self._cut) self.connect(actionCopy, SIGNAL("triggered()"), self.copy) self.connect(actionPaste, SIGNAL("triggered()"), self._paste) self.connect(actionClean, SIGNAL("triggered()"), self._clean_console) self.connect(actionCopyHistory, SIGNAL("triggered()"), self._copy_history) self.connect(actionCopyConsoleContent, SIGNAL("triggered()"), self._copy_console_content) def _cut(self): event = QKeyEvent(QEvent.KeyPress, Qt.Key_X, Qt.ControlModifier, "x") self.keyPressEvent(event) def _paste(self): if self.textCursor().hasSelection(): self.moveCursor(QTextCursor.End) self.paste() def _clean_console(self): self.clear() self._add_prompt() def _copy_history(self): historyContent = '\n'.join(self._history) clipboard = QApplication.instance().clipboard() clipboard.setText(historyContent) def _copy_console_content(self): content = self.toPlainText() clipboard = QApplication.instance().clipboard() clipboard.setText(content) def setCursorPosition(self, position, mode=QTextCursor.MoveAnchor): self.moveCursor(QTextCursor.StartOfLine, mode) for i in range(len(self.prompt) + position): self.moveCursor(QTextCursor.Right, mode) def _check_event_on_selection(self, event): if event.text(): cursor = self.textCursor() begin_last_block = (self.document().lastBlock().position() + len(self.prompt)) if cursor.hasSelection() and \ ((cursor.selectionEnd() < begin_last_block) or (cursor.selectionStart() < begin_last_block)): self.moveCursor(QTextCursor.End) def _enter_pressed(self, event): self._write_command() return True def _tab_pressed(self, event): self.textCursor().insertText(' ' * settings.INDENT) return True def _home_pressed(self, event): if event.modifiers() == Qt.ShiftModifier: self.setCursorPosition(0, QTextCursor.KeepAnchor) else: self.setCursorPosition(0) return True def _left_pressed(self, event): return self._get_cursor_position() == 0 def _up_pressed(self, event): if self.history_index == len(self._history): command = self.document().lastBlock().text()[len(self.prompt):] self._current_command = command self._set_command(self._get_prev_history_entry()) return True def _down_pressed(self, event): if len(self._history) == self.history_index: command = self._current_command else: command = self._get_next_history_entry() self._set_command(command) return True def _backspace(self, event): cursor = self.textCursor() selected_text = cursor.selectedText() cursor.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor) text = cursor.selectedText()[len(self.prompt):] if (len(text) % settings.INDENT == 0) and text.isspace(): cursor.movePosition(QTextCursor.StartOfLine) cursor.movePosition(QTextCursor.Right, QTextCursor.MoveAnchor, settings.INDENT) cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, settings.INDENT) cursor.removeSelectedText() return True elif (selected_text == self.document().lastBlock().text()[len(self.prompt):]): self.textCursor().removeSelectedText() return True return self._get_cursor_position() == 0 def keyPressEvent(self, event): if self.completer.popup().isVisible(): if event.key() in (Qt.Key_Enter, Qt.Key_Return, Qt.Key_Tab): event.ignore() self.completer.popup().hide() return elif event.key in (Qt.Key_Space, Qt.Key_Escape, Qt.Key_Backtab): self.completer.popup().hide() self._check_event_on_selection(event) if self._pre_key_press.get(event.key(), lambda x: False)(event): return if event.text() in (set(BRACES.values()) - set(["'", '"'])): cursor = self.textCursor() cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor) brace = cursor.selection().toPlainText() cursor = self.textCursor() cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor) braceClose = cursor.selection().toPlainText() if BRACES.get(brace, False) == event.text() and \ braceClose == event.text(): self.moveCursor(QTextCursor.Right) return QPlainTextEdit.keyPressEvent(self, event) if event.text() in BRACES: cursor = self.textCursor() cursor.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor) self.textCursor().insertText( BRACES[event.text()]) self.moveCursor(QTextCursor.Left) completionPrefix = self._text_under_cursor() if event.key() == Qt.Key_Period or (event.key() == Qt.Key_Space and event.modifiers() == Qt.ControlModifier): self.completer.setCompletionPrefix(completionPrefix) self._resolve_completion_argument() if self.completer.popup().isVisible() and \ completionPrefix != self.completer.completionPrefix(): self.completer.setCompletionPrefix(completionPrefix) self.completer.popup().setCurrentIndex( self.completer.completionModel().index(0, 0)) self.completer.setCurrentRow(0) self._resolve_completion_argument() def _resolve_completion_argument(self): try: cursor = self.textCursor() cursor.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor) var = cursor.selectedText() chars = self.patObject.findall(var) var = var[var.rfind(chars[-1]) + 1:] cr = self.cursorRect() proposals = completer.get_all_completions(var, imports=self.imports) if not proposals: if self.completer.popup().isVisible(): prefix = var[var.rfind('.') + 1:] var = var[:var.rfind('.') + 1] var = self._console.get_type(var) var += prefix else: var = self._console.get_type(var) proposals = completer.get_all_completions(var, imports=self.imports) self.completer.complete(cr, proposals) except: self.completer.popup().hide() def highlight_current_line(self): self.extraSelections = [] selection = QTextEdit.ExtraSelection() lineColor = QColor(resources.CUSTOM_SCHEME.get('current-line', resources.COLOR_SCHEME['current-line'])) lineColor.setAlpha(20) selection.format.setBackground(lineColor) selection.format.setProperty(QTextFormat.FullWidthSelection, True) selection.cursor = self.textCursor() selection.cursor.clearSelection() self.extraSelections.append(selection) self.setExtraSelections(self.extraSelections) if self._braces is not None: self._braces = None cursor = self.textCursor() if cursor.position() == 0: self.setExtraSelections(self.extraSelections) return cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor) text = cursor.selectedText() pos1 = cursor.position() if text in (')', ']', '}'): pos2 = self._match_braces(pos1, text, forward=False) elif text in ('(', '[', '{'): pos2 = self._match_braces(pos1, text, forward=True) else: self.setExtraSelections(self.extraSelections) return if pos2 is not None: self._braces = (pos1, pos2) selection = QTextEdit.ExtraSelection() selection.format.setForeground(QColor( resources.CUSTOM_SCHEME.get('brace-foreground', resources.COLOR_SCHEME.get('brace-foreground')))) selection.format.setBackground(QColor( resources.CUSTOM_SCHEME.get('brace-background', resources.COLOR_SCHEME.get('brace-background')))) selection.cursor = cursor self.extraSelections.append(selection) selection = QTextEdit.ExtraSelection() selection.format.setForeground(QColor( resources.CUSTOM_SCHEME.get('brace-foreground', resources.COLOR_SCHEME.get('brace-foreground')))) selection.format.setBackground(QColor( resources.CUSTOM_SCHEME.get('brace-background', resources.COLOR_SCHEME.get('brace-background')))) selection.cursor = self.textCursor() selection.cursor.setPosition(pos2) selection.cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor) self.extraSelections.append(selection) else: self._braces = (pos1,) selection = QTextEdit.ExtraSelection() selection.format.setBackground(QColor( resources.CUSTOM_SCHEME.get('brace-background', resources.COLOR_SCHEME.get('brace-background')))) selection.format.setForeground(QColor( resources.CUSTOM_SCHEME.get('brace-foreground', resources.COLOR_SCHEME.get('brace-foreground')))) selection.cursor = cursor self.extraSelections.append(selection) self.setExtraSelections(self.extraSelections) def _text_under_cursor(self): tc = self.textCursor() tc.select(QTextCursor.WordUnderCursor) return tc.selectedText() def get_selection(self, posStart, posEnd): cursor = self.textCursor() cursor.setPosition(posStart) if posEnd == QTextCursor.End: cursor2 = self.textCursor() cursor2.movePosition(posEnd) cursor.setPosition(cursor2.position(), QTextCursor.KeepAnchor) else: cursor.setPosition(posEnd, QTextCursor.KeepAnchor) return cursor.selectedText() def _match_braces(self, position, brace, forward): """based on: http://gitorious.org/khteditor""" if forward: braceMatch = {'(': ')', '[': ']', '{': '}'} text = self.get_selection(position, QTextCursor.End) braceOpen, braceClose = 1, 1 else: braceMatch = {')': '(', ']': '[', '}': '{'} text = self.get_selection(QTextCursor.Start, position) braceOpen, braceClose = len(text) - 1, len(text) - 1 while True: if forward: posClose = text.find(braceMatch[brace], braceClose) else: posClose = text.rfind(braceMatch[brace], 0, braceClose + 1) if posClose > -1: if forward: braceClose = posClose + 1 posOpen = text.find(brace, braceOpen, posClose) else: braceClose = posClose - 1 posOpen = text.rfind(brace, posClose, braceOpen + 1) if posOpen > -1: if forward: braceOpen = posOpen + 1 else: braceOpen = posOpen - 1 else: if forward: return position + posClose else: return position - (len(text) - posClose) else: return def _add_prompt(self, incomplete=False): if incomplete: prompt = '.' * 3 + ' ' else: prompt = self.prompt self.appendPlainText(prompt) self.moveCursor(QTextCursor.End) def _get_cursor_position(self): return self.textCursor().columnNumber() - len(self.prompt) def _write_command(self): command = self.document().lastBlock().text() #remove the prompt from the QString command = command[len(self.prompt):] self._add_history(command) incomplete = self._write(command) if self.patFrom.match(command) or self.patImport.match(command): self.imports += [command] if not incomplete: output = self._read() if output is not None: if isinstance(output, str): output = output.encode('utf8') self.appendPlainText(output.decode('utf8')) self._add_prompt(incomplete) def _set_command(self, command): self.moveCursor(QTextCursor.End) cursor = self.textCursor() cursor.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor) cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, len(self.prompt)) cursor.insertText(command) def contextMenuEvent(self, event): self.popup_menu.exec_(event.globalPos()) def _write(self, line): return self._console.push(line) def _read(self): return self._console.output def _add_history(self, command): if command and (not self._history or self._history[-1] != command): self._history.append(command) self.history_index = len(self._history) def _get_prev_history_entry(self): if self._history: self.history_index = max(0, self.history_index - 1) return self._history[self.history_index] return '' def _get_next_history_entry(self): if self._history: hist_len = len(self._history) - 1 self.history_index = min(hist_len, self.history_index + 1) index = self.history_index if self.history_index == hist_len: self.history_index += 1 return self._history[index] return '' def restyle(self): self.apply_editor_style() parts_scanner, code_scanner, formats = \ syntax_highlighter.load_syntax(python_syntax.syntax) self.highlighter = syntax_highlighter.SyntaxHighlighter( self.document(), parts_scanner, code_scanner, formats) def apply_editor_style(self): css = 'QPlainTextEdit {color: %s; background-color: %s;' \ 'selection-color: %s; selection-background-color: %s;}' \ % (resources.CUSTOM_SCHEME.get('editor-text', resources.COLOR_SCHEME['editor-text']), resources.CUSTOM_SCHEME.get('editor-background', resources.COLOR_SCHEME['editor-background']), resources.CUSTOM_SCHEME.get('editor-selection-color', resources.COLOR_SCHEME['editor-selection-color']), resources.CUSTOM_SCHEME.get('editor-selection-background', resources.COLOR_SCHEME['editor-selection-background'])) self.setStyleSheet(css) self.set_font(settings.FONT_FAMILY, settings.FONT_SIZE) def load_project_into_console(self, projectFolder): """Load the projectFolder received into the sys.path.""" self._console.push("import sys; sys.path += ['%s']" % projectFolder) def unload_project_from_console(self, projectFolder): """Unload the project from the system path.""" self._console.push("import sys; " "sys.path = [path for path in sys.path " "if path != '%s']" % projectFolder) ninja-ide-2.3/ninja_ide/gui/misc/find_in_files.py000066400000000000000000000467411216641277400220430ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import os import re try: import Queue except: import queue as Queue # lint:ok from PyQt4.QtCore import Qt from PyQt4.QtCore import QDir from PyQt4.QtCore import QFile from PyQt4.QtCore import QTextStream from PyQt4.QtCore import QRegExp from PyQt4.QtCore import QThread from PyQt4.QtCore import SIGNAL from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QMessageBox from PyQt4.QtGui import QSizePolicy from PyQt4.QtGui import QSpacerItem from PyQt4.QtGui import QRadioButton from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QGridLayout from PyQt4.QtGui import QGroupBox from PyQt4.QtGui import QAbstractItemView from PyQt4.QtGui import QHeaderView from PyQt4.QtGui import QDialog from PyQt4.QtGui import QWidget from PyQt4.QtGui import QTreeWidget from PyQt4.QtGui import QTreeWidgetItem from PyQt4.QtGui import QLineEdit from PyQt4.QtGui import QComboBox from PyQt4.QtGui import QCheckBox from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QLabel from PyQt4.QtGui import QIcon from PyQt4.QtGui import QFileDialog from ninja_ide import resources from ninja_ide.core import file_manager from ninja_ide.gui.main_panel import main_container from ninja_ide.gui.explorer import explorer_container class FindInFilesThread(QThread): ''' Emit the signal found_pattern(PyQt_PyObject) ''' def find_in_files(self, dir_name, filters, reg_exp, recursive, by_phrase): self._cancel = False self.recursive = recursive self.search_pattern = reg_exp self.by_phrase = by_phrase self.filters = filters self.queue = Queue.Queue() self.queue.put(dir_name) self.root_dir = dir_name #Start! self.start() def run(self): file_filter = QDir.Files | QDir.NoDotAndDotDot | QDir.Readable dir_filter = QDir.Dirs | QDir.NoDotAndDotDot | QDir.Readable while not self._cancel and not self.queue.empty(): current_dir = QDir(self.queue.get()) #Skip not readable dirs! if not current_dir.isReadable(): continue #Collect all sub dirs! if self.recursive: current_sub_dirs = current_dir.entryInfoList(dir_filter) for one_dir in current_sub_dirs: self.queue.put(one_dir.absoluteFilePath()) #all files in sub_dir first apply the filters current_files = current_dir.entryInfoList( self.filters, file_filter) #process all files in current dir! for one_file in current_files: self._grep_file(one_file.absoluteFilePath(), one_file.fileName()) def _grep_file(self, file_path, file_name): if not self.by_phrase: with open(file_path, 'r') as f: content = f.read() words = [word for word in self.search_pattern.pattern().split('|')] words.insert(0, True) def check_whole_words(result, word): return result and content.find(word) != -1 if not reduce(check_whole_words, words): return file_object = QFile(file_path) if not file_object.open(QFile.ReadOnly): return stream = QTextStream(file_object) lines = [] line_index = 0 line = stream.readLine() while not self._cancel and not (stream.atEnd() and not line): column = self.search_pattern.indexIn(line) if column != -1: lines.append((line_index, line)) #take the next line! line = stream.readLine() line_index += 1 #emit a signal! relative_file_name = file_manager.convert_to_relative( self.root_dir, file_path) self.emit(SIGNAL("found_pattern(PyQt_PyObject)"), (relative_file_name, lines)) def cancel(self): self._cancel = True class FindInFilesResult(QTreeWidget): def __init__(self): QTreeWidget.__init__(self) self.setHeaderLabels((self.tr('File'), self.tr('Line'))) self.header().setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) self.header().setResizeMode(0, QHeaderView.ResizeToContents) self.header().setResizeMode(1, QHeaderView.ResizeToContents) self.header().setStretchLastSection(False) self.sortByColumn(0, Qt.AscendingOrder) def update_result(self, dir_name_root, file_name, items): if items: root_item = FindInFilesRootItem(self, (file_name, ''), dir_name_root) root_item.setExpanded(True) for line, content in items: QTreeWidgetItem(root_item, (content, str(line + 1))) class FindInFilesRootItem(QTreeWidgetItem): def __init__(self, parent, names, dir_name_root): QTreeWidgetItem.__init__(self, parent, names) self.dir_name_root = dir_name_root class FindInFilesDialog(QDialog): def __init__(self, result_widget, parent): QDialog.__init__(self, parent) self._find_thread = FindInFilesThread() self.setWindowTitle("Find in files") self.resize(400, 300) #MAIN LAYOUT main_vbox = QVBoxLayout(self) self.pattern_line_edit = QLineEdit() self.dir_name_root = None self.user_home = os.path.expanduser('~') self.dir_combo = QComboBox() self.dir_combo.addItem(self.user_home) self.dir_combo.setEditable(True) self.open_button = QPushButton(QIcon(resources.IMAGES['find']), self.tr("Open")) self.filters_line_edit = QLineEdit("*.py") self.replace_line = QLineEdit() self.replace_line.setEnabled(False) self.check_replace = QCheckBox(self.tr("Replace: ")) self.case_checkbox = QCheckBox(self.tr("C&ase sensitive")) self.type_checkbox = QCheckBox(self.tr("R&egular Expression")) self.recursive_checkbox = QCheckBox(self.tr("Rec&ursive")) self.recursive_checkbox.setCheckState(Qt.Checked) self.phrase_radio = QRadioButton( self.tr("Search by Phrase (Exact Match).")) self.phrase_radio.setChecked(True) self.words_radio = QRadioButton( self.tr("Search for all the words " "(anywhere in the document, not together).")) self.find_button = QPushButton(self.tr("Find!")) self.find_button.setMaximumWidth(150) self.cancel_button = QPushButton(self.tr("Cancel")) self.cancel_button.setMaximumWidth(150) self.result_widget = result_widget hbox = QHBoxLayout() hbox.addWidget(self.find_button) hbox.addWidget(self.cancel_button) #main section find_group_box = QGroupBox(self.tr("Main")) grid = QGridLayout() grid.addWidget(QLabel(self.tr("Text: ")), 0, 0) grid.addWidget(self.pattern_line_edit, 0, 1) grid.addWidget(QLabel(self.tr("Directory: ")), 1, 0) grid.addWidget(self.dir_combo, 1, 1) grid.addWidget(self.open_button, 1, 2) grid.addWidget(QLabel(self.tr("Filter: ")), 2, 0) grid.addWidget(self.filters_line_edit, 2, 1) grid.addWidget(self.check_replace, 3, 0) grid.addWidget(self.replace_line, 3, 1) find_group_box.setLayout(grid) #add main section to MAIN LAYOUT main_vbox.addWidget(find_group_box) #options sections options_group_box = QGroupBox(self.tr("Options")) gridOptions = QGridLayout() gridOptions.addWidget(self.case_checkbox, 0, 0) gridOptions.addWidget(self.type_checkbox, 1, 0) gridOptions.addWidget(self.recursive_checkbox, 2, 0) gridOptions.addWidget(self.phrase_radio, 0, 1) gridOptions.addWidget(self.words_radio, 1, 1) options_group_box.setLayout(gridOptions) #add options sections to MAIN LAYOUT main_vbox.addWidget(options_group_box) #add buttons to MAIN LAYOUT main_vbox.addLayout(hbox) #Focus self.pattern_line_edit.setFocus() self.open_button.setFocusPolicy(Qt.NoFocus) #signal self.connect(self.open_button, SIGNAL("clicked()"), self._select_dir) self.connect(self.find_button, SIGNAL("clicked()"), self._find_in_files) self.connect(self.cancel_button, SIGNAL("clicked()"), self._kill_thread) self.connect(self._find_thread, SIGNAL("found_pattern(PyQt_PyObject)"), self._found_match) self.connect(self._find_thread, SIGNAL("finished()"), self._find_thread_finished) self.connect(self.type_checkbox, SIGNAL("stateChanged(int)"), self._change_radio_enabled) self.connect(self.check_replace, SIGNAL("stateChanged(int)"), self._replace_activated) self.connect(self.words_radio, SIGNAL("clicked(bool)"), self._words_radio_pressed) def _replace_activated(self): self.replace_line.setEnabled(self.check_replace.isChecked()) self.phrase_radio.setChecked(True) def _words_radio_pressed(self, value): self.replace_line.setEnabled(not value) self.check_replace.setChecked(not value) self.words_radio.setChecked(True) def _change_radio_enabled(self, val): enabled = not self.type_checkbox.isChecked() self.phrase_radio.setEnabled(enabled) self.words_radio.setEnabled(enabled) def show(self, actual_project=None, actual=None): self.dir_combo.clear() self.dir_name_root = actual_project if \ actual_project else [self.user_home] self.dir_combo.addItems(self.dir_name_root) if actual: index = self.dir_combo.findText(actual) self.dir_combo.setCurrentIndex(index) super(FindInFilesDialog, self).show() self.pattern_line_edit.setFocus() def reject(self): self._kill_thread() # Crazy hack to avoid circular imports self.result_widget.parent().parent().parent().hide() super(FindInFilesDialog, self).reject() def _find_thread_finished(self): self.emit(SIGNAL("finished()")) self._find_thread.wait() def _select_dir(self): dir_name = QFileDialog.getExistingDirectory(self, self.tr("Open Directory"), self.dir_combo.currentText(), QFileDialog.ShowDirsOnly) index = self.dir_combo.findText(dir_name) if index >= 0: self.dir_combo.setCurrentIndex(index) else: self.dir_combo.insertItem(0, dir_name) self.dir_combo.setCurrentIndex(0) def _found_match(self, result): file_name = result[0] items = result[1] self.result_widget.update_result( self.dir_combo.currentText(), file_name, items) def _kill_thread(self): if self._find_thread.isRunning(): self._find_thread.cancel() self.accept() def _find_in_files(self): self.emit(SIGNAL("findStarted()")) self._kill_thread() self.result_widget.clear() pattern = self.pattern_line_edit.text() dir_name = self.dir_combo.currentText() filters = re.split("[,;]", self.filters_line_edit.text()) # Version of PyQt API 1 # filters = self.filters_line_edit.text().split(QRegExp("[,;]"), # QString.SkipEmptyParts) #remove the spaces in the words Ex. (" *.foo"--> "*.foo") filters = [f.strip() for f in filters] case_sensitive = self.case_checkbox.isChecked() type_ = QRegExp.RegExp if \ self.type_checkbox.isChecked() else QRegExp.FixedString recursive = self.recursive_checkbox.isChecked() by_phrase = True if self.phrase_radio.isChecked() or self.type_checkbox.isChecked(): regExp = QRegExp(pattern, case_sensitive, type_) elif self.words_radio.isChecked(): by_phrase = False type_ = QRegExp.RegExp pattern = '|'.join( [word.strip() for word in pattern.split()]) regExp = QRegExp(pattern, case_sensitive, type_) #save a reference to the root directory where we find self.dir_name_root = dir_name self._find_thread.find_in_files(dir_name, filters, regExp, recursive, by_phrase) class FindInFilesWidget(QWidget): def __init__(self, parent): QWidget.__init__(self, parent) self._main_container = main_container.MainContainer() self._explorer_container = explorer_container.ExplorerContainer() self._result_widget = FindInFilesResult() self._open_find_button = QPushButton(self.tr("Find!")) self._stop_button = QPushButton(self.tr("Stop")) self._clear_button = QPushButton(self.tr("Clear!")) self._replace_button = QPushButton(self.tr("Replace")) self._find_widget = FindInFilesDialog(self._result_widget, self) self._error_label = QLabel(self.tr("No Results")) self._error_label.setVisible(False) #Replace Area self.replace_widget = QWidget() hbox_replace = QHBoxLayout(self.replace_widget) hbox_replace.setContentsMargins(0, 0, 0, 0) self.lbl_replace = QLabel(self.tr("Replace results with:")) self.lbl_replace.setTextFormat(Qt.PlainText) self.replace_edit = QLineEdit() hbox_replace.addWidget(self.lbl_replace) hbox_replace.addWidget(self.replace_edit) self.replace_widget.setVisible(False) #Main Layout main_hbox = QHBoxLayout(self) #Result Layout tree_vbox = QVBoxLayout() tree_vbox.addWidget(self._result_widget) tree_vbox.addWidget(self._error_label) tree_vbox.addWidget(self.replace_widget) main_hbox.addLayout(tree_vbox) #Buttons Layout vbox = QVBoxLayout() vbox.addWidget(self._open_find_button) vbox.addWidget(self._stop_button) vbox.addWidget(self._clear_button) vbox.addSpacerItem(QSpacerItem(0, 50, QSizePolicy.Fixed, QSizePolicy.Expanding)) vbox.addWidget(self._replace_button) main_hbox.addLayout(vbox) self._open_find_button.setFocus() #signals self.connect(self._open_find_button, SIGNAL("clicked()"), self.open) self.connect(self._stop_button, SIGNAL("clicked()"), self._find_stop) self.connect(self._clear_button, SIGNAL("clicked()"), self._clear_results) self.connect(self._result_widget, SIGNAL( "itemActivated(QTreeWidgetItem *, int)"), self._go_to) self.connect(self._result_widget, SIGNAL( "itemClicked(QTreeWidgetItem *, int)"), self._go_to) self.connect(self._find_widget, SIGNAL("finished()"), self._find_finished) self.connect(self._find_widget, SIGNAL("findStarted()"), self._find_started) self.connect(self._replace_button, SIGNAL("clicked()"), self._replace_results) def _find_finished(self): self._stop_button.setEnabled(False) self._open_find_button.setEnabled(True) self._error_label.setVisible(False) if not self._result_widget.topLevelItemCount(): self._error_label.setVisible(True) if self._find_widget.check_replace.isChecked(): self.replace_widget.setVisible(True) self._replace_button.setEnabled(True) self.replace_edit.setText(self._find_widget.replace_line.text()) else: self._replace_button.setEnabled(False) self.replace_widget.setVisible(False) self._result_widget.setFocus() def _find_stop(self): self._find_widget._kill_thread() def _find_started(self): self._open_find_button.setEnabled(False) self._stop_button.setEnabled(True) def _clear_results(self): self._result_widget.clear() def _go_to(self, item, val): if item.text(1): parent = item.parent() file_name = parent.text(0) lineno = item.text(1) root_dir_name = parent.dir_name_root file_path = file_manager.create_path(root_dir_name, file_name) #open the file and jump_to_line self._main_container.open_file(file_path) self._main_container.editor_jump_to_line(lineno=int(lineno) - 1) def open(self): if not self._find_widget.isVisible(): actual_projects_obj = \ self._explorer_container.get_opened_projects() actual_projects = [p.path for p in actual_projects_obj] actual = self._explorer_container.get_actual_project() self._find_widget.show(actual_project=actual_projects, actual=actual) def find_occurrences(self, word): self._find_widget.pattern_line_edit.setText(word) editorWidget = main_container.MainContainer().get_actual_editor() explorerContainer = explorer_container.ExplorerContainer() projects_obj = explorerContainer.get_opened_projects() projects = [p.path for p in projects_obj] project = explorerContainer.get_actual_project() for p in projects: if file_manager.belongs_to_folder(p, editorWidget.ID): project = p break self._find_widget.dir_combo.clear() self._find_widget.dir_combo.addItem(project) self._find_widget.case_checkbox.setChecked(True) self._find_widget._find_in_files() def _replace_results(self): result = QMessageBox.question(self, self.tr("Replace Files Contents"), self.tr("Are you sure you want to replace the content in " "this files?\n(The change is not reversible)"), buttons=QMessageBox.Yes | QMessageBox.No) if result == QMessageBox.Yes: for index in range(self._result_widget.topLevelItemCount()): parent = self._result_widget.topLevelItem(index) root_dir_name = parent.dir_name_root file_name = parent.text(0) file_path = file_manager.create_path(root_dir_name, file_name) try: content = file_manager.read_file_content(file_path) pattern = self._find_widget.pattern_line_edit.text() flags = 0 if not self._find_widget.case_checkbox.isChecked(): flags |= re.IGNORECASE if self._find_widget.type_checkbox.isChecked(): pattern = r'\b%s\b' % pattern new_content = re.sub(pattern, self._find_widget.replace_line.text(), content, flags=flags) file_manager.store_file_content(file_path, new_content, False) except: print('File: %s content, could not be replaced' % file_path) ninja-ide-2.3/ninja_ide/gui/misc/misc_container.py000066400000000000000000000157241216641277400222450ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QWidget from PyQt4.QtGui import QToolBar from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QIcon from PyQt4.QtGui import QStyle from PyQt4.QtGui import QStackedWidget from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QSpacerItem from PyQt4.QtGui import QSizePolicy from PyQt4.QtCore import SIGNAL from PyQt4.QtWebKit import QWebPage from ninja_ide import resources from ninja_ide .core import settings from ninja_ide.gui.explorer import explorer_container from ninja_ide.gui.misc import console_widget from ninja_ide.gui.misc import run_widget from ninja_ide.gui.misc import web_render from ninja_ide.gui.misc import find_in_files from ninja_ide.gui.misc import results from ninja_ide.tools import ui_tools __miscContainerInstance = None def MiscContainer(*args, **kw): global __miscContainerInstance if __miscContainerInstance is None: __miscContainerInstance = __MiscContainer(*args, **kw) return __miscContainerInstance class __MiscContainer(QWidget): """From Miscellaneous, contains all the widgets in the bottom area.""" #Miscellaneous was to long and dificult to write :P def __init__(self, parent=None): QWidget.__init__(self, parent) vbox = QVBoxLayout(self) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) self.__toolbar = QToolBar() self.__toolbar.setObjectName('custom') hbox = QHBoxLayout() vbox.addLayout(hbox) self.stack = StackedWidget() vbox.addWidget(self.stack) self._console = console_widget.ConsoleWidget() self.stack.addWidget(self._console) self._runWidget = run_widget.RunWidget() self.stack.addWidget(self._runWidget) self._web = web_render.WebRender() self.stack.addWidget(self._web) self._findInFilesWidget = find_in_files.FindInFilesWidget( self.parent()) self.stack.addWidget(self._findInFilesWidget) #Last Element in the Stacked widget self._results = results.Results(self) self.stack.addWidget(self._results) self._btnConsole = QPushButton(QIcon(resources.IMAGES['console']), '') self._btnConsole.setToolTip(self.tr("Console")) self._btnRun = QPushButton(QIcon(resources.IMAGES['play']), '') self._btnRun.setToolTip(self.tr("Output")) self._btnWeb = QPushButton(QIcon(resources.IMAGES['web']), '') self._btnWeb.setToolTip(self.tr("Web Preview")) self._btnFind = QPushButton(QIcon(resources.IMAGES['find']), '') self._btnFind.setToolTip(self.tr("Find in Files")) #Toolbar hbox.addWidget(self.__toolbar) self.__toolbar.addWidget(self._btnConsole) self.__toolbar.addWidget(self._btnRun) self.__toolbar.addWidget(self._btnWeb) self.__toolbar.addWidget(self._btnFind) self.__toolbar.addSeparator() hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding)) btn_close = QPushButton( self.style().standardIcon(QStyle.SP_DialogCloseButton), '') btn_close.setObjectName('navigation_button') btn_close.setToolTip(self.tr('F4: Show/Hide')) hbox.addWidget(btn_close) self.connect(self._btnConsole, SIGNAL("clicked()"), lambda: self._item_changed(0)) self.connect(self._btnRun, SIGNAL("clicked()"), lambda: self._item_changed(1)) self.connect(self._btnWeb, SIGNAL("clicked()"), lambda: self._item_changed(2)) self.connect(self._btnFind, SIGNAL("clicked()"), lambda: self._item_changed(3)) self.connect(btn_close, SIGNAL('clicked()'), self.hide) def gain_focus(self): self._console.setFocus() def _item_changed(self, val): if not self.isVisible(): self.show() self.stack.show_display(val) def show_find_in_files_widget(self): index_of = self.stack.indexOf(self._findInFilesWidget) self._item_changed(index_of) self._findInFilesWidget.open() def show_find_occurrences(self, word): index_of = self.stack.indexOf(self._findInFilesWidget) self._item_changed(index_of) self._findInFilesWidget.find_occurrences(word) def load_toolbar(self, toolbar): toolbar.addWidget(self._combo) toolbar.addSeparator() def run_application(self, fileName, pythonPath=False, PYTHONPATH=None, programParams='', preExec='', postExec=''): self._item_changed(1) self.show() self._runWidget.start_process(fileName, pythonPath, PYTHONPATH, programParams, preExec, postExec) self._runWidget.input.setFocus() def show_results(self, items): self._item_changed(4) self.show() self._results.update_result(items) self._results._tree.setFocus() def kill_application(self): self._runWidget.kill_process() def render_web_page(self, url): self._item_changed(2) self.show() self._web.render_page(url) if settings.SHOW_WEB_INSPECTOR: explorer_container.ExplorerContainer().set_inspection_page( self._web.webFrame.page()) self._web.webFrame.triggerPageAction( QWebPage.InspectElement, True) explorer_container.ExplorerContainer().refresh_inspector() def add_to_stack(self, widget, icon_path, description): """ Add a widget to the container and an button(with icon))to the toolbar to show the widget """ #add the widget self.stack.addWidget(widget) #create a button in the toolbar to show the widget button = QPushButton(QIcon(icon_path), '') button.setToolTip(description) index = self.stack.count() - 1 func = lambda: self._item_changed(index) self.connect(button, SIGNAL("clicked()"), func) self.__toolbar.addWidget(button) class StackedWidget(QStackedWidget): def __init__(self): QStackedWidget.__init__(self) def setCurrentIndex(self, index): self.fader_widget = ui_tools.FaderWidget(self.currentWidget(), self.widget(index)) QStackedWidget.setCurrentIndex(self, index) def show_display(self, index): self.setCurrentIndex(index) ninja-ide-2.3/ninja_ide/gui/misc/plugin_preferences.py000066400000000000000000000045051216641277400231220ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QWidget from PyQt4.QtGui import QTabWidget from PyQt4.QtGui import QVBoxLayout from ninja_ide.core import plugin_manager from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.gui.misc.plugin_preferences') class PluginPreferences(QWidget): """ Plugins section widget in NINJA-IDE Preferences """ def __init__(self): QWidget.__init__(self) self.plugin_manager = plugin_manager.PluginManager() vbox = QVBoxLayout(self) self._tabs = QTabWidget() vbox.addWidget(self._tabs) #load widgets self._load_widgets() def _load_widgets(self): logger.info("Loading plugins preferences widgets") #Collect the preferences widget for each active plugin for plugin in self.plugin_manager.get_active_plugins(): plugin_name = plugin.metadata.get('name') try: preferences_widget = plugin.get_preferences_widget() if preferences_widget: self._tabs.addTab(preferences_widget, plugin_name) except Exception as reason: logger.error("Unable to add the preferences widget (%s): %s", plugin_name, reason) continue def save(self): logger.info("Saving plugins preferences") for i in range(self._tabs.count()): try: self._tabs.widget(i).save() except Exception as reason: logger.error("Unable to save preferences (%s): %s", self._tabs.tabText(i), reason) continue ninja-ide-2.3/ninja_ide/gui/misc/results.py000066400000000000000000000050421216641277400207410ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QWidget from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QTreeWidget from PyQt4.QtGui import QTreeWidgetItem from PyQt4.QtGui import QAbstractItemView from PyQt4.QtGui import QHeaderView from PyQt4.QtCore import Qt from PyQt4.QtCore import SIGNAL from ninja_ide.gui.main_panel import main_container class Results(QWidget): def __init__(self, parent): QWidget.__init__(self, parent) self._parent = parent vbox = QVBoxLayout(self) self._tree = QTreeWidget() self._tree.setHeaderLabels((self.tr("Content"), self.tr('File'), self.tr('Line'))) self._tree.header().setHorizontalScrollMode( QAbstractItemView.ScrollPerPixel) self._tree.header().setResizeMode(0, QHeaderView.ResizeToContents) self._tree.header().setResizeMode(1, QHeaderView.ResizeToContents) self._tree.header().setResizeMode(2, QHeaderView.ResizeToContents) self._tree.header().setStretchLastSection(True) self._tree.sortByColumn(1, Qt.AscendingOrder) vbox.addWidget(self._tree) #Signals self.connect(self._tree, SIGNAL("itemActivated(QTreeWidgetItem*, int)"), self._open_result) self.connect(self._tree, SIGNAL("itemClicked(QTreeWidgetItem*, int)"), self._open_result) def _open_result(self, item, col): filename = item.toolTip(1) line = int(item.text(2)) - 1 main_container.MainContainer().open_file( filename=filename, cursorPosition=line, positionIsLineNumber=True) self._parent.hide() def update_result(self, items): self._tree.clear() for i in items: item = QTreeWidgetItem(self._tree, (i[3], i[0], str(i[2] + 1))) item.setToolTip(1, i[1]) ninja-ide-2.3/ninja_ide/gui/misc/run_widget.py000066400000000000000000000352611216641277400214150ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import time import re from PyQt4.QtGui import QPlainTextEdit from PyQt4.QtGui import QLabel from PyQt4.QtGui import QLineEdit from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QWidget from PyQt4.QtGui import QMenu from PyQt4.QtGui import QTextCursor from PyQt4.QtGui import QTextCharFormat from PyQt4.QtGui import QColor from PyQt4.QtGui import QBrush from PyQt4.QtGui import QFont from PyQt4.QtCore import Qt from PyQt4.QtCore import QProcess from PyQt4.QtCore import QProcessEnvironment from PyQt4.QtCore import QFile from PyQt4.QtCore import SIGNAL from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.core import file_manager from ninja_ide.gui.main_panel import main_container class RunWidget(QWidget): """Widget that show the execution output in the MiscContainer.""" def __init__(self): QWidget.__init__(self) vbox = QVBoxLayout(self) vbox.setSpacing(0) vbox.setContentsMargins(0, 0, 0, 0) self.output = OutputWidget(self) hbox = QHBoxLayout() self.input = QLineEdit() self.lblInput = QLabel(self.tr("Input:")) vbox.addWidget(self.output) hbox.addWidget(self.lblInput) hbox.addWidget(self.input) vbox.addLayout(hbox) self.set_font(settings.FONT_FAMILY, settings.FONT_SIZE) #process self.currentProcess = None self.__preScriptExecuted = False self._proc = QProcess(self) self._preExecScriptProc = QProcess(self) self._postExecScriptProc = QProcess(self) self.connect(self._proc, SIGNAL("readyReadStandardOutput()"), self.output._refresh_output) self.connect(self._proc, SIGNAL("readyReadStandardError()"), self.output._refresh_error) self.connect(self._proc, SIGNAL("finished(int, QProcess::ExitStatus)"), self.finish_execution) self.connect(self._proc, SIGNAL("error(QProcess::ProcessError)"), self.process_error) self.connect(self.input, SIGNAL("returnPressed()"), self.insert_input) self.connect(self._preExecScriptProc, SIGNAL("finished(int, QProcess::ExitStatus)"), self.__main_execution) self.connect(self._preExecScriptProc, SIGNAL("readyReadStandardOutput()"), self.output._refresh_output) self.connect(self._preExecScriptProc, SIGNAL("readyReadStandardError()"), self.output._refresh_error) self.connect(self._postExecScriptProc, SIGNAL("finished(int, QProcess::ExitStatus)"), self.__post_execution_message) self.connect(self._postExecScriptProc, SIGNAL("readyReadStandardOutput()"), self.output._refresh_output) self.connect(self._postExecScriptProc, SIGNAL("readyReadStandardError()"), self.output._refresh_error) def set_font(self, family, size): font = QFont(family, size) self.output.document().setDefaultFont(font) self.output.plain_format.setFont(font) self.output.error_format.setFont(font) def process_error(self, error): """Listen to the error signals from the running process.""" self.lblInput.hide() self.input.hide() self._proc.kill() format_ = QTextCharFormat() format_.setAnchor(True) format_.setForeground(QBrush(QColor(resources.CUSTOM_SCHEME.get( "error-underline", resources.COLOR_SCHEME["error-underline"])))) if error == 0: self.output.textCursor().insertText(self.tr('Failed to start'), format_) else: self.output.textCursor().insertText( (self.tr('Error during execution, QProcess error: %d') % error), format_) def finish_execution(self, exitCode, exitStatus): """Print a message and hide the input line when the execution ends.""" self.lblInput.hide() self.input.hide() format_ = QTextCharFormat() format_.setAnchor(True) self.output.textCursor().insertText('\n\n') if exitStatus == QProcess.NormalExit: format_.setForeground(QBrush(QColor(resources.CUSTOM_SCHEME.get( "keyword", resources.COLOR_SCHEME["keyword"])))) self.output.textCursor().insertText( self.tr("Execution Successful!"), format_) else: format_.setForeground(QBrush(QColor(resources.CUSTOM_SCHEME.get( "error-underline", resources.COLOR_SCHEME["error-underline"])))) self.output.textCursor().insertText( self.tr("Execution Interrupted"), format_) self.output.textCursor().insertText('\n\n') self.__post_execution() def insert_input(self): """Take the user input and send it to the process.""" text = self.input.text() + '\n' self._proc.writeData(text) self.output.textCursor().insertText(text, self.output.plain_format) self.input.setText("") self.set_font(settings.FONT_FAMILY, settings.FONT_SIZE) def start_process(self, fileName, pythonPath=False, PYTHONPATH=None, programParams='', preExec='', postExec=''): """Prepare the output widget and start the process.""" self.lblInput.show() self.input.show() self.fileName = fileName self.pythonPath = pythonPath # FIXME, this is python interpreter self.programParams = programParams self.preExec = preExec self.postExec = postExec self.PYTHONPATH = PYTHONPATH self.__pre_execution() def __main_execution(self): """Execute the project.""" self.output.setCurrentCharFormat(self.output.plain_format) message = '' if self.__preScriptExecuted: self.__preScriptExecuted = False message = self.tr( "Pre Execution Script Successfully executed.\n\n") self.output.setPlainText(message + 'Running: %s (%s)\n\n' % (self.fileName, time.ctime())) self.output.moveCursor(QTextCursor.Down) self.output.moveCursor(QTextCursor.Down) self.output.moveCursor(QTextCursor.Down) #runner.run_code_from_file(fileName) if not self.pythonPath: self.pythonPath = settings.PYTHON_PATH #change the working directory to the fileName dir file_directory = file_manager.get_folder(self.fileName) self._proc.setWorkingDirectory(file_directory) #force python to unbuffer stdin and stdout options = ['-u'] + settings.EXECUTION_OPTIONS.split() self.currentProcess = self._proc env = QProcessEnvironment() system_environemnt = self._proc.systemEnvironment() for e in system_environemnt: key, value = e.split('=', 1) env.insert(key, value) if self.PYTHONPATH: envpaths = [path for path in self.PYTHONPATH.splitlines()] for path in envpaths: env.insert('PYTHONPATH', path) env.insert('PYTHONIOENCODING', 'utf-8') self._proc.setProcessEnvironment(env) self._proc.start(self.pythonPath, options + [self.fileName] + [p.strip() for p in self.programParams.split(',') if p]) def __pre_execution(self): """Execute a script before executing the project.""" filePreExec = QFile(self.preExec) if filePreExec.exists() and \ bool(QFile.ExeUser & filePreExec.permissions()): ext = file_manager.get_file_extension(self.preExec) if not self.pythonPath: self.pythonPath = settings.PYTHON_PATH self.currentProcess = self._preExecScriptProc self.__preScriptExecuted = True if ext == 'py': self._preExecScriptProc.start(self.pythonPath, [self.preExec]) else: self._preExecScriptProc.start(self.preExec) else: self.__main_execution() def __post_execution(self): """Execute a script after executing the project.""" filePostExec = QFile(self.postExec) if filePostExec.exists() and \ bool(QFile.ExeUser & filePostExec.permissions()): ext = file_manager.get_file_extension(self.postExec) if not self.pythonPath: self.pythonPath = settings.PYTHON_PATH self.currentProcess = self._postExecScriptProc if ext == 'py': self._postExecScriptProc.start(self.pythonPath, [self.postExec]) else: self._postExecScriptProc.start(self.postExec) def __post_execution_message(self): """Print post execution message.""" self.output.textCursor().insertText('\n\n') format_ = QTextCharFormat() format_.setAnchor(True) format_.setForeground(Qt.green) self.output.textCursor().insertText( self.tr("Post Execution Script Successfully executed."), format_) def kill_process(self): """Kill the running process.""" self._proc.kill() class OutputWidget(QPlainTextEdit): def __init__(self, parent): QPlainTextEdit.__init__(self, parent) self._parent = parent self.setReadOnly(True) self.maxValue = 0 self.actualValue = 0 #traceback pattern self.patLink = re.compile(r'(\s)*File "(.*?)", line \d.+') #formats font = QFont(settings.FONT_FAMILY, settings.FONT_SIZE) self.plain_format = QTextCharFormat() self.plain_format.setFont(font) self.plain_format.setForeground(QBrush(QColor( resources.CUSTOM_SCHEME.get("editor-text", resources.COLOR_SCHEME["editor-text"])))) self.error_format = QTextCharFormat() self.error_format.setFont(font) self.error_format.setAnchor(True) self.error_format.setFontUnderline(True) self.error_format.setUnderlineStyle(QTextCharFormat.SingleUnderline) self.error_format.setUnderlineColor(Qt.red) self.error_format.setForeground(Qt.blue) self.error_format.setToolTip(self.tr("Click to show the source")) self.connect(self, SIGNAL("blockCountChanged(int)"), self._scroll_area) css = 'QPlainTextEdit {color: %s; background-color: %s;' \ 'selection-color: %s; selection-background-color: %s;}' \ % (resources.CUSTOM_SCHEME.get('editor-text', resources.COLOR_SCHEME['editor-text']), resources.CUSTOM_SCHEME.get('editor-background', resources.COLOR_SCHEME['editor-background']), resources.CUSTOM_SCHEME.get('editor-selection-color', resources.COLOR_SCHEME['editor-selection-color']), resources.CUSTOM_SCHEME.get('editor-selection-background', resources.COLOR_SCHEME['editor-selection-background'])) self.setStyleSheet(css) def _scroll_area(self): """When new text is added to the widget, move the scroll to the end.""" if self.actualValue == self.maxValue: self.moveCursor(QTextCursor.End) def mousePressEvent(self, event): """ When the execution fail, allow to press the links in the traceback, to go to the line when the error occur. """ QPlainTextEdit.mousePressEvent(self, event) self.go_to_error(event) def _refresh_output(self): """Read the output buffer from the process and append the text.""" #we should decode the bytes! currentProcess = self._parent.currentProcess text = currentProcess.readAllStandardOutput().data().decode('utf8') verticalScroll = self.verticalScrollBar() self.actualValue = verticalScroll.value() self.maxValue = verticalScroll.maximum() self.textCursor().insertText(text, self.plain_format) def _refresh_error(self): """Read the error buffer from the process and append the text.""" #we should decode the bytes! cursor = self.textCursor() currentProcess = self._parent.currentProcess text = currentProcess.readAllStandardError().data().decode('utf8') text_lines = text.split('\n') verticalScroll = self.verticalScrollBar() self.actualValue = verticalScroll.value() self.maxValue = verticalScroll.maximum() for t in text_lines: cursor.insertBlock() if self.patLink.match(t): cursor.insertText(t, self.error_format) else: cursor.insertText(t, self.plain_format) def go_to_error(self, event): """Resolve the link and take the user to the error line.""" cursor = self.cursorForPosition(event.pos()) text = cursor.block().text() if self.patLink.match(text): file_path, lineno = self._parse_traceback(text) main = main_container.MainContainer() main.open_file(file_path) main.editor_jump_to_line(lineno=int(lineno) - 1) def _parse_traceback(self, text): """ Parse a line of python traceback and returns a tuple with (file_name, lineno) """ file_word_index = text.find('File') comma_min_index = text.find(',') comma_max_index = text.rfind(',') file_name = text[file_word_index + 6:comma_min_index - 1] lineno = text[comma_min_index + 7:comma_max_index] return (file_name, lineno) def contextMenuEvent(self, event): """Show a context menu for the Plain Text widget.""" popup_menu = self.createStandardContextMenu() menuOutput = QMenu(self.tr("Output")) cleanAction = menuOutput.addAction(self.tr("Clean")) popup_menu.insertSeparator(popup_menu.actions()[0]) popup_menu.insertMenu(popup_menu.actions()[0], menuOutput) # This is a hack because if we leave the widget text empty # it throw a violent segmentation fault in start_process self.connect(cleanAction, SIGNAL("triggered()"), lambda: self.setPlainText('\n\n')) popup_menu.exec_(event.globalPos()) ninja-ide-2.3/ninja_ide/gui/misc/shortcut_manager.py000066400000000000000000000302601216641277400226050ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QTreeWidget from PyQt4.QtGui import QTreeWidgetItem from PyQt4.QtGui import QDialog from PyQt4.QtGui import QWidget from PyQt4.QtGui import QLabel from PyQt4.QtGui import QLineEdit from PyQt4.QtGui import QKeySequence from PyQt4.QtGui import QMessageBox from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import Qt from PyQt4.QtCore import QEvent from PyQt4.QtCore import QSettings from ninja_ide import resources from ninja_ide.gui import actions class TreeResult(QTreeWidget): def __init__(self): QTreeWidget.__init__(self) self.setHeaderLabels((self.tr('Description'), self.tr('Shortcut'))) #columns width self.setColumnWidth(0, 175) self.header().setStretchLastSection(True) self.setSortingEnabled(True) self.sortByColumn(0, Qt.AscendingOrder) class ShortcutDialog(QDialog): """ Dialog to set a shortcut for an action this class emit the follow signals: shortcutChanged(QKeySequence) """ def __init__(self, parent): QDialog.__init__(self, parent) self.keys = 0 #Keyword modifiers! self.keyword_modifiers = (Qt.Key_Control, Qt.Key_Meta, Qt.Key_Shift, Qt.Key_Alt, Qt.Key_Menu) #main layout main_vbox = QVBoxLayout(self) self.line_edit = QLineEdit() self.line_edit.setReadOnly(True) #layout for buttons buttons_layout = QHBoxLayout() ok_button = QPushButton(self.tr("Accept")) cancel_button = QPushButton(self.tr("Cancel")) #add widgets main_vbox.addWidget(self.line_edit) buttons_layout.addWidget(ok_button) buttons_layout.addWidget(cancel_button) main_vbox.addLayout(buttons_layout) self.line_edit.installEventFilter(self) #buttons signals self.connect(ok_button, SIGNAL("clicked()"), self.save_shortcut) self.connect(cancel_button, SIGNAL("clicked()"), self.close) def save_shortcut(self): self.hide() shortcut = QKeySequence(self.line_edit.text()) self.emit(SIGNAL('shortcutChanged'), shortcut) def set_shortcut(self, txt): self.line_edit.setText(txt) def eventFilter(self, watched, event): if event.type() == QEvent.KeyPress: self.keyPressEvent(event) return True return False def keyPressEvent(self, evt): #modifier can not be used as shortcut if evt.key() in self.keyword_modifiers: return #save the key if evt.key() == Qt.Key_Backtab and evt.modifiers() & Qt.ShiftModifier: self.keys = Qt.Key_Tab else: self.keys = evt.key() if evt.modifiers() & Qt.ShiftModifier: self.keys += Qt.SHIFT if evt.modifiers() & Qt.ControlModifier: self.keys += Qt.CTRL if evt.modifiers() & Qt.AltModifier: self.keys += Qt.ALT if evt.modifiers() & Qt.MetaModifier: self.keys += Qt.META #set the keys self.set_shortcut(QKeySequence(self.keys).toString()) class ShortcutConfiguration(QWidget): """ Dialog to manage ALL shortcuts """ def __init__(self): QWidget.__init__(self) self.shortcuts_text = { "Duplicate": self.tr("Duplicate the line/selection"), "Remove-line": self.tr("Remove the line/selection"), "Move-up": self.tr("Move the line/selection up"), "Move-down": self.tr("Move the line/selection down"), "Close-tab": self.tr("Close the current tab"), "New-file": self.tr("Create a New tab"), "New-project": self.tr("Create a new Project"), "Open-file": self.tr("Open a File"), "Open-project": self.tr("Open a Project"), "Save-file": self.tr("Save the current file"), "Save-project": self.tr("Save the current project opened files"), "Print-file": self.tr("Print current file"), "Redo": self.tr("Redo"), "Comment": self.tr("Comment line/selection"), "Uncomment": self.tr("Uncomment line/selection"), "Horizontal-line": self.tr("Insert Horizontal line"), "Title-comment": self.tr("Insert comment Title"), "Indent-less": self.tr("Indent less"), "Hide-misc": self.tr("Hide Misc Container"), "Hide-editor": self.tr("Hide Editor Area"), "Hide-explorer": self.tr("Hide Explorer"), "Run-file": self.tr("Execute current file"), "Run-project": self.tr("Execute current project"), "Debug": self.tr("Debug"), "Switch-Focus": self.tr("Switch keyboard focus"), "Stop-execution": self.tr("Stop Execution"), "Hide-all": self.tr("Hide all (Except Editor)"), "Full-screen": self.tr("Full Screen"), "Find": self.tr("Find"), "Find-replace": self.tr("Find & Replace"), "Find-with-word": self.tr("Find word under cursor"), "Find-next": self.tr("Find Next"), "Find-previous": self.tr("Find Previous"), "Help": self.tr("Show Python Help"), "Split-vertical": self.tr("Split Tabs Vertically"), "Split-horizontal": self.tr("Split Tabs Horizontally"), "Follow-mode": self.tr("Activate/Deactivate Follow Mode"), "Reload-file": self.tr("Reload File"), "Jump": self.tr("Jump to line"), "Find-in-files": self.tr("Find in Files"), "Import": self.tr("Import from everywhere"), "Go-to-definition": self.tr("Go to definition"), "Complete-Declarations": self.tr("Complete Declarations"), "Code-locator": self.tr("Show Code Locator"), "File-Opener": self.tr("Show File Opener"), "Navigate-back": self.tr("Navigate Back"), "Navigate-forward": self.tr("Navigate Forward"), "Open-recent-closed": self.tr("Open recent closed file"), "Change-Tab": self.tr("Change to the next Tab"), "Change-Tab-Reverse": self.tr("Change to the previous Tab"), "Move-Tab-to-right": self.tr("Move tab to right"), "Move-Tab-to-left": self.tr("Move tab to left"), "Show-Code-Nav": self.tr("Activate History Navigation"), "Show-Bookmarks-Nav": self.tr("Activate Bookmarks Navigation"), "Show-Breakpoints-Nav": self.tr("Activate Breakpoints Navigation"), "Show-Paste-History": self.tr("Show copy/paste history"), "History-Copy": self.tr("Copy into copy/paste history"), "History-Paste": self.tr("Paste from copy/paste history"), "change-split-focus": self.tr( "Change the keyboard focus between the current splits"), "Add-Bookmark-or-Breakpoint": self.tr( "Insert Bookmark/Breakpoint"), "move-tab-to-next-split": self.tr( "Move the current Tab to the next split."), "change-tab-visibility": self.tr( "Show/Hide the Tabs in the Editor Area."), "Highlight-Word": self.tr( "Highlight occurrences for word under cursor") } self.shortcut_dialog = ShortcutDialog(self) #main layout main_vbox = QVBoxLayout(self) #layout for buttons buttons_layout = QVBoxLayout() #widgets self.result_widget = TreeResult() load_defaults_button = QPushButton(self.tr("Load defaults")) #add widgets main_vbox.addWidget(self.result_widget) buttons_layout.addWidget(load_defaults_button) main_vbox.addLayout(buttons_layout) main_vbox.addWidget(QLabel( self.tr("The Shortcut's Text in the Menus are " "going to be refreshed on restart."))) #load data! self.result_widget.setColumnWidth(0, 400) self._load_shortcuts() #signals #open the set shortcut dialog self.connect(self.result_widget, SIGNAL("itemDoubleClicked(QTreeWidgetItem*, int)"), self._open_shortcut_dialog) #load defaults shortcuts self.connect(load_defaults_button, SIGNAL("clicked()"), self._load_defaults_shortcuts) #one shortcut has changed self.connect(self.shortcut_dialog, SIGNAL('shortcutChanged'), self._shortcut_changed) def _shortcut_changed(self, keysequence): """ Validate and set a new shortcut """ if self.__validate_shortcut(keysequence): self.result_widget.currentItem().setText(1, keysequence.toString()) def __validate_shortcut(self, keysequence): """ Validate a shortcut """ if keysequence.isEmpty(): return True keyname = self.result_widget.currentItem().text(0) keystr = keysequence for top_index in range(self.result_widget.topLevelItemCount()): top_item = self.result_widget.topLevelItem(top_index) if top_item.text(0) != keyname: itmseq = top_item.text(1) if keystr == itmseq: val = QMessageBox.warning(self, self.tr('Shortcut is already in use'), self.tr("Do you want to remove it?"), QMessageBox.Yes, QMessageBox.No) if val == QMessageBox.Yes: top_item.setText(1, "") return True else: return False if not itmseq: continue return True def _open_shortcut_dialog(self, item, column): """ Open the dialog to set a shortcut """ if item.childCount(): return self.shortcut_dialog.set_shortcut( QKeySequence(item.text(1)).toString()) self.shortcut_dialog.exec_() def save(self): """ Save all shortcuts to settings """ settings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) settings.beginGroup("shortcuts") for index in range(self.result_widget.topLevelItemCount()): item = self.result_widget.topLevelItem(index) shortcut_keys = item.text(1) shortcut_name = item.text(2) settings.setValue(shortcut_name, shortcut_keys) settings.endGroup() actions.Actions().update_shortcuts() def _load_shortcuts(self): for action in resources.CUSTOM_SHORTCUTS: shortcut_action = resources.get_shortcut(action) #populate the tree widget tree_data = [self.shortcuts_text[action], shortcut_action.toString(QKeySequence.NativeText), action] item = QTreeWidgetItem(self.result_widget, tree_data) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) def _load_defaults_shortcuts(self): #clean custom shortcuts and UI widget resources.clean_custom_shortcuts() self.result_widget.clear() for name, action in list(resources.SHORTCUTS.items()): shortcut_action = action #populate the tree widget tree_data = [self.shortcuts_text[name], shortcut_action.toString(QKeySequence.NativeText), name] item = QTreeWidgetItem(self.result_widget, tree_data) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) ninja-ide-2.3/ninja_ide/gui/misc/web_render.py000066400000000000000000000026331216641277400213570ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from PyQt4.QtGui import QWidget from PyQt4.QtGui import QVBoxLayout from PyQt4.QtWebKit import QWebView from PyQt4.QtCore import QUrl from PyQt4.QtWebKit import QWebSettings class WebRender(QWidget): def __init__(self): QWidget.__init__(self) vbox = QVBoxLayout(self) #Web Frame self.webFrame = QWebView() QWebSettings.globalSettings().setAttribute( QWebSettings.DeveloperExtrasEnabled, True) vbox.addWidget(self.webFrame) def render_page(self, url): self.webFrame.load(QUrl('file:///' + url)) def render_from_html(self, html, url=None): url = url and QUrl(url) or "" self.webFrame.setHtml(html, url) ninja-ide-2.3/ninja_ide/gui/status_bar.py000066400000000000000000000433651216641277400204660ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import re from PyQt4.QtGui import QStatusBar from PyQt4.QtGui import QLabel from PyQt4.QtGui import QCompleter from PyQt4.QtGui import QFileSystemModel from PyQt4.QtGui import QTextDocument from PyQt4.QtGui import QWidget from PyQt4.QtGui import QKeySequence from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QLineEdit from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QTextCursor from PyQt4.QtGui import QCheckBox from PyQt4.QtGui import QStyle from PyQt4.QtGui import QIcon from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import Qt from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.tools import locator from ninja_ide.tools import ui_tools from ninja_ide.gui.main_panel import main_container from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.gui.status_bar') DEBUG = logger.debug __statusBarInstance = None def StatusBar(*args, **kw): global __statusBarInstance if __statusBarInstance is None: __statusBarInstance = __StatusBar(*args, **kw) return __statusBarInstance class __StatusBar(QStatusBar): def __init__(self, parent=None): QStatusBar.__init__(self, parent) self._widgetStatus = QWidget() vbox = QVBoxLayout(self._widgetStatus) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) #Search Layout self._searchWidget = SearchWidget(self) vbox.addWidget(self._searchWidget) #Replace Layout self._replaceWidget = ReplaceWidget(self) vbox.addWidget(self._replaceWidget) self._replaceWidget.setVisible(False) #Code Locator self._codeLocator = locator.CodeLocatorWidget(self) vbox.addWidget(self._codeLocator) self._codeLocator.setVisible(False) #File system completer self._fileSystemOpener = FileSystemOpener() vbox.addWidget(self._fileSystemOpener) self._fileSystemOpener.setVisible(False) self.addWidget(self._widgetStatus) self.connect(self, SIGNAL("messageChanged(QString)"), self.message_end) self.connect(self._replaceWidget._btnCloseReplace, SIGNAL("clicked()"), lambda: self._replaceWidget.setVisible(False)) self.connect(self._replaceWidget._btnReplace, SIGNAL("clicked()"), self.replace) self.connect(self._replaceWidget._btnReplaceAll, SIGNAL("clicked()"), self.replace_all) self.connect(self._replaceWidget._btnReplaceSelection, SIGNAL("clicked()"), self.replace_selected) self.connect(self._fileSystemOpener.btnClose, SIGNAL("clicked()"), self.hide_status) self.connect(self._fileSystemOpener, SIGNAL("requestHide()"), self.hide_status) def handle_tab_changed(self, new_tab): """ Re-run search if tab changed, we use the find of search widget because we want the widget to be updated. """ editor = main_container.MainContainer().get_actual_editor() if self._searchWidget.isVisible(): self._searchWidget.find_matches(editor) if editor: self.disconnect(editor, SIGNAL("textChanged()"), self._notify_editor_changed) self.connect(editor, SIGNAL("textChanged()"), self._notify_editor_changed) def _notify_editor_changed(self): """ Lets search widget know that the editor contents changed and find needs to be re-run """ if self._searchWidget.isVisible(): editor = main_container.MainContainer().get_actual_editor() self._searchWidget.contents_changed(editor) def explore_code(self): self._codeLocator.explore_code() def explore_file_code(self, path): self._codeLocator.explore_file_code(path) def show(self): self.clearMessage() QStatusBar.show(self) editor = main_container.MainContainer().get_actual_editor() if editor and editor.textCursor().hasSelection(): text = editor.textCursor().selectedText() self._searchWidget._line.setText(text) self._searchWidget.find_matches(editor, True) if self._widgetStatus.isVisible(): self._searchWidget._line.setFocus() self._searchWidget._line.selectAll() def show_replace(self): self.clearMessage() self.show() editor = main_container.MainContainer().get_actual_editor() if editor: if editor.textCursor().hasSelection(): word = editor.textCursor().selectedText() self._searchWidget._line.setText(word) self._replaceWidget.setVisible(True) def show_with_word(self): self.clearMessage() editor = main_container.MainContainer().get_actual_editor() if editor: word = editor._text_under_cursor() self._searchWidget._line.setText(word) editor = main_container.MainContainer().get_actual_editor() editor.moveCursor(QTextCursor.WordLeft) self._searchWidget.find_matches(editor) self.show() def show_locator(self): if not self._codeLocator.isVisible(): self.clearMessage() self._searchWidget.setVisible(False) self.show() self._codeLocator.setVisible(True) self._codeLocator._completer.setFocus() self._codeLocator.show_suggestions() def show_file_opener(self): self.clearMessage() self._searchWidget.setVisible(False) self._fileSystemOpener.setVisible(True) self.show() self._fileSystemOpener.pathLine.setFocus() def hide_status(self): self._searchWidget._checkSensitive.setCheckState(Qt.Unchecked) self._searchWidget._checkWholeWord.setCheckState(Qt.Unchecked) self.hide() self._searchWidget.setVisible(True) self._replaceWidget.setVisible(False) self._codeLocator.setVisible(False) self._fileSystemOpener.setVisible(False) widget = main_container.MainContainer().get_actual_widget() if widget: widget.setFocus() def replace(self): s = 0 if not self._searchWidget._checkSensitive.isChecked() \ else QTextDocument.FindCaseSensitively w = 0 if not self._searchWidget._checkWholeWord.isChecked() \ else QTextDocument.FindWholeWords flags = 0 + s + w editor = main_container.MainContainer().get_actual_editor() if editor: editor.replace_match( self._searchWidget._line.text(), self._replaceWidget._lineReplace.text(), flags) if not editor.textCursor().hasSelection(): self.find() def replace_selected(self): self.replace_all(True) def replace_all(self, selected=False): s = 0 if not self._searchWidget._checkSensitive.isChecked() \ else QTextDocument.FindCaseSensitively w = 0 if not self._searchWidget._checkWholeWord.isChecked() \ else QTextDocument.FindWholeWords flags = 0 + s + w editor = main_container.MainContainer().get_actual_editor() if editor: editor.replace_match(self._searchWidget._line.text(), self._replaceWidget._lineReplace.text(), flags, True, selected) def find(self): s = 0 if not self._searchWidget._checkSensitive.isChecked() \ else QTextDocument.FindCaseSensitively w = 0 if not self._searchWidget._checkWholeWord.isChecked() \ else QTextDocument.FindWholeWords flags = s + w editor = main_container.MainContainer().get_actual_editor() if editor: editor.find_match(self._searchWidget._line.text(), flags) def find_next(self): s = 0 if not self._searchWidget._checkSensitive.isChecked() \ else QTextDocument.FindCaseSensitively w = 0 if not self._searchWidget._checkWholeWord.isChecked() \ else QTextDocument.FindWholeWords flags = 0 + s + w editor = main_container.MainContainer().get_actual_editor() if editor: editor.find_match(self._searchWidget._line.text(), flags, True) def find_previous(self): s = 0 if not self._searchWidget._checkSensitive.isChecked() \ else QTextDocument.FindCaseSensitively w = 0 if not self._searchWidget._checkWholeWord.isChecked() \ else QTextDocument.FindWholeWords flags = 1 + s + w editor = main_container.MainContainer().get_actual_editor() if editor: editor.find_match(self._searchWidget._line.text(), flags, True) def showMessage(self, message, timeout): if settings.SHOW_STATUS_NOTIFICATIONS: self._widgetStatus.hide() self._replaceWidget.setVisible(False) self.show() QStatusBar.showMessage(self, message, timeout) def message_end(self, message): if message == '': self.hide() QStatusBar.clearMessage(self) self._widgetStatus.show() class SearchWidget(QWidget): def __init__(self, parent): QWidget.__init__(self, parent) self._parent = parent hSearch = QHBoxLayout(self) hSearch.setContentsMargins(0, 0, 0, 0) self._checkSensitive = QCheckBox(self.trUtf8("Respect Case Sensitive")) self._checkWholeWord = QCheckBox(self.trUtf8("Find Whole Words")) self._line = TextLine(self) self._line.setMinimumWidth(250) self._btnClose = QPushButton( self.style().standardIcon(QStyle.SP_DialogCloseButton), '') self._btnFind = QPushButton(QIcon(resources.IMAGES['find']), '') self.btnPrevious = QPushButton( self.style().standardIcon(QStyle.SP_ArrowLeft), '') self.btnPrevious.setToolTip(self.trUtf8("Press %s") % resources.get_shortcut("Find-previous").toString( QKeySequence.NativeText)) self.btnNext = QPushButton( self.style().standardIcon(QStyle.SP_ArrowRight), '') self.btnNext.setToolTip(self.trUtf8("Press %s") % resources.get_shortcut("Find-next").toString( QKeySequence.NativeText)) hSearch.addWidget(self._btnClose) hSearch.addWidget(self._line) hSearch.addWidget(self._btnFind) hSearch.addWidget(self.btnPrevious) hSearch.addWidget(self.btnNext) hSearch.addWidget(self._checkSensitive) hSearch.addWidget(self._checkWholeWord) self.totalMatches = 0 self.index = 0 self._line.counter.update_count(self.index, self.totalMatches) self.connect(self._btnClose, SIGNAL("clicked()"), self._parent.hide_status) self.connect(self._btnFind, SIGNAL("clicked()"), self.find_next) self.connect(self.btnNext, SIGNAL("clicked()"), self.find_next) self.connect(self.btnPrevious, SIGNAL("clicked()"), self.find_previous) self.connect(self._checkSensitive, SIGNAL("stateChanged(int)"), self._checks_state_changed) self.connect(self._checkWholeWord, SIGNAL("stateChanged(int)"), self._checks_state_changed) def _checks_state_changed(self): editor = main_container.MainContainer().get_actual_editor() editor.moveCursor(QTextCursor.Start) self.find_matches(editor) def contents_changed(self, editor): #TODO: Find where the cursor is when finding to position the index current_index = self.find_matches(editor, True) if self.totalMatches >= current_index: self.index = current_index self._line.counter.update_count(self.index, self.totalMatches) def find_next(self): self._parent.find_next() if self.totalMatches > 0 and self.index < self.totalMatches: self.index += 1 elif self.totalMatches > 0: self.index = 1 self._line.counter.update_count(self.index, self.totalMatches) def find_previous(self): self._parent.find_previous() if self.totalMatches > 0 and self.index > 1: self.index -= 1 elif self.totalMatches > 0: self.index = self.totalMatches editor = main_container.MainContainer().get_actual_editor() editor.moveCursor(QTextCursor.End) self._parent.find_previous() self._line.counter.update_count(self.index, self.totalMatches) def find_matches(self, editor, in_place=False): if editor is None: return text = editor.toPlainText() search = self._line.text() hasSearch = len(search) > 0 current_index = 0 if self._checkWholeWord.isChecked(): pattern = r'\b%s\b' % search temp_text = ' '.join(re.findall(pattern, text, re.IGNORECASE)) text = temp_text if temp_text != '' else text if self._checkSensitive.isChecked(): self.totalMatches = text.count(search) else: self.totalMatches = text.lower().count(search.lower()) if hasSearch and self.totalMatches > 0: cursor = editor.textCursor() cursor_position = cursor.position() cursor.movePosition(QTextCursor.WordLeft) cursor.movePosition(QTextCursor.Start, QTextCursor.KeepAnchor) current_index = text[:cursor_position].count(search) text = cursor.selectedText() if current_index <= self.totalMatches: self.index = current_index else: self.index = text.count(search) + 1 else: self.index = 0 self.totalMatches = 0 self._line.counter.update_count(self.index, self.totalMatches, hasSearch) if hasSearch and not in_place: self._parent.find() return current_index class ReplaceWidget(QWidget): def __init__(self, parent): QWidget.__init__(self, parent) hReplace = QHBoxLayout(self) hReplace.setContentsMargins(0, 0, 0, 0) self._lineReplace = QLineEdit() self._lineReplace.setMinimumWidth(250) self._btnCloseReplace = QPushButton( self.style().standardIcon(QStyle.SP_DialogCloseButton), '') self._btnReplace = QPushButton(self.trUtf8("Replace")) self._btnReplaceAll = QPushButton(self.trUtf8("Replace All")) self._btnReplaceSelection = QPushButton( self.trUtf8("Replace Selection")) hReplace.addWidget(self._btnCloseReplace) hReplace.addWidget(self._lineReplace) hReplace.addWidget(self._btnReplace) hReplace.addWidget(self._btnReplaceAll) hReplace.addWidget(self._btnReplaceSelection) class TextLine(QLineEdit): def __init__(self, parent): QLineEdit.__init__(self, parent) self._parent = parent self.counter = ui_tools.LineEditCount(self) def keyPressEvent(self, event): editor = main_container.MainContainer().get_actual_editor() if editor is None: super(TextLine, self).keyPressEvent(event) return if editor and event.key() in \ (Qt.Key_Enter, Qt.Key_Return): self._parent.find_next() return super(TextLine, self).keyPressEvent(event) if int(event.key()) in range(32, 162) or \ event.key() == Qt.Key_Backspace: has_replace = self._parent._parent._replaceWidget.isVisible() if not has_replace: self._parent.find_matches(editor) class FileSystemOpener(QWidget): def __init__(self): QWidget.__init__(self) hbox = QHBoxLayout(self) hbox.setContentsMargins(0, 0, 0, 0) self.btnClose = QPushButton( self.style().standardIcon(QStyle.SP_DialogCloseButton), '') self.completer = QCompleter(self) self.pathLine = ui_tools.LineEditTabCompleter(self.completer) fileModel = QFileSystemModel(self.completer) fileModel.setRootPath("") self.completer.setModel(fileModel) self.pathLine.setCompleter(self.completer) self.btnOpen = QPushButton( self.style().standardIcon(QStyle.SP_ArrowRight), 'Open!') hbox.addWidget(self.btnClose) hbox.addWidget(QLabel(self.trUtf8("Path:"))) hbox.addWidget(self.pathLine) hbox.addWidget(self.btnOpen) self.connect(self.pathLine, SIGNAL("returnPressed()"), self._open_file) self.connect(self.btnOpen, SIGNAL("clicked()"), self._open_file) def _open_file(self): path = self.pathLine.text() main_container.MainContainer().open_file(path) self.emit(SIGNAL("requestHide()")) def showEvent(self, event): super(FileSystemOpener, self).showEvent(event) self.pathLine.selectAll() ninja-ide-2.3/ninja_ide/gui/updates.py000066400000000000000000000107161216641277400177560ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . import urllib import webbrowser from distutils import version from PyQt4.QtGui import QSystemTrayIcon from PyQt4.QtGui import QAction from PyQt4.QtGui import QMenu from PyQt4.QtGui import QIcon from PyQt4.QtGui import QMessageBox from PyQt4.QtCore import QThread from PyQt4.QtCore import SIGNAL import ninja_ide from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.tools import json_manager from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.gui.updates') class TrayIconUpdates(QSystemTrayIcon): def __init__(self, parent): QSystemTrayIcon.__init__(self, parent) icon = QIcon(resources.IMAGES['iconUpdate']) self.setIcon(icon) self.setup_menu() self.ide_version = '0' self.download_link = '' if settings.NOTIFY_UPDATES: self.thread = ThreadUpdates() self.connect(self.thread, SIGNAL("versionReceived(QString, QString)"), self._show_messages) self.thread.start() else: self.show = self.hide def setup_menu(self, show_downloads=False): self.menu = QMenu() if show_downloads: self.download = QAction((self.tr("Download Version: %s!") % self.ide_version), self, triggered=self._show_download) self.menu.addAction(self.download) self.menu.addSeparator() self.quit_action = QAction(self.tr("Close Update Notifications"), self, triggered=self.hide) self.menu.addAction(self.quit_action) self.setContextMenu(self.menu) def _show_messages(self, ide_version, download): self.ide_version = str(ide_version) self.download_link = str(download) try: local_version = version.LooseVersion(ninja_ide.__version__) web_version = version.LooseVersion(self.ide_version) if local_version < web_version: if self.supportsMessages(): self.setup_menu(True) self.showMessage(self.tr("NINJA-IDE Updates"), self.tr("New Version of NINJA-IDE\nAvailable: ") + self.ide_version + self.tr("\n\nCheck the Update Menu in the NINJA-IDE " "System Tray icon to Download!"), QSystemTrayIcon.Information, 10000) else: button = QMessageBox.information(self.parent(), self.tr("NINJA-IDE Updates"), self.tr("New Version of NINJA-IDE\nAvailable: ") + self.ide_version) if button == QMessageBox.Ok: self._show_download() else: self.hide() except Exception as reason: logger.warning('Versions can not be compared: %r', reason) self.hide() finally: self.thread.wait() def _show_download(self): webbrowser.open(self.download_link) self.hide() class ThreadUpdates(QThread): def __init__(self): QThread.__init__(self) def run(self): try: #Check for IDE Updates ninja_version = urllib.urlopen(resources.UPDATES_URL) ide = json_manager.parse(ninja_version) except: ide = {} logger.info('no connection available') self.emit(SIGNAL("versionReceived(QString, QString)"), ide.get('version', '0'), ide.get('downloads', '')) ninja-ide-2.3/ninja_ide/img/000077500000000000000000000000001216641277400157225ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/img/activate_profile.png000066400000000000000000000026221216641277400217520ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<4IDATxڼWohE%mL*"i$h*hl,j"( E `/B(BZB[A!RKBĪMhViDI/ޟdo%sy߼{ofu/vjF,!CЀ_ccw A֒*z)L5QBGYo>mSmD pGNϧX ~Ԥm}yј^#vM l< yWN;['PRJB:4{U_x ;Ԁ@SiJ]-"@=0&911Xh*VBGQl5k ^U_~Җ "N 4EmR#ʎ@ 9ڶ l3Ҋ|K <σKzhI&byF41+!ޗH'/WӸ4eƚv<.ZfFy~}3JV{t mq-]Cyh{CU2OBR$I' 2QRm8[ѽ-3!x( qV_}gf:xny#Ɔ_7 L1HPBcpm迷 sFGpϿp83UbEB_/_|A"DbԀ`[e{^ڽΌlw[(9fwssK\Ե !u]CsA *_@x)z[!VKhwڱe} ꫼XYQ ]Kb6B,$ !DSqhA]8>ң߉o=.hFX JB5 UU $0G`6Soy1>Z#^D5?'t7B}U,Z5pjpc u ENe)Oմ5*Fg:qH(D1}H(pR768ɟa$ [#$Ӳ&<7PB8"pRk|hB*, %@ hqK|it;S7IxZ񶾇Kp+TxiCyq5=^jL!jv&\8?*l#Il_Q x^""W.0,(:G#EGIfa=HtWjCv :iJE/蘛0x)i^6 qxH t&U$dlV㴐GS&V(Sfz%q_* (4IENDB`ninja-ide-2.3/ninja_ide/img/attribute.png000066400000000000000000000031331216641277400204330ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIME 0sGFIDATXåW[oEk{m'E\ҸP'M4iRGHx 7BB ~ THQ*(OڂH$N;^9vlbqyֲ\W0#o'ϼ;fo2vh*HČ#Cã&+7B6"~ٱ?Z'=` @=3dG}ɟ3Ñ F"eN,֫ע.Ձdމg^.6)M A8:?v%HÅś7I @B!`'ہ#0A/I{h3GzPOH${9ε_/y^g~ϾG3TaKv%\OA)jB9Tp\Id`7g_W|@́jLp=I]WW*xW< ەp\\o7_^8Cg41rɚ}*ʘ$&bAP)'\o<)!%A)30 !mL]  `&M@"ب.ԭC 'L3PD~sV->5w[ \`iv@`jyyi>?>N&"$z |9l+h2&s @.sڨݭ*‰x*eڮJRok7ڕOfK%m-C{aᓬJԴnWkґD= }x|7iTdhGKN;.GI8gdb!C= !(kfIENDB`ninja-ide-2.3/ninja_ide/img/book-left.png000066400000000000000000000034641216641277400203210ustar00rootroot00000000000000PNG  IHDR(#9HsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< tEXtTitleGo Next-~tEXtAuthorJakub Steiner/!tEXtSourcehttp://jimmac.musichall.czif^KIDATXŘklU>ڲlav RG$D *I!_@H䃩~0Q1 JDA$ T-Q5m¶ܹ~3c'ウ;{w9ޙ!!i4lC6ƒmv\?( C/+ytVN:vCͼ/o~\SY<ϪQDDk/6U<^V@»@ (3vFp ر7h˙f_qQH` uM+X5QK?P<XC#8 ?EZyUjquAUA8 #f׆ow cO[7,@-vcxK{1%HO?%C !F0?R o?'\ ]ƭv1+40!dIݹ'VN@whꩫAQgiZGs@XzhZ%sWӞ=铡?\vPg$97b`žϮX{}&g PL8'eV -r _΅U,X H&DBJQ 4`@5d:A9rP*'yS{vRpLk6mGl %hq1PTy~Dl/~X'\ܶf͂՞@ ܓg$y)6p pqm )c3S8ꥻVyl$ٷ`mL@OKX;Z煶-H3{OCމ:qeB$ 5mib1)`p34]?][pBݚΆMdsk\ïuIf{{>Qpdf\yl~Cz`jaף=MzmO{]z6xDTt - oL:P !#+D[덪Ӈ_߶&6aRf MHKIᐩGM|o7褨+.IK>`H+)dNVoYWN(oc(JJI)SiǦM?o`Y;s8VS_m^bL-('  B{j!=* D #̘)|64p~!um1" C)hjF!ZjۇFD s:v|Sr9eӄl_ǶÂ+WA0JQtYϿO~$e➣;/ s@q@0(Is\x]IZTX%yS⃄?5 :^`|>bm'ǧ֛K?d4'ZGS09'Ozb\v<^j >YC+Oi_)F#`0eĊ#9ȄcKujSsp%yڰju hme(*)>uv8pR +%bO4.y غ(R\0 HOtbչH}#QfK-כiC; o 7j\٩#-ظldNoj箽wM.2f @>:{οV~H[0 K%.*{ye@AL&))r9\;SC`X:;;rG'Oߖ,[9 xk1V(j/+ LVf8^W०vg+<횳"س^("Pʥ|RPRK2hB̞ 1ۏv%j 8Gv*ʢb{@tRr%[JCNM~NW@ R@ur* !SK!a?X@usKs^܊vVnԂP~L(H\ t^jvy9oKಞ ?(TQ;Vh@ a6KR46V/ܧ[5 !*GABhh`@^>s{mͼb0`DX'@ `H׋Cڝ7~{ yaaơ : A"OlL 5ua-j(xϹw>0HNb{EIENDB`ninja-ide-2.3/ninja_ide/img/break-left.png000066400000000000000000000034711216641277400204510ustar00rootroot00000000000000PNG  IHDR(#9HsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< tEXtTitleGo Next-~tEXtAuthorJakub Steiner/!tEXtSourcehttp://jimmac.musichall.czif^PIDATXŘ]]Uu̝;3cui?AK>411ŤLL(ь!i*i,/I5@BSri;s9{pιs{aJo`%+{s^;kwEU駼"R09^ U}fYJ|>o0~Tz}ݾz)/mzgDYWۧtǖmwŀC`Pc>;|Ul{, , B wyFF?@~h jEp"&Ăͫ6..ɶ׫]l>Ͻm"XEDC b:MhN0j3T?KHf ǟ֔n-;gceŪ1`9 <^GMh6#c.24JH&y.Jvaj$8"X|#ŵ7Zb*ejއF<R*a #+wXSFM/L9zgq+NB;Xѥ%ti 37U,FR d'0Wַ.[;G& 7T*PDyɀ" i{ax <O?/ˣEodPJ,q4RVq_w|zXFS"ҚV guGU|'0r|Ưf&2 6' V?9zȃ'ϿsrqB]fgŴs1Zus5}z֚ch/}mc~59`j?g52-x|ѡQ(AJ؉:ć :Now6!LP>G6Fo8 .GG%{k5ziQP_Tؓ]6pC/]+MflYh6ڽq:ާ[!G--0[!;Bh!x@A2MJ%.øI7x\?x=hLmY??@+nR]sV#Z}QW{rjLmU3=r'W+(CR^Q[tX] hdpK~v~[=vo"R}Z0!XJ^mY{+}MFV@(BAшb Ʒl-mkxQ9&[iB}2QE"! "Rp-mtvTjG#?67ˍ-z'{6 8 @v)<\1L= 9vl4~HS348qǁH򐀨AM HбR[wT_>y=Xhٯos*bB1>zDAm,&0tMb Nآ!m\}U_ޱb@P4 aAЮ`wci{cܰu-dbeV22s(^ڦ Bέ#{6wMQxR/`vzv`} wCUOvqwZ,'{(=.5&NI"+t$(鎐$>Z1h/R ERvظS>D_?, af`piۅL 0 (;SSdj+szu 3YzYϣ:vn.ʕ1p"lL5L0uؖr]Ǫng̬#ހ06u g.xrS,"} ƻ/&XfÆRSO_;v/ohmj?M[/7ٗ"&&[n2vv&k 8*"F)$ׇ]s_GۺOٗXZrQ u%yl`7VuѱۙKSg^'핥!ۊL = 8PܴWlq2%%$&Rp Ukb~3E ^> Qr](Ӎ{#A3"ֆ֛̀*5gQ38L7eD9x0BIENDB`ninja-ide-2.3/ninja_ide/img/bug.png000066400000000000000000000045351216641277400172140ustar00rootroot00000000000000PNG  IHDR00WtEXtSoftwareAdobe ImageReadyqe<IDATxZ[lW綳WK8N lŢjZT*} T HLRTĥ@  *RhP+&.IIHdmk{;sfYoRs9Y+y1w C/zKPOL!P;Ǜ6;iR;$_5KoFHf'bSNh)/ڔ{)r=郺oaN]P~Fj%r6YyA2QX'Pirs~>PzO>L_!mSRAE`);,egaď6(${ N&Jͬ !a1™$20FW]{¤[*mExnߨfkCW?Ф+$C[ECSdH ޖ2\-fff,*G9i9+Pyߤ~ܾL]==^zҥK`-|%{Hu)U퐢I^`8~ǡa~8;11A K,yٵevAD`^sL:.i^Fu(L˞9L$ڹ,r UJq.^0k+*j@uDHb~cfVB˦/L~&mkk#۶innP(BZ(9R" C~_E#4(Ţ TS#56pdw<'37 1#bCvɒYՔXFL&CLP~$hވifjI P V"A2{~Cy7ͦV1orYՔl1Nl6˹@OmZ]z0NJNPFd ;'#󈯸P8Uդ,酷]3ʿ{zNVAN%8YRqM{ψ7 tٲnɬ#&H?$tiHd"%Э@ D9 <7`Y,e#|&kC>wb%X-2#+Zd,eL]3*L#y[t_xW <7`Y,ޟ%P+~"S.O)f8%T>"Q2|@[GB7) .#PpREٔjw*fű7n ~6@]H`K^ :;Pd} fț׽8U6`pkk+Zdddz|4Ά j,$eRV"P0@!E$щK===?C ]z8Ҏ;h۶m$RolCT,U=V-V^soyKP ޟ655ɚJiz# RiHTkl|'{TY&fj1TMȦ Ǭ N֯F\ l4Jsb̛e,Tjۖ1 c; d]X;>Z=<673ȼYFYR֛{yW(a;>(#Ʋ?\u5yK^_a `TBŀh9M6^X%Iy5hI| a+aj<8\" UxByCoƇt}%(U0#_^! H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-bKGD pHYs  tIME  (oXIDATXnEY8 P/KpD x<PBܢ(Qwg3kc:\+zq=^p&?bgTʳU֯ttoNn}7y wLosӍ`* f `4q8~%Fʲdrn ^'ڽUk׮09{߾g pgI5  2@ELxxd E#bc Y|7%P@ ]Cm;O1Rq̠ƘV B` g P7' gXy9Ih Ҡ h6:kSRX"!3E5ùDE{\2fI dD3LW̡O&#ZDfcv}HՂ,L4z3&L(&O ˝ڵUHV" ₙu"LO[I5̐Y (0cVny9CE~3މ_;P`шWJ #*>sj 8uNJ!j.,=ub둗qN] ]k+%:OhS(ڜ.yVILZۈp>Ic W$.N|Nv|X)V &|S2ITUIm+K, 2QrFyޓ= śK@ӟ.~{>ǣFc&L˝~hU5C2A6[BSc+ `sp7jW VDNjPfJ˜IENDB`ninja-ide-2.3/ninja_ide/img/comment-code.png000066400000000000000000000020151216641277400210000ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGDC pHYs  tIME -ItEXtCommentCreated with GIMPWhIDATX?HWGchbED %تXNtRPD+ԭ\*tpB"F1 PuRJ)TRRn$>whrL—/7}|yO6S˒ )6"KsRLϿE$ŀ5 b$L>$Rssstvv$ub`A1?V%IŔ{y<d[U1B!C[ۣ62Y1@.@ ]^Xlttj[৲PR GޖёBr$~cnM<ѨJE By@<,cr5&JY11Wx+Hy~:Uj' RQHgggSWW SSSݝs?piXLߒ􉤇Y[[ 2kwwbJ*bÒjJE[!.r1gׁk jI{IBdR{{{yxWW666n~,)XrIzx'''I$v?HFߏ~8^L0X2l$5Jlt'X[[[y]mqqJm:3333ᲚgYvqqKH$R$_2Ƙ8Bt1+I`DFFFx<kJBI;Ns!G.p6GlgOJzSNIe~&Ja{ ZCIENDB`ninja-ide-2.3/ninja_ide/img/console.png000066400000000000000000000017031216641277400200730ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<eIDATxWKOSAfDO↠nYDt6 .ԐƨUV. +`B!H$ ig3s-ޙ|gs7Sn6>Ӟgy8 'eI(`SɾW.闔w |S)׵MzJiEZ)z֙O[菰#DK]}=Z,9y9<=l~[C U'pfg7ڏi&BR* ,Eŏ O`gE5رX_NS 55TPGHKu(ɉIξ7?q~sS4 _-Y: 7 lYű'?q' 'N#Mf2`MM.v $FGG&,()FDt$t?G +o+/:A!kA*cq/5+# J \;x(kH7?T"f1+IiTCTc#Λ+H$vf0Ń;ƴbmǻ088P(Ǹgs1R'z8gotXh,YȀѲlb eUs$op. L0LaaU,FQb ȻS倠9`BP:l΀cP!,"ኑO !r@8 p3EU`kR1XTCP4^&Im K ݂L7!]a 1}޻{̧K{8w<___'lr$ܐ?Ჶx_"Жn ĽQؗIENDB`ninja-ide-2.3/ninja_ide/img/deactivate_profile.png000066400000000000000000000032651216641277400222670ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIME ,uu]5IDATXýl?9}ス-ЂL֢ ӌ!1 1q [ǒ9X2m9m12dq.(J!Kjn-%1;y=<9Bڟ#&Ues F7DՍ꬛0]#2}Yr@g,{xƮ yO. ikpVp` FpIRVVlR#k/h! =ytWWѐ2委\&$Zc:*ҳ=%bKDz-.m G}97Wԏ*ZLg,rifE>~rbJxuߚ?B+Xc&6{`Ѯ8g12g3ԟIɥ骟XK`!KԏEbZ cǎCVaUݠ`K8)L;UEX Yru11cFZgIP8uQKp yvMaV]v 01pɃ&;:{2$'EKi Mwzmz*G;ذ; (FY2 T pgtݷ iI -EKX }?M!TevCOaO{\̮Uimi7А"cYQ;ۋ4 ̵1P w簭9~C͝㶛(F"@/vr#XXb8&\3#]JqK{H#G0Vji]ęǼ?=cO68s:@J8%V^Xsj0+=kl/0 ?pֵh> ~8@cauġs֬N@Uq<ҳo4ͻHM1yr@ŋeɫB\'pW$Sc]C߇i,/|X!uV$I/htO?9XU'tc W>I+s(Xcd60Jx3x&pXKc|ĔXH&M?B:(rQcѩ" Z8[fײyÄm?1HN52 b[s|6XASʴbB Eᬷ]!-2[x}:FT؎(7ýr"aC]4gSR,Id=Kիzg! ͎gNaV!!1{Q1:2MwSE/$\?Ce$l%%w($^v$4L?MPbQ AIENDB`ninja-ide-2.3/ninja_ide/img/debug.png000066400000000000000000000123541216641277400175230ustar00rootroot00000000000000PNG  IHDR szz OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-bKGD pHYs  tIME  IDATXWil\}˼7=ό$&v@!P B*P٤VP&PQ񗲈D)&1JI'qKNl3Y̛D)@ԟ}q;߹;;1AouHrg^ik[#>Cib%+ɉ/6śq¹ kVvZQ?JP 't#߶}[Yf7~6w˛N0,É\6HHq"8"$I,I d&c(b*~(ҩ2,/G{EvȒQ2L7:KE]~?ZdDaSQt$A%Ji%^_3MW`&dIem >-[{c_4Glw%T5 dRk"",c iXpyxHMEc -Idեp4MLSp)NDT+pm]:T7}Q$YmVoAN* R2=gM/Y+cĠts4߱nݺ 4A!R Q@2]B 8k>/bS,Cl펿_޾409$k/ќC_\{9YGGǝO<-_\ غl.[E"Ao5 Pueں[C!( ]+vLwz7_r@@ 8ݜz4DVT\8&i=M뛜4{U2gHg2` ׋d*=z'"jLp*N3(`uT@&5{?~^NTNTja4#E"{Ng+kLj95 \fD1F%I2}(T H$8ې:s<l 8t89 4jkzPH^ gR 9dp.9M6LAd<|ܑݷڭq: bB89>18·QEpp05)BQ IpQŋp{+!tf̲PVCt*w1q3Sd (iP=v^B7 (J܀:7` BȻ8Oz+\4JBazʈr-ԉFƯ"i|Вi"( Ip&q% fM̀ XZ2oURa:RmOt"MM>ӗ.[s^N90<B(qCbxPm۰]P dA&2`@Ϙ O7b5 A)StUe Jf -F*‘ǐJk! SPQ@,`f(`,X``$'w@e& ӎI 'G| B8C>%]? -:wSS`2X(fy!sȰvqsv:.}j}FGG ֍@H$~-$GX4a$Y^14<PAaFP DM ψS!61/0Xd"9  W/%Z1442p×ڶ 6D*@2<9|ը艈w`|KהR W5mw~2P9G}p4da >1i^R'zvu-epms/os>}0̄$IEY]h(rkl>.^RNWo<{ꃃush@k. enѝ;w.݇++Q/2)lw{?\WWWrC\p*֮],m]J|[?vX>44ںutCoՑhIENDB`ninja-ide-2.3/ninja_ide/img/delete.png000066400000000000000000000035361216641277400177010ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxڬW[lgz/^Y8NHlQcJR(K!} !QU*!E*X%HI[)!ik'N|O^_\8矱c>Z{g9w.`׫}"4x>}?OQ>r6Qk+|׏tmUG@5qIsX,O]Euީ\';vP:JD~s2GK'PVeb>KC?!w+v:oNO?FyhՉq8{4k]聱w?b 5A-7QL <=;|h탧=~ ` C˨$.<vį׏'f);?y5{{Oa^  r!?CY4سyxw"qszsc\nUb:}[ `}!HD ͉P؅%ZE@/b~)K ?9lr'oür"oҾ݋t 0DX[;:`yGbs=L0zeۗU\p$!lC%. F&tsM>҂oj~ 7MjxZx'l DJ7?#B:[clvh8Wml B_%"D!(uXm FDR;HYf4DwWRf)}|ۦo:' V%3AN؅b{yg띠+4=LNqZ( 4 iwɗGr/s8HvfCշjFB=$E#;ߗ={Q]-WQ@R:_ zH&$ѵIzbRUH$3Cv_NWF ^!v݌ 0WҎIENDB`ninja-ide-2.3/ninja_ide/img/document-new.png000066400000000000000000000073711216641277400210450ustar00rootroot00000000000000PNG  IHDR szz pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F$IDATxėnUsf@uZɻF|N"SQ5$ A+w,A|:}]U󎄊AD6=T-l.}mIŪǩӨӼUYBRxQ 3UBX;o)_Pv*?{ft$t#aD׍A"h*ۥGW"H59p$p>9s(z %'",-LٽֆgPl߫: x': 1 oWĘ&HHP,~Ch>Uyap?`XƁ6b׺26Ύ2yB#z`x=0Y&:`6N3Ւǫ+ ]qØ6fx}{.<%0;Y 8R$,.ݺqȖ<{;{GO]i8(xF/eAiX$\/${Y0(EBZͨ:Q1p RڹyыnŰ-ف-*T/ PԦ6l(s.8h C_4cbR1;InE{2 (/ J2]>A67n|8pΉ!N([d= 1q"b݅}@tiii*"Y__ׇzfgfS3 NUSI}O'N$̌jvܹkkks7jƑ IENDB`ninja-ide-2.3/ninja_ide/img/document-open.png000066400000000000000000000026351216641277400212130ustar00rootroot00000000000000PNG  IHDR szzsBIT|dTIDATXOlHTJY]6p}rs_j|t['z 95ћQ"ȡAIJ٠(2ij)q]A"%i*oy;g.]϶mwc 4MLibY0 &ۋ[n k_uB|199i@kݿ{mۙzׯmիW?gDa8((ӾzRqqV+֚۷ol6yÉ'8}4v0˲vjٳg رci 31yz!D=#0fT*fx"ay>|M?BQyf;biݏ'C8jN#wEfJٳgq@0S*iPZۀ(^קU/fwIY߮;ʿbH$LMM!kPc 3ÄsB*5a23YYk 8N}lF)ӖLr 6$RE $WeYA@X4-~Ja&TfMRM$q@!oVs9 H֛H)B+ʇtj$qo0F*ݿL?fi*Ԗe(XsbϾE;LUoZ?Cb\^1Si|2</[V͛7fʻgDTN&[&mja&Νa6n`\}0`NLl/_.1yh&M@:G)lUlrX@().k{o`%GlzQIENDB`ninja-ide-2.3/ninja_ide/img/document-print.png000066400000000000000000000017651216641277400214110ustar00rootroot00000000000000PNG  IHDR szzsBIT|dIDATX嗻O#Wcf444dYnR`/^9Lx<M@QyT)u]<ϟ?'0<<` d2"{{{wLNNTe<bP%m=Nyq ۶C1|{WM/`RI|%bDuuttbvŭIh5 E i~o$?zaaAed2cccTզķtuureVWWKR/VVV$}133ӛL&HR.//I$j58nmm}| r%q-82۶ś7/u},J !8=}(C4RBnT @ iܬF@׫Xv5pJ!ǡX o8YaF{q޾۶x60iDJD(jTTUEӂujtzi!M W_cYV(~4CQ^|qC:LLdfBຮuq=|n+cj Ԃf>ZDjUb@߽{ $IrP=k.$IRB~zzҟ !Z\rw@~5Q7H=ſ ,/\@Q@}g`;7TUIENDB`ninja-ide-2.3/ninja_ide/img/document-save-all.png000066400000000000000000000076361216641277400217640ustar00rootroot00000000000000PNG  IHDR szz OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-bKGD pHYs  tIME V2IDATXŖKlTUqϝLahyBS-Q cL0a+] 5nt[H7\ 7" APBC)m睹s1eʔ$;s}祖._(-:n|l2 r\18#k.Rξᓍj| 2P+W{Τ:3G"ڙË^J>;>/@єbY&I ht F1JIk3KӬrBVqF#߳B) Rx߻k4Ζ0F!H]RhX0ghcZSjV(AŨޛ<'  E`z̭-&& <"iRJ 'N=K{Ĉz"-kp'c:jc5'apعyt¸#j)Z+Rnm&[^@FeOq.N0Ж0bMEl6ey[z.-H&~chO9+[ƈ5(ER@_SMsξa` l@q`'{3jc] Rj]lre'?eqp׎8]?J-r]!mO0F9A^d_;&Rso#ߏm! BD !b4\ sWb1 e'Zʹ > Hq "N1Su7jJRa[i_n XҷH%H7F,Q][DjV+/GaE ͹k X6bDP$$IZ41IIҸ @`Fk,D:%uon,`FBgi1t:^= &MlE\(!<}Q=]S#:ҡ#te (ʃJTn^4hƥ8cqV ))M033]f-fYqgv_qmHPK֖U Q$icz - <z?5~y]!oIENDB`ninja-ide-2.3/ninja_ide/img/document-save-as.png000066400000000000000000000105701216641277400216060ustar00rootroot00000000000000PNG  IHDR szz OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-bKGD pHYs  tIME (IDATXŗ[UW>37D(T%>74؈5KSkFMD*"K>0f9s.̅ ƒ;Y9d{}[kR>.}_3Ks ])"]\8zftv#߽8 n?7na('}B-+;PK>~c52:6ɫg+Cپ J20<"+TrhP }>=Dﴉ})E2iXGcȂJA SHВK#(<>j-lg_P$>SM7(j {w:i*`l@85V huXPh "`m@ 7 E)P'fA4X7UJiVs"3OS$n@,C+ɮZ+jS$ z5Z1VQmBY2[WUF3@kE5zpi"(&ϸA@v˖\`v֛羱;ӟago ss6?Mg>T E={<(:;jsqҢQot.!3c, _~}bzL &*<2.0yZ)J)˙bB "sRnhqa97㫈CRhe k[u\.~~s(^v}zn\‚(T.hGaIPtڛZ#dӝ; 8Iw#2H ܶy9.҄4(곌^˧޿k@2`Rl^.̫$!M9"YI%VYɩg89tX;m,yUEeJ"v߬0QbQ>0W1 ,*FeW^M-Np1RLޣL@ԅ-RDn"ԉ 7q<ىO+r"t!@1rR/+Z644EP 64% a8ǾZqO# KNKBgsA5"ja=ƪ%MShmgm8HtR k-X~ǿa#ig_X2$Gs Hsv΅c1P(ؑ=[f(BD1\)2y'HThN_xv/xw#Ο02Y֩:[rL S~Gctw;ާk-{ƌvfc Y^b㒔/}+P]Ľ}Ï=,{?f##1A;g[?7lLmDM܍֚[7VRjt>B}58.'VuO} ݶuFj~HSOZ)(lh굪ٌzV 'GP.tv[ <4%tv]| ߷o_owYG](OՁ'>Qy2wN*lebbL*qV+[k}'(Q}@65f.O#@!{S{WD>bNZ""Gޮa`X !΍2}y\ѭoF|$eS5IENDB`ninja-ide-2.3/ninja_ide/img/document-save.png000066400000000000000000000100121216641277400211740ustar00rootroot00000000000000PNG  IHDR szz pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F5IDATxė͏G[ӳafNF D)9!p%W8srႸan !!EHarD K uwgg^;^G1x\MKOSS-fƓln๗f?{ṍK׼9@Ą6glݣ8)6*_xk6aޠ6]q'l7s/0 ww: |\CԑЉsݽ#y-!!bWŘ ul&Y8Ŧ`>'{@!:Bi%P!Db2-y@< S0#&T8#+>H"$aue 3Rz|)U"õ\@P3>#KP9P(yDR@D;VS$Ā #T( TG{G,Ni5A[؇S``B\Zy  Hp| ]8)*dIk m "g{ko{Gx3#cCT<&a\C޺iK$L]@_+!޻rLJԯ}l#FO9RX2DڋQg@;\O%)8uh <)Fb{Ք7'dy814 2{Plg<{B[My Ϳm*m3ʗ.ͫ/19P5U]4 O;L2"^`𸯠`"hox@!b'F!d=`>@37[`鬢gTU@֯LQ2{ Zb  +PbF%5%J۶Ҷ-B7yN:ޓ+ fݺ~w׳`t gySB E 0܉3wysLJ.7_LnnV3}jbP9ER"_p}΍k귞}jW?l:LmhGEAw4Mp@2 Yd4XE=;Ci p?O$/x ᘝaL !ږH iۆ;쾷36γ}o{1ʀq_뀈@#FYdC,i'){0#vh,zt'ʓ<vLnªIENDB`ninja-ide-2.3/ninja_ide/img/edit-copy.png000066400000000000000000000013231216641277400203240ustar00rootroot00000000000000PNG  IHDR szzsBIT|dIDATX͗MkAxI~dA=-F'KтxWR-ţjU!I٤K^f<,;􁐙<8h1˅MY54fByv3/j(ES|>O=t%ʩDF5ոaPV%{˫ p`iiIy "%" a0Y"*Y!"OXqBT*AeIe tС( Dsev`px(@&,T),+ L]LjH BSU(HR璂~9BxHZ_H"D0n~$?T), DM c'a)iƘia IbJ{"~{b`{ fPi&"<|iLp4D>.k*q`n9We7;{7  I*c@O{a=?\I ڵJXup1 `6}9Y.W& TD"i҇1!}3?NP]zo.'[F񩤰nߵ1v0擕յiט{VT MIENDB`ninja-ide-2.3/ninja_ide/img/edit-cut.png000066400000000000000000000040471216641277400201530ustar00rootroot00000000000000PNG  IHDR szzsBIT|dIDATX{lǿ3;ww;M b40U$-U*"l7D@CMTJ64QԪ"`@0X6=vwf9dvl?]ff &ٵ0b/Gæ&v֛c9M6EƋ5fr7<^SI^S\V'w,TW\mwY8RJڝ{/}ʯJN55;Tۂ3.;lۓ]dP!$JKK5'SkJeeNW8R"7ap83%߹sg tWn0!Dzz&RL @?oHHiZXUҥĜƜ g6E8'P8@O8BJ CCAA]5SkRBSss٥) oJ-uRuuu?-`S˖L GunZ'*q~ƆÇtUR0Ŧ-$Ph25-5!!|͊S[V/DŽP hjjB %E=nOA]]:۞ʥ-w)%laD&BENi2cR"aE*7e~9XZH5ѯnթDMUU/ PUU.sıp|*=HR@uKrsX|o ! ݾ~SHt{wOg0 ,¼B,(uE"H)ii-^EEQӲk%3^xS0ݓPXgkXp9k}_ ndBL'*e +S\N'] B5?!Mѧ[7n#TQ,WФC(CDzRS^ @ڻHtFavCKw{_R !h z@nS!@ @EiHN~dNAsi $H{9@ngmmlGҎ`3@t[lRr} cÙ99ߜSͺŹv7`Yo% YV̟5K&{zA]Wcb…yӆ$`lE07Pz:1P@H.lC`$WSؖef:|>@OokkͲh`}Z12P7{Nw2SNɌ=3 gL۰r .LIJ1W~>>2bL]?K?{4HWIENDB`ninja-ide-2.3/ninja_ide/img/edit-paste.png000066400000000000000000000102641216641277400204720ustar00rootroot00000000000000PNG  IHDR szz pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxėo\W3ןSI;MKE*4]B,PH/`ߐEʢ " 6La&1N*Jةc{{ƞ؈Eꞹ|<}9犪$ply9(:X2!~iRx2V N!U^xW>var2<%VPn{B0\qpP80s~vs8u*q,?0 nZ9-byqwAӟshow)"1xH_' zga%_슫Uڔ4l֏1Z$HWCb UJ þlj>1i67"$/E QbQ}*~ЈC?k"+#|i/pn+>[||G9FG43M{3$aӔ;JWY]]kZ{p4_:([ñ+%c64yA`Z6Ν X-^@4A`Du##@033˗wEd Z4a0d DI%$" aE9vPhuWbfgp[]AP&&9zzz8 bDu!"h `nJu%Rc 233wI6VD! < ʡ}C |*U`u ۰ ~18?o&֏]UU1ѣ%&'7PDW}"Lf^fơSn5ȉ<澡yծ={Y^^}$auu#uO\jdQnr?ECE}uQBZŋLMM}±j ۺolZT\.[Sxf4M⩞^9 X,2>>.7nhЀJ~;>V[cBSC)J=RÓ̒e8H6S *Ÿ! i qLSvGo k$ܯ]rj-Y ?${0w$(q@ qnoѿHd ۣX Rf@!Gy0dP98rJmglij@tHMZ|G;X99'@Dw s4ɍKSFPHiQl:ysKoOm:H)ʁƑΎ`aq(|Dpr&wN AMȤ>ND$aJ(i[XtlCN @id6aO$pj{EU_c΋MW0%3(4vz9s;7N tYf<ȔK63ۓ>H2܆D$3!QPuQ݊JW'Dҥ,f@e։U?V!R%lf'$ˤ eS|LI8gMcv8ǖ~!9'д'#p++d4roRY ]7%;3X 8. Վw>vP9AD|J_}vTϯ }5If6 qs{1v.jD]{ѥO-k^ ӠYT1Tf#>Wsq<g`oD6  Q&\0jC` 죈SK}27h&}qZI֦Mo^@OM/ ULo|/\A>S~7jE3=bml)|>cG^6Ԣ&& &j,pP Ac" 6̟Ϗu{ G_^xE"*~04H f CbD?e2{[ꐕ`cD?|xASDjwA3h09qOAYv=\WlkwNزY!XwdWŞ߸܂'Ѕ{?:{/cu}VSV5Ț {HJ%7|X놭e^|]ک"vdew<.1|KgPn©X7xC>BBXU3v|6* 5pu-311Η2_]v&!T%Vuf}MǿngG*ڀqmf[d8/e@¨I@#-Ϟ9 fJۅrv~BdUjf vvy̓?[|#mܨI]≄ 36 @&[#cgSඈX0{i "e$FYi:"B@8Hz@(+g}j,A<J/*lD""P9y"D>лp~4ՖW#H<̏&[E\(I$¹|}m!71b́DŽLnԨvn_V3gڦ#uT*FG:N*fU59 Og.}ՕQ$s͝t P87Wՠ0v]* D`**85w5q-u,}b1į^Jhu?5XU]Sx+ᕷQY{3gH&q"A- tRqqo5P$}-,=P3@~YxnGXtS@P T`JaIENDB`ninja-ide-2.3/ninja_ide/img/exit.png000066400000000000000000000105531216641277400174050ustar00rootroot00000000000000PNG  IHDR szz pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxėK\GSUyxvfH1P@,l &Re `KQxl CB(#'F`Gv"3?=ӯ<3x咎?O*wsp{?3TBUP&)YbED@Ugq] '`1kTOEi둇>ݻw159ХWv4MA4H҄,Ȳ8Iy&F#`1޷© $xr 2ݳ=|_h27WҥnܸAĤiJLNMV Yؼyw2]e' VL{e _}W233C9V RV1F@EsvL+<ز nvNU8)191F$Έb6Z"4#c~K?JυH\@gW'p7SBYbe2DFS"eQx/BUU, .`amaU7:x!"7/\"C . .t=#ʙ&IB #= BuXgB ykN@*BWaT[QOZs+2c\fƚ\·zOo31kS޿ӇI *Zenu,:U 0|XM5) {] z{Ͼ1`u `rrٿsÆg-~YBc~~}>O@TQL{7o&a(?u[6q3Ш7<:Q!{f5͠VU*;̲syT˄ig~Hǁovuc>3Dk ( PTFG R )ڼZ56FߊygpŇ>)BZU_cLe7v.\@ Z-4IF;)YR)'³@e>Q-DœVkzCZ'tgUB]' \$o>@X.Sfbman&H~b[1 p쎝+ ED*y4 L .BO06"ӀsGF"RaoFk>m@O}F2R,vP޸+CS:hPDqnÖu}v6o<'F}v?נRf+狼p[.@T}7\r{(B9fsO(ĵ: l:|mVJGR)36>V bqQl&e~D=j5q)rg}l+1<6ʿνͽ71!Uf4Mu2Kv"\#&GZ-fe(]]CޏVΕ7.s1^yWQ"Hau0y㺻>62l48/nIENDB`ninja-ide-2.3/ninja_ide/img/file-run.png000066400000000000000000000023661216641277400201600ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIME 3LfvIDATXOoE/q4MZӴqmJhB\8/[U[%8TH*!(AZKإUҦzgCzc''FZzg~άބҥK1 Du]G4AJY?lۮƀmNOOglQ$ .Dk}l0 R yG/^x^2)%]]]X DJ),+g4{ ovn|E8 A2L~;Au]'L6d[.^ =V$Ɏm p 4K;w l=ztw3>>^lV@$h]HRB8NCC; jp u-Mf\M[:nxPSqt]o 0::ڙYG*j: 3 ܐ;T88J0B #5# Gv\*!q([(g2UA1!ųJ=MBgY*R {S(8E6MJ#V:3|Ws/f{1tn L4] _N&&88x74-DLli @  QBUA(1&ϐx%Lt+'@, MNNN%pKr|_Ы@rSR쭚r&dnn`x8^`? ā`mرWN߶6 suF{X@oU jM_p~zﯹ_kkk]5?Q/cliWbIENDB`ninja-ide-2.3/ninja_ide/img/find-replace.png000066400000000000000000000040701216641277400207620ustar00rootroot00000000000000PNG  IHDR szzsBIT|dIDATXypU?wyK#HB5HŶQR[.:L:t:ጴqJ;qDCg(6LD$3yֻ!oνg9sݤK;vqf%IR,ȲeYXa9k{>I5Mۼk׮_I}I0MM<?8@0 <0tv;99.#''ILPz$|h(ǭQ]S** 't:YY߀ `.Y,˚ dρ.p  w41ާ| lٲ9_ cl~!DyCpU9y`%,1s𸟿'nq----ٴq֯&pvn" so- ;.cgCC1uɓ)%4T;l9/QC׃;ANÁ+IEia6y)֬^C8<ʊ+(//aR䭟O4 KQWWGM5,,@QUFphwEK&dff='Գ[> 29aG=0mX)oUj->Ehl ]vH2V6dI)i_.@lh!;Ym*uuu’8z|BNn$ E,.,Ae$IBQΝa(8'>YTW9sd_ yz!Ě˗GF+$UVx< ff!2Qm-חPPq?əx6<oR`d_RZJ'TWՒ]D<GeE0P%c~|ݍ7dr Hct&(`J, Yljmm]ϑ, EQp:N]]$zu5ظ29pT OjGdYu ۂ@MD^npIPU˅i|>LD$"#|w;kĘ 05|FۀLaz+\la92e=I#qr(AװG^~-p!ޖRVa6[.L2-niϥ[KC5>.ҏa;EHpp8Ǝ9޼Ļj_ ܒ2 &Ux-?d܅__pKTzb~+jY{g ͵P8'l`11زXMǹQ444OדO_O,S-YDisݲ)ωt"`C~Q51i'ow^ZJ^TLp8 Oci8< " dq"sXDz1&Yr7k|GjWPd^l7-ʙ6W-gl26'Dͥt6ĐsϢw 2np8a]m,f tHnqܒLd &LN[jr &ɲöm~iÁMn%N!$ FR- |v{`Y#FĘ S/ކj\.۶cg ٘$Ca/2`3ED`s.pXHm86H$d;I{7o+-hr{{@8@" ˉ<dFes?]v:x"TU=090p,y'u6cp n0xL1@n'/vUz8]+< (ʴi^g?-WK|Ob7M_~oX(0;; yi:p7۷oi^¹O>NY̹],%0jsm:d:ƐN133 .H$~2oo߿7n05čE,R}97W",*++199d22R)9s H$rc]+@^/mmmU(0 lDՇB!CE$ |>XSN-D>X7@ (z UUN iokvrΝ=PU(b~~. $ٳF*z)n{dclvwvv? BueAUU<FD~@K؊RXP(㺺:tH#4Mχa=.jA쪪;|t"@Ex|o$P(Bb1E9r)[sppB,K(++CII AD/3/*g2.k MDg?>zuvC$qycTȋs^4Vݯ|9^zV6ODo~ X Se!"&|Pתj{@P(CŃQ'"ц V4_ A[?IENDB`ninja-ide-2.3/ninja_ide/img/folder-open.png000066400000000000000000000022551216641277400206460ustar00rootroot00000000000000PNG  IHDR szzsBIT|ddIDATXoDǿc;vҴMJj RYzJ_P/;wb/TT* !XMwM؍8q<&q~T ,;3}ޛ_׿,;;;(a/cOV (q^(PeXur۔7|um yzz:XQlll,ڶsةTd23_ D"vPJ~&!8m۶ZrXZZhsvv8<<|lyQu !&&R|߇<!dm@,YIS+J)ܜ @9tfRڣCa (WUFѣK;!80 'ḱW7?௾D*p]ضJ b}lզ9C]iRmûa&<5 7i ݡPd#[Aj5;Rj5T*T*TU62$ \]AC߆s ]Sprn7yȿЗL&"DMb%:YpɥB"91؄@<(riYiYlwi,.p|р6Q P&k>=ViCB<ۄ8`KK  + BQ@BJYjpRx<̲ :-`wrryr蚊B)| 3ƘL{sGO1ƕRRұ *972z@>e-' w?B+aک *98}IA%;s{Lb,8审ûj-b^jEgCTt$L_E?}ty~ ~y;˕}/w[j_D+T<3;OK}g}2 R WCCзwTxwC%ow4ܐwڨx=pW/KYnǧ Pimqm.x].^_]*ɘC$gI@RX u*Ǜhi/%h#kZ4ɗK@%?nٽ= Mvgt~lx&gHրmlhuӹ.L&CUݽ{׷/ ,@:M4`;|j XTTF˥Wv6f*eD Z˖-1lRXE׃7իy&d@K(JE"@5.]j6u uּ5vϭX^E` OAak \/fko{ыE809 "Mtc, JYt55Y] wE |:[KN%hZeq`S}$r9b}-E@ u9NHϋB.NX4X*x6L/ԭ¾tkY"O~4ƱD"lo ui hD6쟘 !b>ȯ߻l6MUEE4s9 ;S0qmz]C,Z\H$MU>ދŝ `(; )kܒ.ƃXc{:xixG[6PF0pmfTFf U#l 7"  3~R,>n?24Kڰ?2E{u5\bKwM""D"Z`6" dR[J" sy1v5uqp&2IsGfrr"P=L"hb ;^:} kb3M1.nkf*.T0<<4g--U^d_Ŷp1 c@;l@P[YΦB'c."]db~ыlG}y)2?4?LcǎQE(b]&ӑ?uk׽aN;CULP((YGpm%b|kGɁz{{U:-PkmNUXh4u7СH$Ν;}ؼy~OOI$fY+`61[>w7hlu ׉㯪r\vGG̾m;Ů\2O?W޿SHy FsJDشiSkWWZ wžӟ};sʮtww]׆m3}ѳ" 5ݏv7)컦Iy{&ݦH{^ k-]Mc'pǯT ,=uӇ4uoW3!kc `eeIxgn}2>ч;D@ä>x4IR2m7[ܲW;,3|f:<|!g`zAo>@ue6 +$ %(bTQܨi #F6GP$p\(Z_19C7QT?kCLkrc;,h ,>QS|UgdT9QR8fS#҇i4ϝ)L@9bEgKvNEneJ:(PJSuT)00\ 8Dea.Z"~ r $c4:JWnt8X8WNaM 3`ͨ}OOw΀r1@(s| Gz,xxxhUǪܩ7к-ZZnZ].a(71K``R()Կe>zHrd]v #;\Ć._YVXD+^V*3λ jI #߃H5icV1 (XR'?Hd`[z2FÍ g`h zg@r =384z&pݸg3Q @2Hh3Px/pNГG5u1fN2'!|I4@el16d#8*Ca_#| \:z‹ӳ{QLG~?:;A`z @(4ͶmW%J‚MxQ R4WWfΟ03ucJIENDB`ninja-ide-2.3/ninja_ide/img/function.png000066400000000000000000000032531216641277400202600ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIME 0;֊-+IDATXˏWyxDZ3I&qđBD,X Dlٱ`D6,X%0(Q$crm0!g=w}Ty8XQUUWUs9;~^>'p@Q+ڍ=y`sgX:!:n<Pr $Z+@h|#aݵG ~x2{f<{-)""\o7EtNL4GӿxϿwr~S-n/ |~VqٹB*պߞb6Kb^0O6ΦTZU̓Fvl\罕yk" ce90RTo|tv?޾s  oĞ<<N~~C&M (F>9VgSr:£^Xzhm v|y/4˝f|#P(DbE}Ћ|O3ѷ!:0:l={@FD>ntTK `CD' z[Q3NTHO%+[ (dsF( כw(NŠŊHDd-'r!1J#("`7q صMl5hFAZ{xZQXđRF5AL~]_bSc&S4 V5֤ )cHiCZ{wP)RlqTo# DD:h5h? ) Z+D`,UKw˱Ab;~YAj1Jcxz`8+gK]yZ6 ݘN!kv﷯f[SyoDDX,VkmzQk~_Zs;y`z3g6ml-U-ψQP2E )IJyjL4QIo<~UB? f{G<'\AVu)A zYO}sk3f@ݕXؽ@,F2@5I`+pO|dK&ɇ^sl*\|Ug喳 Ԁmw%Hb#9"EG*ť5] tYcݽq&IJ+pZ? \Jځĵ=b+r_M:C% $x&d>Wv͢)Iv>p\|(͇p IENDB`ninja-ide-2.3/ninja_ide/img/go_to_definition.png000066400000000000000000000031151216641277400217470ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxWYlTUL;3--e..AE@ qDQDmA |Ĩ "$ZZLle6⋍I3ssO۶1sї>r^G_(.-8TR~o-87liζ3Nx=x߅[ce J,PpD2.9Ͱ^ 1L/tX 6aٌ<ߌM"Os62`16Kј@W07/y[zyor_ gC|Ns9IA\ zTqug4s-ޚ@6NTä: w n=l%O"J&` j) K"MPOGJ= A Eco>Kih+)IRXy-ҞoLމkN lz YNT_4OKhR(ÒĹ Rv ˈ8I׉6Wif:n˔(MācOlCt|>n%x_ +hK+9 vKm*-#NࣷE9&KQ~rm[?3+hliN- _"ψu7t-,uqpdWyk߀MN` 3t_ 7N.փJQStxI9e>)9MD6l]E  Uvr1@jZN3ꚔAS8`AU2*8@Ɋ`BP# lNC/ %!u]41 53iT9I*}ס*U.Y3{n/[F^~>0l2 !F0,Sێ&FRg0 *VU%"*410C 1絕+-砓2=)B!`"x}T uh~ToZkFH ]%PCWIq!כ;>Ex2YwC`S%3V䟟} tQX.r1飼R= Y93h4A>:R*N9r-i@+W;G{8D6ztNm՛`t"eZsKP,.1ȨoAɲ]#b)EIcjdM]q7SF0(b`tӾi>̏/"_0rr3Oϩނi zJ-Sw($3JhM?8_d>wvC=,ne^zB9 "+Rq)H%Ȓ_*_M6Dp X/ "״d0mqz"g7jbfj]RT=)YǕ˭u;i]gdd.@tO K6ǵ,ٰڸ0= Uj:K> XY V IENDB`ninja-ide-2.3/ninja_ide/img/icon.png000066400000000000000000000420201216641277400173560ustar00rootroot00000000000000PNG  IHDR>atEXtSoftwareAdobe ImageReadyqe<"iTXtXML:com.adobe.xmp ֻ@IDATx}E랴̒A@NI P 00zS;t* *AɠHafUWuW.o>t{2cp,eAqq1۷-Z;w={8gφx<a8999PRR{իYYYΆݻ͚5cwN~`0a+ @׹i&Xp!|wƍaŊߤ!#WR^e_D"пv饗86V8VZ]wԬYDTF@2BIFw#[:QB_=^c ڿ? >B~DW 5#MIFT0Q*ʕ+J8T_~ 7|3֙PplB1@;O0M3<9aqP İLUp *С7,acƍq=1%jԨ}\f4mڨI 7hP~zuU^ jj@ZHOX8dׁ~ؠ(^XTDSRXXXg[mہ yڵ7_pR!0vPn];wfd<Ѹqc @FH"Dnx45jt>]vnUcGݾ}i~-Æ :^\?]sް|muի1k֬;6]wGXQD,z =^ãE'adcc CxWn3n9J.‹&LB x5 a˖- sv@qٻ y r' iCՏ3Gyiii\GkWt5yiӦ'M4GQaaރGCf}MS[~hn ϳA懲KR o姉Swqdj[|9{Ν;1$ 6HOKcڷgs[dIDx7D樅>UC7D]?h5s9O6t[I2ծSCAβeL"a׃`~4|$yOr.]t=LLzl>0+G;nȑ#y䑇OuT=Z(; @_*3jڥ˹r2*k׾ E8ԩSլU233XzzE! X49@)D??+3M/ |SO;.6<쳏y <۠1hđTWP1HAt1r~Z5lu\iuYvMQA,( b17ǣ]v8c>;sr *2eʱoKB\2yЛ^{geΥ Cd[ꍹVN:0zdU3kv9=KS{S*Ӿ⌅~ @e:vh[|?gL\md:z1HCIzG{ݲ@CfGDw̢GizsT1y.؏qy>q|Θ8 o~8K`0o~GYq@/<8XIk@L6QQ0`ffª/g_˖rIf ,bJ`ڧ{:u:S 5^ U&L4ɨJVx'&x#O~=~@z~ %VO>By\rDxѳ{ ;v@ x$O?̚5 X[~^|oC(*Ö̙3"GS{t`l>I&>E 읩[ TŠ4b?}Hg@>Zt3(/~z' Ĕڒ6}t:$C)b@ԸѢE'pq\X Fe'8\!cz;?zmd֪ ,_ΆH[5{>f̘۳ jA!~|GU|~_ehݺH$OM`܅SPn>x>+hv5Cinzڰo`@zx*8N1P_MII&|KLp0~ $~5mЙmIN&Rr+N$5[ [`<8}hؼm+ߟu\_ "pPvgnHd%6Z *(~x#/D8&a-/K KՅޱc $c[ nHWիg:D/J5"(xc"X۶p3k B03:'GYk5neEPZXPQʵc\&~?s̙3q׮]}R`΅Gҗ_-m@"Ic@< ggC;GBM AMBsz:s?"1VKKK صk}`?x E>D{|@eAYA!ƭ*eDҦ !b)#XL~Yf0=n'K CڨH0Cx^"ޗsGx@(3g΄Yf7x#e˖UN ?D dpa _.n6}۶>}E["ѹU7x@*77{%+V<@@b@H" BhjqG5ڴi=bĈ>5j}i~+}rwޝ};ƶm[rrruYB |* x vVP֠Ao6u4H jժ]N:ԭ[+X 0U/'Qvz2ǺukG5Rɸaހ(/x\'-e9}8Ѩ5lٲe+P~3sƌol*aEuq'`]v 2> lKNjvNrYڱcVx4jԨaM<3˶ׯ|9KD`&sU WONt`,05˅n Ə_%ZRRR0}?khhQhݘ+ʼnKѣG}jذQ_ [ouz `r&8=%]%ƳGA^~bы v;>!,Ш ).8!tTPcp%C hJ7t;IH(&>1o>SJ? .h׮sї_qŕ]vrՕW3w. rAU 87Age˖Z~W!\6lꫯ>sQ~߱oᅦ +a-<-^ ,m^պuk8S#|ɇ-g I67>Cz^6~*HFC/cœ`|nP< Y=t˿Uj_|!p%2X0>㭷bRD.]˗/Ib~|0Yf-ÅP"! 8/yKHNF,%YӣIz@K %x4r u%I096ZAA~!zpxWy#OrRҵ^2QDz ;uz,h*ƷP6;3w = hv1c(Q1c`t1ҋeO2M#"-JK.!Cp QQzڧȑ#*C5*wNH!"Òu b[+3[NsS0^d︣$#iӦ9LRb&Jk+ln:7y?p~f` [0B:2g 4h™B;{̙3fΘ=9S^Hb':g9br 0_ZV5jFlpi B'cwp/Hd . )ɧ8DC,{,hg!; yN0H8@LUT*ol<w+nL0@"7K@nPZ$2H6J ,mm۶W^C/,Lf0Vmט1O]>g''s}O5dY%y)T2B5V^ 7xҶ-፧ KmyP!|m\bI?]I\FOWPqH)`Hs5F ADĤ>t[^Nh 52%3͛y{6кߡCeKYA#%kLJuW2ӇE!5~jyA5=[ J]Rϐq'P$n R׃?ne˖6jă>m^$?( ChUyax$Ȝ!m?Ъ&wtk۱@Iv]C@Zߠsーst)׶18:ug$JycbcxO2=y-)YeJ/Xlː?F@ M* rhLHɹH v'Q7z!!^Gjl7|<ЫgO^NFS߾} 'L~{g "<5k@xmS}0ի+3ZhSؘO^ `xl$8@<(0hX_G-kf4d{pe/\.XIb[=b2BIKKЋNdj+4H_,Q3Bu%wB]qqf>s9Ydž" 6 +[Y[=XdT zvR|an̹nSc]I!}1xW5֯Ffs| 9O?tذa;3u2޽RDf0|Ed̚tdTի_|3o Uejp!źYV3:SnI$ʎ3FQZ(1 =Mh_Rj|dj|E^C܈JZ!EFg2BXSF00=g "X&Nm&/< ժ9{I3@]sXg*g^$]T\dT !IJ"}RXd֏ ϧO%iФIcy2r 湾)2dPMZ%L6l5hԆhwA2| F͚N/qCY.5-!jkGۭט)`Q-q.Ө A9KJ;͖"1Զuhc~ui3v:68 a}盘'9z62R!IjINh:~AKxPHt`aC)wӣJzX]e)NB+& .]z4tPo5ojn@eZUHU#aEEb*lF;' aONAPajJbT^@>wq⑇a5&0d%&cwcG}-aj'xGx| yc*9(\ gP#Um Q* CtC"ďzL\đ)(Rm8_F|;nxwg~U6p4T\/F[HcEʨP7d4@ XnU.EH1Q.yMy3cp/Hڃ@T$<~r*ɱx1 ITFB(Ïv V * I[S[g(~J|1# hs~z|Ƅ***KyPPX)G]DWB)Sa(QF. E mC ׵UͼbʿHH*I_Z7cxuJ:F)0B}~ q)@Ay-K8Z6C lı'r9'*VJqEIԬmdhoG|"yw;q>|uQ~ܹ7e([)*+n([7;ԯ7VmB^8efd2%(,\U'ĖuBz9p4.(&pyFB6IeYf仙5ztUϼ.dI|"@.L2^~%#Q/@FUD"&1B6zAvv=SԬU9㵑վfͯ<*ð Tr @-}AҐs 4T+4Tڻ cǎ䚁y\/IS]wW NH#jWWۧLYߒUT>* TڸTjf5x^s:nɔLXw\/ur%Mħ"/3E2«~^! 6HdTNԒ'&Мdc)2 81i;SV+QuJ*a\#. ִq0 z5Ў**da茑`ݺ-?ЧbO0uQeGm"Ys 6AßnYul`y@2@Ld\Q!$Q#F$񊤮@b."YEJ:q}`\fܟ~oj"Aؘ݂62NӨ;mڴ4XM6%R?񟥖;YUwjH7QY4Ehf ^742C E 菦a$eT[ lymeehuјiq%7,&KS]Up7[J LH7u?XL4A2#CLeJn@OIlzV *9knOaj7|w7 (bܕn 1Ĉ89_t'&ncIS3l̗}vd% J^_gŋﯼ+{H5Pf%.vRկ8# 0O{i3\afKːBRfDM,***h mi98`]?MОL6 qzSG:U8s.ڟuуv0TBr"Ev@Z`|hx@ J|CO&y?:E/={ Di'*jׄ8^ɉi7\t^$[֥2@r Rj 1>r&iAf\L#żaq.I[PNܯۙ N $z@3YWUMjPB<1榒އs.t֠aC/xu3e z$@s#8–'S۷ )$A"Rb^K_Vfg ef1'Pu}VURtm9 怷/]6mʂ)Tھ}I fU9J҈y77P2 D[nG)s+WɒW_őDtJ Po1y&DŲ -C7\ɠ0rjg}&-|\=<7f@X wZ=֬Y5Wlܸ1 6nܩa C娆*j+mPtgʬUΤ>\3)rXԿ^/CNt9(TFelA4I4 bnidtl 7^H\s 7JJy!F2F,X煦)ZE.D-(H4ȘRt=Nf%eNzV@% U~Ev?/\^~>Gp((IȄ)DsB"a%#MۅL6;)<k!#Q3n;*(b6oޜçP㤹`µ?#W4Aצ#*uvqe$i*@rl``!lRZCZ)~DPVIYC3wšdb0 ZrŪs"0lIa*>&2Pb'MRx;Z I"B +_eөpĉNjn(oҨJZέQi=qZ4J@Pz{$:WHjeKe\"R4yYF%&h$] 63$60GL0<~ߡa[PE%m,5oOV F8Qdwj)W^nMQe.B=^Q;iN* X`wulٳ73K0N.f>.eҼ{3F# SUVmp~Ԕ\!6Ja&gA4kfZcдT(zG"GTcĈ#[of1s&"#&QO|/ĩɈ#ଳx>߽_:^D1p$Zw8ݻw3-_6k֬sϿ}91|mJ~)$H  { ꪫ *g0!1(:.F6H"6l/?K/\*|(O[-lJU`shtf'^ș[UX~r /%%yWsB!P}G._Xt?RTȥ>F`7@˞$_P?&F fo׾ ׬iӚu3V2DM'Ї=ksԧOod'3- oGPRM֭O9{N|4.?t@$S%%*T#\gs]ʡ{ ÇAc)}F34G6N}q@*#S W|Թ 0}6б d*RKYu0Es <-M1IM< naha=ǍfϞMDM1M2:*m٢E˻.}FjQT_ T!ZX J`.>@ |g!8mK NQPMHm@=Oj${[2o'Of3hgI1| ^{+2 WCȭTV{:N"Ez!D|D&b!z4}H˟a; m?p.bS 0͏~̠gi41cǦ78eagr }T )?^H@&|/B>O0a[{mBԽ54mx* ~L2+qט<rkTg2`YbiwRF$-$mJ;t^]~kaѰzbxlg|صiI@|$fI83 KM$Qj(^!f.Y)̒U@xUs92J\=%ZrױF^nAgժu$e|W [y⃨q|C|>yRMPfM7ҸgϞ|R-h9;w&p+#P2X> E1[W o߾ul,H-+)P꛸c\{=Ė D-qpΝڌptU O3m O2<vwa;4f͚: M7v6޾n-DKK!NmuRMÊC'emGW0WY7[B;vGIRj;9#l)Z%a ]̙3СC !YA-6ڳUaRx(QFMoڶ&/kS٠ W۷Ub+t=/;mACӬy9oO<"~#9sVٽH7i6,_Nk8\94m QBߺ o_%OE!Dg]]v[^~~^Os>bdY!uɜ(a rᆧJJJ,لKLd̝;lYG5UfР@ 15Jz7F8,+^H!hmYS?/K.ht0?#+FM '8NZy&woP?{]GG=_QNvlMO:uQ ϹP08,TYO~k4i m޼.^E\^xB5UuJVgC5TC:ՙ#kh<&M8oNF0=!q )1cưq>WjڵkgR0M28 }Ms,;;#]8hN:rHMjqZBj$!EV'0wm32 c54>)&_ }p8yÖ-[}?->% ;|?A ԡF@fVZxg]ڿZ GAu K/ 򝗗 ܲ-"zzgSol{; լT4 (Tc-*q)4ԩS;69U속U]VzfVV88ՉvH(-ٳgOm6nܰw-\i1%ы_}Bb3 @mGY7a}>6@HiPƥ$zիUKHZZv0==v4 U#G౒xyiiLˊ9*_G!lK]:ԉPϾ~ ı?#t]@a?)L1+䇈.T#_  <wa:|II ockLa@:$s r-IvPǵYf(_~ tATށ `W1@eʶOٳ3)ꫯ6hgmZF A/2щ@e$Ӂ~e[zމ'ny0A]@hǧ~ޏ ~nߣn[){j*D3!CMǏDO+Oԙ[v$)A}]B b ʊ,3>Ky,u~饗apf*3n2e +rCkx<ᓼR- %FB!cРAmhqQwA*#ЦP۷7* cHcC!L!G<&u q+DQAȏ9j=ѦM㡇*h#fAvmm,)_h=-IENDB`ninja-ide-2.3/ninja_ide/img/indent-less.png000066400000000000000000000013771216641277400206650ustar00rootroot00000000000000PNG  IHDR szzsBIT|dIDATXŗOAnm4M(NxBY#*?CgCTB@c@aOP1jPz!@C<ؔPvCa%d3ylLT:>NAYVϞ4dtpq3;,}lo˅jH˕ !*}o,[ZzJhdd#p[M Ø%Zmq#Ѧ¤`xzZDX  PkQ m O-*XgVkZl姮?CoBb<|$ckŗE_5 _}a2a8޿@4E42Z;t!igڑeq0Y{uNu?$YSo"t%Bڔxe!i CY.'hB( c7:p I#Zpc(J kU<_%L"BY~$"d="`/]]LM>0H$TTUEUjh4(rǰ*U&9"zT-1>:A~a\.HIk0Ju}q- tf71!(H q8VJ/E#JI*wIENDB`ninja-ide-2.3/ninja_ide/img/indent-more.png000066400000000000000000000013761216641277400206600ustar00rootroot00000000000000PNG  IHDR szzsBIT|dIDATXŗO`?=.@0.DGà4A ""=h?AMؿ^H<mumzڲl$oy#b_5Mk%`,&{N:`4֍ۭg~^rcc"PrwӁ h xH>a;<h4o kTl9n` tWIhu"ELr۵t걱s.ggV@oB l Z([ h4(LAmV* ouMPUr(E{l} _=SEXݠ򖄔ϓ$dYF%dYBUUǞR'Ig8~gr:W^;t]QX,O?SE9`ڗ5_ůu/z{Sd2X,Bm~ 2rvAMA ADӮSAꦻ_/9uEQH$ddz{ƌ.M^ӥzvԮ R.޾WuT ex XWշBx-6`u0LzWZW\.w?@€os=a4 _ ^ӑf4@JP "^IENDB`ninja-ide-2.3/ninja_ide/img/insert_import.png000066400000000000000000000023221216641277400213250ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<tIDATxWmlSe~ѭ\ bD![7Dc 383Mn K38&n,BJ9kh;:rz=zΩuw$BlM$ˢT$|>__۫/:YBŶ#GA(ukk`,V0$])džWze%Q0/K (R%UJ_\K`.<  jcyha9 j*H6w_y,(ILkok*B4EGG'byzB <  TVTܼ̬! NETSCAPE2.0!,@@pH,Ȥrl:ШtJ]OP:I!yBx,~ppaf'tW.0 Jyz}H l( I vEH(z(J )*)R G"l",Jb#RbI% I)zR!pcpt(R&&pzמg#(Sq "")oS..p HРA.O\`C@%&3/3 a31- çȮг6W,R% Idd" (EIPLJ0`$$"=˶۷MD=X* $+6:]˙[F`t$4Hg4Lub-` D' lϠU PY  JE闗h`k@"p1Ԓhp/ ICE!e5}~Hĩx FEjF#$YY6G܁F-p [hnp!,@@pH,Ȥrl:ШtJZIPJI]`(z2!5 x(:uOKz}G$n$J#se(M_UtY"")J 'e' T%ze kf'Tzaz+Stj+S jnSyt(T"n",} W% щC  L { `(!  H +ci 0@v1C;n\1фE,pCv\i$Be#0pӧPJ]'!,dTEBiPԈyEÀx2 P,t#) CGV ޻U ļ.\JB6/&,1IAE: +й E$ Dw+E8$dN|:D`COVn F@x^O06 [qn'>"8!X=W!`Ad@X7zAy wA9tGyW xGmv E!ԅ!,@@pH,Ȥrl:ШtJZ]N -ay]HWǻT)[Kyv|En#M dO$$V)tkTtknU((k n.US#a(q..T "")UIFnBtE$i$]?7)7Ѝ>!%1`FCh:VB4ٳl00HdC.b. 2A,LPBdsB5* &ݔjDB /3Jl-f$" D,pe:x áPЅ!ONKuEIOv:hh8Kbx)r~cRZC$4H M qQ1 D4d(ɂ* /'3'و8(bc\{r"-N0`8_6[ԭC0"x"$TpRdy\)FY̭AZ/`V* dV1F!,@@pH,Ȥrl:ШtJZجDhFpxd$%L():C'z.Hzg~DbdfHTO..E #Q 'd' YPt'YP+Xyt(P+XldQctdX!!S Z RԼQ Sz|U!GS M:rTBE &z܃W׀")Aa*@2OB58CPU&0YNlEeV4DQGjX$+h`WRItc4ǕI t`EϼT*$ È+Á-& H"B F,TYp iRp57]Re%f 2.mcU͈(I)`,4 "8"ܐ1y` D`HHdd 7 pwhM " 0 P2!,@@pH,l:D v <`-:Zrd,F_y`|LaEjG #gxB`D.aeiCEy[ `B{PBqtDZD!mD--P(E&PF U"YF&PվH @4mָ'g^`䒳'K4@ce(" Bɳϟ@ J `YaP(hZGHtT9ࠊD%hjCQHDATY6,T%bwD h.@XNܪa UP୼sMӨS^@' qHq ; ``N,kVDFѧqƯY )ׁAd7' R= "ˆ`Tf% 0F98zQI(kx` #*"`܆(Z!,@@pH,Ȥrl%I6]@XW`Kh\LS/ek^nٕ *Z* G|d&HlF&[G  `!EZG d( VEYuF|nNEF sMEcsMZ[*EGQI|NiMo (|(5ej֛`lQ ! cZP`^g]t/ hƞ(JDu/ܙ|ۀ/~_ ~oX)rR~-gΜ9W~aRI"'/J"P9W8ei"ʕ+{_C<\_>RZJllʫ_yjiiy'0TUŵL%0usI%h,J4Uazzoy,z=gk7ܘr{ԩ=RϿ1ξ?EW$|+9i]~~TNۂ[|ッ[V꺿u;Z:HZV{/؟=#w)u۫e^:R\<6&_xG 㥗^*] v?}loϑho*Nq"* 'M闓xGWʼnޖ|}_icQdDSHH )\_*H$^vĉ _Gz{1 &nRwO>T͇gNvu'I AWq. I6=Z-X.RH<O~|/hk9?noo x0 &jjjQxp@.3q9Osac Vt1R?3|}}mmmp~v?}l_ϑ[[ɑ &A@@H I6QUJgmrUx,`pf{.#[-3 tR%;6ՄVEρI5 @yX,jk`6Ki,K9S,;kY9q3f=3BDv~eZ!PPUן9sv+JOݛ/@ _~ 9G|F$"#)JRJ[A]5EɲjAV7/|JMd `饤90,`^ЅL|]^̇ !>s [H4 :9+) e%*d1rްKꆪiVt?Of&T'Hb WWqdYpc[QZ #TJB0@(̊lXYYCk$hB< >tcq3FBeɓ kYvRM4ol Z;Tնܾ=-$1D S /ABFyQu,%2w$#_0M7DDkzETWelnn_D 2:cÇ1 m@|~HD![p64VO GHzY9#x㛝_~T}>>#ynQ㛋gCUGe_7-yAbY)vAOG ~d$#+pf&41 f%.dt< ^3M󙑑-eD4efH>#Wv=f;'x+u,GKNnxlC@W1 9kQ遰/ T;׿ubtt333UTq {ALXmn7|'HbB8'\8sKH5HCwJ ǖ@3Y%W˪-*9*c>sd˲L&Q[[ X,vxǎoFQUy2]?/ÛG$q!@Eqaoy}8y$"r97cmB{c9:*QSd2ʐJpkN ޫ`b!kեJԬQPB : C5Hnhf|g3iIj+Bس;0fggq9\pa~uu5˺80vU555Tssh4p7Bōi&ys#mq@}j&2'qgsff&311ONN0<<+WnݺL&͛7 ۶ UUe,,,iCCt4zRomݎ\8Q$&:?>$K􎎎T*uٳs@)m۶:;;\4M 077߿{iQt\bmu5ұ[Hf9l\Sx~ro:85)ڥKEM @][[S` H-..d$1,:::hIIɶh4ݚb%%BSkg}ٜH<;}[nIMW??7ܢӝv---*r.B((qzzz|ݸ@@^*|A.R:qĶm۴;wkǏG,|p#2>~F> -fy8;;@=v˲cl~'LJQTFc<jDaNwgiEfyJQP55JS nɺIENDB`ninja-ide-2.3/ninja_ide/img/locate-class.png000066400000000000000000000050411216641277400210020ustar00rootroot00000000000000PNG  IHDR+ sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATX՘{pU?gr{j6IcZTK-*txO*0((8Œ3 AGI @ B[$MinwM5MR0w٣DxSwy2Y"X RJz)zuוO+۷o<ka{{;MMMԐ癜ȑ#n.mɒ%ۯJqmbݺuW{K6_Y,e" G\}- |`~{g}}}͛?odXuTeE&[,Wu4U ^a}r2$=f`n!m AyP) 傛^sWx~ !W=Z)^l5tG״nLt]wխ:n`MM߸oh뱌/ E |'qm_:ζ<-45ݡj <{`ѓϼ:&\aÆ{ϽXk>hcò$L9V̖9tpnܳѤ5}ؖB)(,P(GY{Ň^SW ye2\lv?n&o>Egs5~k51  y۶v.|;4===5v^dI@B(^E^=.fR-f(+ch(kWOc;4M]MKlGYбlO#aR԰ !Ny˧ 1hס:܀R";X,G̥q4UHCOQ#D(|%J "|z1LNN:۶m{`M0+u1`WbFɻE$cSG%(1 ']1{yd2/Ia@s)0`m_-~ -m#Fڄd5~_>3haݒ~xʰ@%5%@@w^>s/?EaLu7}vExI{3O?4cccL`A>^&#000.(?zok-%R8x47zذL&òe8*v饗^N󮺾u[V{KR!B3uk?YgE-s(,OcMYė.Xm)>L$CkM$aftt]v8IY0FOsVxoi/\r%\i Pq]p8D u]>(`s9h488_ljƍONKKKsUUxG2oHէz-+?ZeKIΑnl+HЪT1j@(jN PYiǎS[l}񫮺jA<'NL&ihh`ttgE3vJռ 榱Er˸nlY=D>REO=Mit{+";w|qOCGT*Ekk+7o>W)MmdϿ͟=ﷶ?J@#'\JM{/w:HwwwC[[7nܸ~ŊR)"b1l ]?xaN+Bd# DfiV4iH5g*4leI3oq|v߾}cIӗ_qhjj6FFFs=p|͇_2D% gŚa |W( og=>>1f8]xq'g]Y.>9],޽{ "t[n8ߏI*]7fp{ؙf=ώ,؆ 6ۖ.]fg΄6/UJC5_Tg2,?my(_WIENDB`ninja-ide-2.3/ninja_ide/img/locate-file.png000066400000000000000000000052661216641277400206250ustar00rootroot00000000000000PNG  IHDR+ sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< 3IDATXkly333{!IQHJt1e'2 f,C0Zru[Hl \$VJ ˲%ӑI+m(Hx3oxݙ9?$kT/přg~ߙ֡Cꁯ(Z5M4'x"Y /KJ\4h4 jzzzPJ =3S /]B|]ӴiH)}9w[[ ,,,0 4M}!}{R.{ˑ>_Z[hi0v-{ٯIR=gΜp`ͰK-A.```={y>3<;: MKl _?zO7{vq?zyꩧv`' !gpp~R+ B^|ņ 8LuV,w8vn?j[MW蚎a蚆ht]1sF08Weޞ(ZErr X[}lnas}b]%ǥ88N ߿-4 eSY57?+A@2q'6& !4u!n̛sM[fuvbMVfṹ9.\@<q X$ol@>o"|2JVpW N$9tnZZ4MMM̠:JaFC C.RI\){hH*{Qu)rp8LPh ؈Rjׯc6J)!45Hv@XV"@H'lJ UxG6u krv)KZ:%;`Y_h2riȥ6fQ Ca>RRyRrb{suf3=`L&C&qRޝu|'3Gr 啈MZǢBFIٹ>ƫr[vi[*T*E*"N344D;8u~ca5 "+C#I_` 쒈3_޷v>}۶__,ܸqtռضͷJB0Dh O+2;۷)p={rk]ٺ*G*;wH$xK{N2(1t3Dه|b'kBzgrAt:}3&ljjj|2'N`}mv7VGL~9e1Z%aJw~9< y۷g7k2C:&N"N366F6ebb{=xkw8a!Wb[K l7|H$B2\.X,ƣ>ȑ#纺?M'NN%okkkH$B(²,,ZI.Euwup)vѿcǎ ujy:;;Ŷmw߲)˙RdTWW:RJt]G4RCGbHTu}c";t@(B)EvM,{ܹsulef+\;۷i&Z[[imm%H0  E8nӧO>k-KUUJ)8<㴴<ݽ1XY>#0 0"qiBt]G4P.P.@ٳ322W_}uq(H)yR>"* d2uO>iVԧ?jv믿~gWGGpMFJynѺVj BeY477SWWKvQS[[KKK 2P д[ի],m2V===J4Dz,4nK)F*Z.V&@ r)qᇀ G͉D} 7np8|jߎ*!+=۶~ʕ^z,.lU@ Hq zӁ[>݆PMk@Z)&w]7μ9*Runɕ?$dIENDB`ninja-ide-2.3/ninja_ide/img/locate-function.png000066400000000000000000000056671216641277400215400ustar00rootroot00000000000000PNG  IHDR- DsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< 4IDATX՘{pTǿd7 YHH 2AF0әEQSe)-v::-N2 -/y(I&{w 3sϽw=G(+͛7RJgB's:׬Ymn&[=۸qc5YZZ  IۋT"t:~7w]Xh;wܗ&LaH}!\Eۆ.8Vf;v 8eƪן֡2YF0555ªUv-^r28ξ859fE9EIx,bvءD"g֭[[ ,,c^W/_d,o>1&{\l fT' ۯ]W=P6}|l}]QQ 1 88 Y,aK'N_ASmɕYcKa B(!8~4xR?~D缏?;Xx/^͛7^n{#A3#<Yxrܜa uAem O|*=ba|wvwifϞ J(}tZe!O%+s8 {k_|+Y`P%.9@LMzRI)*AT(Ma_tn2}Æ #;nt,pP'qWWRH&"''=aXxLS9BTAE(T*(jR5Y / 1 lpg_8l Ye|ΌySW_YLOEJX,85`@dht]AQj5YM' [b& QCg)YdaˋdYi0 PBV, bng]GH_ux zp{9P^Q5ⲲD4ï鎔R(qXutZnC (S\E"qy( ̀!D$N=w! 8cUN <2jBJOf(P䨡c,u|IA"@29b1L,x'ND 0(HE8L_uk:]2Xs\DW4%)YXQ~#5n̐tI9`GoC/PSS qˏ"1$BP SLIuBuM54NAK\+k96mJ}8,6A3tޏdaUDXM ]b=9!F0 e(`9^ !&J JJ?OOÞ={H$6|+h .OhPš, a0GXWedW~vTt|uߧ~SIU~ 0 e Ře1>< GG$"cC

gΜ{ P& ~bؾ}{aݷΨmNp=xeom4*ɳ:`*#$s*۵/8 `0m,T)攗isD`6100am۶7  %fMoW u`*+_|- ׎X,l}}uuuO#eYaܸqغu+vrkk[micgPw}3득`IOzGIC':7̂7,mmmk)}HER\(*ٳg !M)#b̀:5 dM+hPdA+iEiS3Y{n4 r4O?f"ˑ#GoRz°]1tۄoe_HK*Ym:0}$L&ӯ***@ NR?޷o6ڼG&9eYHIdJfpndg\p,^mii5R& lņ U68ܨsy$hg:<̽0SNv9}{f?7]Hx~1 9QFO5}JC' }jCQe8NtuojU)!5.ٍ*D@.$Kqҩ' /\Xbp>U L`a1<I1Bc~ $ D5esrDg)LŸ QLHEhmkS.^m@ཟ!B^ر2'QZo93`t(m5 ;ŸϋK/x^4GXՁ=kzS*?~h$Ui r͉n0;9r L* <ѣ8k=unkO~GJCZL|aZoQ.G-ߡob? s3 'm\,>HJy#3%'kḻ89Xѯ}64{KC09! 椤%5u RWs% oj%7r"٘</XOҋ\[}Ml_ %*8pg ]aXyљ VaG3!^CC$ɘ{NS# j'>yts@{c{4S}H:dlj Iԁ1g& |@t uodВJX:FDQm _|*a2#mհW$K=[)aӊ+z}$^ICtnq o}; 2ML=P\ Ӝ :U)wK:]U  BXTj&x~r\kAOuQMY2pe`Scy# ToM(Y1N{eT4V`.cc<ޜ|_#,FƜ/B yYO>u2%2,g#ڻK׭C jyS5֝`m\XpS@w\2;Hen;%/Pb )٦,g6[gKo,tP`M5U)HM9}t%2MF-i@CV@dH#miH͵ &! Zh積EOid78D:hfuXb"g"@KF-Q<_m-}mIENDB`ninja-ide-2.3/ninja_ide/img/locate-nonpython.png000066400000000000000000000035021216641277400217310ustar00rootroot00000000000000PNG  IHDR+ sRGBbKGD pHYs z z11Dtp.0Kk "[n7]]]݃>yfzXyMyR|cc&s09?8`8Fe<m8 9 "\~ذCD0 tc 1h)4 \CnADhP1 !e{[F/`.';Xmo?oO lo}0 ,NB0c3*- Ppv_;gyn—Hqh@ !؉eXEv$ ,||AɵwBCrM ™1g/oTXR%+9` U $A?܉LAT4IL]2[~%TEVs\'QsdHrZ 8ߗ68 8cЏ"p]K >-`qB7b@0 ! qj`r@ WFfHܠpe8<υU?bQ-`PÒ`(v ݝ]>b:dnWf )u]Xw`0؍2d (K@H`baN6;ӧ$IOT(mۨT}ۘO>]T`qM շʺX[Z]YOT/c RJZ-0jI7V/F7LUެ8?`&˲0===kMڱa= 9mcrrsP^C!5[`L6lǘ*I)rJfǮ`}gRJ:u ccc!J!!L(0hGu]j5bRמ}o߾me]YsT*ض<ϐ8ޟضXXXk4RRʖRiq|޽ZE\ZIRJ!D5<n`Ҳ`Ylۆm;, yNjuz=Z__R{qhvvvR`oooӟБl1<ϑe$AQa8""a~~OK.amm?BVc.\իWBHIRJzm^h /viEa)* T*qTݻ9Ɵl6ז$I҅Hn`ǟܸqG"TD2R"8,z^@ܹs'_ {ssssRT^[IENDB`ninja-ide-2.3/ninja_ide/img/locate-on-this-file.png000066400000000000000000000043371216641277400222020ustar00rootroot00000000000000PNG  IHDR+ sRGBbKGD pHYs Q QSptIME _IDATX[hΌvWeElr8%S׹94mLikK@(BhЗp_җP[lMbqɺ^ɖvWYis;}%vJifgs?Ӆ;O{qSSKWCO+|O{D}q߶=/ݰ%=|Q\y(ݴ[jE=z, ԑ#͑LGJU "C*)MPhfz7ǫ%/'h4$'N(/H$^ܻwVzqL⯺oˬkJЛhB\)sim۳T**=CuqرGGGꫯV ,zzzώ0Vkm ![47pƅW;dӲgC{ߠ_}뺿Vl(܌pb-!`C~ ^q{Mb6KqєɪXJ)pv[a+K='9FFFB $ҭ?pDt ?G}LN_#mw"JEXI!uqkѽ?tFlܸq4p?q%6 {r!$uB$BČ|.- hxuf*S^4"8ԍ)ar׋$p-Ec*wDߧFx0MfVQu:hWA f%,Mfb|Fv╷q}bxvm#s/~6l a:F_fى"$?_!LbY&BX4!>ϞaJ(&cEbLTu;^+¬{FuDTsD_j/<0E:P@ \m C`$LR ф|zl `=0Tk#LFĠ=;MFG6e@ I1K-(jqX1Bh#D)M(LOQo:zىEPjGBH|쳫D#Gh+u!MK>bE'`nVȶ >-=RsTUk wŘ<H|ߧZbYa"q\L֭ăqRJe?nJKK m#Pa֭;|. D"t]Dz,kWap=;T:O4{epp^2 HDT*裏?}|0o\%"RbǼإĶm,But]T*^S. 00y߳gSSSjll.L&ERQ[w.\*pVU~xÇ׿rڗ.]JR?ubJv\L}s c,\.7;;؞={ {BZq[:H) R)bA*r(naW)%kTRbfСCS&''y70pnXx)q\Ahu۱cNZF"ԩS$?ƿ48E С$IENDB`ninja-ide-2.3/ninja_ide/img/locate-tab.png000066400000000000000000000045761216641277400204570ustar00rootroot00000000000000PNG  IHDR+!@RsBIT|d pHYs htEXtSoftwarewww.inkscape.org< tEXtTitleNew TabętEXtAuthorAndreas Nilsson+tEXtCreation Time2005-10-29/ItEXtCopyrightPublic Domain http://creativecommons.org/licenses/publicdomain/YGIDATXŘ[lߙxk^;^;9W )EQ$(*EmTћZJTj(RQ)}"؎C)O9;1HRq)!Mnx+6iNjDi\3ٵ?g ӎ8X+gU@I=% C_ nlnpb( ap1nq) Y2_ l!؂a](LjAB`&$xF+ȳz [>g0+x=0jj %+lS.)h@31&Sx'U&<к}.#{'hΆI<,Ρ̻(@IT;bځ -9K`-`E6}ߵ#x O".UA¡ B 6b(U vm#lJ8k,G{vDkRn 0A?dq. bZQrR ~r|נO 6Un5z(aR3JU,@DPJOL.BTMb BY) 065RLm; 0;"2Ս7BR[ۅH7sc oӻg?1 $|h3ūWno:iA3 lH4r0@WK]l=E*ޠQj S3Y]V,˺kT*Euu5m۶󧳗_plҵs+ŲǕ$x-~8v/N>{b "reo\}֤Mg6N!21e.'+If98Ve 9zEfB aW2ۗky\.ŵ6+]l3|r}d5B*SB!_o+ DJiD %#޲ .Ҳm_݋`){<8wN|[Y6RiSjlldff?ŋėq,gѸZP Q~]$I[[y'#@ Ng~U6p'"y333mۜ>}'N,{ϳD2x,Ja>+qb\v|>qd^{58#r)#h- l}\׵\]s$֛mhhhXl tP(裏[o͜y |DDODJlW F67%m ^Ӟ%pHU*cXțt63F !LZj$S*hkk#˵:tR62"ݥt?~`ZPuȑ'|r_wwwwcccg(ڲpcllJg}<577םu̙'D^r~px3 >|ܜ@ұF{tvv!s}7|3}XׁuQVWkx59z[n}d||}}}}~^, Ƭ_B)IENDB`ninja-ide-2.3/ninja_ide/img/locator.png000066400000000000000000000012311216641277400200700ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<;IDATxWnAfΎm (B OCD&Bv  JRB}v {.Xkg[of9M.  p0Zk٪Z)a۽疀zJ=ۂ-j?~y߹AY CS ɍW:[ yB*&PF! _DhQPp)mzwtb ϽWs!@X$F8:zx d$YܽHsٝ!IO!~qq&'U0 V(9cJE$q|R5[MX9$*)C:=2 JQkUrbOv|^\ie(]vZnJ`"nfG'߸dܤq*C9;D99~cVr!12_r9ͺQ9YbR >)59! D3n8~)KUD@SlmoElgl)m*妟dVϟ V&IENDB`ninja-ide-2.3/ninja_ide/img/module.png000066400000000000000000000022041216641277400177130ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIME 1 w,fcIDATX?oEwv=?;N !@BB(E% QHЁEv۝bg7+f͜s޹ /_*cuIm ~Hܷ0yK Qݶ*$ܷ|Riy@qjiڤ sa YNX<}㍏?9X7=qf;7?;%kaLm"&r/E0vù.@}mKE* 9&!d8OGq,.*p8rE[bYDIc,Tr />@ "ˏJ @RDNUBbyC>XR`1xQs݂,,CY+#ъ|.HTo-HKDX]G`'m]i2ۖ Dn&aXtC _.\lp4^H ܝ6y<=^'B#HA>͸grG^pw1{>0rc {.B>ً#{&eᚗX< :2e<c6W.5 CHwYp9;է E  G!4.YEβ wȡsViqjSX-U]ԦAHl%4C0rf0r1\`B21Mv7-* / ѨE@£1-JKsg=;s:_Ι;ss;!fF>sױQ?y77muTO%Xy\;š_??=ذ,oq1H+_גQ>:.Ϟ[%HD:ykv0׉ª X^akSϧ-:x*Щ6h `W2o<"9g#)7#_AF@B$K^oǟ)0j)u @ 00,Au$tD;,ٴ`R7y/ PR9"l(k|MJf4?3[ݲ+vy6M>󎹫FzOBq@S9` "PԂ"(ٕK0KMJtc_c5RUugκk'=@#8,s}  .J pR J1gj:ҮN2% iʥ%Z2ɴ&di\;6Axޟꂂ2fг+' B$L`)1H/{{1Ԇ`H)ʑfqR5yK(k]&dԻamG!8@!T\p@< :,@[E MA6lȵf&vV̽v;=_󧏘iM70Qv,^?\ P}2nI n+2u.J#ZϻM_x+'O\GGGG0:@f6 2^/ ;iK-]m҈#64@.RTEM^)]U ==X6HmMTlS\}d #b1fD#` AD*3'VԧF66]U ]>%d %WXpW~w}?ѩOxK/l@XL  387kn/E0(S9'\;0qm[/gE-q*kEQoƋGzZ{O-[ӜY%ňL p$zo?: K|U<@g#610>9rt㈒BB .C">>13,%mWrb"ޭ`ҹw9[qgHom?FK!D*PCX}TMgk̺]dշ_0hs"X`GN^P& tm Hs=K_Oc1op7Obىm۶oV Asxz)n1%jfN;1t =u̺Gߞw 2a6ZY_8ot9mqv,4T"P&p•P>ci1Ɲppv@8ж3=z:+b-괬G~6F:+WZ3nݻVBԠ&t%"_e4j]/E=t IENDB`ninja-ide-2.3/ninja_ide/img/nav-code-right.png000066400000000000000000000034011216641277400212350ustar00rootroot00000000000000PNG  IHDR"#gsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< tEXtTitleGo Next-~tEXtAuthorJakub Steiner/!tEXtSourcehttp://jimmac.musichall.czif^IDATXŘklU[ZuQ "!ZD1 EcDMLT|_h}F !Qj<(Vڇ-m}?L;Nj(79{f{oϜ{3c(mTJSCZ;i8أz};K䜁@jZz|΋%LwJDtN@ }dy5}̔iKf>yl7Ve̟&S`)lEGaL\(m&**7a4 Z+"ojgƣOg+4ox@!HDqlju[KPL*oг &4t3_XRd'`/x]ăw0d,\ OߪLҊAԙh> 6aX7"; [>5:b" DDD 2Ɇ&vU HK=Hϛ3zz.K0(@JtW$@B8 Lz\ItN\|]Q/;璊-ˋ hCA>"|lBj*n]Zs]V3ZZ -A/ J.)$m-ciX0;R *} /gzDJDJwIMnYUm"Qt/{%le6 GI D`l_s &tHĬWH@AB:W.\~Ǽ^]^ç;Gl&&,eb3QXZOjOF"hѶ9YqE 3Ft+O;GE%._Ӊh7HHy:"'Pe1.esJ|":cjf)|Quxσmh̸_#E"ۖ~A.7܉_ҽmqAhˑ=x]ފ~ DDpA* &OM߼یnaY!sHC٦Phݪj@Z< 4/߆DLo5kn Ŧ}!;j;}#_tkkwBf4"3XUͣ@O/r/E3azV,݉3WNi|ow! @CK@HK3C0@ qx$z+D֥W!gʍk+Ј"[N%,s+{О{ai0 Bԛ7?7xY4%6ZJV#9dN1, T]w+Ȧ ї^m[~tp{q"$7DwK=d,{Qmc[ DJtV`_ܳ.D[N(]n/Z]O@ HDt"r@?5ڃ߶mu47}+ 5 .2vPC旐R!hDGuwu8^E_=p&psl%ގ ~ ux5>w蕡F BG~FIENDB`ninja-ide-2.3/ninja_ide/img/panels-change-orientation.png000066400000000000000000000064611216641277400234750ustar00rootroot00000000000000PNG  IHDR@@iqsRGBbKGD pHYs  tIME  6F IDATxُ\u{>cQ&J2E+e$`-A$/ yk8$F^2,[V,QH 9K3={V塛HiH5pW|;<\E^r3/mew{Dt? d=3}o <"ba`nPߨV" b,?5_>1{ɅGB}.$5ֻW=z(&*9RB\*NMMVg#KrA1R֞@z҄dG&Ԡ@y44q8.@ ڧ(i TQHhO1   }PՂ! >5HV!!_t̝v,$DF{&xPn(΀րr1b~ut;{tnQ?䇎npSZ__շ,5tڻ;t4O'/w O%"=CWSN>Mo{Oϛx6˜sJ *6+E}ZOD~Ih 5(7/f?{4vgwǭ?8IcۻC~e]=Pku26A)5RDA]qqRr'7/>P .HiDզ 9'!d&Q4Jh^c s18]`3?{z}5;nc6(e<8`78B)YUͷ{?m XZ-L3u) B 8мm-9j0,*:B!D"#hamdV|LO(UXd#Il8S=0FWv76YrqGrT)^/=H#c"Z]Vk}qs Oj1Ĺ& qZNurIqe.nfio|DIND\֟Bdn_]Շ9fg Rs,ziN`ntH! p U9,7{;}T09=8ʑek-k];uEӷ>Õ]B&Ktʥa KeBClnL}1F 'u'Zz}ǬRđh `HءSl :'(D 5dQGS(ѹc$)n10: e9`jJ! Fr(D@f4v2:( w)mV6bZG+ӥpfjwsy'|ipx 0..7&u.F12XT'pq3p m^ԥ^2tQ( v∨+;Y|%r` Vx<ќ=Y3J;KL hA(T'5,;@+1ZeXV#%X'ke MscN'cGxK[!RV$I#3LYZRGBBH &҈1<&2Eu^xBE%6Isț[͝lZ5I)5 0LQ̣G9puss;tb;;wryQ w]ګ?ɑmklnn=9w.\^k]ZO9sNdk-f,{ZS _^ʂb~T頮)x*0I,q z+AZ99PPJym6;[~ޓϿ?ȤX,WyZeZƣ믾xe܇aV.@2Z@ o@ dh!LEOyyAKvv{?B ?onzCes/-#.NhJܺm)2#VpcߟlnL2`}K>p(/J?\#(IENDB`ninja-ide-2.3/ninja_ide/img/panels-change-position.png000066400000000000000000000103431216641277400230000ustar00rootroot00000000000000PNG  IHDR@@iqsRGBbKGD pHYs  tIME 4 cIDATxKlcys/ˇHc4xfNjgjy;iH鲻.Т.).iAY8I`xf;>>nl( pyy|ytbt0:d:ɠ.j}%_jv681]FGGŋǟyײ㍧Zʕ+oWZ$ P#\j%^:ẮdL7nqH$\vC" DR'Q,EQWK@ބiJw^R&JC10 ֚҆˳gL2;O5YE>oN…R =DQtDQBJ/%R  J@J{ſKT%wAc'Ę% ϳ@O{Cn ѿ 4i=C*RiwrEfM5=/Z K bP%rZk|G{WK@+^B ۼy{ 239ë =$ RRJZ!BkQ@J Bc Knu q!mUkټ4}*K_`'?C˲-9+%]@e UN bTNbţidJC/^V{S\uC*+~装uv&ּA@!B*yX?g.Q(FUS\+[%7Md _k d|q2#R7v_w"ఉGёFlԏW Bg~92cy|vy7or\Kkyn%H199I:4AFRT>Wmd$%HSE#, h,Dz>ʒ([b)m޾_ < h|l,}v^>STm8lW 矜fp lFe5HX!&)VΧ6L죬M?uJ׍7pP=q0`cc)9_e#W"ݗ&f7[{g7fHı,ZI[,09VV5 [&lj BR47%[ '>NSjrRHR &{ -bރ_y5|2zNB[KF!WkBigms;#h9"F޹I>7߫%t6fKp9 rx}qmqq7Vrw'1n]EQX_^IR,k("_z_-xqm ʲۯ£d/uؘ>s7J)Ùӟ^?Wo/--m^|Y@)hWrۀ<=MoxϏ"*^)_+8JC zɉd D'G_}w,^t" 0;ߪ3]'uǺ6U|NlnWBb)kH??N^(S\ǜ`2^8q|r0 1:X# =07&~'<&xSÝ?0;&|o 5lvMA4aԞN}[#nx@40owfenTV2hrm0Ks17yP^):j:,hM|t9'#4SmWkeiqIY$ȗ+h'E7 Lu? PR`9z2Udgko' Hʀ+,Ētģ2o2(&$G?#BGY:cl-ܢon*-Rg'0gduo}~nޚd$y]?Rw r]ܴK]j.s+ 2\e0*S9k&j4@Dɏ g=uVkw.5tP'=d4gyy"A$6PT}opaDP Ӭ9YF3vKʾ?<)V3pUO#E㼧Yb!G97d'suwP'.v=,CVх"gNm3RںG@ 7ys̍K[U:$;b' aMܺO=Q#\G\P.x>NiIip))w{B8d;,,,bX'o.Q Iq1@&HРC& IUKXdAPR,ϑP.zzma~H$PQ.M$DGkĸȍw "FQB~D LuF)nm2vW>cc3n-;:<TJẍKY*yB"DH0]*b+\8;mS DH,\Gu,%OH^H-q{ ~uQ!< [c<0Vx"NE&}/0+[R{-hCX}[S:OIa W4ґFȆTIhnYl~zoI!0J{m81~Pׅ1F5uڣшnStKVmwOwm6Zk>@wokH^+ W; Bh0Q{?>Me: aRVaGc Ar\3 ІM෥mAcLOw }_rX,cΑAr1ҺYF] ^[[]ׯi}^rE`:ǫh-&Bݻ[ZǎRR(nJzm2+t8NKP BT*c DQTwg Z: ʶcCJZ_o !N=y@TU6222<4?L&\ǦJ) ԛOn4ܵnl5y|#^F(vjd3l~ף{&J ޅӼk_t"! |~; 9__9 ?oa2BMRhԇ~ (B֌:g;ڟy,3XZV??K)Xk$M^~(|Z~,BD Z̉?ΜXBۖ$So/~-/W nxEr'9!Nv<$l/?9HZ?^<©ʗ/fp p@{mf/_zl6,Ϟ{-y!\ wQē>"9:[̐4ʏ@JA\V([ $d|>o9<ϳaFufW;:flQs$ ĵcxef:V:Z_ZQp\@NX6WΙOqk)7)&MS1X깳FL’ S x$s@:cb~EI5I@$C;{ @Ipx;G 1<$j5/I$s%+A =@l`fyyy>CHӔjt0Q!Z)?Zs(gFch?[kRRc'Np]PVm-2bs3V'pq1~c!ɒ|&azMQHV7P~@$LC$8+͕7,6<#585X0D| fsOxYn_4;?ü@:qʛK,oD>;UT /(w^o,vX;+5n,׹[M%|W$$x,%Tj-:Wdp1y ٿZkRüB "brE>xIAJA XC>R^o1>#9#xTX߭!VH!$cQr,J2;YXD7I]vot!6[BE^v%?襠 17Ŝə^}\45!H%іNCĻ Mo3GOqx@ w_\nɛw̖B| 'gDT ]hٮTYw;7W#vq`mm#Iv_I4?X"lnr7|yH<^Zi"\L  $IIӔ;;Tvd4^k?_J/VsKO>kOԅ驌[& Nlp$ĉڱ~ )')بXkwկ~i`\D9f'Hranwz-ؼZo^}mÓNY|?/O{biTދrRy6r.:nyK ʫ(5n=Vt*'jl2=']$@I)כTwj|.pf6Z~g>*i‘-Xps't?gys+y~CIcw?'~#!G)<%pC"Щjp2/vcɅJc}^wfHĜ6ZU(;qJFzF'ګ6H 3yVﭲUrq?k0il'0(BeD`qPCY 7q_Cжy; f;^$I5)O/T]^:ۗʽ.49D ^._t ~ҘÈ@7+ r,l@iwPp$VB;In4߬]0./f#uR 'p$h զ)yEtd!:#q ~NnWcVt:Dsse v'kb-hm?[>VUHԛ1;zs]]kʭܪg"{i:SXzƱmcMj5j;MRʔ&wjTǥմKj$3KZל ,{.QRo,ayMf OC07p\$f"orT|.㹊*7lQvijt)vEYuE9+י.4c4VH*ۍ.u)r|..~Z8X6E\PWf(f=Ii6/G6i*6:>aRW!J4.w iӽYq$$,5,L{ie-TyQ&vkO:WŐWr!(=&!@<݂Qi찐 T=]p<߬0ӻƚb=0K!8N#QI̢uR*KYaaE%QzShJn&ʐHۻ2hL>}؃|NL'2U~@$w yD'~iвa Eso ;,Fy`kp GQ2^-x0 ֚c'OT#q NEɾ4M߃'u1FhnJh_JRd T*#6jlpzau@{vNTUZkIDVe\K\Srzli !<+ !H[Tӛk+ !to5kر0ۭV c:bhV*;C^c3o!'d҉o{Mk;?Ǝ@λ\lvJfZ}kxv[[ &!W71ѵkN)e}k8>Ic+++[@qSZM-`}X(0kt`?ۓDmX R`(m9jNX`o$;,";}Ò#l0&B_ 0N1h1|^@0V9q_G4F(IENDB`ninja-ide-2.3/ninja_ide/img/panels-change-vertical-position.png000066400000000000000000000105321216641277400246070ustar00rootroot00000000000000PNG  IHDR@@iqsRGBbKGD pHYs  tIME 9]IDATxoygf'9\ZeK%۰; 4m-@M/h@tAڴW)4MQhDclj㠱$'em$EJ䐜}^ ΡF f39y}o/OB'H)tE@U(]kBY?HJV 0'NG_3qDkaq߽J%|w4 vڣyuӝD;vL8q= 1 I4f' fбR{IO~+d\~*0c"8_ɼף̙3y@-nsotN `{ u=G yO B0_/."P(AҀVYWhY|ɇFO:>q"˔|?|cB(tODnI+YjVt&BpDb` CQ `%IxdÃLéq*WZ;RfuTw&5H)L?#֎PF wRG@qCTZc.GXJDQD=4IWʠ7&Ě`D |-BBnZcvHf  )@Kjw(aj PW6V"?@u$HK (zc!ʆ?(Ўk1b9mgusZ3"gneG r!$"Ԇ2 \wi)RJ,f `Ib9S7Pkܺd$u).jZy{OG*/*v;mLu?(R6ߨ+f_]ZK|I^5'EbTzrp6l<Ιʌ |g4ڴ'^Y_7 KSoDZK}˷,vb0N0q !_J8Eۼyk'N9cCmk~ƀhM/tb4߻A4 8J"l4R9X!Z#B՘jk+5y³gmbjF6v`ܺ[hϧjhJÚBzB뺾H J A.AH9\,f$U?4^B3kz7ت,1 R~vk.Ax"B|F ccY&֩zR.8Jlq˓uZxPry՛/5Xkiu#^8CŜ;9WȸN/ZwcNL<ř^@O7S =)I1A (93=}Hj-|1Z0uc ؊G5huB;V'x/P%0`XCW鹡?s;{|,bҏ.HX C0P%v I-T. [!o1]0Y5R)epe'p$R1Hh46 ; 61ۜ!|ϥuc脖 vzqr B*w/Z3Q?džǏKcfa1Da;Iuɩ}_}w(wnU4"%:Q5҉R-h6[nZ4ݔ u QoΝ;OZ[Zԩ?'C#c wF#IDUk拯^}5+ۯd/VgD 85t#ņVd xb#Mrn،j R7>f8-" ]z|N;h)=IQh%d|t-Gߟy\;;;6Պu]GLM%a"20 ÈVH7ƞ^4ݰsbGzX'ܪBV d<!,B  U_GOU=F򗾰?644d}ϥoO/[ HՅ*!wY`PJ\ىC۫4&y }|-PBk$ezwx_EN_==el/Zk)=@q;>$3QbHӔr!PVUVdl,݋ +$M RBe~v99r4-k-KkW6+%\ͽ:p`Vpºwi#8Z$Sc IЇ39^G&ߗhnL'䃞#Ƽvio8ΑSS{ƌ1\[ʓ+RÈnv\N ,Qq^{ 0W mR.(ILZj./q'(ʲl`qƘ&42,䞤NCcnSM՛\9C,cc N7D?ڳx{3sc㶷02nu7!`w5&( :c {Ƭ-9 {R.S M @9t”NlI$N.MPQo>xp2WܕZVLj]JJcB Խή$ $!"O'rY&SO /M_{?u]ǑE)|u]:.`1$Jx^)ij㨗Shڕ|{sg=[!$κu,+Jͬ`+ZV\CJ:9Rx^5/id5I]ݽB8,[öAqF]4Ν;rcN!Î/ilJ[v$IBn'I"v[ZK" xݻ̓$0&E;~ ,ijQjzPBqخ*i }8z` PJQ5lv#dfggv0@kttdvݓXF|uRAa^f5`^Juc\T{v8ҋ/vmS__}v4vmfvfR)UfL!T^wgrV,RʃZogD+Mi `DdIɔ*2vnzsHZjի>Ñh/)Њe]TsybY&{q=z절L hI-+k} /P?NMD*. (tgCzw۝=rŊy{9)[kvִosO.{g@(G6nyM/JO2ӋrOϞ8=ϛq]<;;X,omim-f̦JsoIG.<}ݓ7UX# "YӿS^Ozi|Oƍ'N:u@&u z _C'2_/#j0?}yppսA}|K E - H"B:+֦ߙd8'(&>І g+o$YиH"b ( pL0H3?@wtK,WSI2M2d+.Z4n&̇z|>Y^υ?^SzRb`/Γ$ $M'$ͣ,E(@b u FPFV':2 }y@ mDDDlc]AQ(t r@5'U0L5W *X,m@@؄=&iST&!kctd ds߶qXkyե 5\++MlXzU+Uؾ~'vy`TqHl+9x_viU\DUfs;qkdWW@PYJƱ6DSA[0zwۻk'Dd. Él6{K1,0n $mL| XQZz̋6%cnY,DijI&GȌgvL-[2hje&.Si|DM+{Gy] nЛ+4mR4G~#:ͥ>o"`5̵jBc&(~rρRT2Y@c4ţ=5Uϼg|@n@eۇLSْXk7,M @pSm?:jxܯIENDB`ninja-ide-2.3/ninja_ide/img/preferences-system.png000066400000000000000000000122771216641277400222640ustar00rootroot00000000000000PNG  IHDR szz pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F IDATxڬW{pU=7n $`1@T!P RjXtqq:u{:Lkm[l):"@ܛs*Há5n[ҁnLL>)I  H.M%z{WXǛ.o;}fUpmyjj3ʚO􍍎]r|СÇz_$ I|x Q1ɖoh}r>uFXXVz(m3 0Ƨ9@DXQuSuYuH[f ؖD",(_I_! H"GҔ@`Vd8xlð BG撦pd99;#/Bܱb/}tq[jzHv,pҀ@^h2@ MrAxqyİu.PU ͍hYnvsE;{d*ۛ\Ƚ~o ‘ Eb8c9U ,\MF%f7/fXs3!۫I < zv{֯WKx|#/^Y[k+$IBLadt$ ٖe OlRLJ0;^ 8`Y6ƒGk)& LR2K:ڷ ''gYwowE}ڽ=߿}٪B099y&1z, $~eYުaK*$ME4fQ~Fced$~]僡P7MSQ* ]ifffB$8ϷٝTU8v^'4W|cMԗ<VܱzYgG{QK˙zzzCh\H?r%"BIIѳI+ H$N65lߟ~077.pHKUtP7 sj?߷$ysoގZOKp BWgtYs 7nk<[ouw>i+';;=UhGQmU0K Gk6Z,WȗmuӝyYڸq3@ e4ӣ@dELinƓgضn/+-<[_؊p23u\| 0XoǷBaA2b-(X5,4 ^K?Ο;Anf0Vdμ{䔣$3-g7xR(ز^@~&\Չ`2W%ťhz3A@h?6cמ=sQ[[ YQ]KoS pHF0'x tš0JʱdI5.^ *qG7:i;,NZ 6VҎSpQ[;;p^ˈhR%124%sĢ1dg؉F~\jx{v)} ztfۿ㙣$u4cPa BN^,CUT a#J!CrP\T$ w&FCXTWٞ M"@lran|v HfN8vzJ gn!e%0"…>0w.E0mB5_Aʻ *Є>Đ 1%sHLtFXp+nxsJ^GtMY3dzvsaa<=(]7|p@Fδav^""HdiVA8]T]  S,y (2DSfs5!g H+O Fb/s4V4u k=)*c\IɘMBcD*sJuR@sI6+'Krb'40g63q@[K4m>:8T>;|nq0 'p=ˀi_<ǖS"|%H "W4!ogp;ǓF4Lb}8[ ~[4{׮d\i6Ii ssc0uA v\['XZSU$Dϓ 4am @~>ˡ=S7ԍ3H.O3^ XIENDB`ninja-ide-2.3/ninja_ide/img/preview_web.png000066400000000000000000000050101216641277400207420ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe< IDATxڜW lUs(Xڎj܄e21.lɲh5Sl@2dDfJd)"b GssypE_ 'HtLSv " YɔKomɺ?n۞I/N91eCxhF7(azRz9x"@-rpx%Pe%[h=!`/|aBYEUh,[  \ ER5t>dcȳ`g!f=~%lZW]`yʞ^XYeQe|}q$,2'⪇HiZ]$]r sH2!ށu-#I7_<|eDw0 Cd~TS#3pB0lB:/@Uj}kŪY?0kfOE]I4=-D)CEVLvȚ&(-| Ź{vhz g/ y!: T$k/H4ܭ#i29n? s~ T)^!A`woD,A~h|Kݏ쳈uՐkovǦtME_ӟ-C&Vzw]@rs@Udg{=7D|Lv=V|&P }uvaq*PzexGnEe{̟TY`>MR pr I8MЌQ#|9QIhW)8lQ 8m .\{pwڢHY4U;M@\' =N0acb1# 6{>8i"D|l@U_@ #X0u- pheQ>$ˤNMNXF@GIQμK6! 4ߊE`E88k4L.|a3DQͥƔ1ވ,.P0#垐lX($o0m \ƙ' Ou c Nʙ5r"eRδuYH(qW'?gx%FbMۼn=`긣t]ҁAŨ$y"d&"2P\,Lքȁ(0Z:^u idH8zC ref!J\5#b1Ҁ!^&p=v~1^T+t)AZe((8=~A^YK6Ԕ =D"Aq`$"iDoarsb \.f+B!22 vd-@X-Z)nl C1  \Ր= /$+W$8B'kv ,t29FmT`wA^4u:"2 Re? GEh3)Lcp)Jaft5K$\\ lXgB4=GPD?D$Ԭ9e53sۜGO@jOG/ӿ꾏0,ug>G<8a/n* XZU(}=eW\##Q0省z$0z4 d[Fr410IhpⒿn3^Binڈ8JL(sԔ|=ODŽ< !+>t&t݄uw֒mk+%I$2VKtB1h젙)ƍx!,ނB&|ZY}xF^]SF`lHR<ԫm>(:32MU $.QJ -Ck[ ٮ(ΡL[Ũp BC3azJ_a a9h( K/v,1~I霈FX7YPڪzVYF.?rll!.FWH/Hdr<3EHN33#%!%s棆8R2g8G|HKÚc;Y 1S;7IENDB`ninja-ide-2.3/ninja_ide/img/project-new.png000066400000000000000000000074621216641277400206760ustar00rootroot00000000000000PNG  IHDR szz pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F]IDATxMhe;3M6mljJkz{^xЋ zPO`DxRыXA؏M6I!IIlI}403;bfr WBܠԉ,X_; }-w7f1Jު}QhgBwl@)EDzv8]gYQ`e֓WѬAu4ܘC"sWr5-MX'!VxT@8zpFvl _Hjߗp'۹ng[+?3}jB>W=Бrg}rqaN2(#6݁`RL9O֮#[?TҬp`dx3Mz٥$s >jF{qn3 O1OvB5*S@U$B8 !RR*4)#O#T{I[˙f!Xc aJXZpN台6S_xTS\wj/g[A 7 Џ<VAY\qs.42U<)djo*5!2s9 ݈3ATre^X cA :ݏQ'hF5ϝ.DDAFoʀo;q҅|=0;h4|p*ק (`h-0@1({vyYLng>)N'Is1Qўǵ%C%|!0^zjWTo!@-VFb?XR`3,fb;g8=LC_,3cP7,˩,S&XKعnq覶'snmVE$%MT@l688Ç<.>pP/ [%@U8!qAi+ZJi&m$ן[wvf^·8量0j3 M?qRy]yJ"2{ p߿?̟n5 /8qFA=|3ӡBfpr`‘/w[@ sss,q  `r(]. Rcz̾&1 "Wg=ܭc'b8EJ8|lf@(]쨀v!!R#t !g*,UUiZ*MSX-!kڡ4M}Z8AE9ՕW.niee%SJXgEwwH)ccqcǎʼn:GeѯC@/> ;VrTQQF$,;/{$TجbW>hj0$ ?sI38sLKkM^Zz,} A>vNzw6n6 |5(JX K{+EIbX'Rz#BήxWVaVկs<{dbFbwF!ƽyX)Й4Ro=-AX`VP!fRȪqBW[!@13DJ"" zz XgIRGjce`RmM䴮;6ˑ |j} s=2)FUI(/hvЫ1Fh֓qOzlU`hYG??|:ź' S>YbÜ;9`c$I &)ngS9w3mbQ{KM98m>g #d'f`X3T@ +9x@ev;Ð%q 58+ SjL[7s\t),ڻT;{{=osv,pXP/^L:4wG>.]r/quC8퇧 J>LLVti0-|`dw4,Lʵ5`ЃP@epdC FA_ۆ8?bUIENDB`ninja-ide-2.3/ninja_ide/img/qtdesigner.png000066400000000000000000000041321216641277400205750ustar00rootroot00000000000000PNG  IHDR VόsRGBbKGDC pHYs  tIME ('ƏOtEXtCommentCreated with GIMPWIDATHǥ{pUksIȽ$ F1""0UQ;:TG[+ҙ#3JZ_EJA#@y{٫ܛ\$L_wg]Z[־dBAթ }ׂG?|OƣB bժ/!'񑳉y͛xcN۞t\9vVq?ƥ i")fz: .,FU8PQ$>ADPJ ISƔ4.RJ;}pMDQPDL&K#5F{4,5OV;a˛y-: ϩ ZUγ~ UU@5LlڵE.عn}*V@Trn/Y*PF"=u\Z_ky%XV{ӊj8=CV!%} _(衎Ɗ8Lrsk3:KX}n{]:#gk5.ˀ9Ax~~־Q*:rB1vvb\8uBs>1h0'kQOiSMUU*9C/9mmkjNПPP֢2vEqH 1ixuxm3 \'ӠVU|wN@ǎ5w9A yaCO<>t[u#iUrŞ4ChkϦmη{{z֑}@rÆdwᩈp)5kͮ!mojn{%a: /m}d1`U),ɀ jJS%U*3?h}-37@( }K:BΎ_uץ'3Y xIsN+6ܝHKuW)곳idH5f\^&heݪ ΨcH},|pҼKfeԥ*g/fX2b2k.e~ej2ʂ"~Cfgk q5H#h"PUe?GoԄƹG ߲uŠM3F!=-[0!G.fO1B9/ܲ`uE9a(dSbM'(β42zrF"K{xü})NNf IuߔnZY\ՑS|9b!svj^V#Jlؽ9ɷPQXkFr[_d&ڷ[9D%F_1vxn b1%v.u+1>  VBj1({۞d$K^s& Ѿ z,/xŜeIp0L"2ՔR%l ;(Hx~݊~Q)ښGxa%[ w\wn^GE]Wڤza'cAz4xؗ=T]DsuKȘČfa5MU偭wǿGmo<L卆׵׵n{Hw;۴P{L.88CG_j#?zary$|l Iuq@,= DwlɘǤTT+]=wvuHEs3ә5\,۪D NgRʴޔP-I4 JJ TZ 4rDCo<4&Ac:{ƄI[A_xZzҨzv>kSdAOTaOa$3ﶻ=a(@g֕|ZhPO _/D*t}҅_ov~lو+a3մ|yЅe&g)D\+~>#a v|q>#bXnطV;f傾t@Ůi^EY=zpC {5]H:Wumcm1֟Y.Gv{6) SA~_>v-HjL j^ɞcBHoN1UBD)"ј K.E#?9j0bA}#9˸/MZV/'1#$o}maXr| 縟⤰EF/)+eP]1(ڧ-G_) `1^wsu8R\ؽEۊ橬To͐jR0! p~D޵<}^³{h.[qTvx1o<侖 PT];C,s*j$a JA iMfOS`5@\0\<'J*Wdg.:_ۧJ5s: }|{ᖚK ,҃)*I0}#nJ+qJWv^zu( r5Buh'B'!c[Vb*K+n܇m۬AI'𚢔Ns.#-"h\ 2dbqRHnM9H>}M:]5YM R"nB?ө(8S98wYGc[m(D31Āp!s码#r?paH=+.\sDq2F,8!y4̗P첫6߅,Oj22',x~JcYrtVR RVenP.8' &xxf[coU0ͨc3 TKuM!..{ʽlIENDB`ninja-ide-2.3/ninja_ide/img/separator.png000066400000000000000000000021621216641277400204310ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxڼ[OQg^A|41>`x1'@cR )5ᣨ!1ƒ`1\¥\*BU h|0a:3]wR)Lg~;?s*Pja`?(d+4V٬qP|O-Z}CC<{zT8XPVWZ Hԓo[a/}d#l&I u>&9 &:\pgf< *9t<|053cGl.r {_ܺuj 2DBBH|rhãuH/8wPp_s rhhG`0C8w H꧔VmB9<#L@:hqX\LNeN:K. BV D!`tt{ T `W<5 T?!2 Qܧ'̛ʮI'(0!Ԋ8,Ba|bAv-:l[\MPΤQA侱X7{}3:`5*M 1HpnX]]uO:AE\)mrvDbzʄ>n lMCl&D8,:,ո^`tuvA.`E\KG ! Qubv&7U/[ܺ$WԀAMwh!XMbT~]%9O@ ThVdruE; wΎtP ss<A}nCaxG`T"Lj"j666Y]tS .PJw_ϳ`/gv jmM9B3`oxE`ohlp~6,ᚦ/ {QZ" jzAk".D{eD oE.K=O3 TgniiDcc*t(#rvUAD`sss___OW v>4ooh?,ȄT?w(oEGC޿ NG@7'IENDB`ninja-ide-2.3/ninja_ide/img/splash.png000066400000000000000000001706331216641277400177340ustar00rootroot00000000000000PNG  IHDRTΣtEXtSoftwareAdobe ImageReadyqe<iTXtXML:com.adobe.xmp ~}.|IDATx $Ey{w_Kr)*&E%5 FԈg<'Qj5(*DQQ@e{yꪞwv3TW}\sߏ٭E%?41]Jsj/_߮>1D|~^7rʪ\%Ծ4~Lߥ^3c9']ͥ l5q~,%^3kWaks׾&uq{5)}<9ߠVC_\y%߻/>]Ljé^5@tZc\&R=Z|5kn;2kPPR`꿵{ϙ%:P2*ք9j^E$VisZZbiӸ_`50ZTKi 6cilJ,C(BN:cӜkV@kBkn%L)@;SM/tk 42}}>jTӱӄY1Bv Ͼ Y eZLI}i-Sf?*;6lРOZ1ߧ}}YSSK(;cTOkBtVյQ--aگq = fj xKݷc%TYԱQR=Km h 4 `*j:BzXӡҒ`:VuuvZ?2Z*z:Bx5^ %Յ:isZ LZEXԚF t((&-D B-Dks}F-<> 1Bl 4k:i,T_09ͯy>\K*5TRkj 8-5'󴼂r5scTZQf_*1cH55-@u] Uy ]-iq@́!sN?AScc/ 9 tsuJZ-\i -B5. 1FiLZATV.i@zBO1Uj"4S'XujNNK)@9xY5rQ`k)k3ɚQ}H_JEMofj((] #xjp 5N29Wjp04L=pk]%j_wIx5GIM`ץ]t`:MPK1io5y}NKܡ"9w>:6:fEdmYROkO?8-z֚'sptTcWV׶ DqH5T e֘f5Pj1BZ4 ;F~@]\9Tgҡ4tRܴZY"XPZ \RhePg],tLLיTPg_J́9+P CKաݧ:i?FrcL?5|VMZJ?aM-5P*OM0gJ}+jSUVBi5c1/@i 5xjڠZ N25^̢i*.dS,%Ζu`EQKKkg58jHc̦@6D/48-[gZRa3?j*x`̼< HcӜ՚AE:Zz@1)5`h:n`tE̴0 jZFPWsRWr:: 5%c>_ZIM1 SLUO"MRZs4 s CȅYTNׁi9Ӓj>kH ܿRPmySkiM4Pk Fe⟖b5~b 0rNk+FRP>XSTJ75VŴ&β:F0TPNR(CZԟ}XTZTK4\sil`Zڗ: (MQI6krZR=]ZZ4K,hɱaqꕤaO\P%]PNsVÿtZR*jm%ujI`!L-tN[-C@-ZlSĖ}&ǴOȆ>8TR}@CW*ΊZ;hVTMX27jRk NUs9 VuڦT>]jk_B_%^SZRL#]iZÔ_SM1i.0K1ik6pE=\A85_ߺ\4ڧgIh<18tԔjNiYWNKC* T)%tx\< !tp@UKsLraT3AQR}k.րPZR*)I)ZCItZLc!4LNkqQ>3JCj Tc?Loo*<Ԃk%U%Lk+B`T s~ u Ji sr.LP-OMJ: (?jZ;Lzx́!VrZʩK*@u, jm` J]FG)](pZF1N3UC-9uCjU %v1\g%jmVPSuL*Zsӵ L NBPTVPŤyIUTK *\5 1F \c+sZzYNTk@uVspZ}R]:+EJ**kϮYtLU;9%OcRIC|Nßt *i8s&Қ?RcڊX 5ujT0[t}t(JcTRǨfEAY̅:jۣԾZR=(w\rpAi)`PYM5:Z_b߾*4PSƉYܧ]t)"pOcFYNS%INii`u/>j笖2R%J͚:ĸVFǨ 6 1j!iN R.ԙO_sT}'ԄM5ϝ }ZR9MY(TkRPSQgK=] j 6U\?@ާjEߕLjUZQҪTh-4.RCRRȻXZi/=ς.48q}-鬵&iI$ցJȁh!)j8@b]/rt Jr?Du T!McT-ǞtyMԔ+& V-UV5SU}`Z$jdž}R4wXuZ"(R[I^rLص/u!kc,h0Rf[)MWҫc)C(Zx]UPss=/uht.Ĥ =(jfu`},j&̘y6,4 㫭R Z5\kړD TߴR u*Xrm5ᗄuyPG!4Zt@TԚʩFKjHJ%-ҵ5Ԑ*&YQO:F0uL@ (xN%T%}L@TpպHI{hjOi9;0ͲfLׁKLjD c9ZT]!Pocj SAIӾ@~gɬ? j58KJ˞.ԔSYӅ tVYݧ()fm4Iڏwk~F!6*Zŷ677Ք(vU )>6Lup:5XPZS9imMPtӵMA3?MS!]HmAj__Ӕm]CfvK: /k:+8wcs;aoz47o|N`ډo ~%CAPt c/{: S%Ao4T`r*Y:D RC]>i>o}[SO=kT:ev /x [W}/ ! CX@uX 54 hKN4PP׆Scup:8<iKvi#;[Ϻ:N}mͫ^fwoIOzRsYg5K.ut봚vq;G?)}w޹~훯31hT k>үU!zʩY1:8g%`*횙ԡxEwzEuTRxrvNr"{^o9?_W5ScOQOkܴsZYZZBcWOKLcZLׁl麨5=9i{N9=蠃뮻9Ai6Sy2!|b-:?h$SDB,iIus {C(cϱ캲1~uY6C)`]`:3ペ I'o.j{N޷ϿENw4ny~ݫ]ɛXoٗٵ;X=jݾ,/u#էꫛ?{+~榛nj筑gTTiH - CCX4'鬂jӄYO?M8je3WTS6]{;^f\'dy֝]L\y?_N󓋚yR^q4zhs;/_P6s3k>#w_f+u)jPT4sPʆN Ni*TGoo~{5ĴodN<:D+WAjENym_mQ `};0]̿&߾9<{nO~=]v٥'6׷g7k[P++յ)b jLIժV7gќ晝;j`:y~qSէ~@jl*j.NV@ }YWUYK c3~iԴ! 6 ZNǹߵΕvl-(@5 i|MpjQ1k瞗co4[pJug7:EzeTZz/I4;CV95R]@i.2kڰrܖ C|NMu1ϒWO^cW9}sV-:(qO U A)f8]㵮'ʪZW7P\Nx݆J~^z*E=Հ2Z(M'!9fumվ0U e 6/ӚT2PvTR!EH]ETD}T?T&W ]tƠ fWZ{m~v]G>9ͣۍ*- է`/H5Jb}rTӐ} }`Sb4k x( }!`(o[#(mZ` մ6ZjMXb, Vժ:xRRW*55ٱO^*>6ʞU]zJb.jp$t$/Yr`:*NK&VRkiZ27uM{tyNC*ϭnw3T3+ Amn_TN]P{KVw&#^{=|$_`! K*!7[hrഔojM8ʝDr> UmPkkXLӥy@mКvp_sS X5BB`ܱܘ:CCM%QPUP^*m|4ATo_^;>A9袋]%TXpLZհ`XRM+}Rmi*iKK1k yCxkCij-`ڦR]|Ҁ?t_́Z0jE לk/9N ]Ԅ!ue~Ms܋WEۍ}]Ӝ_n =:6A5tބGu56o͓i}| N!'lq7w^sꩧ:/UVEM贫im)Y!Z4iUJ*5 y+]Rq59ӡ5i %awj@>?t՛PST>n7t[TOT(lQSlGWUgucX!zL;؂郟wt6?'uoYst*ON:)d樨%Bo\p;fN9}`VEڰ}NJK}mpa)*wVzʾ5ӚgL&9W률5i؜Ms5/ii8u:DIM🢤. 6y@6v=n/w{+^iYti}y3& rԜ3 ], 5 }`/Z}kåԽRPNkYj>ey3(9Z Rk|SkATL.᳔z XMJ=>+}=ivG6}C{bI%E2o|W\2-YY"PK? ;@jjj%^/=*}8RCE#0PVKDrקR;CɀվY-P՞~PN5SIwvmrM+w,t| V6L+}Ac[- ,.nV=6lVm>+NrվVvϯy+W9 zƶ\k|k_mhRAjl0 UJQ4Q1>Y+}O `V"PKBjiVYRai"tBݐ}Wr/' t1h]TN|Wvv͙Y%_1qu}:vءRfs)*jtC-u\_y+QB1vQOs]6 Hզ,sD}kG P Z}, }}ƲTTxMעxHM-؝`4 nv-~p̞> 2ߗkm?i:4TT*WLFS}NKMݜPCO-G9- =Ն믦5屾:c y)!PbOYLXbp@(L'**Y)G 8S2{Ptynwi/8[BE- 9SO]LI@Sᴶ2ZTH.aJ̅TwTMU5rUT@]R Zi Xe~RPl j RQA Ct4 o^R}yBnlR;kM.HeR5y|N1X gk~(u*>ׄ? 5e2񤎍)[WjVjYjS0wƸE{|;7~;NcؼU* 1X ԕPlR;Z_N*H:yЃTdei^{mW"\yoT6S4m.sAҥI ϚJHNZKH Z[jin?LY<՚xJj_U u*w^.u~sTb2ׄݧOH=c:8?ձn6^{՜xk()sbc RKAQLc 6ooP38뮛W4M5LtJ5ۓRK%*U1IX2OhoP jmԲZh>PRS~T"!gaTB=Ε6T"TfɱjUUiv<)O逖@?x TH-5.ԾuS~TԷuQoJ媦lf]U|>7x`뮻6|p\'gv"cw,9'F.X~׿˙*2. jg}PNkmɁo>0/i޻6R'!HAG:jCjgz.&k1jǫC.T1.sm-VWۅ/UV9TSP j7|v}o~3}6dW|en5{SN9es)&)ʅI}a뭷6ڽu՞X/?as9xAކROKjhw ľl >˅9e >C(A9%Tc~+lBꐵ5Q[+=6fG(_c.PPSr+oFyO~:?}sKV}SGi!uw>mݶSK4PW?i| p !/oN?u{] @+ M$.HucG=Jgy;ܡou[5~{)mV`bvSI%bj@iT!) ]ve>#<߮*䆙kM6ߕ9%tSd.QGX϶j+5lĥ^5-X4}])uq]XU]2*88\rIw+) Nsa5`ר&lN\l͇RQoucP?V7=of>[6j ~{|@~_T/ՀQFӬ/peï@6fw7R37'|rU/HST>~ϖP3oh`"]*^wmc1.hoƽޟ>ܦ/1a@%Kxq\qRZ Ƒ׿믟1pYV~E|.\ |.@b v+%$sḞ%y,=}j`T_P["Ph|Yr1 Y+)@j9p@'L?}Ouχ1T})ءl@tnb>묳/*3 % Lx[3ՔT1ET _`R><|B@Tշ-裏 5[-+$>;&&v>sMZ Ca 9_?}L{>~p@۾PHns,m`sч>Ԭhm`lCqd3>v%}_۰ 1ݨ \m;' V 4KC7 rK|†'>!a{j~o{?_ CP|= 8k*%u)8eү|IT.\Ź:C~uyWwc6؄ >`J,N'q27=/yK:uvQJlfy 䲌 L/I5RQy{ӝWh7gZPK&;oVSO=[-, O-oBė{f?߼e/e;K1 R(&r &uC&u rh)NQbΜj-_,&&\nCyL fer¢(ZK cҙֳy󯹤 ډePPVחϘ Hxlf5[gs [LU.& A2] x/}K R4nJ¤DƔ5dr}䙀:g.XJ"tz, 5ߢUsbR_ۜkX333=ӏ|#FA b|;灿9\/}KoO.,T=yԋQ"@a_-%N:i?Œr ANRV6 =nنUfU$S<Ӟ \\T.8OŌT_4&hؿ6ibܤMO_Ll#??O>%70>ߥ*dž iNLHvٷLflsO@D.jAv#|H`,UL&4'}}% y[l@jgw K>e(kPKY%K`<k>,072AI3ճ>QBbEEe0;]@Ld5bHSlB J&*U_r`Pz.tm=l )N9V29դZ!.vϢX5}0U*j׼@-B}k6b]B)$pZ>@yomg.c!\63hh|[|](NRŗc )s t%?5WD4lR} ~>4 9[e(QMVDmSƅjW}է"&E}ҙz('L sF2A5~,]Rx *i<;Jy{ll2y(׸`/x b.'?tH,K4PʪI5%QN&!׹RJi"cK>F1*Ձޙ-\4wU\{!*o)f.~ >ׄ4q& >\~hé7H .}>'?na`f1Ä OR]Ph__?pRlXS6#M -(zϽݖssK OՓ@i [pvzfO|yJi{~oev'p$Eeb8j p~z/BEwbLT"QI[W{1\ΐN$;)Fa|>_Lmr}׸ڷ;H%/R=cnOQ} u+>~w%jJԶ|m̗ E.o ֚]AZ!f0Wl slrenCIIz˝Yzj}}Jj?@1&߄smk_r, <5U&0p(f1W.՜ @))b(B#Y d޶e MOO fCD-#Pzzܵ}'](pB|b:v4>9rB2omnӽPg+>\ D1> rnQmEUzT=_X.ԘE.%盯E)8 }H؈ou{iEMK7[AՎY4)CQj =7Lj _},)y%TPK@giMi>FA>)R5S{f*S-m%˷KUs<ӥQ(( j*.uTjU ZaOcbrz3 ħX~GoS|ʕO i@(K_/viXbʑKeP&%pl?@Xx5P2~iޤQoߺ@Ik!3:fR8iQLq8 i1ܐc> $(2z^%Ek\h8qXk5گXHAt  ]Cׯ4bjaH] 2&:UV9(&ڷv];BAl!7 JG135)~w\͉ S|TCH "'4Lܬ1nvZ{ ^nl߷۵-nтԃZ0s{{A0[U\k)ilW+ي)JeُCGc|?ہ}\("^yPEuTLW AƼ ^MINɎwa;@!_kEiXLi}ͿCBE(7'a=ڈt̂9(u,֣T2˵+l17)-|j}(:15Jny:ﻨBKF=QM 0A5 7>oRפ`*~gFƂ\ .EE%L_M3/mhq ھI7Ԝ̚\)ߗ<^|w]VJQ|>sp7CY_ѭ)< |ӻu̓$|V N1>?7s!h.ZXGد-4fn8p>pTC&Ђ(G9r-iyӤ؊cr6X0tܮ&lYc,[mռ =IOY Y| j> L SG0KMph*܃Kg5Ғ1ܪ2!A|PcAQfi92VDiLb!{0B!P՘ab[̜f$T1H%i1&VfKpDjNm3\L"Wd9[EA.j-v-[|Pz<Bѭ `B=@R((ZqD`B@*V!-ګ%/iW8S4-f:-c0U\D]Z C;bµF#Hf"n#Zt[mJ9wd!` @@u>T|OSpu؎; .w&46` POYQ(wy]*@JːfQ~𝟦iQ@FX6Yx6e)(dvƱ>.^m5R(`+ <;PVҿU.g%7DMPC߆íYC;'P41hADU"`(L{D kYA҈i&dmU%f^|gS*LAI=wBAbvE,l>7eG~Y|*ɢ^i3DUӟCsJԀ;! Rlq&*clnFЀC|W+ۆ% w7|PQLL̑5;/ARڀ'BCکb) v7chhTB85Ub|F!ʜR]~hkY)(PIs}9D}&13,]t5ST]e;ˌ->B}5P y Uy TUQ[M+$cנ(C9#:8>̔l `)[iWb F (;@? UsQDL24G).wn (ܨ`) !XڭWD TP<JRD꣬}3(\1ӕlCR˘CVE-ʭU%*g )1?Tr++TY]j]>[lPI֘S >GR|sc8O{&kP3bI%Bkv5P b! #600s_W.aR4ycac/CZ.tG>Ё*jTa{{9ϕc3X,H**qlT g|HM@}7p[1 hjh,ʢH*2(9#m_Kq't0iJ(~R$吔!y1WuNi:/f>@M s]K!ES6\ᇷ})1PQ$6X*+jD?s?1'Vs. U5%$(ɘ)1%!To*y/24 081I|*(%vj &TR* B^i$@HYkJMhPs) E0Aw҅ZOYp 7۵Vwu-bf Bh`i>KBEN4)5f\_^׾K@O$ϹPmn,>5T|m躷o雗 Qu=W܄SҁRŬhB"62<(1X1oJ%17iRO6U3K> j$$=5oŗ(f =;xU}Qȴ򛴿51 U'H萬޸Ri(ҹm5Uc]̿5&z >+Cb -tbUbBݏR&^,pDU0Teê}$#rQ\~lj$uH]` " j $˯fJvp N-q5aoVq7q5 \>koaR}ΉͨjbR}(tB NCeC9(4X;W<'yP7h^~r*O8PPaއHs-RVJIjj)W OS-<Ud4o>$daM.gQJƀO:|&P7 g>O~r)Sm4%e˗+TOԘ}d,ֲ[B~5w,QRP<#*JvT/{T/) rZY \dc!huB3!["qMa]~ ^uU)+7gӘ>cȵ8fׂf*Kc|++\) *jYu|COWP5,3)A|HBHD'il5d)ٗ-@',i' kw(qMn7暔}4*b9`w1v( rR̟GgH)Gfܐi9D=DS1\v7K>UZ*=`#ކ TT4L*)uW$/JG͗FI`խbQ!5)&i;Ho> ] i\i4 5M*jU K@F"=ـC|XQ_MםBSxS\ps)EƤX-d>pqu HF[&.r2M>v'{ 9pk -V>ICa\r%;Uɹ\SÌ-p5!e95JXm$n f9L`JX״.x5E?=Q&r(i7)D2iZL?r-c *\ L}ŮV%ۃ4WT-%W4yHm3_əhBAH>|V&AR'oYTT^j^ظys\r:I9Q|;)Q,>XU'%J>#YT_ʐZPlj X>|#iеky_QȌy+`òlr$y]5 @aUkp}w}~(RTKS5wJ)E"XDZi{1@ծBbIUbQ!ئBI3t\Jƹx}ܗhw:*9(S5 hOd.W i]TX&r*hnw >Sb9y(ye@+0>6IJuC){lCn1k%peRr\9J3껡C 'omAۍTB0v-\A(z $dZ:1 J K_j4]fdCN'e/{ټo9e?;*1e_˱oh559JXV1 $ZJ{ISL] m^0}߮ΕPxF +WdX6P}yC5-SАI@cb&SR4$"Γ]|,Z|h-$.3؋hc_!E6Ngu4 $ —myp>2XG0E>dW.|"M$&]ExhMADȏc}j_h!P Mʥh!p FyPT7 k5}h`׀њl&.^}>|!Sݿ49%C 7MJB#}<R.kAha|z5s3sm{챝i?TƗN*V:vb'ES6qnf gƔN;j$c1Pį1çrJb+kWi ۓF7˥ 2\ibL'13vu)w*VgC1E;dE1^-4)bI]I/2%d\?!7yZuJ*R|)4SP>]mN5Ic%m5%3tE֪-|G#HJSbچ-oj!u:%堯@ }6iPdcѺLgHݏ񒝁x -c%<.[hjw*R9:+TmTPꤦ$54WkL9AjjUPoF̏ҵZTTH!v>Bi[U{| |9\~;Cf[QOļojr`Aޒhƕz(@Ukw].5ӈf|):餓ϟRs3gIן+KO-F@c~ÐҞ?m $)oARsC>'lPRPGEfUj,/%[o@-7Y~PM޵_Ϗ4}ϙF-NSi0ꩦxB4\>ISTca e]y5|9RCjR @L ZMTzTX1 OxBTSlih<=  r-CH*XXZ=mw&R} 8la< }>JSOeC j$M " :J d 5^WLUPc&>c obҨ!T"Zj\Bi((`05C}׀R|fD4EAnPkP4WW2" |S!?dJX 3V\~$7r)B Т/{?1o eҿb.גK˪b[xDUv5$XR}nb>wFBo` OCj̍&14.CCy؍C9y{)vVLI3Kz+^<kLPovT0Kv)j}ꩧzd)Gi⯡jJM VB)ZT#t&K"7u+JL*($t N쫸hi: %w\RS$( 4@[pDX?[oT$y?Տ9T6W߂&YPVӸ/S:ع]/|@m7j~6&t3+pXPQQK6m SvZL5v2ǏPX*:B!NcV.K9.[8<7} sp"6bs!L6F0}Q-I#}ޛc0;eG=k$XgPs#drE+Z6~6$wO5 a %Z*`c,q\hR(|r$g HP[EҮZ*T+VNm^0@~`*G!bnHm*&ʀ/p9µ̱܊a!j45ˬ()_JpsG)2f1T>BlfI%'IOUK)rп$gAD hb+~5s[5 Qz NBRM5)1Ǖ̜S]pj/mڷRzϻL}x թ4s "Ul,TZ(:*<%ݷIQOGm\C&J[q9k6pF}bO5 eRd_Wx5˹q?G=9VY@ZdjoL.4*VHMf!,7HJ',?]"6Ź|l2P}LjQӼ(lL2ٻX)ŭ }8f*s$%6e3!%1.ۺQO[v ߌk)`D *Ӝඖz:S:Ǘg馛:@J>E ?(4k J2HWӍvږeHs7¥~bDDk~hA#4(afjjug7޸93~jW>`KqTfI"RQ@Ckಣe  =h\+ݬYh% ;N]}w>0u{XM7ݴYGp!M,R &,izI/T p,i5U 5Qj,A+NS6./}K>vޏ4%/Xʏ4D˾cͼOJ˹W I ((1S\}ʉVM5A6e)NSF\4BA,!eU ꊨ U2!dծ $MUz}bfC M[13\KSԕG#)pַs3 eVpE/ѾKID,iS13vOM;*ZSƍ;zϤ2hb6~_s C'JFmAlRPC>fQkך|\6B{#4A[ տ|{J%Q;m&Xg?+ Bc{:8 %js&DM37+M)EcH8/Y|vH~Ε=ιItf7s#( z"\Ǭ|k0p@&*jrxmPZ$G-;[RQ?OT#9pUЊӧ1b3aLQj1ϴ\N?NBjTSS DDB& UׂFji}Rnj9Wj4s"vT"[I0qKrEk7s&(0[𸢼Z?\P5iwmvϣJ3+fa:ZSc\lEuQ +[h5BJ@q׼%//ڴL9Dr4TM_X)Ԑz!,M  *HS|l.bI}IjnwKAj|_tNDߒЕgru) (Z/| cU/\0SShr%7hJKZPRKuj@ )fWҘnm^|S]6ckAD>QˉQgG!/b||?Ctp!s>?QjG7~U0Ak8RTfNe%H׼y3eaEҝqYS[;&VOLw>Tj_3*p©VTKT8RBy]*w+&h S:uJ<l?2kjp.$[~Sfg;] r\Ud3x*v1u[-uTHqMx>eJE4&VTM^\i?y3b<6jXI |H&:؁QL4TLK10ur 9uDi|nGс~͕?qsC gL5ckǍ[ G>u e]=5&46arqM6i8@9zCI?r~K5%ʰ/Bhb;HJOBȜ?*Yrg5\pA -P|~PKv UUMU` IjL)jj1A>BJImM -r1"mu ͎UB5|I=lTMĿOE /4 WoS=~5ͣ!8>߷آʾo1Alc$G=ŬoM$!skJoC V]X9)&~W_aw:77PR &|vیc[t|Y^n׵?;%Jc􌤧c /bB}| zK_WL}A۶hucv_aE)f~WTk%X-ع`&~Ȇ&&\X(qT&suP_:%_Tajc3`×_èT;0g2Xӓh*{곟,1Lsc]iȼ'Z>~*Yji6@͵uA[(3\&Bc}y]CMTSba(v\j#.K@4]n~;1+*;bopq pB3_5QeCv[u>.zd~"cǂ\ j>R~5׾k|I*|!wH`h.RUT$_Rk?k5ѻW*dӬ>=.4T[|eBQ*T>rwM1u<nj ۄP-3=?s:d% x)&&ۯїxu]\j*UF4 M _/idAJQj%&w)xҺ'M$$79YT!7 [ )^c^Vc-ׅG@S _~3E?QD)h57RTCUT:@maI, <2GqbBRx};љw.*96A4㟽2 $>҇`OQ %a4\+!soHEb!7ސiԧ[$)NNգ498c=@S9 S|+$dޏO*5hL}[qL`guV [; f.`H`եLҗJ3\bp3Tߵ mxbvvr7r*!eEQEbxROdc2f?6qMARh>jr̛jKfkD9E1\PK=`wL϶}ZRĂ\ROs&Iw_Ib)Y0]]U lf}%9ZI/\׿ySjr>iKݐL?.?@ rIl_}\>7i?ʥЀ^c'r}ue''*^5{^-oyYvac1hN8wX̜c5)ٱbO|u5sϭ^6Te5.>dQaWC}Np)2pp>(K:CȂc,\Go=|5>ؿo )ٶ:  4O#y_Nt۴ݺ6n&b>}NmL|SXV\)ߕs>"!},dIBAU4?q*@ Wющc8e,(Kc%;v^4]P1ϮUb>%O38D0o*/a5ay "I;> ԧdvXz"ZR(lQc.s)q~5$Q{j.<6_SDJӟh J !+Uc3׺΁563ڕjew<ee>:;-b2l\,~OVCe(\|Y&qTacTof5=k8or P QVW[8ݦ6s5Mf5D#=v- Y<|Rܞ'ui7W]uj19It\CvRW k Ċ$}JLq  .ouR'٧U8L#TQ$SV3kW_bs4@bz޶(&҈X4I"bVc-V:&ݎشu}k)(:o 2M mf1Ț }TB<ʶσ*AP( S\j $h}I]JKL>SXƑ\r[r\5`\|4pj:o>;~qif'=gH>ڙ'TS﹛'Qi;kwnfSp1©4gV@*Y"4r ( R8n%ʎr =3kM%EUE%PPhM6&t3/o';hwqn7ӥK6C#RQq)0ؤѐ5fӂ˧МD6ITTA ^@z?1_yQ*D̏9Pe* &cD]M4},Vh3Sh_DwIT fk>1W5/O?q|%\һ\$T'`-ַE%{ UM?P_q-:B:QSۙf_w}$swo)DI}qRMloqc]Zڅ}>SϞ Zjxa`-)r!ՕNOT\`L(UJ+S/^ 1o"cL J@%Kr_τk1>Ҁ|͟OGoTe:dҀm(>5[qY=*~@oTYۇo_؁ JXG@j[e dg>Ӂ*h,&4)k@@/R@A̮tK3AP])3)|S CKּ ?W߀*>aSOck)}#ևlO@Wrk,on>)PhE=RFZ)\[߼, @|P59ַ9;#OP)+ X"ܟguEU\PVs\r`5v?T5P%z+(p-iLМ\S7+ʧ? +NNט1cT_*"QNw22p>ߒmZw d*Z=`Qߚ?$4ЗrRWo{?Wİ=IT` c4`!f[Pkپk>fOb=.J#0Ӯ>3\j4})m}:\_wrJSI%L 5U/)6|Cp[T\q|&~\( Nx_ީ!¡ RK3QRJ:ɗ _W>ݾ/0N i*H.Sa#$eڏ}<Ỳ6t)L駊9 o ))qι %كcöD0 :Fi=VϤgҦ; !'LIE *TT!L93|L=BsM1T&JPFRg( J;HP©﷏c$,@.j&'AQA3~b' Al\)qX| uL81N%HWUb6rKoe'5,j@MWYV L(ōo%d1V\@2=k.\>M@ }oz>l|^5%h ?kF l(l`Gx|>4 TDLsn?,}P=A4fߐЬ@dXI/c|Gvy54g( .PDSGQ<}ᨣyKDʴk1ZPrL:)S(S}KAob?id!!tp%RE>ܧඇ U3QS22krVJQP>A$QQs@*կ7cw] KAuݧ%j ~s/OU@S+ƢST*a1mx] LaSm([ Dh2Sͨ ע$ڠJAU|5cҎA6B||S=%(_3C~Ѩs/E\v@ ~mC9)ŏo_zTPc=&M?]w@^7&9NdIP=\!ꩯSCc}\>\nEwPp1-?)K^6qJc~U1([,>>](/S18Bտ}B&R&h|Tϖ/W<6f>us 4R¤ }al!`NSƦ*5co&Y&8>yX( ۏo)ߔ0E=qsusfU99|.dP5RM2&o*o)́7%ϛossjP0#jf#xTQvu ]kN,ւ WX>Px-Rr,K>;hcyk =mN5+I姭޺[`%$B5*6(½PPM\̡GX!:egɒ&.FeU2! ?20v߿T31iTjKyS -vj/<_p2t $U-E4gU""?T @cJ|.T؀ƹ6K?Kjj󽤒jNۯR8Tp v <n!ߧ/&[; Q7W eY}qsԆS0v\g4przRB9@CrdLƜw'9Pfqg_yIB\ᶂ*~8oi3"J88&6=C@U{x~D?Fkۏ̾J߱z)#IJM; eU bې-f0}vAd$5.UFDrv,Uc䇒;w>%mW_=vLd>p='u971| dtV)U]Y-N/?f+*9^{N R9Wvy;~m+;gy@cl0Q9z+פ̕,Y\rc]18!MHCۀ{gC︱梈kAFZX@ X0.Jׅ~k^l޿tN3ه<1E9nbڧH8Ed$iO{|*>o~l*@'\hX%ldLNԱL3WOTiԧ3K |-1gqkek@~; \IBMk_`pZPLDGuTבE*oSRMpjN"Z@ IMrљ10 TO;5ZfuTעT~(%*N?6|+4?B6{3PLԜ$P]1I=QTH̙>#E@}\|~lbLI8&22 U 4 0abd5M;hZ*co}( MW*y|>gr}ǹ(G,{\;m-AQ8R$TRpJJMoj2S\H8űȚWCJ0.I ))3'6|81\IX߆USv8]+`嫹1M=4\(9׼ZsUcјvRTքԐ7`ɧ &t11e ՈƅäIt>0fzoh!U (EH\ ,J/j2$b U%;=|Ν座tUj/ 1 #ӥCuvRv~{oҮn?'}RTS3(j}~d#c[11'KN/nڎnᔄ~=7TR](oܚҗF|>&f"r!e6EEu] }J13GRVc2Hf>nNaY@]u-ߥKE$eXX)'Y:@ j@VSMHe`‹S9gSkK񅢱j}7Q #So;#::~nZ:"j}Z H]Ds1^ѧ0i̡k>K\ %BZcZ@i@MHy8IŌ- @5UPH1w(_)i1Nbbvn(84|Tqa\-@?l)υ HT"P*E1]ߥiA,H-h3Q M*DZ@h>چk _BV& l`N;ү'YJTRsn +Scߎuk ױ h.1P^ondlN8bTHo򉚺0/nču&7o([p1Nqj 9\fF6D c ,iYɾ3۟5ȼ۵nsPb(Q8SM%DuZaS ERO T"h2b[\߁6jhbE$_"ɇK6A6U/L_^4|dP ڷ_PNsA@V}2ul5i'#%>W.rK贛 n(nk!%u ^>o .55OżYV n.7lۭ[ ]Z w沋.HO)`:tg*Ls.jX>z.D3U@"7DKZUP5]M?`4cf6>S6m0VnP_vV[55yRhd >YJǤu]^jrmY$: @ R#HKgɅ̡R@QBu))PHe_VqE1Fʢ1X]N܅OZj +3OC3ل9nn ’9tIYlylMj7Oϯz|II?K%um2vpR[$ >pJU_1M4yJe';`Ώ}cݱfƱjH]2 @:4+ } JzԺcPMǒ PKj_ yX/X%4:ܲh7Si%u \訦'x:ݴYz.H׭]hG]> ӔsP{3)'$%Te8=3a&8B&]=mQ)YesCPƀ0@bNrNK=-9rj4!GOUBLC*Z8h}J,xVR}Լo+V0<z-|njt]>la뗵pWk/~sU!V:IjO*L/5liT6Uj/D;/wd. cJ> XU}Қ];EQ"kqM T%5U.^_}WM){wz{㺮jޥ~Zj|J[}ؤ1SUv*!Xܒ̏e|ļ϶Yy$'f|/YEp!4J"N %t;>HEDtPg'm'NC6˓=!Cs?ds.Z.wυ21M L } ;XjK D}߫jHT ҇Ƭ>Ԩ(MUs u* +J ݒǹ\kC5(VAmL/jF 琊/| SXT*jjiJѾ`%BRYB0 IY3>`VPcV-.RՐh9ܚhHHb'b?`00!pI0f c#,1LqQY$ !! ]뛯5}gzV{W׿aZM_4 CJtJ;WUs}i0 i{[{.CZқ#$fR $TJ|HSIg;uM^67YP懏Il蜺Nqc{N}$,'%>?6}T_$5$5ɘrGNk!eu%i%Y˚')M2gIQZYjeURVbCK *TkJH L+i"5EOKC&ylǣ q:[ ?pAUɥYVٖ]?.h!Yư]ȩO՟z> J2\_pIu:]i-I4NRˏZS$*jTkܴLA]JFa#j{@PZa1R؎60Y34Y k[^q*qfHv-~S)_W"ot|%}ҤkK2m:W/UG2񍙢w%q~W!q:tq=)kDZZI|I}V_EKqc51`=$%8!m4VhP }m7BZ> :|p)ȒAv,4>'4D5}ez:yW?P7NI/aP3_ j=WQu(jjb9_ Mb)=\\J +#tڈ/$mfz!Y|ؖz.tVz Frr\Vi,DʇB=&{ߤ4kDF!WX-g֕#{6i jj!ON4GxT@PBվ~tFµV #F绺;,m;ȩ7 j87BGNo6{ҥ7&FO8_#ǍOb`F~=ؤ>q02L/#ͤe ,C4#2C:o+jOy|`$%kH4 - 6[$)mRp/&ZWrZ<&,t̙r6Qϊ'tbkU=".ĸ1 !x~2O6Gf]ȩ($y\u/WrR9iz- Z-zu4s}Qh{c֡5]> $adaFQS e}=R Tz=2=P) jRrҾxmp6/9ڭ__=Ose:iرB>!9s8x=MW+RY'ެӬUϧn[bQk^vw-&%Yo5 HlԙV DWN)*Z)QR==WS- or &'5J"9餓ā(>`'6rJyӦ&#!7锿V_y<E(tZf#EyY;@R.Շ,)qI2/ n\< JH(Hjʴ]."X䴷_!YVLNԤ$ՇT܋DSNN9,g4$5-9NKjAOFCʛԷ-^^ecd5<6|a-T,0 RScoSHNAL{˿^jBNm ODr(!EP} u{bͧni9ja_w!qԗbyIf#O4y.a7"՗yӬDt9ŗObZD "yRWrL4{z1JDvI0aL/6m$#KuTS'(Y_/uʐ"YH˵Iǎ;(bU4)@$c#uY-u$tVnc P$%ijBz$𙊨Qt-]z2t?pQ2 "N11Au!k]Qtƫ*8qc å_)ջ5{ȼ RS TXxTGJMa>k?CR2ȃ!yZc"!)LS 1۶nR c GzJӞ@*TP,#;i_(<jY5q3elMҋ믿^?uܿ˘"\_hQj)!MYȉϥ`&PLDI_p$.ǧ$HvM6DZv-ԭb˖-ȑ sZ&vm&._R_!2jUKhh,e5Cik倊#IQ]{=qgԥf,#w[xSu?+9B o'M=LiiùzIDǤ9ݒ.׃ D5:Yl(#u B R@"#zz{2Aݺu|WHAZ}J} сn*3)_THWğRqT6*\KRZb#H:bD"yS-ԡ-Oޘ=y2}wĆ b~bo̺?koDC4V:6aX5FvWحsj#AQ#Ř1cŘnI8$uVյMc)TВcLnx7NVCԂKFO|ӧ>ʕ+ڥKD߉}:n!x\7+9BMZ*r)OXOR"I6PɃ5\gٞ;4%|D(H蟂ﻶuDPKD2$c;HV)Hd]@tHS*%C&Q,Y" "-o??EqYP|uww}A{K'tI !A]`A"R&ERrZCV&`&y*IC@Ɏ &i4zKև[),K]2D$xO鈊?P* XVefIV jO*5e5T˄IG)>KbѣcuqFHNΝ+z!K/SˑO7AΖ$goV_㶼`m:yCԣMyf2ZwKZy-,e~hR01jh1FD$5X1%},KPouƍb b0*_r!3جM3kz>m$cQ)vI g l cƊ&Nw"ƍNJXC|,Ò3=HS7 u䶱LRK:rpG[S8i͊+Yg%.bԸqy*T ~)I={'믿>hBӈui_~ߊcB{'}ddi'6J@x1󣢞3I%y g>I>-HOۂmJFS M-^iDv wR^&A$)!CB>>]+6_/a\br{7O|Zbڴib3O&]_IN4Y& "yCH;@8^% FO}G1yd1}tVH{I-۶k>$~Pa}8j_&TD\HmٲYnRr-]IUIc45 AP]NٮqGMcQLiqG okVڤE*D bz%H0~YFVɩmI_V! 1m'h7+; \zp^{{Z5_ XGt(._BZñ~KEqH~!ߦe*:nTḁpɿ.QA)ErZBmpF +Mf:_v./MA^&N?tiZJLiQ]w$i:gZ{ p3 e2Zhie(/c/Hc %-c-HڮSKO4ØlDbd4=@ %s̑ rKi&u(5j6}}4Ge\ZwBb]\;GE--2 1tR+Er '}J+s2?!Y_1MX嗵(&a#If)Xs"'0} X6PCGǍ',YR9$QuªU2[2JVF$ ~aH.cRb6b@K.oj02>Fȴ[J0j$eZCjOBV g1ERuFHm4MWQ 0a X=8q# >p T"  Ҫ\n\ecǕIvr)29EJ>TA"u#R\L*,Ǭ2t{2OrU܋I Õ`0T+UPV%y@B |Uom6|\;n9j$2dmKuRLPuIr \R՞͔-(%3 IkZHBpY&w]r,1qc С X~UHKᄏFzdx"=8G)XY?Oet=AXA`APA>qj ]]C__% 4RMф5 IUb֥!IMmRԴ/ae$2 )0z)gL<C>JPa]Bqc)[gtg6IIA`k瞫ނ ŖB!!QoӮ2)ڇ . 5A.ۺeT Y?I5'AuOBRu2.hڼuDUBZ0)-4"%e2( 9P"HU6m$XI-[MDה|0(_F[U%h 5x*p!%E=ejMK(o+V,o%R?QozGz}5Ye$5J:}FJBMԗ랤RԬD5I> F3[F:tttHI@`6[>7~qH~NArX%h+z{M.@x Q>$]bmB" H2 M+&">H ?IMeM;]qROr%K Fm`!6`|E\6ui,ThFuSW' %f qN"FLR]?,W ]SD#h\o DZmk]ݡEcTSTW%x6д4aK""U;2 H͜9Hd JWq RRC}$HF$!L!)O?]ڈ0eaRJuy?Æ D$S"X)-qԐ .$tM4UGDJR.Qu%Iȩꓜ2qe0.@\ ay:7.20~QG?0gҴEJmw;=Q i)&ꥴ^Tun-$ t$0u!YU]L1<_~}[Lzza er`0\TEcǎm(,GJQҚ&" IlUmM%mkOD|2>8~w E`#R U^nJuq\u䦩SRD™Fjʟ)us:Xʟ3gΠDh;z>m@ҴL x@۫]@8;XJ@^S[˄umbxHLq]UĨni b'}b!HPuD3uItVu4Y|"1˒}Rr`0}Sʤ/O]3eʔ7)$ AD9 m "ATmimK hKKI]U m`X!IPkATudyER}"i 3zQXrmݴ$ l3zƍAw (HOC/'J--Š%}+Q9OƎÆ$r΅0&&jS՗ZTĔ)\L8(3$[:ԹP"RH3*.oG)zǵ3AS_}a+stP" K%iOOIe6𦁱֋VF',9eQu!6bxVrf!LNKNsGeJgvI_&tPSZXruz!-S:qP&R z] ReKWeNPDJDtdT )Hr-I]޾p?<+8jMbbԩ2AJTӒ8*5A7Ĕ`^p=seJ}NK /~< w\%‘TNt䵳SA_}a+st]DbŊ޻Rr*%}J.FNNA$)8 j4 pcU;N' cԤD5-YVAs^d1er`4/@ֆ/,Isqae KÆI„P_VaM\#$quU$._\|J֣7KJ+K j[ $TZ_ᇒΕЏ+ TQmĂB5$ؐ Օ|"&brMy& # 6y 3i#dFN~PqyX+(|"2@R'O,1@w O5=i\e1 1!Jd ׭ryZ/G?Xh衇ʥjB֭@~PA!=5oU>ZᷴpZ+O]a4)}]j~aJA:*6%$,QBՕx0$^K" 9i }M7VItD(0z73pcy~-#Xi16B>z,.z|3DQXS׾ZftPDB9>TPR@5 ƴT&XKA-r[Ϙ1Cq&CijZT26?Z=Nܻ{ȶ1O\.r&һ{[Fu}{\XjqTV$e~!KɁ? @\^-kATW1 /LP8ҒUi  І J_M]wU@A@lT齭ܓCkS$8yG>(tMN 7xWaFm =EH|P,9H(\oQVwʯ1SPiZn^i47ANQW%%oP%LPY-DXK!Hk(`̙e{Y=8uzoz°=. 4zۀ4)]a-./MR7Nڰ(XzU+#?{'TQM#U݈Byiނ(ң?~<Tak&LH F-SGǸtg2#7LXZ?3=K>uդ f1 ]$w10?A4 T-c~h7Xt\QGTFjRWT$`$86C"W_hJ>+Kyʍ \Ddw0IG]vuiä:Ie7SUJ1 ]-6aPzMtpA- |\aR!ROInAĦbCy[DkwAM\%90IY2A۪K#9& `/G]8.ªD6mwyVI; ,Qo1 7Z0H\$BII-#ÉI z"d30XQm-n6*nZ*~NOT$aS}~gLP ckֈGy$S:qє@aՅC= A^ɩ+ȯZÇW%t-Ze*kߐw2 ÊETnEjzk)|H>> b/H%[R#)]A2!VYo)RրWL18'}~="__]*iqP*A]Hlul P+bEy -!& qK=.\_l$U&y tVSNJa)tj[[r^ZC"ֹLP[ 52 qq(jSH%)> 5U]o}*I$4Q5n/. Oj箪t@3آRH@98JFa`0(I7SHװ= u#06&2%1F ҤOp * qCJ)m_J`{E@uFaY@5Q ~?y f0 m*)НR)ƍ{-^u|;Y˃vXMPzʣqu'\&B8aUbq$PHM!OI4Ae *or7k7`0Vآ(IHl)-[&> ifw߫ҭETLfS%DPҙ% *҅D~mQ"2O)Em.``0Dtp2vQ} uߪ.0z6G):Hj YH1!`0>Ïr HQkd ]QR1*9U)>(FiLjn2 iEލ]v%> æ#?RUiA$v؁f& *hf7${g$,T񣤔t@ALy>SxT@:& ɰ`C`0V&BS wm7Hロ#@ &H>і :?+)59f+99b0Ae0 F7e}z7Ikć~(VZ+IE^0@IDlA%*R8;SL`0MR]|@9} *j QzSXOnph#k}98qLP `4`nI[  DJq$yZ*%t :A|g̘`0 F3n_*)+I9KSlQN k]xqɽ+@AB22 h&& zK.R<5wߝi`7S `0bڴiL & `0Fpj̙҈gQիWs^g0 QW$L HSaȴ;J+u։͛7g ^o65& `0N.Ӧ>)4i RO5Lr-]tՋdgmD`0 EdqΧ=g:~M7o;Qs5A3GLuw!HZE LP LrO= l/ۢDRr,5㐢[Jt/q@j2kcY]Qc>T`$"UI^Y/E\Ħ&Yw$iNF&{ww$YgߦsjD%6v^`0hRP"qdGq裏iaNW# IP/rqaiϝwyns9%Kk(K.D1Bns0nI_Bh4$/[RCRauP/_>H??t3N$OR>'t曫|5kֈ[o57=Uz»;HJ6Oc=V~P'?<1D#2k$ʤZ$&mmmrlgUݨ%Š_G̙#Ə?8>O$KQaH⥗^ڰqF4S 09> NbkA FZFs"UwGAh՟---$E~mJ7S9;]qLuQV4K0rIUAK:*>/hVK²e˜c) VlZ1 ?T|ʬ*ky?AiTHKD@ B2N8z 3@B4߬yAEO˻>X p6`0C"RˀT4x jN^]WpBrJ<`<+/2"gb$ `"i_X׃03j1s 9}F@Q=W/%. Pk*ܯlRe`0XH"LcyO~7s 73  :`0 FR2i{~|MDŽ * V@guVM)H&"i%1h'dXEEb0 rk)T#M1A,A50x7oزe:ujn!o^G?QA4W^Yu{AfϞ-B%jw ͂p9`0Z; =6l DF}{ *'9ȖpdSO=%-҉p|AtFE y>6PI# *9@ݣiVpu"-  oiXIf/|(]Ço b˃Գ4W/J@@t@sXjV:8EO,kDH,XGӥРAkT]]~}C`4@@A*(0DHRm6yjD.V1czZԀ2  )\{{{T@28*RI0o-ȩZ|DR_:duP!e%rI)`)-۪NߪSҼ,A %H`0w"f֬'c&n\Gާ#1%-T&LP3iuJ Q[HZ,֫,A &5>t?:TI}>!oƾa`0×kz$N3umTd%,:-Kl4"ESu-izRzYm9$.uJ*ZS[_j *`0j "]]]^'g:#dD% 餮p67o֞=UGp_.~N 1G::DmuRAN$vYpQ; 2Ae0 F%-{_iƥeF<\>IBjm:r',>Q1ƣmRT& 1l/Rv&IBd+t/I}!M{hS##/,W<gLP9>MO?],{98oi{̋y:3W_]?ѕ~W'MTu]\?xy}ݗ'(jFmۀ6u]W_}]1MF$ܘ#iz!QiI)$u^'!|5dJ'/ҎF }QTjYJpUu⤓Nw%5ktVB~@p=zW9E2zy!m䕅Μ9SzbA@\~B`؞5/#?rJF<$0ʅ,6JLղ^qҖUG:]IMZ j\ߤ!;,gFɻG0AGuHRIܼydS.)E' uҁSOxaq ~~c>#zgf`F 4QGT]u;mVK[@~6eq'Gn ,'tigCi" N3~jwЩ}oOV(?֓O?s˪Ǐ=XqT>h1rHpJX-zZ_lxƛFb_+RDs=WL6MlٲE.c}G@zSN9ECmOHsYgI륗^*/r˼A]%zܔqիW[ ;uA<~M7>=C+` Gbl4)ȠZF6JSĘPӕG-oO(G|l'k~C9sL"69U}>Qx]@+5(#zi,\8lg^x1Ki&cz ,ꤍ6R? dfM 6]x$UڳT/id*.4$bS*QZBB7w\?zh=pz+hnI^Y&F|饗 &1)z{ߓ/_B{Iv/~!E|e@/H (3>BzJ>I7|GyjT q`#]&CސyՕG-o*o p i# 1DaXҽ踢66zmQ'Ʊ UDZ sCZmRS;5\+TU չk /\Ou4|KE ub*%O\^XN2 ѥ.?Ou%9`tnMi"MJqQ1$i '# :+UVyQ@! }LjNR}4Kx[ti,۝%T>e\ P(L Q/t|vhä/51Aن7ʿ^yUH\\ydҸ6A~ wygqek;/)ZgStb> 5uIw=>h=y+[:$[&YJ%.z>Pk&k/WB2Z&1bJ ,'?/h9OEz.N,4E뮻N7<ܑ>Hjr RqLR2lP]%zlԎtP|vmb!EH?j111{y!janݺ|>&}wRa]Ѧg(sۥQJ倾&#Vk? 4y&p%vIC6-aL^KT=[^4! H%4*)ƌKa,ђ -'f\v/ִ:m 2y(! zy pAE"6)Ҵ=AP@I1 wfHwyZgH#A,Ue#Z҆ -bLʉ1 *? ):cL4v0r!i2$I=W,h1=yH/5ndYoӻL{ε H}^h7E.iS8.ZBdtk)6quCCR%2H1r5j(EHR*`A uqU@Y!zĥY%x;KgHR0!0XdԷmYN"IFQуt$QZԝd GDrJI pT>`rv$@nL塺'StHHm`0@* f\~Z"y7Dیƈ g?P?6@^r}?= _Ь#uq>Tm(}2 61NzrfN8) *TY+u`  2iۧ<0q$ /$]u+o۪cd⢋.r /Oֺz=`@ ``d[x!|t 7j(J}0Q'+pzSxh`G:y7"hhuu5)H Aj"Z>{>+!-uܠM cj~d-=˶q `L"E͈0y[uCQw/ 9Cʍ"o$ ΂?i:6Ҙ$}q.L]fJG(]$Ϫw(^埕iD^c% sR H"WXaUupu*TC:vuz_8.bG^s'ڦ2`"> \K "-@ƒc0ե>K~N /y l93 -i>ɩM "c>H :FWne)'*DZOu /tMҏ(CjiXZ8$J2jɭA2Y~r(/`Te! F#8M\B*T: !#郠;bCϔDJ[$7Amf:T jBo RT@Xڍ@ p QcQ_DK\t H.KTqHk宪\O-}TľhK F/a@u~JެR<%~..WpNM2OU"|v= Q,m 2lMuU)T[[8B^k+ky6FKrLP |}Gg8,_NQi3ev]~Dr0 /frҸm՘iCT`Cei3Ԏ#GVna `0|y`0 `0j & `0 & `0 c=Ľ+xʆ}wG-~߈ .;dv]in/b:f?6).Z1k6iʃz"ϸ^MQ%_Q@č429'7"Qyc͆fLP I'M$OD{ 1'|g}*W]u pA8Fl(?5-Y}&K*7AM7Jd^RHGm~j])}*Yi¤TCmDݨ<Ԗj~96]$Ph4QS[Qp=78k_;:ΐn Kږgꠎ?Sˬ_6~V m;:us lb?:7Ik͸y \0ܣk?u\Ϗ݉hvtX9ACmmCmAi-7:muF>cz7ht_%NƬzZ76ۼPh91մin0FԸgW7g#v+t$sV ?}{sP^fmݤ [o%~{y~ѢEr? Hoo*qItnA:SLبQ}'z!yLr@oq*4|'VSU@۪u]pa5R=OA~J]W?oh5\mK\Om]Zkqs狃>XƸ;˿/+u1cԅR,sj']RY}+π 剹Ҏ.iv-E]Tṳ_J:X|J]\#cq]vmǤZ6k73:4{\z;>@{o1W駫uP[۸Un;&@|1>F(ja`G=zu|R]0a=Opyj+H}]WvSns*7DUFqPhi"m_W4,^|ih޸Ct"$^{I=L8%)ȏ%L~D '2oUFY?\z/π, u.g;b.X`}ꩧjI+Je,=䓕=ʇ}CϤo SN92֓ L} 8UsN} r>HGP}> {饗7LemҨ xqlj+$9ӟTJ>1!_$I۴i5k$Y\D^hE|gUodGRz>/|aCRt* h32~,\r{.X e2C |``qOFx@J땱" Z@h|=ocR1?ꑦAN9rK{h]wu9׵ )Ar:\1Yc_*O˩t//ÍaҥrRd}y I/i&_JX HA4mn7n&ʀe3|*/tu)׹ ,crVaÆK ),}Ot?YZ` m31v{(mzm{PS&ڎqV6J[;]<'Y7ۼMkJ#z \;J6&'u1dk X;bt+\pѶ+QcӘyP˘tNֵ{Ԉl_}$ud3$6[!CBB>/Pͫ'Xv> tPHXҶZR֕,2Muu1C|QVMTtLSh?DḮߢFKR75hD-3CպfzBң-u?:~ uT?MlcEO.k׮24MNtu99z.n~4eg4׵ot0XZ|*b/+T7usfse蹬.Hly7jL 硏 F}DSTsXsOmctVu#h *DP&'nםp 7RP=`0 QlF ~:4- 2.j| RtZv$͛MEP SEd'Tt*"OMh9= 򩨋:D5L4uMtTQLe~o4F7x7ފJJQ GE;k'.G\`H (T*[/8#_Hy*SA>T7SO'NԦi -Iyr!Uõ?P )EPO )Hb# TnlaVu -#zmqoTvנ~WqgWʁاZچT4 `A}wgyFq/ZH]jbyׁR7xcl A./"6 k.S5˿7tӠVXQ!upӟ-xbChKx  .4CQ 0yՇ~x`0Sw1cF<}I#HǍWB jZ"0m4cCp 5m`02*ϊ3Oe'x ?j[ϗi0Ae0 Pڵk'|"N*=XIƎ+{oBo9rزe0a5k rxꩧ+VsDŽ_|sHk2/3td$F+opG%ns9z8"[^x,)Sd;z衕D@QzS& `0Ra̙/HB R,[*,9@Ub/Wv{$Qa.ʣs=W>}3~xq뭷ᾹsK$#Ҹ~zq7KJo< $$ gϖW_}$HO=馛9HPӦMV^z$~]wb|1Ae0 D7lPE^!#I"$s:* 9U=DظqUҨDtogg,)Hի岸}|[j_re]mT2NJex : ZvԉZ/uB `4-@>AN! QG5HK QtKZ,Xx)K/RFҥKVM2ptIEnmtkz!y K$-Z$~|M d/IOpBE*`o@aW:;;t??" `0\i۸q*;K֭d~p'Hz i2E,K;K2 ) tK */3 )~ᆒ2j6b0 QS09e0Ae0 `0Ae0 `02 `02 `0LP `0LP `0Z 0Mx[IENDB`ninja-ide-2.3/ninja_ide/img/split-horizontal.png000066400000000000000000000066631216641277400217650ustar00rootroot00000000000000PNG  IHDR szz OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-bKGD pHYs  tIME _OIDATXNWs_l0D %. @YED}>I7]`VUD+ V7Ppb3 AE?>ryf?4=EMUI *J)Fwg{c`B{}Q.tO'SO8RG_~~'nN{ވW3O DeP5g].rq:R6lX$@Yxg8Ar[[!cvv VyAZԆ^}!:::0 #~(AT H^k @?PUE(_1D \}R j5R@AAc D`Mƙp%*ր 1@JQTAp Xn`ǡ{89W_^&ht1:??<9Q Ǐ}l|彟B_qHi)jENww?5Q{~:-XY#> !fwr+"i>Fmޟo߽t's3+!/M*<Nnrn^G$DN'F-|3ngy ]mI!44#2vPaT R.@ gixixWz(YJ|2LI:eVQ. ᜮRIENDB`ninja-ide-2.3/ninja_ide/img/split-vertical.png000066400000000000000000000070461216641277400214010ustar00rootroot00000000000000PNG  IHDR szz OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-bKGD pHYs  tIME 7XIDATXMoU3smĮ>-jCJA"Bb$x ;b%?.;K(@, Q$U}aaq4<ϙss;3??F|j=7qL%폺wޯ4ȤFĿmol~VyH$_y0^0-BzepЌgg/wo/WYi5jJhPh%Tq% @9㙪RE*+2 (9{ POEM`+|6Ƈf^x&bW`[M^K m?TCCȲ=7hx @fpDF%ȁ HQaIQ3I73E lZdC! bX~x77$:PU}@s PST+ z ׄ6|KHM'p (ʛKPq Mx($;woZu mG5 H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-bKGD pHYs  tIME QҭIDAT8˭1JQ]wH Ar=wk6#,-,BTDD3ŒŨl^>ytȉU kφô*Pޟ.'G0:Hb$|\e@koB4@@.1r4@@(5h,)H<9eݲmd ɺB -'+F 9eYl{Y_4 UUd28t?^nKӡh4{?"Dr:;&Nu%#U`y|EQp*lv(J>,"avm;b׉Y뾔lL&S4sKqi͞)YB1q 6gZZZ>R\|I\D8>>8%Ӯi[l4Sc`KWU"(X, ǀ(d2---K0P,SP\ .K9+4[H]AyUȪ1\ٳMVm۶R{'5oX'O`6MMMttt:o߾ݻR)6nȎ;X~="aLLL3Jp2MpLDuΟ?Ogg'###\xN\rcǎ( 7nܠ: txFxm[ Ð7oʛ7odzzZ*<ߋir=9qq 'sss2??/XL]&DB={V2: b'ӧҥK{χeY  عs'?;;hoog۷ټy3@4~^y2 IJ,1 C&''#;w.}<YI$L&% ~_=z$TJ"H;uv L&sccc2::Ç=z[n100@0IJ,n߾͇pak.? {Ԟ={r}vikk(޽{ i݄aFFFW Io'Od||A^xH$8~8罹;wFillDUUzzz8|0O>":tH_iJ:fڵ8mPSSúu Juaa)t]gÆ 9D˲&044}~P%ǒ*]ٴiguF***V< UUMgYƳ땧%]瀯8`0 SNT?|4`C>jvjl)yOfWd) ,di؈C%IENDB`ninja-ide-2.3/ninja_ide/img/tree-code.png000066400000000000000000000022201216641277400202730ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIME 5%IDATXOoG3;6P'p>F=p;cO 'W H9# NTꈍwvz`w8Ɖ詯4ڙ۷!PJE1.}J5BL&ZV_KK0[ idg8.׮]{snKcss̚0t:G#s5$IF\p$IRc`E rџ#\ǚ?\Q&TxRNtTρkf7'MS֘NdYvv 59H)i4$IRx1;N偪AYY9@kY@k$<#wJի}ε,~cZ(6Icg>3kC!9b„iRc Y|.g-j8O>Uŋ9qpk ͽQxbiZ9!Bk1fٲP(Ȳ Vc RJer5X‰T2 `23i6?a<"cִmX]]%IhZ\r=RJ)_f2vܸqV΋u:8bNϏ>hwr֭U͛_YUd F|7;s?N6$ '@? )jbIENDB`ninja-ide-2.3/ninja_ide/img/tree-folder.png000066400000000000000000000017671216641277400206530ustar00rootroot00000000000000PNG  IHDR szzsBIT|dIDATX햿oE?{{1+h\QQF))6(Bԑ(m%ɢAP crbng=^|^c_rh47;;ϼ73{VLxSM^>M} .{'2D!xG.(ZLjH%Dj བྷ,#P@D0Xc̃~c̲evJ@U^!2P_h6]:%DEc8QU7xnv㽯drY1̭87ݨ"C*C yVC)05Z'*=&2ɄH w;N^9WXkt:8h1.l 4Hblq98˲i~ofE) E\whih@@z1iV( @a9ndTYT kgu$ܫї("B\5f J›c]AHWDd9QA2EWrAZji yM2_bj*:qΙP,<5,{ 0=NB_+FDIԐ;Y}r:ߥs2D & ,ff5hQh^.ʹ==9C?v5~}8^|sы~~B5kJ @!i\35eY vtZE۲-!'}7 X3VURr||\]Qnpe1wV$sf'|H 0 KQZRMk@VT:e>k {,/PSlq,Sxya~Z?o4J)*;6f jwkrԁv-a\~6 x4'=8{+?O7?yľ|I"IENDB`ninja-ide-2.3/ninja_ide/img/tree-html.png000066400000000000000000000033371216641277400203370ustar00rootroot00000000000000PNG  IHDR szzsBIT|dIDATX[lW3ޫ^{}8I]N5 !OEa DE@ЂJ6B`ZMۑ/:g;3;s!^N%9}n3g|rAIE۶,PL,<[i'sm O:\c&. q֌+no}@,C,"#y:r\h '[_/G۶8-v455Ux$D|ܶ*imm+`Y֚ U8 DB{ r.Hc)@^RI899eYۖ]$ βƹ9 @hd:;#O®5bbbs퍛}I=7CMȏ1+ #5GB}p}d[eAX&,R':g|r߾ttni=ѣGO?~UUqp8 % ^),d<9ty[wאs$4E!(y.:[`h``wvf?qO¶6 GlF??Il6T姾@^ZSUۀOȁ_q{47NP̦'WIŋg`= ܏n K²s.>{׋(|1* 7U̡{4׮Qùra'pbϖ1>C֕AZzLJ8,n18ݫ7V7l#I_XHopX%["Tpxކi^}450tc 5142ISS5ђ hf=Ԅ*0]d 9%dѳ&Z@ |A'vqvVT9rl6[PL |YMH$Dl94 A^4Fna jXPTs<$I\4q0 LIh|gp$t]2fH)D%C8.M!FQH}u ,gEYLjb:&̠*t/^#gfnQU N/<[S闘ƣXwbI*+NJՑ>EeE!1`zvӦ :4**R!W,hF6% ˲ ANg0sR3 U1WWkmRsQzX\\$H뾳$r>Fbnd2I|)EEM37)i{lfdνzed9br&fI:IE#U",!OAoeqEQǩBE WN<}d{[bd7s x42k,_{p\,vwwhii1L\.hIC.vF!?öYe!bn|,F2;;{ a Jᾰn|(zuzC;]0-;KGm;'wI`P+7]u:>W'4@Yu݈ zeIENDB`ninja-ide-2.3/ninja_ide/img/tree-image.png000066400000000000000000000022131216641277400204450ustar00rootroot00000000000000PNG  IHDR szzsBIT|dBIDATXk\U}2I26MlT G/',>7 AKQJhhK-VK[L~ə뙳/ˇImCA^}{7^Ź>xߝf-"xhpNNuNv.&Ja ϥ=8voLԫ\DH,lu~H'P $\{}|}^T*@9 iG_;[A) P:H.86D+@aZ=˩FV]]=#5]<ЭL:HJ-o{ 6"IeO^^* kn,LHro!K\6wt4A6qi6YAìy$7: mOP:BQ!TȌlɤnbi_FWI'e Betnz:sl:E/1t$pS#$Ą2"1KҿMvy]aH2Bq" "!?z}uiET֗9.c-Hy.0@kMP bAGuPG8gTKDi`L*vzsc,Ze<aL-9hFRp|qok-Au;C4[+k+9wf@)ŗ_;w/cZxժk̜319};9y0]]^x܅F ^r~AR\~pff~ ԁ -NK 7</?nײIENDB`ninja-ide-2.3/ninja_ide/img/tree-python.png000066400000000000000000000022471216641277400207130ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIME $ŋ''IDATX[hUgfvdmlȥ&!ij4(XAl"b\D"/" Rb%iIjŇYw'{'>y`3;7a<(eI0 ]3iwvi<>>N4 EQ0M3g]w}e8]=!uUUI$@ـ4MZZZH$oe.dT;0نVVV2eߟ7$EX,؁ 躞a6 0*gP~U Xe'et]/vY 1YBPEg(GeD822KKK3WB 1 0`pسb1,Weȡ1\yb&, Tl/ǟ-%Bvts/2@jP1 &7fz dGWIxiO N(bppO 8N+F [?/b9jo,nn"$>=~QEnT  g6@ױnFSÏbQ#q'ח}YgRh ͯfP>e>;gB= 7o.FY*B`;j}7=4]y $o# j`_,Id8𰵞;S SqKl ֺ.'G.]@9^xd_`ΉWi;}Hbg/l(=>L<g@9pB yq_93Jy^[-4<OQ*ڀ@ 4bjPh4 d.`dK GP^xm?p;iڰ+ HHix cIENDB`ninja-ide-2.3/ninja_ide/img/uncomment-code.png000066400000000000000000000023171216641277400213500ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGDC pHYs  tIME / ׏8tEXtCommentCreated with GIMPW*IDATXOhTW71S"$v5RH v?JЖP*M"Ha u! 1AjڦmJMi:)1 ^"^$Lw|9s?)f$|>3&(f+c̍|ޞ+IIX1϶]%sc̃ee@ҋ$ގmDQ6m`N$`[co\$ %pDRzxxXdR F_Iɓ$MIH*YoٲE^ P|> J_J|8v㄀. l` ‰gmX=t:`0f… NiIz35so(>a{Rϝ;3fdIWJy])iM>oHJq]6Kc}UU࣠cYNLL8$/˗]L}JeG@Xw޽|FkN CZ>%P__O"pCCCu/cyunsX~}`0XpTH|َttt|V $_ͬ^=RЪU4;;EA?+nxtF"mݮv?8޿%wAے 4eY*//WhN:{V555.SNDZ@`ݤR)w>J118K9slV~-/盛7ݵTVVVcb2PʲJ5ЗU@bN>Z}L9/=QN===ؖEϜjl Çٻk{KZǹ{.ܹ3WCCCQG6_Dܴ:wiH$+#Eҏ[͛| aÆ,vrJYޗG?#]Ҙ Hh*P(FIjkks D"71y#gt 09 +V ydoRb1xJʳaZVX?&aW5뀩LUƘO(s~=b_";b9IENDB`ninja-ide-2.3/ninja_ide/img/web.png000066400000000000000000000043131216641277400172060ustar00rootroot00000000000000PNG  IHDR szzsBIT|dIDATX[ls]kc8vML.`&A ) R*Z*REj-I[ZZ%NR$b!1qww.ga&*jzO9:3g{SLM轊\IYNBB9Uuhu#-u ^NII brr$aN2ayV%m;<'/hk{(Z⣕(|GNa|ڤ}+b;.dGѝ)Zע*B0:?,9pqsz)@GvK,(0,]UPUEU(OXjm#8iRJ*+˸a$ehk{(co񋸦| cyt1lڨl//9DU˩MUJr9 aOf];o}@~[B,<}):;5|^߇(xk45F|>;>h供\(B)}U HxuM#Wƹyc#ãY,[۸B;ݓ9M64.?CU*kmr$y[!Q+8.B‚ҲgeRVײ=T.JR ׏мWʍ}EIF#6Hu*b3 XBb iKL!$U^Bu})Q?7{!B[Vs~(R骠*Plf)CKdlV( >$Ä HCH9xi)};RJlƶmTUAE29#?[0ΞːhXBrk1ԓ'1,A޴ɛ6)Sn|4,k?% Mg&}U;\Tyri7u kY)_+.qi铞^ԥ$c? 9uzRri\TNSPDq}gi2==âd%ac"=ޙ  Cyk[Q?gPQ,<`>y ^E[_KK8,K-Ew,LNz^f c:θIM2Ʈ z3333t&C8p3Yr  à<{'١CCer@m^4|"&e0B~]%Zd9.`4蘦ɅW4;#epxiYC,?~" J `C '>ZB"Xm$?H{4=/hA8> UW;5д}^]Yd"#D OX/La=}c/2CgkgMF{HmC$\fH~էb{,ٚ ?-eLIM;!.CǷ4oVhEE]#x}LŅ,VdxTyd<|L[@_aDj0hřcVd39LsȒ716j; ^^7H.W>~} `f0H* :@Jds6'd>L E#Kͣo3ݐ9o:5wFE9oepL_R2"4Fnǭe4EClBǁSQlpl+{\XNQrmmuK>|ۍXl>6xy289s꣈gax8N#Ngwݮ=U/llj*C尋W2'Mbd8gVS=.O25jtr;r^Ɋ $Y|ImZULQI%;whG7oxaFeخzNun!bJӊp !$Z0Ta;lMNs]eb SlU6iӣ,r6b:0?nIBw [i]`DΪ. from PyQt4.QtGui import QKeySequence from PyQt4.QtCore import QDir from PyQt4.QtCore import QSettings from PyQt4.QtCore import Qt import os import sys ############################################################################### # PATHS ############################################################################### HOME_PATH = QDir.toNativeSeparators(QDir.homePath()) NINJA_EXECUTABLE = os.path.realpath(sys.argv[0]) PRJ_PATH = os.path.abspath(os.path.dirname(__file__)).decode('utf-8') #Only for py2exe frozen = getattr(sys, 'frozen', '') if frozen in ('dll', 'console_exe', 'windows_exe'): # py2exe: PRJ_PATH = os.path.abspath(os.path.dirname(sys.executable)) HOME_NINJA_PATH = os.path.join(HOME_PATH, ".ninja_ide") SETTINGS_PATH = os.path.join(HOME_NINJA_PATH, 'settings.ini') ADDINS = os.path.join(HOME_NINJA_PATH, "addins") SYNTAX_FILES = os.path.join(PRJ_PATH, "addins", "syntax") PLUGINS = os.path.join(HOME_NINJA_PATH, "addins", "plugins") PLUGINS_DESCRIPTOR = os.path.join(HOME_NINJA_PATH, "addins", "plugins", "descriptor.json") LANGS = os.path.join(PRJ_PATH, "addins", "lang") LANGS_DOWNLOAD = os.path.join(HOME_NINJA_PATH, "addins", "languages") EDITOR_SKINS = os.path.join(HOME_NINJA_PATH, "addins", "schemes") START_PAGE_URL = os.path.join(PRJ_PATH, "doc", "startPage.html") NINJA_THEME = os.path.join(PRJ_PATH, "addins", "theme", "ninja_dark.qss") NINJA__THEME_CLASSIC = os.path.join( PRJ_PATH, "addins", "theme", "ninja_theme.qss") NINJA_THEME_DOWNLOAD = os.path.join(HOME_NINJA_PATH, "addins", "theme") LOG_FILE_PATH = os.path.join(HOME_NINJA_PATH, 'ninja_ide.log') GET_SYSTEM_PATH = os.path.join(PRJ_PATH, 'tools', 'get_system_path.py') QML_FILES = os.path.join(PRJ_PATH, "addins", "qml") ############################################################################### # URLS ############################################################################### BUGS_PAGE = "https://github.com/ninja-ide/ninja-ide/issues" PLUGINS_DOC = "http://ninja-ide.readthedocs.org/en/latest/" UPDATES_URL = 'http://ninja-ide.org/updates' SCHEMES_URL = 'http://ninja-ide.org/schemes/api/' LANGUAGES_URL = 'http://ninja-ide.org/plugins/languages' PLUGINS_WEB = 'http://ninja-ide.org/plugins/api/official' PLUGINS_COMMUNITY = 'http://ninja-ide.org/plugins/api/community' ############################################################################### # IMAGES ############################################################################### IMAGES = { "splash": os.path.join(PRJ_PATH, "img", "splash.png"), "icon": os.path.join(PRJ_PATH, "img", "icon.png"), "iconUpdate": os.path.join(PRJ_PATH, "img", "icon.png"), "new": os.path.join(PRJ_PATH, "img", "document-new.png"), "newProj": os.path.join(PRJ_PATH, "img", "project-new.png"), "open": os.path.join(PRJ_PATH, "img", "document-open.png"), "openProj": os.path.join(PRJ_PATH, "img", "project-open.png"), "openFolder": os.path.join(PRJ_PATH, "img", "folder-open.png"), "save": os.path.join(PRJ_PATH, "img", "document-save.png"), "saveAs": os.path.join(PRJ_PATH, "img", "document-save-as.png"), "saveAll": os.path.join(PRJ_PATH, "img", "document-save-all.png"), "activate-profile": os.path.join(PRJ_PATH, "img", "activate_profile.png"), "deactivate-profile": os.path.join(PRJ_PATH, "img", "deactivate_profile.png"), "copy": os.path.join(PRJ_PATH, "img", "edit-copy.png"), "cut": os.path.join(PRJ_PATH, "img", "edit-cut.png"), "paste": os.path.join(PRJ_PATH, "img", "edit-paste.png"), "redo": os.path.join(PRJ_PATH, "img", "edit-redo.png"), "undo": os.path.join(PRJ_PATH, "img", "edit-undo.png"), "exit": os.path.join(PRJ_PATH, "img", "exit.png"), "find": os.path.join(PRJ_PATH, "img", "find.png"), "findReplace": os.path.join(PRJ_PATH, "img", "find-replace.png"), "locator": os.path.join(PRJ_PATH, "img", "locator.png"), "play": os.path.join(PRJ_PATH, "img", "play.png"), "stop": os.path.join(PRJ_PATH, "img", "stop.png"), "file-run": os.path.join(PRJ_PATH, "img", "file-run.png"), "preview-web": os.path.join(PRJ_PATH, "img", "preview_web.png"), "debug": os.path.join(PRJ_PATH, "img", "debug.png"), "designer": os.path.join(PRJ_PATH, "img", "qtdesigner.png"), "bug": os.path.join(PRJ_PATH, "img", "bug.png"), "function": os.path.join(PRJ_PATH, "img", "function.png"), "module": os.path.join(PRJ_PATH, "img", "module.png"), "class": os.path.join(PRJ_PATH, "img", "class.png"), "attribute": os.path.join(PRJ_PATH, "img", "attribute.png"), "web": os.path.join(PRJ_PATH, "img", "web.png"), "fullscreen": os.path.join(PRJ_PATH, "img", "fullscreen.png"), "follow": os.path.join(PRJ_PATH, "img", "follow.png"), "splitH": os.path.join(PRJ_PATH, "img", "split-horizontal.png"), "splitV": os.path.join(PRJ_PATH, "img", "split-vertical.png"), "zoom-in": os.path.join(PRJ_PATH, "img", "zoom_in.png"), "zoom-out": os.path.join(PRJ_PATH, "img", "zoom_out.png"), "splitCPosition": os.path.join(PRJ_PATH, "img", "panels-change-position.png"), "splitMPosition": os.path.join(PRJ_PATH, "img", "panels-change-vertical-position.png"), "splitCRotate": os.path.join(PRJ_PATH, "img", "panels-change-orientation.png"), "indent-less": os.path.join(PRJ_PATH, "img", "indent-less.png"), "indent-more": os.path.join(PRJ_PATH, "img", "indent-more.png"), "go-to-definition": os.path.join(PRJ_PATH, "img", "go_to_definition.png"), "insert-import": os.path.join(PRJ_PATH, "img", "insert_import.png"), "console": os.path.join(PRJ_PATH, "img", "console.png"), "pref": os.path.join(PRJ_PATH, "img", "preferences-system.png"), "tree-app": os.path.join(PRJ_PATH, "img", "tree-app.png"), "tree-code": os.path.join(PRJ_PATH, "img", "tree-code.png"), "tree-folder": os.path.join(PRJ_PATH, "img", "tree-folder.png"), "tree-html": os.path.join(PRJ_PATH, "img", "tree-html.png"), "tree-generic": os.path.join(PRJ_PATH, "img", "tree-generic.png"), "tree-css": os.path.join(PRJ_PATH, "img", "tree-CSS.png"), "tree-python": os.path.join(PRJ_PATH, "img", "tree-python.png"), "tree-image": os.path.join(PRJ_PATH, "img", "tree-image.png"), "comment-code": os.path.join(PRJ_PATH, "img", "comment-code.png"), "uncomment-code": os.path.join(PRJ_PATH, "img", "uncomment-code.png"), "reload-file": os.path.join(PRJ_PATH, "img", "reload-file.png"), "print": os.path.join(PRJ_PATH, "img", "document-print.png"), "book-left": os.path.join(PRJ_PATH, "img", "book-left.png"), "book-right": os.path.join(PRJ_PATH, "img", "book-right.png"), "break-left": os.path.join(PRJ_PATH, "img", "break-left.png"), "break-right": os.path.join(PRJ_PATH, "img", "break-right.png"), "nav-code-left": os.path.join(PRJ_PATH, "img", "nav-code-left.png"), "nav-code-right": os.path.join(PRJ_PATH, "img", "nav-code-right.png"), "locate-file": os.path.join(PRJ_PATH, "img", "locate-file.png"), "locate-class": os.path.join(PRJ_PATH, "img", "locate-class.png"), "locate-function": os.path.join(PRJ_PATH, "img", "locate-function.png"), "locate-attributes": os.path.join(PRJ_PATH, "img", "locate-attributes.png"), "locate-nonpython": os.path.join(PRJ_PATH, "img", "locate-nonpython.png"), "locate-on-this-file": os.path.join(PRJ_PATH, "img", "locate-on-this-file.png"), "locate-tab": os.path.join(PRJ_PATH, "img", "locate-tab.png"), "locate-line": os.path.join(PRJ_PATH, "img", "locate-line.png"), "add": os.path.join(PRJ_PATH, "img", "add.png"), "delete": os.path.join(PRJ_PATH, "img", "delete.png"), "loading": os.path.join(PRJ_PATH, "img", "loading.gif"), "separator": os.path.join(PRJ_PATH, "img", "separator.png")} ############################################################################### # COLOR SCHEMES ############################################################################### COLOR_SCHEME = { "keyword": "#6EC7D7", "operator": "#FFFFFF", "brace": "#FFFFFF", "definition": "#F6EC2A", "string": "#B369BF", "string2": "#86d986", "comment": "#80FF80", "properObject": "#6EC7D7", "numbers": "#F8A008", "spaces": "#7b7b7b", "extras": "#ee8859", "editor-background": "#1E1E1E", "editor-selection-color": "#FFFFFF", "editor-selection-background": "#437DCD", "editor-text": "#B3BFA7", "current-line": "#858585", "selected-word": "red", "pending": "red", "selected-word-background": "#009B00", "fold-area": "#FFFFFF", "fold-arrow": "#454545", "linkNavigate": "orange", "brace-background": "#5BC85B", "brace-foreground": "red", "error-underline": "red", "pep8-underline": "yellow", "sidebar-background": "#c4c4c4", "sidebar-foreground": "black", "locator-name": "white", "locator-name-selected": "black", "locator-path": "gray", "locator-path-selected": "white", "migration-underline": "blue", "current-line-opacity": 20, "error-background-opacity": 60, } CUSTOM_SCHEME = {} ############################################################################### # SHORTCUTS ############################################################################### #default shortcuts SHORTCUTS = { "Duplicate": QKeySequence(Qt.CTRL + Qt.Key_R), # Replicate "Remove-line": QKeySequence(Qt.CTRL + Qt.Key_E), # Eliminate "Move-up": QKeySequence(Qt.ALT + Qt.Key_Up), "Move-down": QKeySequence(Qt.ALT + Qt.Key_Down), "Close-tab": QKeySequence(Qt.CTRL + Qt.Key_W), "New-file": QKeySequence(Qt.CTRL + Qt.Key_N), "New-project": QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_N), "Open-file": QKeySequence(Qt.CTRL + Qt.Key_O), "Open-project": QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_O), "Save-file": QKeySequence(Qt.CTRL + Qt.Key_S), "Save-project": QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_S), "Print-file": QKeySequence(Qt.CTRL + Qt.Key_P), "Redo": QKeySequence(Qt.CTRL + Qt.Key_Y), "Comment": QKeySequence(Qt.CTRL + Qt.Key_D), "Uncomment": QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_D), "Horizontal-line": QKeySequence(), "Title-comment": QKeySequence(), "Indent-less": QKeySequence(Qt.SHIFT + Qt.Key_Tab), "Hide-misc": QKeySequence(Qt.Key_F4), "Hide-editor": QKeySequence(Qt.Key_F3), "Hide-explorer": QKeySequence(Qt.Key_F2), "Run-file": QKeySequence(Qt.CTRL + Qt.Key_F6), "Run-project": QKeySequence(Qt.Key_F6), "Debug": QKeySequence(Qt.Key_F7), "Switch-Focus": QKeySequence(Qt.CTRL + Qt.Key_QuoteLeft), "Stop-execution": QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_F6), "Hide-all": QKeySequence(Qt.Key_F11), "Full-screen": QKeySequence(Qt.CTRL + Qt.Key_F11), "Find": QKeySequence(Qt.CTRL + Qt.Key_F), "Find-replace": QKeySequence(Qt.CTRL + Qt.Key_H), "Find-with-word": QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_F), "Find-next": QKeySequence(Qt.CTRL + Qt.Key_F3), "Find-previous": QKeySequence(Qt.SHIFT + Qt.Key_F3), "Help": QKeySequence(Qt.Key_F1), "Split-horizontal": QKeySequence(Qt.Key_F9), "Split-vertical": QKeySequence(Qt.Key_F10), "Follow-mode": QKeySequence(Qt.CTRL + Qt.Key_F10), "Reload-file": QKeySequence(Qt.Key_F5), "Find-in-files": QKeySequence(Qt.CTRL + Qt.Key_L), "Import": QKeySequence(Qt.CTRL + Qt.Key_I), "Go-to-definition": QKeySequence(Qt.CTRL + Qt.Key_Return), "Complete-Declarations": QKeySequence(Qt.ALT + Qt.Key_Return), "Code-locator": QKeySequence(Qt.CTRL + Qt.Key_K), "File-Opener": QKeySequence(Qt.CTRL + Qt.ALT + Qt.Key_O), "Navigate-back": QKeySequence(Qt.ALT + Qt.Key_Left), "Navigate-forward": QKeySequence(Qt.ALT + Qt.Key_Right), "Open-recent-closed": QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_T), "Change-Tab": QKeySequence(Qt.CTRL + Qt.Key_PageDown), "Change-Tab-Reverse": QKeySequence(Qt.CTRL + Qt.Key_PageUp), "Move-Tab-to-right": QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_0), "Move-Tab-to-left": QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_9), "Show-Code-Nav": QKeySequence(Qt.CTRL + Qt.Key_1), "Show-Paste-History": QKeySequence(Qt.CTRL + Qt.Key_4), "History-Copy": QKeySequence(Qt.CTRL + Qt.ALT + Qt.Key_C), "History-Paste": QKeySequence(Qt.CTRL + Qt.ALT + Qt.Key_V), "Add-Bookmark-or-Breakpoint": QKeySequence(Qt.CTRL + Qt.Key_B), "change-split-focus": QKeySequence(Qt.CTRL + Qt.Key_Tab), "move-tab-to-next-split": QKeySequence(Qt.SHIFT + Qt.Key_F10), "change-tab-visibility": QKeySequence(Qt.SHIFT + Qt.Key_F1), "Highlight-Word": QKeySequence(Qt.CTRL + Qt.Key_Down)} CUSTOM_SHORTCUTS = {} ############################################################################### # FUNCTIONS ############################################################################### def load_shortcuts(): """ Loads the shortcuts from QSettings """ global SHORTCUTS global CUSTOM_SHORTCUTS settings = QSettings(SETTINGS_PATH, QSettings.IniFormat) for action in SHORTCUTS: #default shortcut default_action = SHORTCUTS[action].toString() #get the custom shortcut or the default shortcut_action = settings.value("shortcuts/%s" % action, default_action) #set the shortcut CUSTOM_SHORTCUTS[action] = QKeySequence(shortcut_action) def get_shortcut(shortcut_name): """ Returns the shortcut looking into CUSTOM_SHORTCUTS and SHORTCUTS """ global SHORTCUTS global CUSTOM_SHORTCUTS return CUSTOM_SHORTCUTS.get(shortcut_name, SHORTCUTS.get(shortcut_name)) def clean_custom_shortcuts(): """ Cleans CUSTOMS_SHORTCUTS """ global CUSTOM_SHORTCUTS CUSTOM_SHORTCUTS = {} def create_home_dir_structure(): """ Create the necesary directories structure for NINJA-IDE """ for d in (HOME_NINJA_PATH, ADDINS, PLUGINS, EDITOR_SKINS, LANGS_DOWNLOAD, NINJA_THEME_DOWNLOAD): if not os.path.isdir(d): os.mkdir(d) ninja-ide-2.3/ninja_ide/tools/000077500000000000000000000000001216641277400163065ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/tools/__init__.py000066400000000000000000000012641216641277400204220ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . ninja-ide-2.3/ninja_ide/tools/completion/000077500000000000000000000000001216641277400204575ustar00rootroot00000000000000ninja-ide-2.3/ninja_ide/tools/completion/__init__.py000066400000000000000000000013121216641277400225650ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . __version__ = "0.1" ninja-ide-2.3/ninja_ide/tools/completion/analyzer.py000066400000000000000000000314101216641277400226550ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . # DISCLAIMER ABOUT READING THIS CODE: # We are not responsible for any kind of mental or emotional # damage that may arise from reading this code. import re import ast import _ast from ninja_ide.tools.logger import NinjaLogger from ninja_ide.tools.completion import model logger = NinjaLogger('ninja_ide.tools.completion.analyzer') MAX_THRESHOLD = 3 def expand_attribute(attribute): parent_name = [] while attribute.__class__ is ast.Attribute: parent_name.append(attribute.attr) attribute = attribute.value name = '.'.join(reversed(parent_name)) attribute_id = '' if attribute.__class__ is ast.Name: attribute_id = attribute.id elif attribute.__class__ is ast.Call: if attribute.func.__class__ is ast.Attribute: attribute_id = '%s.%s()' % ( expand_attribute(attribute.func.value), attribute.func.attr) else: attribute_id = '%s()' % attribute.func.id name = attribute_id if name == '' else ("%s.%s" % (attribute_id, name)) return name class Analyzer(object): __mapping = { _ast.Tuple: '__builtin__.tuple', _ast.List: '__builtin__.list', _ast.ListComp: '__builtin__.list', _ast.Str: '__builtin__.str', _ast.Dict: '__builtin__.dict', _ast.Num: '__builtin__.int', '_ast.Float': '__builtin__.float', '_ast.Bool': '__builtin__.bool', _ast.Call: model.late_resolution, _ast.Name: model.late_resolution, _ast.Attribute: model.late_resolution, } def __init__(self): self._fixed_line = -1 self.content = None # self._functions = {} def _get_valid_module(self, source, retry=0): """Try to parse the module and fix some errors if it has some.""" astModule = None try: astModule = ast.parse(source) except SyntaxError as reason: line = reason.lineno - 1 if line != self._fixed_line and reason.text is not None: self._fixed_line = line new_line = '' #This is failing sometimes, it should remaing commented #until we find the proper fix. indent = re.match('^\s+', str(reason.text)) if indent is not None: new_line = indent.group() + 'pass' split_source = source.splitlines() split_source[line] = new_line source = '\n'.join(split_source) if retry < MAX_THRESHOLD: astModule = self._get_valid_module(source, retry + 1) return astModule def analyze(self, source, old_module=None): """Analyze the source provided and create the proper structure.""" astModule = self._get_valid_module(source) if astModule is None: return model.Module() self.content = source.split('\n') module = model.Module() for symbol in astModule.body: if symbol.__class__ is ast.Assign: assigns = self._process_assign(symbol)[0] module.add_attributes(assigns) elif symbol.__class__ in (ast.Import, ast.ImportFrom): module.add_imports(self._process_import(symbol)) elif symbol.__class__ is ast.ClassDef: module.add_class(self._process_class(symbol)) elif symbol.__class__ is ast.FunctionDef: module.add_function(self._process_function(symbol)) # elif symbol.__class__ is ast.Expr: # self._process_expression(symbol.value) if old_module is not None: self._resolve_module(module, old_module) self.content = None # self._functions = {} return module def _resolve_module(self, module, old_module): module.update_classes(old_module.classes) module.update_functions(old_module.functions) module.update_attributes(old_module.attributes) def _assign_disambiguation(self, type_name, line_content): """Provide a specific builtin for the cases were ast doesn't work.""" line = line_content.split('=') if len(line) < 2: logger.error('_assign_disambiguation, line not valid: %r' % line_content) return type_name value = line[1].strip() # TODO: We have to analyze when the assign is: x,y = 1, 2 if type_name is _ast.Num and '.' in value: type_name = '_ast.Float' elif value in ('True', 'False'): type_name = '_ast.Bool' elif value == 'None': type_name = None return type_name # def _process_expression(self, expr): # """Process expression, not assignment.""" # if expr.__class__ is not ast.Call: # return # args = expr.args # keywords = expr.keywords # ar = [] # kw = {} # for arg in args: # type_value = arg.__class__ # arg_name = '' # if type_value is ast.Call: # arg_name = expand_attribute(arg.func) # elif type_value is ast.Attribute: # arg_name = expand_attribute(arg.attr) # data_type = self.__mapping.get(type_value, model.late_resolution) # ar.append((arg_name, data_type)) # for key in keywords: # type_value = key.value.__class__ # data_type = self.__mapping.get(type_value, model.late_resolution) # kw[key.arg] = data_type # if expr.func.__class__ is ast.Attribute: # name = expand_attribute(expr.func) # else: # name = expr.func.id # self._functions[name] = (ar, kw) def _process_assign(self, symbol): """Process an ast.Assign object to extract the proper info.""" assigns = [] attributes = [] for var in symbol.targets: type_value = symbol.value.__class__ line_content = self.content[symbol.lineno - 1] if type_value in (_ast.Num, _ast.Name): type_value = self._assign_disambiguation( type_value, line_content) if type_value is None: continue data_type = self.__mapping.get(type_value, model.late_resolution) if var.__class__ == ast.Attribute: data = (var.attr, symbol.lineno, data_type, line_content, type_value) attributes.append(data) elif var.__class__ == ast.Name: data = (var.id, symbol.lineno, data_type, line_content, type_value) assigns.append(data) # if type_value is ast.Call: # self._process_expression(symbol.value) return (assigns, attributes) def _process_import(self, symbol): """Process an ast.Import and ast.ImportFrom object to extract data.""" imports = [] for imp in symbol.names: if symbol.__class__ is ast.ImportFrom: module_name = "%s.%s" % (symbol.module, imp.name) else: module_name = imp.name name = imp.asname if name is None: name = imp.name imports.append((name, module_name)) return imports def _process_class(self, symbol): """Process an ast.ClassDef object to extract data.""" clazz = model.Clazz(symbol.name) for base in symbol.bases: if base == 'object': continue name = expand_attribute(base) clazz.add_parent(name) #TODO: Decotator # for decorator in symbol.decorator_list: # clazz.decorators.append(decorator.id) # PARSE FUNCTIONS AND ATTRIBUTES for sym in symbol.body: if sym.__class__ is ast.Assign: assigns = self._process_assign(sym)[0] clazz.add_attributes(assigns) elif sym.__class__ is ast.FunctionDef: clazz.add_function(self._process_function(sym, clazz)) clazz.update_bases() clazz.update_with_parent_data() return clazz def _process_function(self, symbol, parent=None): """Process an ast.FunctionDef object to extract data.""" function = model.Function(symbol.name) #TODO: Decorators #We are not going to collect data from decorators yet. # for decorator in symbol.decorator_list: #Decorators can be: Name, Call, Attributes # function.decorators.append(decorator.id) if symbol.args.vararg is not None: assign = model.Assign(symbol.args.vararg) assign.add_data(symbol.lineno, '__builtin__.list', None, None) function.args[assign.name] = assign if symbol.args.kwarg is not None: assign = model.Assign(symbol.args.kwarg) assign.add_data(symbol.lineno, '__builtin__.dict', None, None) function.args[assign.name] = assign #We store the arguments to compare with default backwards defaults = [] for value in symbol.args.defaults: #TODO: In some cases we can have something like: a=os.path type_value = value.__class__ data_type = self.__mapping.get(type_value, None) defaults.append((data_type, type_value)) for arg in reversed(symbol.args.args): if isinstance(arg, ast.Tuple): self._parse_tuple_in_func_arg(arg, function, symbol.lineno) continue elif arg.id == 'self': continue assign = model.Assign(arg.id) data_type = (model.late_resolution, None) if defaults: data_type = defaults.pop() assign.add_data(symbol.lineno, data_type[0], None, data_type[1]) function.args[assign.name] = assign for sym in symbol.body: if sym.__class__ is ast.Assign: result = self._process_assign(sym) function.add_attributes(result[0]) if parent is not None: parent.add_attributes(result[1]) elif sym.__class__ is ast.FunctionDef: function.add_function(self._process_function(sym)) if sym.__class__ is not ast.Assign: self._search_recursive_for_types(function, sym, parent) return function def _parse_tuple_in_func_arg(self, symbol_tuple, function, lineno=0): """Parse the tuple inside a function argument call.""" for item in symbol_tuple.elts: assign = model.Assign(item.id) data_type = (model.late_resolution, None) assign.add_data(lineno, data_type[0], None, data_type[1]) function.args[assign.name] = assign def _search_recursive_for_types(self, function, symbol, parent=None): """Search for return recursively inside the function.""" if symbol.__class__ is ast.Assign: result = self._process_assign(symbol) function.add_attributes(result[0]) if parent is not None: parent.add_attributes(result[1]) elif symbol.__class__ is ast.Return: type_value = symbol.value.__class__ lineno = symbol.lineno data_type = self.__mapping.get(type_value, None) line_content = self.content[lineno - 1] if data_type != model.late_resolution: type_value = None function.add_return(lineno, data_type, line_content, type_value) elif symbol.__class__ in (ast.If, ast.For, ast.TryExcept): for sym in symbol.body: self._search_recursive_for_types(function, sym, parent) for else_item in symbol.orelse: self._search_recursive_for_types(function, else_item, parent) elif symbol.__class__ is ast.TryFinally: for sym in symbol.body: self._search_recursive_for_types(function, sym, parent) for else_item in symbol.finalbody: self._search_recursive_for_types(function, else_item, parent) # elif symbol.__class__ is ast.Expr: # self._process_expression(symbol.value) ninja-ide-2.3/ninja_ide/tools/completion/code_completion.py000066400000000000000000000251771216641277400242100ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . # DISCLAIMER ABOUT READING THIS CODE: # We are not responsible for any kind of mental or emotional # damage that may arise from reading this code. import re import token as tkn from tokenize import generate_tokens, TokenError try: from StringIO import StringIO except: from io import StringIO # lint:ok from ninja_ide.core import settings from ninja_ide.gui.editor import helpers from ninja_ide.tools.completion import model from ninja_ide.tools.completion import analyzer from ninja_ide.tools.completion import completer from ninja_ide.tools.completion import completion_daemon #Because my python doesn't have it, and is not in the web docs either #but trust me, it exists! _TOKEN_NL = 54 class CodeCompletion(object): def __init__(self): self.analyzer = analyzer.Analyzer() self.cdaemon = completion_daemon.CompletionDaemon() # Set modules reference to model model.MODULES = self.cdaemon.modules self.module_id = None self.patIndent = re.compile('^\s+') self.patClass = re.compile("class (\w+?)\(") self.patFunction = re.compile("(\w+?)\(") self.patWords = re.compile('\W+') self._valid_op = (')', '}', ']') self._invalid_op = ('(', '{', '[') self._invalid_words = ('if', 'elif', 'for', 'while', 'in', 'return', 'and', 'or', 'del', 'except', 'from', 'import', 'is', 'print', 'super', 'yield') self.keywords = settings.SYNTAX['python']['keywords'] def unload_module(self): self.cdaemon.unload_module(self.module_id) def analyze_file(self, path, source=None, indent=settings.INDENT, useTabs=settings.USE_TABS): if source is None: with open(path) as f: source = f.read() split_last_lines = source.rsplit('\n', 2) if len(split_last_lines) > 1 and \ split_last_lines[-2].endswith(':') and split_last_lines[-1] == '': indent = helpers.get_indentation(split_last_lines[-2], indent, useTabs) source += '%spass;' % indent self.module_id = path if not self.cdaemon.daemon.is_alive(): completion_daemon.shutdown_daemon() del self.cdaemon self.cdaemon = completion_daemon.CompletionDaemon() # Set modules reference to model model.MODULES = self.cdaemon.modules module = self.cdaemon.get_module(self.module_id) module = self.analyzer.analyze(source, module) self.cdaemon.inspect_module(self.module_id, module) def _tokenize_text(self, code): # TODO Optimization, only iterate until the previous line of a class?? token_code = [] try: for tkn_type, tkn_str, pos, _, line, \ in generate_tokens(StringIO(code).readline): token_code.append((tkn_type, tkn_str, pos, line)) except TokenError: #This is an expected situation, where i don't want to do anything #possible an unbalanced brace like: func(os.p| (| = cursor-end) pass except IndentationError: return [] while token_code[-1][0] in (tkn.ENDMARKER, tkn.DEDENT, tkn.NEWLINE): token_code.pop() return token_code def _search_for_scope(self, token_code): if not token_code or not token_code[-1][3].startswith(' '): return None scopes = [] indent = self.patIndent.match(token_code[-1][3]) if indent is not None: indent = len(indent.group()) else: indent = 0 previous_line = ('', '') keep_exploring = True iterate = reversed(token_code) line = iterate.next() while keep_exploring: is_indented = line[3].startswith(' ') is_definition = line[1] in ('def', 'class') #Skip lines that are not def or class if is_indented and is_definition: new_indent = self.patIndent.match(line[3]) if new_indent is not None: new_indent = len(new_indent.group()) #We only care about the function where we are if new_indent < indent: indent = new_indent #We need the "previous_line" because we are exploring #the tokens backwards, and when we reach to def or class #the actual name was in the line before. scopes.insert(0, previous_line) elif not is_indented and is_definition: scopes.insert(0, previous_line) keep_exploring = False previous_line = line[1] try: line = iterate.next() except StopIteration: keep_exploring = False return scopes def _search_for_completion_segment(self, token_code): tokens = [] keep_iter = True iterate = reversed(token_code) while keep_iter: try: value = iterate.next() if value[0] in (tkn.NEWLINE, tkn.INDENT, tkn.DEDENT): keep_iter = False tokens.append(value) except: keep_iter = False segment = '' brace_stack = 0 first_element = True for t in tokens: token_str = t[1] if token_str in self._invalid_words and not first_element: break elif token_str in self._valid_op: if brace_stack == 0: segment = token_str + segment brace_stack += 1 elif token_str in self._invalid_op: brace_stack -= 1 if brace_stack == 0: segment = token_str + segment continue first_element = False if brace_stack != 0: continue op = t[0] if (op == tkn.NAME) or token_str == '.': segment = token_str + segment elif op == tkn.OP: break return segment def get_prefix(self, code, offset): """Return the prefix of the word under the cursor and a boolean saying if it is a valid completion area.""" token_code = self._tokenize_text(code[:offset]) var_segment = self._search_for_completion_segment(token_code) words_final = var_segment.rsplit('.', 1) final_word = '' if not var_segment.endswith('.') and len(words_final) > 1: final_word = words_final[1].strip() elif (var_segment != "") and len(words_final) == 1: final_word = words_final[0].strip() return final_word, (var_segment != "") def get_completion(self, code, offset): token_code = self._tokenize_text(code[:offset]) scopes = self._search_for_scope(token_code) # Find section to attempt for code completion (var_segment) var_segment = self._search_for_completion_segment(token_code) words = var_segment.split('.', 1) words_final = var_segment.rsplit('.', 1) # Main_attribute is the stem off of which to search for completions # i.e. in exec(ninja_ide.gui. ..., "ninja_ide.gui.main_panel" # would be main_attribute. main_attribute = words[0].strip().split('(', 1) attr_name = main_attribute[0] word = '' final_word = '' if var_segment.count(".") > 0: word = words[1].strip() if not var_segment.endswith('.') and len(words_final) > 1: final_word = words_final[1].strip() word = word.rsplit('.', 1)[0].strip() if final_word == word: word = '' self.cdaemon.lock.acquire() module = self.cdaemon.get_module(self.module_id) if module: imports = module.get_imports() result = module.get_type(attr_name, word, scopes) else: result = {'found': False, 'type': None} self.cdaemon.lock.release() if result['found'] and result['type'] is not None: prefix = attr_name if result['type'] != attr_name: prefix = result['type'] word = final_word to_complete = "%s.%s" % (prefix, word) if result.get('main_attr_replace', False): to_complete = var_segment.replace(attr_name, result['type'], 1) imports = [imp.split('.')[0] for imp in imports] data = completer.get_all_completions(to_complete, imports) # Move system attributes beginning in '__' (built_in_attribs) # to the end of the list. built_in_attribs = [d for d in data.get('attributes', []) if d[:2] == '__'] if built_in_attribs: for i in built_in_attribs: data['attributes'].remove(i) data['attributes'] += built_in_attribs if data: return data else: result = {'found': None, 'type': None} if result['type'] is not None and len(result['type']) > 0: data = {'attributes': result['type']['attributes'], 'functions': result['type']['functions']} else: clazzes = sorted(set(self.patClass.findall(code))) funcs = sorted(set(self.patFunction.findall(code))) attrs = sorted(set(self.patWords.split(code))) if final_word in attrs: attrs.remove(final_word) if attr_name in attrs: attrs.remove(attr_name) filter_attrs = lambda x: (x not in funcs) and \ not x.isdigit() and (x not in self.keywords) attrs = list(filter(filter_attrs, attrs)) funcs = [x for x in funcs if x not in clazzes] data = {'attributes': attrs, 'functions': funcs, 'classes': clazzes} return data ninja-ide-2.3/ninja_ide/tools/completion/completer.py000066400000000000000000000126061216641277400230300ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . #Based in pycomplete emacs module. from __future__ import absolute_import import sys import types #import inspect try: import StringIO except: import io as StringIO # lint:ok from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.tools.completion.completer') _HELPOUT = StringIO.StringIO _STDOUT = sys.stdout def get_completions_per_type(object_dir): '''Return info about function parameters''' if not object_dir: return {} result = {'attributes': [], 'modules': [], 'functions': [], 'classes': []} type_assign = {types.ClassType: 'classes', types.FunctionType: 'functions', types.MethodType: 'functions', types.ModuleType: 'modules', types.LambdaType: 'functions'} for attr in object_dir: obj = None sig = "" try: obj = _load_symbol(attr, globals(), locals()) except Exception as ex: logger.error('Could not load symbol: %r', ex) return {} if type(obj) in (types.ClassType, types.TypeType): # Look for the highest __init__ in the class chain. obj = _find_constructor(obj) elif isinstance(obj, types.MethodType): # bit of a hack for methods - turn it into a function # but we drop the "self" param. obj = obj.im_func # Not Show functions args, but we will use this for showing doc # if type(obj) in [types.FunctionType, types.LambdaType]: # (args, varargs, varkw, defaults) = inspect.getargspec(obj) # sig = ('%s%s' % (obj.__name__, # inspect.formatargspec(args, varargs, varkw, # defaults))) if not sig: sig = attr[attr.rfind('.') + 1:] result[type_assign.get(type(obj), 'attributes')].append(sig) return result def _load_symbol(s, dglobals, dlocals): sym = None dots = s.split('.') if not s or len(dots) == 1: sym = eval(s, dglobals, dlocals) else: for i in range(1, len(dots) + 1): s = '.'.join(dots[:i]) if not s: continue try: sym = eval(s, dglobals, dlocals) except NameError: try: sym = __import__(s, dglobals, dlocals, []) dglobals[s] = sym except ImportError: pass except AttributeError: try: sym = __import__(s, dglobals, dlocals, []) except ImportError: pass return sym def _import_modules(imports, dglobals): '''If given, execute import statements''' if imports is not None: for stmt in imports: try: exec(stmt, dglobals) except TypeError: raise TypeError('invalid type: %s' % stmt) except Exception: continue def get_all_completions(s, imports=None): '''Return contextual completion of s (string of >= zero chars)''' dlocals = {} #FIXXXXXXXXXXXXXXXX #return {} _import_modules(imports, globals()) dots = s.rsplit('.', 1) sym = None for i in range(1, len(dots)): s = '.'.join(dots[:i]) if not s: continue try: try: if s.startswith('PyQt4.') and s.endswith(')'): s = s[:s.rindex('(')] sym = eval(s, globals(), dlocals) except NameError: try: sym = __import__(s, globals(), dlocals, []) except ImportError: if s.find('(') != -1 and s[-1] == ')': s = s[:s.index('(')] sym = eval(s, globals(), dlocals) except AttributeError: try: sym = __import__(s, globals(), dlocals, []) except ImportError: pass except (AttributeError, NameError, TypeError, SyntaxError): return {} if sym is not None: var = s s = dots[-1] return get_completions_per_type(["%s.%s" % (var, k) for k in dir(sym) if k.startswith(s)]) return {} def _find_constructor(class_ob): # Given a class object, return a function object used for the # constructor (ie, __init__() ) or None if we can't find one. try: return class_ob.__init__.im_func except AttributeError: for base in class_ob.__bases__: rc = _find_constructor(base) if rc is not None: return rc return None ninja-ide-2.3/ninja_ide/tools/completion/completer_widget.py000066400000000000000000000305341216641277400243730ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import unicode_literals from PyQt4.QtGui import QApplication from PyQt4.QtGui import QTextCursor from PyQt4.QtGui import QFrame from PyQt4.QtGui import QCompleter from PyQt4.QtGui import QStackedLayout from PyQt4.QtGui import QListWidgetItem from PyQt4.QtGui import QIcon from PyQt4.QtCore import Qt from PyQt4.QtCore import SIGNAL from PyQt4.QtGui import QListWidget from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.tools.completion import code_completion class CodeCompletionWidget(QFrame): def __init__(self, editor): super(CodeCompletionWidget, self).__init__( None, Qt.FramelessWindowHint | Qt.ToolTip) self._editor = editor self._revision = 0 self._block = 0 self.stack_layout = QStackedLayout(self) self.stack_layout.setContentsMargins(0, 0, 0, 0) self.stack_layout.setSpacing(0) self.completion_list = QListWidget() self.completion_list.setMinimumHeight(200) self.completion_list.setAlternatingRowColors(True) self._list_index = self.stack_layout.addWidget(self.completion_list) self._icons = {'a': resources.IMAGES['attribute'], 'f': resources.IMAGES['function'], 'c': resources.IMAGES['class'], 'm': resources.IMAGES['module']} self.cc = code_completion.CodeCompletion() self._completion_results = {} self._prefix = '' self.setVisible(False) self.source = '' self._key_operations = { Qt.Key_Up: self._select_previous_row, Qt.Key_Down: self._select_next_row, Qt.Key_PageUp: (lambda: self._select_previous_row(6)), Qt.Key_PageDown: (lambda: self._select_next_row(6)), Qt.Key_Right: lambda: None, Qt.Key_Left: lambda: None, Qt.Key_Enter: self.pre_key_insert_completion, Qt.Key_Return: self.pre_key_insert_completion, Qt.Key_Tab: self.pre_key_insert_completion, Qt.Key_Space: self.hide_completer, Qt.Key_Escape: self.hide_completer, Qt.Key_Backtab: self.hide_completer, Qt.NoModifier: self.hide_completer, Qt.ShiftModifier: self.hide_completer, } self.desktop = QApplication.instance().desktop() self.connect(self.completion_list, SIGNAL("itemClicked(QListWidgetItem*)"), self.pre_key_insert_completion) self.connect(self._editor.document(), SIGNAL("cursorPositionChanged(QTextCursor)"), self.update_metadata) def _select_next_row(self, move=1): new_row = self.completion_list.currentRow() + move if new_row < self.completion_list.count(): self.completion_list.setCurrentRow(new_row) else: self.completion_list.setCurrentRow(0) return True def _select_previous_row(self, move=1): new_row = self.completion_list.currentRow() - move if new_row >= 0: self.completion_list.setCurrentRow(new_row) else: self.completion_list.setCurrentRow( self.completion_list.count() - move) return True def update_metadata(self, cursor): if settings.CODE_COMPLETION: if self._editor.document().revision() != self._revision and \ cursor.block().blockNumber() != self._block: source = self._editor.get_text() source = source.encode(self._editor.encoding) self.cc.analyze_file(self._editor.ID, source, self._editor.indent, self._editor.useTabs) self._revision = self._editor.document().revision() self._block = cursor.block().blockNumber() def insert_completion(self, insert, type_=ord('a')): if insert != self._prefix: closing = '' if type_ in (ord('f'), ord('c')): closing = '()' extra = len(self._prefix) - len(insert) insertion = '%s%s' % (insert[extra:], closing) self._editor.textCursor().insertText(insertion) self.hide_completer() def _get_geometry(self): cr = self._editor.cursorRect() desktop_geometry = self.desktop.availableGeometry(self._editor) point = self._editor.mapToGlobal(cr.topLeft()) cr.moveTopLeft(point) #Check new position according desktop geometry width = (self.completion_list.sizeHintForColumn(0) + self.completion_list.verticalScrollBar().sizeHint().width() + 10) height = 200 orientation = (point.y() + height) < desktop_geometry.height() if orientation: cr.moveTop(cr.bottom()) cr.setWidth(width) cr.setHeight(height) if not orientation: cr.moveBottom(cr.top()) xpos = desktop_geometry.width() - (point.x() + width) if xpos < 0: cr.moveLeft(cr.left() + xpos) return cr def complete(self, results): self.add_list_items(results) self.completion_list.setCurrentRow(0) cr = self._get_geometry() self.setGeometry(cr) self.completion_list.updateGeometries() self.show() def add_list_items(self, proposals): self.completion_list.clear() for p in proposals: self.completion_list.addItem( QListWidgetItem( QIcon( self._icons.get(p[0], resources.IMAGES['attribute'])), p[1], type=ord(p[0]))) def set_completion_prefix(self, prefix, valid=True): self._prefix = prefix proposals = [] proposals += [('m', item) for item in self._completion_results.get('modules', []) if item.startswith(prefix)] proposals += [('c', item) for item in self._completion_results.get('classes', []) if item.startswith(prefix)] proposals += [('a', item) for item in self._completion_results.get( 'attributes', []) if item.startswith(prefix)] proposals += [('f', item) for item in self._completion_results.get('functions', []) if item.startswith(prefix)] if proposals and valid: self.complete(proposals) else: self.hide_completer() def _invalid_completion_position(self): result = False cursor = self._editor.textCursor() cursor.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor) selection = cursor.selectedText()[:-1].split(' ') if len(selection) == 0 or selection[-1] == '' or \ selection[-1].isdigit(): result = True return result def fill_completer(self, force_completion=False): if not force_completion and (self._editor.cursor_inside_string() or self._editor.cursor_inside_comment() or self._invalid_completion_position()): return source = self._editor.get_text() source = source.encode(self._editor.encoding) offset = self._editor.textCursor().position() results = self.cc.get_completion(source, offset) self._completion_results = results if force_completion: cursor = self._editor.textCursor() cursor.movePosition(QTextCursor.StartOfWord, QTextCursor.KeepAnchor) prefix = cursor.selectedText() else: prefix = self._editor._text_under_cursor() self.set_completion_prefix(prefix) def hide_completer(self): self._prefix = '' self.hide() def pre_key_insert_completion(self): type_ = ord('a') current = self.completion_list.currentItem() insert = current.text() if not insert.endswith(')'): type_ = current.type() self.insert_completion(insert, type_) self.hide_completer() return True def process_pre_key_event(self, event): if not self.isVisible() or self._editor.lang != "python": return False skip = self._key_operations.get(event.key(), lambda: False)() self._key_operations.get(event.modifiers(), lambda: False)() if skip is None: skip = False return skip def process_post_key_event(self, event): if not settings.CODE_COMPLETION or self._editor.lang != "python": return if self.isVisible(): source = self._editor.get_text() source = source.encode(self._editor.encoding) offset = self._editor.textCursor().position() prefix, valid = self.cc.get_prefix(source, offset) self.set_completion_prefix(prefix, valid) self.completion_list.setCurrentRow(0) force_completion = (event.key() == Qt.Key_Space and event.modifiers() == Qt.ControlModifier) if event.key() == Qt.Key_Period or force_completion: self.fill_completer(force_completion) class CompleterWidget(QCompleter): def __init__(self, editor): QCompleter.__init__(self, [], editor) self._editor = editor self.setWidget(editor) self.setCompletionMode(QCompleter.PopupCompletion) self.setCaseSensitivity(Qt.CaseInsensitive) self.cc = code_completion.CodeCompletion() self.completion_results = {} self.connect(self, SIGNAL("activated(const QString&)"), self.insert_completion) def insert_completion(self, insert): self.widget().textCursor().insertText( insert[len(self.completionPrefix()):]) self.popup().hide() def complete(self, cr, results): proposals = [] proposals += results.get('modules', []) proposals += results.get('classes', []) proposals += results.get('attributes', []) proposals += results.get('functions', []) self.model().setStringList(proposals) self.popup().setCurrentIndex(self.model().index(0, 0)) cr.setWidth(self.popup().sizeHintForColumn(0) + self.popup().verticalScrollBar().sizeHint().width() + 10) QCompleter.complete(self, cr) def is_visible(self): return self.popup().isVisible() def process_pre_key_event(self, event): skip = False if event.key() in (Qt.Key_Enter, Qt.Key_Return, Qt.Key_Tab): event.ignore() self.popup().hide() skip = True elif event.key() in (Qt.Key_Space, Qt.Key_Escape, Qt.Key_Backtab): self.popup().hide() return skip def process_post_key_event(self, event): if self.popup().isVisible(): prefix = self._editor._text_under_cursor() self.setCompletionPrefix(prefix) self.popup().setCurrentIndex( self.completionModel().index(0, 0)) self.setCurrentRow(0) if event.key() == Qt.Key_Period or (event.key() == Qt.Key_Space and event.modifiers() == Qt.ControlModifier): self.fill_completer() def fill_completer(self): source = self._editor.get_text() source = source.encode(self._editor.encoding) # self.cc.analyze_file('', source) offset = self._editor.textCursor().position() results = self.cc.get_completion(source, offset) results.sort() self.completion_results = results cr = self._editor.cursorRect() self.complete(cr, results) ninja-ide-2.3/ninja_ide/tools/completion/completion_daemon.py000066400000000000000000000331361216641277400245330ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . import os import time from threading import Thread, Lock from multiprocessing import Process, Queue from ninja_ide.tools.completion import model from ninja_ide.tools.completion import completer from ninja_ide.tools.completion import analyzer try: unicode except NameError: # Python 3 basestring = unicode = str # lint:ok __completion_daemon_instance = None WAITING_BEFORE_START = 5 PROJECTS = {} def CompletionDaemon(): global __completion_daemon_instance if __completion_daemon_instance is None: __completion_daemon_instance = __CompletionDaemon() __completion_daemon_instance.start() return __completion_daemon_instance class __CompletionDaemon(Thread): def __init__(self): Thread.__init__(self) self.analyzer = analyzer.Analyzer() self.modules = {} self.projects_modules = {} self._relations = {} self.reference_counter = 0 self.keep_alive = True self.lock = Lock() self.queue_receive = Queue() self.queue_send = Queue() self.daemon = _DaemonProcess(self.queue_send, self.queue_receive) self.daemon.start() def run(self): global WAITING_BEFORE_START time.sleep(WAITING_BEFORE_START) while self.keep_alive: path_id, module, resolve = self.queue_receive.get() if path_id is None: continue self.lock.acquire() self.modules[path_id] = module self.lock.release() if resolve: resolution = self._resolve_with_other_modules(resolve) self._relations[path_id] = [] for package in resolution: self._relations[path_id].append(resolution[package]) self.queue_send.put((path_id, module, False, resolution)) def _resolve_with_other_modules(self, packages): resolution = {} for package in packages: if package.find('(') != -1: package = package[:package.index('(')] if self.projects_modules.get(package, False): folder = self.projects_modules[package] filename = os.path.join(folder, '__init__.py') if self._analyze_file(filename): resolution[package] = filename elif self.projects_modules.get(package.rsplit('.', 1)[0], False): name = package.rsplit('.', 1) folder = self.projects_modules[name[0]] filename = "%s.py" % os.path.join(folder, name[1]) if os.path.isfile(filename): if self._analyze_file(filename): resolution[package] = filename elif self.projects_modules.get(package.rsplit('.', 2)[0], False): name = package.rsplit('.', 2) folder = self.projects_modules[name[0]] filename = "%s.py" % os.path.join(folder, name[1]) if os.path.isfile(filename): if self._analyze_file(filename): resolution[package.rsplit('.', 1)[0]] = filename return resolution def _analyze_file(self, filename): try: if filename not in self.modules: source = '' with open(filename) as f: source = f.read() module = self.analyzer.analyze(source) self.inspect_module(filename, module, False) return True except Exception as reason: print(reason) return False def unload_module(self, path_id): relations = self._relations.pop(path_id, None) if relations is not None: relations.append(path_id) for module in relations: valid = False for rel in self._relations: other_modules = self._relations[rel] if module in other_modules: valid = True if not valid: self.modules.pop(module, None) def process_path(self): for project in PROJECTS: if PROJECTS[project]: continue project = os.path.abspath(project) package = os.path.basename(project) self.projects_modules[package] = project for root, dirs, files in os.walk(project, followlinks=True): if '__init__.py' in files: package = root[len(project) + 1:].replace( os.path.sep, '.') self.projects_modules[package] = root def inspect_module(self, path_id, module, recursive=True): self.lock.acquire() self.modules[path_id] = module self.lock.release() self.queue_send.put((path_id, module, recursive, None)) def get_module(self, path_id): return self.modules.get(path_id, None) def _shutdown_process(self): self.queue_send.put((None, None, None, None)) self.daemon.terminate() self.queue_receive.put((None, None, None)) def force_stop(self): self.keep_alive = False self._shutdown_process() for project in PROJECTS: PROJECTS[project] = False if self.is_alive(): self.join() class _DaemonProcess(Process): def __init__(self, queue_receive, queue_send): super(_DaemonProcess, self).__init__() self.queue_receive = queue_receive self.queue_send = queue_send self.iteration = 0 self.packages = [] def run(self): while True: self.iteration = 0 path_id, module, recursive, resolution = self.queue_receive.get() if path_id is None and module is None: break try: if resolution is not None: self.packages = resolution self.iteration = 2 self._resolve_module(module) elif module.need_resolution(): self._resolve_module(module) self.iteration = 1 self._resolve_module(module) else: continue if self.packages and recursive: self.queue_send.put((path_id, module, self.packages)) else: self.queue_send.put((path_id, module, [])) except Exception as reason: # Try to not die whatever happend message = 'Daemon Fail with: %r', reason print(message) raise finally: self.packages = [] def _resolve_module(self, module): self._resolve_attributes(module, module) self._resolve_functions(module, module) for cla in module.classes: clazz = module.classes[cla] self._resolve_inheritance(clazz, module) self._resolve_attributes(clazz, module) self._resolve_functions(clazz, module) def _resolve_inheritance(self, clazz, module): for base in clazz.bases: name = base.split('.', 1) main_attr = name[0] child_attrs = '' if len(name) == 2: child_attrs = name[1] result = module.get_type(main_attr, child_attrs) data = model.late_resolution if result.get('found', True): data_type = module.imports[main_attr].get_data_type() if child_attrs: child_attrs = '.%s' % child_attrs name = '%s%s().' % (data_type, child_attrs) imports = module.get_imports() imports = [imp.split('.')[0] for imp in imports] data = completer.get_all_completions(name, imports) data = (name, data) elif result.get('object', False).__class__ is model.Clazz: data = result['object'] clazz.bases[base] = data clazz.update_with_parent_data() def _resolve_functions(self, structure, module): if structure.__class__ is model.Assign: return for func in structure.functions: function = structure.functions[func] self._resolve_attributes(function, module) self._resolve_functions(function, module) self._resolve_returns(function, module) def _resolve_returns(self, structure, module): if structure.__class__ is model.Assign: return self._resolve_types(structure.return_type, module, structure, 'return') def _resolve_attributes(self, structure, module): if structure.__class__ is model.Assign: return for attr in structure.attributes: assign = structure.attributes[attr] self._resolve_types(assign.data, module, assign) def _resolve_types(self, types, module, structure=None, split_by='='): if self.iteration == 0: self._resolve_with_imports(types, module, split_by) self._resolve_with_local_names(types, module, split_by) elif self.iteration == 1: self._resolve_with_local_vars(types, module, split_by, structure) else: self._resolve_with_linked_modules(types, module, structure) def _resolve_with_linked_modules(self, types, module, structure): for data in types: name = data.data_type if not isinstance(name, basestring): continue for package in self.packages: if name.startswith(package): to_resolve = name[len(package):] if to_resolve and to_resolve[0] == '.': to_resolve = to_resolve[1:] path = self.packages[package] linked = model.LinkedModule(path, to_resolve) data.data_type = linked break def _resolve_with_imports(self, types, module, splitby): for data in types: if data.data_type != model.late_resolution: continue line = data.line_content value = line.split(splitby)[1].strip().split('.') name = value[0] extra = '' if name.find('(') != -1: extra = name[name.index('('):] name = name[:name.index('(')] if name in module.imports: value[0] = module.imports[name].data_type package = '.'.join(value) resolve = "%s%s" % (package, extra) data.data_type = resolve self.packages.append(package) def _resolve_with_local_names(self, types, module, splitby): #TODO: resolve with functions returns for data in types: if data.data_type != model.late_resolution: continue line = data.line_content value = line.split(splitby)[1].split('(')[0].strip() if value in module.classes: clazz = module.classes[value] data.data_type = clazz def _resolve_with_local_vars(self, types, module, splitby, structure=None): for data in types: if data.data_type != model.late_resolution: continue line = data.line_content value = line.split(splitby)[1].split('(')[0].strip() sym = value.split('.') if len(sym) != 0: main_attr = sym[0] if len(sym) > 2: child_attr = '.'.join(sym[1:]) elif len(sym) == 2: child_attr = sym[1] else: child_attr = '' scope = [] self._get_scope(structure, scope) if structure.__class__ is model.Assign: scope.pop(0) scope.reverse() result = module.get_type(main_attr, child_attr, scope) data_type = model.late_resolution if isinstance(result['type'], basestring) and len(result) < 3: if child_attr and \ structure.__class__ is not model.Function: data_type = "%s.%s" % (result['type'], child_attr) else: data_type = result['type'] elif result.get('object', False): data_type = result['object'] if data is not None: data.data_type = data_type def _get_scope(self, structure, scope): if structure.__class__ not in (None, model.Module): scope.append(structure.name) self._get_scope(structure.parent, scope) def shutdown_daemon(): daemon = CompletionDaemon() daemon.force_stop() global __completion_daemon_instance __completion_daemon_instance = None def add_project_folder(project_path): global PROJECTS if project_path not in PROJECTS: PROJECTS[project_path] = False daemon = CompletionDaemon() daemon.process_path() ninja-ide-2.3/ninja_ide/tools/completion/model.py000066400000000000000000000405701216641277400221370ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . try: unicode except NameError: # Python 3 basestring = unicode = str # lint:ok MODULES = None late_resolution = 0 def filter_data_type(data_types): occurrences = {} for type_ in data_types: if isinstance(type_, basestring): item = occurrences.get(type_, [0, type_]) item[0] += 1 occurrences[type_] = item else: item = occurrences.get(type_, [0, type_]) item[0] += 1 occurrences[type_.name] = item values = [occurrences[key][0] for key in occurrences] maximum = max(values) data_type = [occurrences[key][1] for key in occurrences if occurrences[key][0] == maximum] return data_type[0] def remove_function_arguments(line): #TODO: improve line analysis using tokenizer to get the lines of the text while line.find('(') != -1: start = line.find('(') end = line.find(')') + 1 if start == -1 or end == 0 or end <= start: break line = line[:start] + line[end:] return line class _TypeData(object): def __init__(self, lineno, data_type, line_content, oper): self.lineno = lineno self.data_type = data_type self.line_content = line_content if data_type != late_resolution: oper = None self.operation = oper self.from_import = False if isinstance(data_type, str): self.is_native = True else: self.is_native = False self.can_resolve = True def get_data_type(self): return self.data_type def __eq__(self, other): return other.line_content == self.line_content def __repr__(self): return repr(self.data_type) class Structure(object): def __init__(self): self.attributes = {} self.functions = {} self.parent = None def add_function(self, function): function.parent = self self.functions[function.name] = function def add_attributes(self, attributes): #attributes = {name: [(lineno, type),...]} for attribute in attributes: if attribute[0] in self.attributes: assign = self.attributes[attribute[0]] else: assign = Assign(attribute[0]) assign.add_data(*attribute[1:]) assign.parent = self self.attributes[assign.name] = assign def update_attributes(self, attributes): for name in self.attributes: if name in attributes: assign = self.attributes[name] old_assign = attributes[name] for type_data in assign.data: if type_data in old_assign.data: old_type = old_assign.data[ old_assign.data.index(type_data)] if old_type.data_type.__class__ is not Clazz: type_data.data_type = old_type.data_type def update_functions(self, functions): for func_name in self.functions: if func_name in functions: old_func = functions[func_name] if old_func.__class__ is Assign: continue function = self.functions[func_name] function.update_functions(old_func.functions) function.update_attributes(old_func.attributes) # Function Arguments for arg in function.args: if arg in old_func.args: argument_type = function.args[arg].data[0] old_type = old_func.args[arg].data[0] argument_type.data_type = old_type.data_type # Function Returns for type_data in function.return_type: if type_data in old_func.return_type: old_type = old_func.return_type[ old_func.return_type.index(type_data)] type_data.data_type = old_type.data_type def get_attribute_type(self, name): """Return a tuple with:(Found, Type)""" result = {'found': False, 'type': None} var_names = name.split('.') attr = self.attributes.get(var_names[0], None) if attr is not None: result['found'], result['type'] = True, attr.get_data_type() elif self.parent.__class__ is Function: result = self.parent.get_attribute_type(name) return result def _get_scope_structure(self, structure, scope): struct = structure if len(scope) > 0: scope_name = scope[0] new_struct = structure.functions.get(scope_name, None) struct = self._get_scope_structure(new_struct, scope[1:]) return struct def _resolve_attribute(self, type_data, attrs): object_type = type_data.get_data_type() return (True, object_type) def recursive_search_type(self, structure, attrs, scope): result = {'found': False, 'type': None} structure = self._get_scope_structure(structure, scope) if structure and structure.__class__ is not Assign: attr_name = attrs[0] data_type = structure.get_attribute_type(attr_name) result = data_type return result class Module(Structure): def __init__(self): super(Module, self).__init__() self.imports = {} self.classes = {} def add_imports(self, imports): for imp in imports: line_content = "import %s" % imp[1] info = _TypeData(None, imp[1], line_content, None) self.imports[imp[0]] = info def add_class(self, clazz): clazz.parent = self self.classes[clazz.name] = clazz def update_classes(self, classes): for clazz_name in self.classes: if clazz_name in classes: clazz = self.classes[clazz_name] clazz.update_functions(classes[clazz_name].functions) clazz.update_attributes(classes[clazz_name].attributes) def get_type(self, main_attr, child_attrs='', scope=None): result = {'found': False, 'type': None} canonical_attrs = remove_function_arguments(child_attrs) if not scope: value = self.imports.get(main_attr, self.attributes.get(main_attr, self.functions.get( main_attr, self.classes.get( main_attr, None)))) if value is not None and value.__class__ is not Clazz: data_type = value.get_data_type() result['found'], result['type'] = True, data_type if child_attrs or (isinstance(data_type, basestring) and data_type.endswith(main_attr)): result['main_attr_replace'] = True elif value.__class__ is Clazz: result['found'], result['type'] = False, value elif main_attr == 'self': clazz_name = scope[0] clazz = self.classes.get(clazz_name, None) if clazz is not None: result = clazz.get_attribute_type(canonical_attrs) if canonical_attrs == '' and clazz is not None: items = clazz.get_completion_items() result['found'], result['type'] = False, items elif scope: scope_name = scope[0] structure = self.classes.get(scope_name, self.functions.get(scope_name, None)) if structure is not None: attrs = [main_attr] + canonical_attrs.split('.') if len(attrs) > 1 and attrs[1] == '': del attrs[1] result = self.recursive_search_type( structure, attrs, scope[1:]) if not result['found']: value = self.imports.get(main_attr, self.attributes.get( main_attr, self.functions.get(main_attr, None))) if value is not None: data_type = value.get_data_type() result['found'], result['type'] = True, data_type if result['type'].__class__ is Clazz: if canonical_attrs: attrs = canonical_attrs.split('.') if attrs[-1] == '': attrs.pop(-1) result = self._search_type(result['type'], attrs) else: result = {'found': False, 'type': result['type'].get_completion_items(), 'object': result['type']} elif result['type'].__class__ is LinkedModule: if main_attr == 'self': attrs = canonical_attrs.split('.', 1) canonical_attrs = '' if len(attrs) > 1: canonical_attrs = attrs[1] result = result['type'].get_type(canonical_attrs) return result def _search_type(self, structure, attrs): result = {'found': False, 'type': None} if not attrs: return result attr = attrs[0] value = structure.attributes.get(attr, structure.functions.get(attr, None)) if value is None: return result data_type = value.get_data_type() if data_type.__class__ is Clazz and len(attrs) > 1: result = self._search_type(data_type, attrs[1:]) elif data_type.__class__ is Clazz: items = data_type.get_completion_items() result['found'], result['type'] = False, items result['object'] = data_type elif isinstance(data_type, basestring): result['found'], result['type'] = True, data_type result['object'] = data_type return result def get_imports(self): module_imports = ['import __builtin__'] for name in self.imports: module_imports.append(self.imports[name].line_content) return module_imports def need_resolution(self): if self._check_attr_func_resolution(self): return True for cla in self.classes: clazz = self.classes[cla] for parent in clazz.bases: if clazz.bases[parent] is None: return True if self._check_attr_func_resolution(clazz): return True return False def _check_attr_func_resolution(self, structure): for attr in structure.attributes: attribute = structure.attributes[attr] for d in attribute.data: if d.data_type == late_resolution: return True for func in structure.functions: function = structure.functions[func] for attr in function.attributes: attribute = function.attributes[attr] for d in attribute.data: if d.data_type == late_resolution: return True for ret in function.return_type: if ret.data_type == late_resolution: return True return False class Clazz(Structure): def __init__(self, name): super(Clazz, self).__init__() self.name = name self.bases = {} self._update_bases = [] # self.decorators = [] def add_parent(self, parent): self._update_bases.append(parent) value = self.bases.get(parent, None) if value is None: self.bases[parent] = None def update_bases(self): to_remove = [] for parent in self.bases: if parent not in self._update_bases: to_remove.append(parent) for remove in to_remove: self.bases.pop(parent) def get_completion_items(self): attributes = [a for a in self.attributes] functions = [f for f in self.functions] if attributes or functions: attributes.sort() functions.sort() return {'attributes': attributes, 'functions': functions} return None def update_with_parent_data(self): for base in self.bases: parent = self.bases[base] if parent.__class__ is Clazz: self.attributes.update(parent.attributes) self.functions.update(parent.functions) self.bases[base] = None elif isinstance(parent, tuple): parent_name = parent[0] data = parent[1] attributes = {} functions = {} for attr in data.get('attributes', []): if attr[:2] == '__' and attr[-2:] == '__': continue assign = Assign(attr) assign.add_data(0, parent_name + attr, '', parent_name + attr) attributes[attr] = assign for func in data.get('functions', []): if func[:2] == '__' and func[-2:] == '__': continue assign = Assign(func) assign.add_data(0, parent_name + attr, '', parent_name + attr) functions[func] = assign self.attributes.update(attributes) self.functions.update(functions) class Function(Structure): def __init__(self, name): super(Function, self).__init__() self.name = name self.args = {} self.decorators = [] self.return_type = [] def add_return(self, lineno, data_type, line_content, oper): info = _TypeData(lineno, data_type, line_content, oper) if info not in self.return_type: self.return_type.append(info) def get_data_type(self): possible = [d.data_type for d in self.return_type if d.data_type is not late_resolution and d.data_type is not None] if possible: return filter_data_type(possible) else: return None class Assign(object): def __init__(self, name): self.name = name self.data = [] self.parent = None def add_data(self, lineno, data_type, line_content, oper): info = _TypeData(lineno, data_type, line_content, oper) if info not in self.data: self.data.append(info) def get_data_type(self): possible = [d.data_type for d in self.data if d.data_type is not late_resolution] if possible: return filter_data_type(possible) else: return None class LinkedModule(object): def __init__(self, path, attrs): self.name = path self.resolve_attrs = remove_function_arguments(attrs) def get_type(self, resolve=''): result = {'found': False, 'type': None} global MODULES module = MODULES.get(self.name, None) if module: if resolve: to_resolve = "%s.%s" % (self.resolve_attrs, resolve) else: to_resolve = self.resolve_attrs to_resolve = to_resolve.split('.', 1) main_attr = to_resolve[0] child_attr = '' if len(to_resolve) == 2: child_attr = to_resolve[1] result = module.get_type(main_attr, child_attr) return result ninja-ide-2.3/ninja_ide/tools/console.py000066400000000000000000000060641216641277400203300ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import sys from code import InteractiveConsole class Cache(object): """Replace stdout and stderr behavior in order to collect outputs.""" def __init__(self): self.reset() def reset(self): """Clean the cache.""" self.out = [] def write(self, line): """Collect the output into cache to be accesed later.""" self.out.append(line) def flush(self): """Join together all the outputs and return it to be displayed.""" if len(self.out) > 1: output = ''.join(self.out)[:-1] self.reset() return output class ExitWrapper(object): """Wrap the built-in function exit in the interpreter.""" def __call__(self, code=None): return None def __repr__(self): return exit.__repr__() class HelpWrapper(object): """Wrap the built-in function help in the interpreter.""" def __call__(self, *args, **kwds): if args or kwds: return help(*args, **kwds) def __repr__(self): return help.__repr__() class Console(InteractiveConsole): """Work as a Python Console.""" def __init__(self): InteractiveConsole.__init__(self, locals={'exit': ExitWrapper(), 'help': HelpWrapper()}) self.stdout = sys.stdout self.stderr = sys.stderr self._cache = Cache() self.output = '' def get_output(self): """Replace out and error channels with cache.""" sys.stdout = self._cache sys.stderr = self._cache def return_output(self): """Reassign the proper values to output and error channel.""" sys.stdout = self.stdout sys.stderr = self.stderr def push(self, line): """Insert a command into the console.""" self.get_output() val = InteractiveConsole.push(self, line) self.return_output() self.output = self._cache.flush() return val def get_type(self, var): """Get the type of a variable.""" type_line = "'.'.join([type(%s).__module__, type(%s).__name__])" % \ (var[:-1], var[:-1]) self.get_output() InteractiveConsole.push(self, type_line) self.return_output() exec_line = self._cache.flush() + '.' exec_line = exec_line.replace("'", "") return exec_line ninja-ide-2.3/ninja_ide/tools/debugger.py000066400000000000000000000017451216641277400204530ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import bdb class Debugger(bdb.Bdb): def __init__(self, breakpoints): bdb.Bdb.__init__(self) #Apply breakpoints for bp in breakpoints: self.set_break(bp[0], bp[1]) def run(self, code): bdb.Bdb.run(self, code) ninja-ide-2.3/ninja_ide/tools/get_system_path.py000066400000000000000000000015431216641277400220620ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . #This file is not for NINJA-IDE internal use, but to execute it using #the System Python and obtain the correct sys.path for frozen applications import sys print(sys.path) ninja-ide-2.3/ninja_ide/tools/introspection.py000066400000000000000000000177051216641277400215720ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . import _ast import ast from ninja_ide.tools.completion import analyzer from ninja_ide.tools.logger import NinjaLogger logger_imports = NinjaLogger( 'ninja_ide.tools.introspection.obtaining_imports') logger_symbols = NinjaLogger( 'ninja_ide.tools.introspection.obtainint_symbols') _map_type = { _ast.Tuple: 'tuple', _ast.List: 'list', _ast.Str: 'str', _ast.Dict: 'dict', _ast.Num: 'int', _ast.Call: 'function()', } def _parse_assign(symbol): assigns = {} attributes = {} for var in symbol.targets: if var.__class__ == ast.Attribute: attributes[var.attr] = var.lineno elif var.__class__ == ast.Name: assigns[var.id] = var.lineno return (assigns, attributes) def _parse_class(symbol, with_docstrings): docstring = {} attr = {} func = {} clazz = {} name = symbol.name + '(' name += ', '.join([ analyzer.expand_attribute(base) for base in symbol.bases]) name += ')' for sym in symbol.body: if sym.__class__ is ast.Assign: result = _parse_assign(sym) attr.update(result[0]) attr.update(result[1]) elif sym.__class__ is ast.FunctionDef: result = _parse_function(sym, with_docstrings) attr.update(result['attrs']) if with_docstrings: docstring.update(result['docstring']) func[result['name']] = {'lineno': result['lineno'], 'functions': result['functions']} elif sym.__class__ is ast.ClassDef: result = _parse_class(sym, with_docstrings) clazz[result['name']] = {'lineno': result['lineno'], 'members': {'attributes': result['attributes'], 'functions': result['functions']}} docstring.update(result['docstring']) if with_docstrings: docstring[symbol.lineno] = ast.get_docstring(symbol, clean=True) lineno = symbol.lineno for decorator in symbol.decorator_list: lineno += 1 return {'name': name, 'attributes': attr, 'functions': func, 'lineno': lineno, 'docstring': docstring, 'classes': clazz} def _parse_function(symbol, with_docstrings): docstring = {} attrs = {} func = {'functions': {}} func_name = symbol.name + '(' #We store the arguments to compare with default backwards defaults = [] for value in symbol.args.defaults: #TODO: In some cases we can have something like: a=os.path defaults.append(value) arguments = [] for arg in reversed(symbol.args.args): if arg.__class__ is not _ast.Name or arg.id == 'self': continue argument = arg.id if defaults: value = defaults.pop() arg_default = _map_type.get(value.__class__, None) if arg_default is None: if value.__class__ is _ast.Attribute: arg_default = analyzer.expand_attribute(value) elif value.__class__ is _ast.Name: arg_default = value.id else: arg_default = 'object' argument += '=' + arg_default arguments.append(argument) func_name += ', '.join(reversed(arguments)) if symbol.args.vararg is not None: if not func_name.endswith('('): func_name += ', ' func_name += '*' + symbol.args.vararg if symbol.args.kwarg is not None: if not func_name.endswith('('): func_name += ', ' func_name += '**' + symbol.args.kwarg func_name += ')' for sym in symbol.body: if sym.__class__ is ast.Assign: result = _parse_assign(sym) attrs.update(result[1]) elif sym.__class__ is ast.FunctionDef: result = _parse_function(sym, with_docstrings) if with_docstrings: docstring.update(result['docstring']) func['functions'][result['name']] = {'lineno': result['lineno'], 'functions': result['functions']} if with_docstrings: docstring[symbol.lineno] = ast.get_docstring(symbol, clean=True) lineno = symbol.lineno for decorator in symbol.decorator_list: lineno += 1 return {'name': func_name, 'lineno': lineno, 'attrs': attrs, 'docstring': docstring, 'functions': func} def obtain_symbols(source, with_docstrings=False, filename=''): """Parse a module source code to obtain: Classes, Functions and Assigns.""" try: module = ast.parse(source) except: logger_symbols.debug("The file contains syntax errors: %s" % filename) return {} symbols = {} globalAttributes = {} globalFunctions = {} classes = {} docstrings = {} for symbol in module.body: if symbol.__class__ is ast.Assign: result = _parse_assign(symbol) globalAttributes.update(result[0]) globalAttributes.update(result[1]) elif symbol.__class__ is ast.FunctionDef: result = _parse_function(symbol, with_docstrings) if with_docstrings: docstrings.update(result['docstring']) globalFunctions[result['name']] = {'lineno': result['lineno'], 'functions': result['functions']} elif symbol.__class__ is ast.ClassDef: result = _parse_class(symbol, with_docstrings) classes[result['name']] = {'lineno': result['lineno'], 'members': {'attributes': result['attributes'], 'functions': result['functions'], 'classes': result['classes']}} docstrings.update(result['docstring']) if globalAttributes: symbols['attributes'] = globalAttributes if globalFunctions: symbols['functions'] = globalFunctions if classes: symbols['classes'] = classes if docstrings and with_docstrings: symbols['docstrings'] = docstrings return symbols def obtain_imports(source='', body=None): if source: try: module = ast.parse(source) body = module.body except: logger_imports.debug("A file contains syntax errors.") #Imports{} = {name: asname}, for example = {sys: sysAlias} imports = {} #From Imports{} = {name: {module: fromPart, asname: nameAlias}} fromImports = {} for sym in body: if type(sym) is ast.Import: for item in sym.names: imports[item.name] = {'asname': item.asname, 'lineno': sym.lineno} if type(sym) is ast.ImportFrom: for item in sym.names: fromImports[item.name] = {'module': sym.module, 'asname': item.asname, 'lineno': sym.lineno} return {'imports': imports, 'fromImports': fromImports} ninja-ide-2.3/ninja_ide/tools/json_manager.py000066400000000000000000000105431216641277400213260ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . import os import json from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.tools.json_manager') def parse(descriptor): try: return json.load(descriptor) except: logger.error("The file couldn't be parsed'") logger.error(descriptor) return {} def read_json(arg_): structure = dict() fileName = None if os.path.isdir(arg_): path = arg_ json_file = get_ninja_json_file(path) fileName = os.path.join(path, json_file) if os.path.isfile(arg_): fileName = arg_ if fileName is None: return structure with open(fileName, 'r') as fp: try: structure = json.load(fp) except Exception as exc: logger.error('Error reading Ninja File %s' % fileName) logger.error(exc) return structure return structure def read_json_from_stream(stream): structure = json.load(stream) return structure def write_json(structure, filename, indent=2): with open(filename, 'w') as fp: json.dump(structure, fp, indent) def load_syntax(): empty = dict() files = os.listdir(resources.SYNTAX_FILES) for f in files: if not f.endswith('.json'): continue fname = os.path.join(resources.SYNTAX_FILES, f) structure = read_json(fname) if structure == empty: continue name = f[:-5] settings.SYNTAX[name] = structure for ext in structure.get('extension'): if ext is not None: settings.EXTENSIONS[ext] = name def create_ninja_project(path, project, structure): projectName = project.lower().strip().replace(' ', '_') + '.nja' fileName = os.path.join(path, projectName) with open(fileName, mode='w') as fp: json.dump(structure, fp, indent=2) def get_ninja_file(path, extension, only_first=False): files = os.listdir(path) if not extension.startswith('.'): extension = '.'.join(extension) nja = list([y for y in files if y.endswith(extension)]) if only_first: nja = nja[0] if nja else None return nja if nja else [] def get_ninja_json_file(path): extension = '.json' return get_ninja_file(path, extension, only_first=True) def get_ninja_plugin_file(path): extension = '.plugin' return get_ninja_file(path, extension, only_first=True) def get_ninja_project_file(path): extension = '.nja' return get_ninja_file(path, extension, only_first=True) def get_ninja_editor_skins_files(path): extension = '.color' return get_ninja_file(path, extension) def read_ninja_project(path): empty = dict() project_file = get_ninja_project_file(path) if not project_file: return empty return read_json(os.path.join(path, project_file)) def read_ninja_plugin(path): empty = dict() plugin_file = get_ninja_plugin_file(path) if plugin_file is None: return empty return read_json(os.path.join(path, plugin_file)) def load_editor_skins(): skins = dict() files = get_ninja_editor_skins_files(resources.EDITOR_SKINS) for fname in files: file_name = os.path.join(resources.EDITOR_SKINS, fname) structure = read_json(file_name) if structure is None: continue name = fname[:-6] skins[name] = structure return skins def save_editor_skins(filename, scheme): with open(filename, 'w') as fp: try: json.dump(scheme, fp, indent=2) except Exception as exc: logger.error('Error writing file %s' % filename) logger.error(exc) ninja-ide-2.3/ninja_ide/tools/locator.py000066400000000000000000001055071216641277400203330ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import re try: import Queue except: import queue as Queue # lint:ok from PyQt4.QtGui import QMessageBox from PyQt4.QtGui import QLabel from PyQt4.QtGui import QBrush from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QFrame from PyQt4.QtGui import QIcon from PyQt4.QtGui import QListWidgetItem from PyQt4.QtGui import QListWidget from PyQt4.QtGui import QStyle from PyQt4.QtGui import QLineEdit from PyQt4.QtGui import QWidget from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QPushButton from PyQt4.QtCore import QObject from PyQt4.QtCore import QSize from PyQt4.QtCore import QThread from PyQt4.QtCore import Qt from PyQt4.QtCore import QDir from PyQt4.QtCore import QFile from PyQt4.QtCore import QTextStream from PyQt4.QtCore import SIGNAL from ninja_ide import resources from ninja_ide.gui.explorer import explorer_container from ninja_ide.gui.misc import misc_container from ninja_ide.gui.main_panel import main_container from ninja_ide.core import file_manager from ninja_ide.core import settings from ninja_ide.tools import json_manager from ninja_ide.tools.logger import NinjaLogger logger = NinjaLogger('ninja_ide.tools.locator') mapping_locations = {} #@ FILES #< CLASSES #> FUNCTIONS #- MODULE ATTRIBUTES #! NO PYTHON FILES #. SYMBOLS IN THIS FILE #/ TABS OPENED #: LINE NUMBER FILTERS = { 'files': '@', 'classes': '<', 'functions': '>', 'attribs': '-', 'non-python': '!', 'this-file': '.', 'tabs': '/', 'lines': ':'} class Locator(QObject): """This class is used Go To Definition feature.""" def __init__(self): QObject.__init__(self) self._thread = LocateThread() self.connect(self._thread, SIGNAL("finished()"), self._load_results) self.connect(self._thread, SIGNAL("finished()"), self._cleanup) self.connect(self._thread, SIGNAL("terminated()"), self._cleanup) def _cleanup(self): self._thread.wait() def navigate_to(self, function, filePath, isVariable): self._thread.find(function, filePath, isVariable) def _load_results(self): if len(self._thread.results) == 1: main_container.MainContainer().open_file( filename=self._thread.results[0][1], cursorPosition=self._thread.results[0][2], positionIsLineNumber=True) elif len(self._thread.results) == 0: QMessageBox.information(main_container.MainContainer(), self.tr("Definition Not Found"), self.tr("This Definition does not " "belong to this Project.")) else: misc_container.MiscContainer().show_results(self._thread.results) def get_classes_from_project(self, projectPath): global mapping_locations filesFromProject = [filePath for filePath in mapping_locations if filePath.startswith( projectPath)] classes = [item for key in filesFromProject for item in mapping_locations[key] if item[0] == FILTERS['classes']] return classes class ResultItem(object): """The Representation of each item found with the locator.""" def __init__(self, type='', name='', path='', lineno=-1): if name: self.type = type # Function, Class, etc self.name = name self.path = path self.lineno = lineno self.comparison = self.name index = self.name.find('(') if index != -1: self.comparison = self.name[:index] else: raise TypeError("name is not a string or unicode.") def __str__(self): return self.name def __len__(self): return len(self.name) def __iter__(self): for i in self.name: yield i def __getitem__(self, index): return self.name[index] class LocateThread(QThread): def __init__(self): QThread.__init__(self) self.results = [] self._cancel = False self.locations = [] self.execute = self.go_to_definition self.dirty = False self._search = None self._isVariable = None def find(self, search, filePath, isVariable): self.cancel() self.execute = self.go_to_definition self._filePath = filePath self._search = search self._isVariable = isVariable self._cancel = False self.start() def find_code_location(self): self.cancel() self._cancel = False if not self.isRunning(): global mapping_locations mapping_locations = {} self.execute = self.locate_code self.start() def find_file_code_location(self, path): self._file_path = path if not self._file_path: return if not self.isRunning(): self.execute = self.locate_file_code self.start() def run(self): self.execute() self._cancel = False self._search = None self._isVariable = None def locate_code(self): explorerContainer = explorer_container.ExplorerContainer() projects_obj = explorerContainer.get_opened_projects() projects = [p.path for p in projects_obj] if not projects: return while not self._cancel and projects: current_dir = QDir(projects.pop()) #Skip not readable dirs! if not current_dir.isReadable(): continue project_data = json_manager.read_ninja_project( current_dir.path()) extensions = project_data.get('supported-extensions', settings.SUPPORTED_EXTENSIONS) queue_folders = Queue.Queue() queue_folders.put(current_dir) self.__locate_code_in_project(queue_folders, extensions) self.dirty = True self.get_locations() def __locate_code_in_project(self, queue_folders, extensions): file_filter = QDir.Files | QDir.NoDotAndDotDot | QDir.Readable dir_filter = QDir.Dirs | QDir.NoDotAndDotDot | QDir.Readable while not self._cancel and not queue_folders.empty(): current_dir = QDir(queue_folders.get()) #Skip not readable dirs! if not current_dir.isReadable(): continue #Collect all sub dirs! current_sub_dirs = current_dir.entryInfoList(dir_filter) for one_dir in current_sub_dirs: queue_folders.put(one_dir.absoluteFilePath()) #all files in sub_dir first apply the filters current_files = current_dir.entryInfoList( ['*{0}'.format(x) for x in extensions], file_filter) #process all files in current dir! for one_file in current_files: try: self._grep_file_locate(one_file.absoluteFilePath(), one_file.fileName()) except Exception as reason: logger.error( '__locate_code_in_project, error: %r' % reason) logger.error( '__locate_code_in_project fail for file: %r' % one_file.absoluteFilePath()) def locate_file_code(self): file_name = file_manager.get_basename(self._file_path) try: self._grep_file_locate(self._file_path, file_name) self.dirty = True self.execute = self.locate_code except Exception as reason: logger.error('locate_file_code, error: %r' % reason) def go_to_definition(self): self.dirty = True self.results = [] locations = self.get_locations() if self._isVariable: preResults = [ [file_manager.get_basename(x.path), x.path, x.lineno, ''] for x in locations if (x.type == FILTERS['attribs']) and (x.name == self._search)] else: preResults = [ [file_manager.get_basename(x.path), x.path, x.lineno, ''] for x in locations if ((x.type == FILTERS['functions']) or (x.type == FILTERS['classes'])) and (x.name.startswith(self._search))] for data in preResults: file_object = QFile(data[1]) if not file_object.open(QFile.ReadOnly): return stream = QTextStream(file_object) line_index = 0 line = stream.readLine() while not self._cancel and not stream.atEnd(): if line_index == data[2]: data[3] = line self.results.append(data) break #take the next line! line = stream.readLine() line_index += 1 self._search = None self._isVariable = None def get_locations(self): if self.dirty: self.convert_map_to_array() self.dirty = False return self.locations def get_this_file_locations(self, path): global mapping_locations thisFileLocations = mapping_locations.get(path, ()) try: if not thisFileLocations: file_name = file_manager.get_basename(path) self._grep_file_locate(path, file_name) thisFileLocations = mapping_locations.get(path, ()) thisFileLocations = sorted(thisFileLocations[1:], key=lambda item: item.name) except Exception as reason: logger.error('get_this_file_locations, error: %r' % reason) return thisFileLocations def convert_map_to_array(self): global mapping_locations self.locations = [x for location in mapping_locations for x in mapping_locations[location]] self.locations = sorted(self.locations, key=lambda item: item.name) def _grep_file_locate(self, file_path, file_name): #type - file_name - file_path global mapping_locations #TODO: Check if the last know state of the file is valid and load that exts = settings.SYNTAX.get('python')['extension'] file_ext = file_manager.get_file_extension(file_path) if file_ext not in exts: mapping_locations[file_path] = [ ResultItem(type=FILTERS['non-python'], name=file_name, path=file_path, lineno=0)] else: mapping_locations[file_path] = [ ResultItem(type=FILTERS['files'], name=file_name, path=file_path, lineno=0)] #obtain a symbols handler for this file extension symbols_handler = settings.get_symbols_handler(file_ext) if symbols_handler is None: return results = [] with open(file_path) as f: content = f.read() symbols = symbols_handler.obtain_symbols(content, filename=file_path) self.__parse_symbols(symbols, results, file_path) if results: mapping_locations[file_path] += results def __parse_symbols(self, symbols, results, file_path): if "classes" in symbols: self.__parse_class(symbols, results, file_path) if 'attributes' in symbols: self.__parse_attributes(symbols, results, file_path) if 'functions' in symbols: self.__parse_functions(symbols, results, file_path) def __parse_class(self, symbols, results, file_path): for claz in symbols['classes']: line_number = symbols['classes'][claz]['lineno'] - 1 members = symbols['classes'][claz]['members'] results.append(ResultItem(type=FILTERS['classes'], name=claz, path=file_path, lineno=line_number)) if 'attributes' in members: for attr in members['attributes']: line_number = members['attributes'][attr] - 1 results.append(ResultItem(type=FILTERS['attribs'], name=attr, path=file_path, lineno=line_number)) if 'functions' in members: for func in members['functions']: line_number = members['functions'][func]['lineno'] - 1 results.append(ResultItem( type=FILTERS['functions'], name=func, path=file_path, lineno=line_number)) self.__parse_symbols( members['functions'][func]['functions'], results, file_path) if 'classes' in members: self.__parse_class(members, results, file_path) def __parse_attributes(self, symbols, results, file_path): for attr in symbols['attributes']: line_number = symbols['attributes'][attr] - 1 results.append(ResultItem(type=FILTERS['attribs'], name=attr, path=file_path, lineno=line_number)) def __parse_functions(self, symbols, results, file_path): for func in symbols['functions']: line_number = symbols['functions'][func]['lineno'] - 1 results.append(ResultItem( type=FILTERS['functions'], name=func, path=file_path, lineno=line_number)) self.__parse_symbols(symbols['functions'][func]['functions'], results, file_path) def get_symbols_for_class(self, file_path, clazzName): results = [] with open(file_path) as f: content = f.read() ext = file_manager.get_file_extension(file_path) #obtain a symbols handler for this file extension symbols_handler = settings.get_symbols_handler(ext) symbols = symbols_handler.obtain_symbols(content, filename=file_path) self.__parse_symbols(symbols, results, file_path) return results def cancel(self): self._cancel = True self.results = [] self.locations = [] class CodeLocatorWidget(QWidget): def __init__(self, parent): QWidget.__init__(self, parent) #Parent is StatusBar self.statusBar = parent self._thread = LocateThread() self._thread.execute = self._thread.locate_code hLocator = QHBoxLayout(self) hLocator.setContentsMargins(0, 0, 0, 0) self._btnClose = QPushButton( self.style().standardIcon(QStyle.SP_DialogCloseButton), '') self._btnGo = QPushButton( self.style().standardIcon(QStyle.SP_ArrowRight), self.tr('Go!')) self._completer = LocateCompleter(self) hLocator.addWidget(self._btnClose) hLocator.addWidget(self._completer) hLocator.addWidget(self._btnGo) self.connect(self._thread, SIGNAL("finished()"), self._cleanup) self.connect(self._thread, SIGNAL("terminated()"), self._cleanup) def _cleanup(self): self._thread.wait() def explore_code(self): self._thread.find_code_location() def explore_file_code(self, path): self._thread.find_file_code_location(path) def show_suggestions(self): self._completer.complete() def setVisible(self, val): if self._completer.frame: self._completer.frame.setVisible(False) self._completer.setText('') QWidget.setVisible(self, val) class LocateItem(QListWidgetItem): """Create QListWidgetItem that contains the proper icon and file data.""" icons = {FILTERS['functions']: resources.IMAGES['function'], FILTERS['files']: resources.IMAGES['tree-python'], FILTERS['classes']: resources.IMAGES['class'], FILTERS['non-python']: resources.IMAGES['tree-code'], FILTERS['attribs']: resources.IMAGES['attribute']} def __init__(self, data): QListWidgetItem.__init__(self, QIcon(self.icons[data.type]), "\n") self._data = data class LocateWidget(QLabel): """Create a styled QLabel that will show the info.""" def __init__(self, data): QLabel.__init__(self) self.name = data.name self.path = data.path locator_name = resources.CUSTOM_SCHEME.get('locator-name', resources.COLOR_SCHEME[ 'locator-name']) locator_path = resources.CUSTOM_SCHEME.get('locator-path', resources.COLOR_SCHEME[ 'locator-path']) self.setText( "{0}
" "({1})".format( data.name, data.path, locator_name, locator_path)) def set_selected(self): locator_name = resources.CUSTOM_SCHEME.get( 'locator-name-selected', resources.COLOR_SCHEME[ 'locator-name-selected']) locator_path = resources.CUSTOM_SCHEME.get( 'locator-path-selected', resources.COLOR_SCHEME['locator-path-selected']) self.setText( "{0}
" "({1})".format( self.name, self.path, locator_name, locator_path)) def set_not_selected(self): locator_name = resources.CUSTOM_SCHEME.get('locator-name', resources.COLOR_SCHEME[ 'locator-name']) locator_path = resources.CUSTOM_SCHEME.get('locator-path', resources.COLOR_SCHEME[ 'locator-path']) self.setText( "{0}
" "({1})".format( self.name, self.path, locator_name, locator_path)) class LocateCompleter(QLineEdit): def __init__(self, parent): QLineEdit.__init__(self, parent) self._parent = parent self.__prefix = '' self.frame = PopupCompleter() self.filterPrefix = re.compile(r'^(@|<|>|-|!|\.|/|:)(\s)*') self.advancePrefix = re.compile(r'(@|<|>|-|!|/|:)') self.tempLocations = [] self.setMinimumWidth(700) self.items_in_page = 0 self.page_items_step = 10 self._filterData = [None, None, None, None] self._line_jump = -1 self.connect(self, SIGNAL("textChanged(QString)"), self.set_prefix) self.connect(self.frame.listWidget, SIGNAL("itemPressed(QListWidgetItem*)"), self._go_to_location) def set_prefix(self, prefix): """Set the prefix for the completer.""" self.__prefix = prefix.lower() self._refresh_filter() def complete(self): self.frame.reload(self.filter()) self.frame.setFixedWidth(self.width()) point = self._parent.mapToGlobal(self.pos()) self.frame.show() self.frame.move(point.x(), point.y() - self.frame.height()) def _create_list_widget_items(self, locations): """Create a list of items (using pages for results to speed up).""" #The list is regenerated when the locate metadata is updated #for example: open project, etc. #Create the list items begin = self.items_in_page self.items_in_page += self.page_items_step locations_view = [(LocateItem(x), LocateWidget(x)) for x in locations[begin:self.items_in_page]] return locations_view def filter(self): self._line_jump = -1 self.items_in_page = 0 #Clean the objects from the listWidget inCurrentFile = False filterOptions = self.advancePrefix.split(self.__prefix.lstrip()) if filterOptions[0] == '': del filterOptions[0] if len(filterOptions) > 2: if FILTERS['files'] in (filterOptions[1], filterOptions[2]): self._advanced_filter_by_file(filterOptions) else: self._advanced_filter(filterOptions) return self._create_list_widget_items(self.tempLocations) # Clear frame after advance filter because advance filter # ask for the first element in the popup self.tempLocations = [] self.frame.clear() #if the user type any of the prefix if self.filterPrefix.match(self.__prefix): filterOption = self.__prefix[:1] main = main_container.MainContainer() #if the prefix is "." it means only the metadata of current file if filterOption == FILTERS['this-file']: inCurrentFile = True editorWidget = main.get_actual_editor() if editorWidget: self.tempLocations = \ self._parent._thread.get_this_file_locations( editorWidget.ID) self.__prefix = self.__prefix[1:].lstrip() self.tempLocations = [x for x in self.tempLocations if x.comparison.lower().find( self.__prefix) > -1] elif filterOption == FILTERS['tabs']: tab1, tab2 = main.get_opened_documents() opened = tab1 + tab2 self.tempLocations = [ResultItem( FILTERS['files'], file_manager.get_basename(f[0]), f[0]) for f in opened] self.__prefix = self.__prefix[1:].lstrip() elif filterOption == FILTERS['lines']: editorWidget = main.get_actual_editor() self.tempLocations = [ x for x in self._parent._thread.get_locations() if x.type == FILTERS['files'] and x.path == editorWidget.ID] inCurrentFile = True if filterOptions[1].isdigit(): self._line_jump = int(filterOptions[1]) - 1 else: #Is not "." filter by the other options self.tempLocations = [ x for x in self._parent._thread.get_locations() if x.type == filterOption] #Obtain the user input without the filter prefix self.__prefix = self.__prefix[1:].lstrip() else: self.tempLocations = self._parent._thread.get_locations() if self.__prefix and not inCurrentFile: #if prefix (user search now) is not empty, filter words that1 #contain the user input self.tempLocations = [x for x in self.tempLocations if x.comparison.lower().find( self.__prefix) > -1] return self._create_list_widget_items(self.tempLocations) def _advanced_filter(self, filterOptions): was_this_file = filterOptions[0] == FILTERS['this-file'] if was_this_file: previous_filter = filterOptions[0] filterOptions[0] = FILTERS['files'] main = main_container.MainContainer() editorWidget = main.get_actual_editor() if editorWidget: filterOptions.insert(1, editorWidget.ID) if previous_filter == FILTERS['lines']: filterOptions.insert(2, ':') elif filterOptions[0] in ( FILTERS['classes'], FILTERS['files'], FILTERS['tabs']): currentItem = self.frame.listWidget.currentItem() if type(currentItem) is LocateItem: if currentItem._data.type in (FILTERS['files'], FILTERS['classes']): self._filterData = currentItem._data if filterOptions[0] == FILTERS['classes']: filterOptions.insert(0, FILTERS['files']) filterOptions.insert(1, self._filterData.path) else: filterOptions[1] = self._filterData.path if was_this_file and len(filterOptions) > 4: currentItem = self.frame.listWidget.currentItem() if type(currentItem) is LocateItem: if currentItem._data.type == FILTERS['classes']: self._filterData = currentItem._data global mapping_locations filePath = filterOptions[1] moveIndex = 0 if len(filterOptions) > 4 and filterOptions[2] == FILTERS['classes']: moveIndex = 2 if self._filterData.type == FILTERS['classes']: self._classFilter = self._filterData.name symbols = self._parent._thread.get_symbols_for_class( filePath, self._classFilter) self.tempLocations = [x for x in symbols if x.type == filterOptions[4]] elif len(filterOptions) == 4 and filterOptions[2] == FILTERS['lines']: self.tempLocations = [ x for x in self.tempLocations if x.path == filePath] if filterOptions[3].isdigit(): self._line_jump = int(filterOptions[3]) - 1 return else: self.tempLocations = [ x for x in mapping_locations.get(filePath, []) if x.type == filterOptions[2]] moveIndex += 3 if len(filterOptions) > moveIndex and filterOptions[moveIndex]: self.tempLocations = [x for x in self.tempLocations if x.comparison.lower().find( filterOptions[moveIndex]) > -1] def _advanced_filter_by_file(self, filterOptions): if filterOptions[1] == FILTERS['files']: index = 2 else: index = 3 self.tempLocations = [x for x in self.tempLocations if file_manager.get_basename( x.path).lower().find( filterOptions[index]) > -1] def _refresh_filter(self): has_text = len(self.text()) != 0 self.frame.refresh(self.filter(), has_text) def keyPressEvent(self, event): if event.key() == Qt.Key_Space: item = self.frame.listWidget.currentItem() self.setText(item._data.comparison) return QLineEdit.keyPressEvent(self, event) currentRow = self.frame.listWidget.currentRow() if event.key() == Qt.Key_Down: count = self.frame.listWidget.count() #If the current position is greater than the amount of items in #the list - 6, then try to fetch more items in the list. if currentRow >= (count - 6): locations = self._create_list_widget_items(self.tempLocations) self.frame.fetch_more(locations) #While the current position is lower that the list size go to next if currentRow != count - 1: self.frame.listWidget.setCurrentRow( self.frame.listWidget.currentRow() + 1) elif event.key() == Qt.Key_Up: #while the current position is greater than 0, go to previous if currentRow > 0: self.frame.listWidget.setCurrentRow( self.frame.listWidget.currentRow() - 1) elif event.key() in (Qt.Key_Return, Qt.Key_Enter): #If the user press enter, go to the item selected item = self.frame.listWidget.currentItem() self._go_to_location(item) def _go_to_location(self, item): if type(item) is LocateItem: self._open_item(item._data) self._parent.statusBar.hide_status() def focusOutEvent(self, event): """Hide Popup on focus lost.""" self._parent.statusBar.hide_status() QLineEdit.focusOutEvent(self, event) def _open_item(self, data): """Open the item received.""" main = main_container.MainContainer() if file_manager.get_file_extension(data.path) in ('jpg', 'png'): main.open_image(data.path) else: if self._line_jump != -1: main.open_file(data.path, self._line_jump, None, True) else: main.open_file(data.path, data.lineno, None, True) class PopupCompleter(QFrame): def __init__(self): QFrame.__init__(self, None, Qt.FramelessWindowHint | Qt.ToolTip) vbox = QVBoxLayout(self) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) self.listWidget = QListWidget() self.listWidget.setMinimumHeight(350) vbox.addWidget(self.listWidget) self.listWidget.currentItemChanged.connect(self._repaint_items) def _repaint_items(self, current, previous): if current is not None: widget = self.listWidget.itemWidget(current) if widget is not None: widget.set_selected() if previous is not None: widget = self.listWidget.itemWidget(previous) if widget is not None: widget.set_not_selected() def reload(self, model): """Reload the data of the Popup Completer, and restart the state.""" self.listWidget.clear() self.add_help() for item in model: self.listWidget.addItem(item[0]) self.listWidget.setItemWidget(item[0], item[1]) self.listWidget.setCurrentRow(8) def clear(self): """Remove all the items of the list (deleted), and reload the help.""" self.listWidget.clear() def refresh(self, model, has_text=True): """Refresh the list when the user search for some word.""" self.listWidget.clear() if not has_text: self.add_help() for item in model: self.listWidget.addItem(item[0]) self.listWidget.setItemWidget(item[0], item[1]) if model: self.listWidget.setCurrentItem(model[0][0]) else: self.add_no_found() def fetch_more(self, model): """Add more items to the list on user scroll.""" for item in model: self.listWidget.addItem(item[0]) self.listWidget.setItemWidget(item[0], item[1]) def add_no_found(self): #Load no results found message noFoundItem = QListWidgetItem( QIcon(resources.IMAGES['delete']), 'No results were found!') font = noFoundItem.font() font.setBold(True) noFoundItem.setSizeHint(QSize(20, 30)) noFoundItem.setBackground(QBrush(Qt.lightGray)) noFoundItem.setForeground(QBrush(Qt.black)) noFoundItem.setFont(font) self.listWidget.addItem(noFoundItem) def add_help(self): #Load help fileItem = QListWidgetItem( QIcon(resources.IMAGES['locate-file']), '@\t(Filter only by Files)') font = fileItem.font() font.setBold(True) fileItem.setSizeHint(QSize(20, 30)) fileItem.setBackground(QBrush(Qt.lightGray)) fileItem.setForeground(QBrush(Qt.black)) fileItem.setFont(font) self.listWidget.addItem(fileItem) classItem = QListWidgetItem( QIcon(resources.IMAGES['locate-class']), '<\t(Filter only by Classes)') self.listWidget.addItem(classItem) classItem.setSizeHint(QSize(20, 30)) classItem.setBackground(QBrush(Qt.lightGray)) classItem.setForeground(QBrush(Qt.black)) classItem.setFont(font) methodItem = QListWidgetItem( QIcon(resources.IMAGES['locate-function']), '>\t(Filter only by Methods)') self.listWidget.addItem(methodItem) methodItem.setSizeHint(QSize(20, 30)) methodItem.setBackground(QBrush(Qt.lightGray)) methodItem.setForeground(QBrush(Qt.black)) methodItem.setFont(font) attributeItem = QListWidgetItem( QIcon(resources.IMAGES['locate-attributes']), '-\t(Filter only by Attributes)') self.listWidget.addItem(attributeItem) attributeItem.setSizeHint(QSize(20, 30)) attributeItem.setBackground(QBrush(Qt.lightGray)) attributeItem.setForeground(QBrush(Qt.black)) attributeItem.setFont(font) thisFileItem = QListWidgetItem( QIcon(resources.IMAGES['locate-on-this-file']), '.\t(Filter only by Classes and Methods in this File)') font = thisFileItem.font() font.setBold(True) thisFileItem.setSizeHint(QSize(20, 30)) thisFileItem.setBackground(QBrush(Qt.lightGray)) thisFileItem.setForeground(QBrush(Qt.black)) thisFileItem.setFont(font) self.listWidget.addItem(thisFileItem) tabsItem = QListWidgetItem( QIcon(resources.IMAGES['locate-tab']), '/\t(Filter only by the current Tabs)') font = tabsItem.font() font.setBold(True) tabsItem.setSizeHint(QSize(20, 30)) tabsItem.setBackground(QBrush(Qt.lightGray)) tabsItem.setForeground(QBrush(Qt.black)) tabsItem.setFont(font) self.listWidget.addItem(tabsItem) lineItem = QListWidgetItem( QIcon(resources.IMAGES['locate-line']), ':\t(Go to Line)') font = lineItem.font() font.setBold(True) lineItem.setSizeHint(QSize(20, 30)) lineItem.setBackground(QBrush(Qt.lightGray)) lineItem.setForeground(QBrush(Qt.black)) lineItem.setFont(font) self.listWidget.addItem(lineItem) nonPythonItem = QListWidgetItem( QIcon(resources.IMAGES['locate-nonpython']), '!\t(Filter only by Non Python Files)') self.listWidget.addItem(nonPythonItem) nonPythonItem.setSizeHint(QSize(20, 30)) nonPythonItem.setBackground(QBrush(Qt.lightGray)) nonPythonItem.setForeground(QBrush(Qt.black)) nonPythonItem.setFont(font) ninja-ide-2.3/ninja_ide/tools/logger.py000066400000000000000000000044441216641277400201450ustar00rootroot00000000000000# -*- coding: utf-8 *-* import logging import sys from ninja_ide import resources NOLOG = 100 LOG_FORMAT = "%(asctime)s %(name)s:%(lineno)-4d %(levelname)-8s %(message)s" TIME_FORMAT = '%Y-%m-%d %H:%M:%S' class Logger(object): """ General logger """ def __init__(self): self._loggers = {} self._default_level = NOLOG self._handler = None logging.basicConfig() super(Logger, self).__init__() def __call__(self, modname): if not self._handler: self.add_handler(resources.LOG_FILE_PATH, 'w', LOG_FORMAT, TIME_FORMAT) if modname not in self._loggers: logger = logging.getLogger(modname) self._loggers[modname] = logger logger.setLevel(self._default_level) logger.addHandler(self._handler) return self._loggers[modname] def dissable(self): for each_log in list(self._loggers.values()): each_log.setLevel(NOLOG) def setLevel(self, level): self._default_level = level for each_log in list(self._loggers.values()): each_log.setLevel(level) def add_handler(self, hfile, mode, log_format, time_format, stream=None): formatter = logging.Formatter(log_format, time_format) if stream: handler = logging.StreamHandler(hfile) else: handler = logging.FileHandler(hfile, mode) handler.setFormatter(formatter) for each_log in list(self._loggers.values()): each_log.addHandler(handler) self._handler = handler def argparse(self, log_level, log_file): if log_level: if log_level in ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'): log_level = getattr(logging, log_level) self.setLevel(log_level) if log_file: if log_file == "STDOUT": self.add_handler(sys.stdout, None, LOG_FORMAT, TIME_FORMAT, True) if log_file == "STDERR": self.add_handler(sys.stdout, None, LOG_FORMAT, TIME_FORMAT, True) else: self.add_handler(log_file, 'w', LOG_FORMAT, TIME_FORMAT) NinjaLogger = Logger() ninja-ide-2.3/ninja_ide/tools/runner.py000066400000000000000000000025601216641277400201740ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . import code import subprocess from PyQt4.QtNetwork import QHostAddress from PyQt4.QtNetwork import QTcpServer def run_code(codes): interpreter = code.InteractiveInterpreter() interpreter.runcode(codes) def run_code_from_file(fileName): subprocess.Popen(['python', fileName]) def isAvailable(port): server = QTcpServer() result = server.listen(QHostAddress("127.0.0.1"), port) server.close() return result def start_pydoc(): port = 6452 retry = 10 while retry > 0 and not isAvailable(port): retry -= 1 port += 10 proc = subprocess.Popen(['pydoc', '-p', str(port)]), \ ('http://127.0.0.1:' + str(port) + '/') return proc ninja-ide-2.3/ninja_ide/tools/ui_tools.py000066400000000000000000000545361216641277400205320ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import os import math from PyQt4.QtGui import QApplication from PyQt4.QtGui import QWidget from PyQt4.QtGui import QAction from PyQt4.QtGui import QCompleter from PyQt4.QtGui import QKeyEvent from PyQt4.QtGui import QLineEdit from PyQt4.QtGui import QLabel from PyQt4.QtGui import QMovie from PyQt4.QtGui import QSizePolicy from PyQt4.QtGui import QListWidget from PyQt4.QtGui import QLinearGradient from PyQt4.QtGui import QTableWidgetItem from PyQt4.QtGui import QAbstractItemView from PyQt4.QtGui import QPrinter from PyQt4.QtGui import QPrintPreviewDialog from PyQt4.QtGui import QPalette from PyQt4.QtGui import QPainter from PyQt4.QtGui import QBrush from PyQt4.QtGui import QPixmap from PyQt4.QtGui import QIcon from PyQt4.QtGui import QPen from PyQt4.QtGui import QColor from PyQt4.QtGui import QDialog from PyQt4.QtGui import QTreeWidget from PyQt4.QtGui import QTreeWidgetItem from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QPushButton from PyQt4.QtCore import Qt from PyQt4.QtCore import QSize from PyQt4.QtCore import QObject from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import QThread from PyQt4.QtCore import QEvent from PyQt4.QtCore import QTimeLine from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.core import file_manager from ninja_ide.core.file_manager import NinjaIOException from ninja_ide.tools import json_manager def load_table(table, headers, data, checkFirstColumn=True): table.setHorizontalHeaderLabels(headers) table.horizontalHeader().setStretchLastSection(True) table.setSelectionBehavior(QAbstractItemView.SelectRows) for i in range(table.rowCount()): table.removeRow(0) for r, row in enumerate(data): table.insertRow(r) for index, colItem in enumerate(row): item = QTableWidgetItem(colItem) table.setItem(r, index, item) if index == 0 and checkFirstColumn: item.setData(Qt.UserRole, row) item.setCheckState(Qt.Unchecked) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsUserCheckable) else: item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) def remove_get_selected_items(table, data): rows = table.rowCount() pos = rows - 1 selected = [] for i in range(rows): if (table.item(pos - i, 0) is not None and table.item(pos - i, 0).checkState() == Qt.Checked): selected.append(data.pop(pos - i)) table.removeRow(pos - i) return selected class LoadingItem(QLabel): def __init__(self): super(LoadingItem, self).__init__() self.movie = QMovie(resources.IMAGES['loading']) self.setMovie(self.movie) self.movie.setScaledSize(QSize(16, 16)) self.movie.start() def add_item_to_tree(self, folder, tree, item_type=None, parent=None): if item_type is None: item = QTreeWidgetItem() item.setText(0, (self.tr(' LOADING: "%s"') % folder)) else: item = item_type(parent, ( self.tr(' LOADING: "%s"') % folder), folder) tree.addTopLevelItem(item) tree.setItemWidget(item, 0, self) return item ############################################################################### # Thread with Callback ############################################################################### class ThreadExecution(QThread): def __init__(self, functionInit=None, args=None, kwargs=None): super(ThreadExecution, self).__init__() QThread.__init__(self) self.execute = functionInit self.result = None self.storage_values = None self.args = args if args is not None else [] self.kwargs = kwargs if kwargs is not None else {} self.signal_return = None def run(self): if self.execute: self.result = self.execute(*self.args, **self.kwargs) self.emit(SIGNAL("executionFinished(PyQt_PyObject)"), self.signal_return) self.signal_return = None class ThreadProjectExplore(QThread): def __init__(self): super(ThreadProjectExplore, self).__init__() self.execute = lambda: None self._folder_path = None self._item = None self._extensions = None def open_folder(self, folder): self._folder_path = folder self.execute = self._thread_open_project self.start() def refresh_project(self, path, item, extensions): self._folder_path = path self._item = item self._extensions = extensions self.execute = self._thread_refresh_project self.start() def run(self): self.execute() def _thread_refresh_project(self): if self._extensions != settings.SUPPORTED_EXTENSIONS: folderStructure = file_manager.open_project_with_extensions( self._folder_path, self._extensions) else: try: folderStructure = file_manager.open_project(self._folder_path) except NinjaIOException: pass # There is not much we can do at this point if folderStructure and (folderStructure.get( self._folder_path, [None, None])[1] is not None): folderStructure[self._folder_path][1].sort() values = (self._folder_path, self._item, folderStructure) self.emit(SIGNAL("folderDataRefreshed(PyQt_PyObject)"), values) def _thread_open_project(self): try: project = json_manager.read_ninja_project(self._folder_path) extensions = project.get('supported-extensions', settings.SUPPORTED_EXTENSIONS) if extensions != settings.SUPPORTED_EXTENSIONS: structure = file_manager.open_project_with_extensions( self._folder_path, extensions) else: structure = file_manager.open_project(self._folder_path) self.emit(SIGNAL("folderDataAcquired(PyQt_PyObject)"), (self._folder_path, structure)) except: self.emit(SIGNAL("folderDataAcquired(PyQt_PyObject)"), (self._folder_path, None)) ############################################################################### # LOADING ANIMATION OVER THE WIDGET ############################################################################### class Overlay(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) palette = QPalette(self.palette()) palette.setColor(palette.Background, Qt.transparent) self.setPalette(palette) self.counter = 0 def paintEvent(self, event): painter = QPainter() painter.begin(self) painter.setRenderHint(QPainter.Antialiasing) painter.fillRect(event.rect(), QBrush(QColor(255, 255, 255, 127))) painter.setPen(QPen(Qt.NoPen)) for i in range(6): x_pos = self.width() / 2 + 30 * \ math.cos(2 * math.pi * i / 6.0) - 10 y_pos = self.height() / 2 + 30 * \ math.sin(2 * math.pi * i / 6.0) - 10 if (self.counter / 5) % 6 == i: linear_gradient = QLinearGradient( x_pos + 10, x_pos, y_pos + 10, y_pos) linear_gradient.setColorAt(0, QColor(135, 206, 250)) linear_gradient.setColorAt(1, QColor(0, 0, 128)) painter.setBrush(QBrush(linear_gradient)) else: linear_gradient = QLinearGradient( x_pos - 10, x_pos, y_pos + 10, y_pos) linear_gradient.setColorAt(0, QColor(105, 105, 105)) linear_gradient.setColorAt(1, QColor(0, 0, 0)) painter.setBrush(QBrush(linear_gradient)) painter.drawEllipse( x_pos, y_pos, 20, 20) painter.end() def showEvent(self, event): self.timer = self.startTimer(50) def timerEvent(self, event): self.counter += 1 self.update() ############################################################################### # PRINT FILE ############################################################################### def print_file(fileName, printFunction): """This method print a file This method print a file, fileName is the default fileName, and printFunction is a funcion that takes a QPrinter object and print the file, the print method More info on:http://doc.qt.nokia.com/latest/printing.html""" printer = QPrinter(QPrinter.HighResolution) printer.setPageSize(QPrinter.A4) printer.setOutputFileName(fileName) printer.setDocName(fileName) preview = QPrintPreviewDialog(printer) preview.paintRequested[QPrinter].connect(printFunction) size = QApplication.instance().desktop().screenGeometry() width = size.width() - 100 height = size.height() - 100 preview.setMinimumSize(width, height) preview.exec_() ############################################################################### # FADING ANIMATION ############################################################################### class FaderWidget(QWidget): def __init__(self, old_widget, new_widget): QWidget.__init__(self, new_widget) self.old_pixmap = QPixmap(new_widget.size()) old_widget.render(self.old_pixmap) self.pixmap_opacity = 1.0 self.timeline = QTimeLine() self.timeline.valueChanged.connect(self.animate) self.timeline.finished.connect(self.close) self.timeline.setDuration(500) self.timeline.start() self.resize(new_widget.size()) self.show() def paintEvent(self, event): painter = QPainter() painter.begin(self) painter.setOpacity(self.pixmap_opacity) painter.drawPixmap(0, 0, self.old_pixmap) painter.end() def animate(self, value): self.pixmap_opacity = 1.0 - value self.repaint() ############################################################################### # ADD TO PROJECT ############################################################################### class AddToProject(QDialog): def __init__(self, pathProjects, parent=None): #pathProjects must be a list QDialog.__init__(self, parent) self.setWindowTitle(self.tr("Add File to Project")) self.pathSelected = '' vbox = QVBoxLayout(self) self._tree = QTreeWidget() self._tree.header().setHidden(True) self._tree.setSelectionMode(QTreeWidget.SingleSelection) self._tree.setAnimated(True) vbox.addWidget(self._tree) hbox = QHBoxLayout() btnAdd = QPushButton(self.tr("Add here!")) btnCancel = QPushButton(self.tr("Cancel")) hbox.addWidget(btnCancel) hbox.addWidget(btnAdd) vbox.addLayout(hbox) #load folders self._root = None self._loading_items = {} self.loading_projects(pathProjects) self._thread_execution = ThreadExecution( self._thread_load_projects, args=[pathProjects]) self.connect(self._thread_execution, SIGNAL("finished()"), self._callback_load_project) self._thread_execution.start() self.connect(btnCancel, SIGNAL("clicked()"), self.close) self.connect(btnAdd, SIGNAL("clicked()"), self._select_path) def loading_projects(self, projects): for project in projects: loadingItem = LoadingItem() item = loadingItem.add_item_to_tree(project, self._tree, parent=self) self._loading_items[project] = item def _thread_load_projects(self, projects): structures = [] for pathProject in projects: folderStructure = file_manager.open_project(pathProject) structures.append((folderStructure, pathProject)) self._thread_execution.storage_values = structures def _callback_load_project(self): structures = self._thread_execution.storage_values if structures: for structure, path in structures: item = self._loading_items.pop(path, None) if item is not None: index = self._tree.indexOfTopLevelItem(item) self._tree.takeTopLevelItem(index) self._load_project(structure, path) def _select_path(self): item = self._tree.currentItem() if item: self.pathSelected = item.toolTip(0) self.close() def _load_project(self, folderStructure, folder): if not folder: return name = file_manager.get_basename(folder) item = QTreeWidgetItem(self._tree) item.setText(0, name) item.setToolTip(0, folder) item.setIcon(0, QIcon(resources.IMAGES['tree-folder'])) if folderStructure[folder][1] is not None: folderStructure[folder][1].sort() self._load_folder(folderStructure, folder, item) item.setExpanded(True) self._root = item def _load_folder(self, folderStructure, folder, parentItem): items = folderStructure[folder] if items[1] is not None: items[1].sort() for _file in items[1]: if _file.startswith('.'): continue subfolder = QTreeWidgetItem(parentItem) subfolder.setText(0, _file) subfolder.setToolTip(0, os.path.join(folder, _file)) subfolder.setIcon(0, QIcon(resources.IMAGES['tree-folder'])) self._load_folder(folderStructure, os.path.join(folder, _file), subfolder) ############################################################################### # PROFILE WIDGET ############################################################################### class ProfilesLoader(QDialog): def __init__(self, load_func, create_func, save_func, profiles, parent=None): QDialog.__init__(self, parent, Qt.Dialog) self.setWindowTitle(self.tr("Profile Manager")) self.setMinimumWidth(400) self._profiles = profiles self.load_function = load_func self.create_function = create_func self.save_function = save_func self.ide = parent vbox = QVBoxLayout(self) vbox.addWidget(QLabel(self.tr("Save your opened files and projects " "into a profile and change really" "quick between projects and" "files sessions.\n This allows you to " "save your working environment, " "keep working in another\n" "project and then go back " "exactly where you left."))) self.profileList = QListWidget() self.profileList.addItems([key for key in profiles]) self.profileList.setCurrentRow(0) self.contentList = QListWidget() self.btnDelete = QPushButton(self.tr("Delete Profile")) self.btnDelete.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.btnUpdate = QPushButton(self.tr("Update Profile")) self.btnUpdate.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.btnCreate = QPushButton(self.tr("Create New Profile")) self.btnCreate.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.btnOpen = QPushButton(self.tr("Open Profile")) self.btnOpen.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.btnOpen.setDefault(True) hbox = QHBoxLayout() hbox.addWidget(self.btnDelete) hbox.addWidget(self.btnUpdate) hbox.addWidget(self.btnCreate) hbox.addWidget(self.btnOpen) vbox.addWidget(self.profileList) vbox.addWidget(self.contentList) vbox.addLayout(hbox) self.connect(self.profileList, SIGNAL("itemSelectionChanged()"), self.load_profile_content) self.connect(self.btnOpen, SIGNAL("clicked()"), self.open_profile) self.connect(self.btnUpdate, SIGNAL("clicked()"), self.save_profile) self.connect(self.btnCreate, SIGNAL("clicked()"), self.create_profile) self.connect(self.btnDelete, SIGNAL("clicked()"), self.delete_profile) def load_profile_content(self): item = self.profileList.currentItem() self.contentList.clear() if item is not None: key = item.text() files = [self.tr('Files:')] + \ [file[0] for file in self._profiles[key][0]] projects = [self.tr('Projects:')] + self._profiles[key][1] content = files + projects self.contentList.addItems(content) def create_profile(self): profileName = self.create_function() self.ide.Profile = profileName self.close() def save_profile(self): if self.profileList.currentItem(): profileName = self.profileList.currentItem().text() self.save_function(profileName) self.ide.show_status_message(self.tr("Profile %s Updated!") % profileName) self.load_profile_content() def open_profile(self): if self.profileList.currentItem(): key = self.profileList.currentItem().text() self.load_function(key) self.ide.Profile = key self.close() def delete_profile(self): if self.profileList.currentItem(): key = self.profileList.currentItem().text() self._profiles.pop(key) self.profileList.takeItem(self.profileList.currentRow()) self.contentList.clear() ############################################################################### # Enhanced UI Widgets ############################################################################### class LineEditButton(object): def __init__(self, lineEdit, operation, icon=None): hbox = QHBoxLayout(lineEdit) hbox.setMargin(0) lineEdit.setLayout(hbox) hbox.addStretch() btnOperation = QPushButton(lineEdit) btnOperation.setObjectName('line_button') if icon: btnOperation.setIcon(QIcon(icon)) hbox.addWidget(btnOperation) btnOperation.clicked.connect(operation) class ComboBoxButton(object): def __init__(self, combo, operation, icon=None): hbox = QHBoxLayout(combo) hbox.setDirection(hbox.RightToLeft) hbox.setMargin(0) combo.setLayout(hbox) hbox.addStretch() btnOperation = QPushButton(combo) btnOperation.setObjectName('combo_button') if icon: btnOperation.setIcon(QIcon(icon)) hbox.addWidget(btnOperation) btnOperation.clicked.connect(operation) class LineEditCount(QObject): def __init__(self, lineEdit): QObject.__init__(self) hbox = QHBoxLayout(lineEdit) hbox.setMargin(0) lineEdit.setLayout(hbox) hbox.addStretch() self.counter = QLabel(lineEdit) hbox.addWidget(self.counter) lineEdit.setStyleSheet("padding-right: 2px;") lineEdit.setTextMargins(0, 0, 60, 0) def update_count(self, index, total, hasSearch=False): message = self.tr("%s of %s") % (index, total) self.counter.setText(message) self.counter.setStyleSheet("background: none;color: gray;") if index == 0 and total == 0 and hasSearch: self.counter.setStyleSheet( "background: #e73e3e;color: white;border-radius: 5px;") class LineEditTabCompleter(QLineEdit): def __init__(self, completer, type=QCompleter.PopupCompletion): QLineEdit.__init__(self) self.completer = completer self.setTextMargins(0, 0, 5, 0) self.completionType = type self.completer.setCompletionMode(self.completionType) def event(self, event): if (event.type() == QEvent.KeyPress) and (event.key() == Qt.Key_Tab): if self.completionType == QCompleter.InlineCompletion: eventTab = QKeyEvent(QEvent.KeyPress, Qt.Key_End, Qt.NoModifier) super(LineEditTabCompleter, self).event(eventTab) else: completion = self.completer.currentCompletion() if os.path.isdir(completion): completion += os.path.sep self.selectAll() self.insert(completion) self.completer.popup().hide() return True return super(LineEditTabCompleter, self).event(event) def contextMenuEvent(self, event): popup_menu = self.createStandardContextMenu() if self.completionType == QCompleter.InlineCompletion: actionCompletion = QAction( self.tr("Set completion type to: Popup Completion"), self) else: actionCompletion = QAction( self.tr("Set completion type to: Inline Completion"), self) self.connect(actionCompletion, SIGNAL("triggered()"), self.change_completion_type) popup_menu.insertSeparator(popup_menu.actions()[0]) popup_menu.insertAction(popup_menu.actions()[0], actionCompletion) #show menu popup_menu.exec_(event.globalPos()) def change_completion_type(self): if self.completionType == QCompleter.InlineCompletion: self.completionType = QCompleter.PopupCompletion else: self.completionType = QCompleter.InlineCompletion self.completer.setCompletionMode(self.completionType) self.setFocus() ninja-ide-2.3/ninja_tests/000077500000000000000000000000001216641277400155475ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/__init__.py000066400000000000000000000021721216641277400176620ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . import unittest class BaseTest(unittest.TestCase): def setUp(self): super(BaseTest, self).setUp() self._original_values = [] def patch(self, obj, attr, new_value): self._original_values.append((obj, attr, getattr(obj, attr))) setattr(obj, attr, new_value) def tearDown(self): for values in self._original_values: obj, attr, old_value = values setattr(obj, attr, old_value)ninja-ide-2.3/ninja_tests/core/000077500000000000000000000000001216641277400164775ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/core/__init__.py000066400000000000000000000012641216641277400206130ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . ninja-ide-2.3/ninja_tests/core/examples/000077500000000000000000000000001216641277400203155ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/core/examples/__init__.py000066400000000000000000000000001216641277400224140ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/core/examples/file_for_tests.py000066400000000000000000000001011216641277400236660ustar00rootroot00000000000000# -*- coding: utf-8 -*- print 'testing' print 'ñandú testing' ninja-ide-2.3/ninja_tests/core/plugins/000077500000000000000000000000001216641277400201605ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/core/plugins/test_plugin.plugin000066400000000000000000000003161216641277400237350ustar00rootroot00000000000000{ "module": "test_plugin", "class": "TestPlugin", "authors": "Martin Alderete ", "version": "0.1", "description": "Plugin for UnitTest", "url": "http://ninja-ide.org" } ninja-ide-2.3/ninja_tests/core/plugins/test_plugin/000077500000000000000000000000001216641277400225155ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/core/plugins/test_plugin/__init__.py000066400000000000000000000000431216641277400246230ustar00rootroot00000000000000from test_plugin import TestPlugin ninja-ide-2.3/ninja_tests/core/plugins/test_plugin/test_plugin.py000066400000000000000000000001471216641277400254260ustar00rootroot00000000000000# -*- coding: utf-8 -*- from ninja_ide.core import plugin class TestPlugin(plugin.Plugin): pass ninja-ide-2.3/ninja_tests/core/test_file_manager.py000066400000000000000000000026621216641277400225270ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import unittest import os from ninja_ide.core import file_manager CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) class FileManagerTestCase(unittest.TestCase): def setUp(self): global CURRENT_DIR self.examples_dir = os.path.join(CURRENT_DIR, 'examples') def test_read_file_content(self): filename = os.path.join(self.examples_dir, 'file_for_tests.py') content = file_manager.read_file_content(filename) expected = ("# -*- coding: utf-8 -*-\n\nprint 'testing'\n" "print 'ñandú testing'\n").encode('utf-8') self.assertEqual(content, expected) if __name__ == '__main__': unittest.main() ninja-ide-2.3/ninja_tests/core/test_plugin_interfaces.py000066400000000000000000000025701216641277400236150ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import unittest from ninja_ide.core.plugin_interfaces import implements from ninja_ide.core.plugin_interfaces import MethodNotImplemented class A(object): def _a_private_method(self): pass def one(self): pass def two(self): pass class B(object): pass class C(object): def one(self): pass def two(self): pass class PluginInterfacesTestCase(unittest.TestCase): def test_implements_decorator(self): a_implement = implements(A) self.assertRaises(MethodNotImplemented, a_implement, B) self.assertEqual(a_implement(C), C) if __name__ == '__main__': unittest.main() ninja-ide-2.3/ninja_tests/core/test_plugin_manager.py000066400000000000000000000035201216641277400231000ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import unittest import os from ninja_ide.core.plugin_manager import PluginManager CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) PLUGIN_NOT_VALID_NAME = 'test_NOT_plugin.plugin' PLUGIN_VALID_NAME = 'test_plugin.plugin' class PluginManagerTestCase(unittest.TestCase): def setUp(self): global CURRENT_DIR plugin_dir = os.path.join(CURRENT_DIR, 'plugins') self.pm = PluginManager(plugin_dir, None) def test_discover(self): self.assertEqual(len(self.pm), 0) self.pm.discover() self.assertEqual(len(self.pm), 1) def test_magic_method_contains(self): global PLUGIN_VALID_NAME, PLUGIN_NOT_VALID_NAME self.pm.discover() self.assertEqual(PLUGIN_VALID_NAME in self.pm, True) self.assertEqual(PLUGIN_NOT_VALID_NAME in self.pm, False) def test_magic_method_getitem(self): global PLUGIN_VALID_NAME, PLUGIN_NOT_VALID_NAME self.pm.discover() self.assertTrue(self.pm['test_plugin.plugin']) self.assertRaises(KeyError, self.pm.__getitem__, PLUGIN_NOT_VALID_NAME) if __name__ == '__main__': unittest.main() ninja-ide-2.3/ninja_tests/gui/000077500000000000000000000000001216641277400163335ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/gui/__init__.py000066400000000000000000000037571216641277400204600ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals # Import this before Qt to set the correct API import ninja_ide # lint:ok from PyQt4.QtGui import QPlainTextEdit class FakeExplorer(object): def get_project_given_filename(self, filename): return 'fake project for: %s' % filename class FakeParent(object): def __init__(self): self.explorer = FakeExplorer() class FakeEditor(QPlainTextEdit): def __init__(self, *args, **kwargs): super(FakeEditor, self).__init__() self.syntax = None self._id = None def register_syntax(self, syntax): self.syntax = syntax class FakeQSettings(object): def __init__(self): self.names = [] self.values = {} def beginGroup(self, name): self.names.append(name) def endGroup(self): self.names.pop() def setValue(self, key, val): self.values[key] = val def value(self, key, default_val=None): return self.values.get(key, default_val) class FakeActions(object): def __init__(self): self.reset_editor_flags_executed = False self.func_name = '' def reset_editor_flags(self): self.reset_editor_flags_executed = True def call_editors_function(self, func_name): self.func_name = func_name ninja-ide-2.3/ninja_tests/gui/dialogs/000077500000000000000000000000001216641277400177555ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/gui/dialogs/__init__.py000066400000000000000000000000301216641277400220570ustar00rootroot00000000000000# -*- coding: utf-8 -*- ninja-ide-2.3/ninja_tests/gui/dialogs/test_preferences.py000066400000000000000000000145321216641277400236740ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals # Import this before Qt to set the correct API import ninja_ide # lint:ok from PyQt4.QtGui import QApplication from PyQt4.QtGui import QKeySequence from PyQt4.QtGui import QTreeWidgetItem from PyQt4.QtCore import Qt from ninja_ide import resources from ninja_ide.gui.dialogs import preferences from ninja_ide.gui.misc import shortcut_manager from ninja_tests import gui from ninja_tests import BaseTest class PreferencesEditorConfigurationTestCase(BaseTest): @classmethod def setUpClass(cls): cls._app = QApplication([]) @classmethod def tearDownClass(cls): del cls._app def setUp(self): super(PreferencesEditorConfigurationTestCase, self).setUp() self.settings = gui.FakeQSettings() self.patch(preferences, 'QSettings', lambda: self.settings) self.editor_completion = preferences.EditorConfiguration() def _check_values(self, errors, indent, checkStyle, marginLine, checkForDocstrings, showMarginLine, errorsInLine, centerOnScroll, checkStyleInline, useTabs, highlightWholeLine, removeTrailingSpaces, showTabsAndSpaces, allowWordWrap): self.assertEqual(self.settings.values['errors'], errors) self.assertEqual(self.settings.values['indent'], indent) self.assertEqual(self.settings.values['checkStyle'], checkStyle) self.assertEqual(self.settings.values['marginLine'], marginLine) self.assertEqual(self.settings.values['checkForDocstrings'], checkForDocstrings) self.assertEqual(self.settings.values['showMarginLine'], showMarginLine) self.assertEqual(self.settings.values['errorsInLine'], errorsInLine) self.assertEqual(self.settings.values['centerOnScroll'], centerOnScroll) self.assertEqual(self.settings.values['checkStyleInline'], checkStyleInline) self.assertEqual(self.settings.values['useTabs'], useTabs) self.assertEqual(self.settings.values['highlightWholeLine'], highlightWholeLine) self.assertEqual(self.settings.values['removeTrailingSpaces'], removeTrailingSpaces) self.assertEqual(self.settings.values['showTabsAndSpaces'], showTabsAndSpaces) self.assertEqual(self.settings.values['allowWordWrap'], allowWordWrap) def test_save(self): main = preferences.main_container.MainContainer() data = [] def called(): data.append(True) self.patch(main, 'update_editor_margin_line', called) self.patch(preferences.pep8mod, 'refresh_checks', called) actions = gui.FakeActions() self.patch(preferences.actions, 'Actions', lambda: actions) self.editor_completion.save() self.assertTrue(actions.reset_editor_flags_executed) self.assertEqual('set_tab_usage', actions.func_name) self.assertEqual([], preferences.pep8mod.options.ignore) self.assertEqual([True, True], data) self._check_values(True, 4, True, 80, False, True, True, True, True, False, True, True, True, False) class PreferencesShortcutManagerConfigurationTestCase(BaseTest): @classmethod def setUpClass(cls): cls._app = QApplication([]) @classmethod def tearDownClass(cls): del cls._app def setUp(self): super(PreferencesShortcutManagerConfigurationTestCase, self).setUp() self.settings = gui.FakeQSettings() self.patch(shortcut_manager, 'QSettings', lambda: self.settings) self.patch(resources, 'QSettings', lambda: self.settings) # Test data custom_default_data = { "New-file": QKeySequence(Qt.CTRL + Qt.Key_N) } # patch shortcuts with test data self.patch(resources, 'CUSTOM_SHORTCUTS', custom_default_data) self.shortcuts_manager = shortcut_manager.ShortcutConfiguration() def test_load_default_shortcuts(self): shorts_count = self.shortcuts_manager.result_widget.topLevelItemCount() item = self.shortcuts_manager.result_widget.topLevelItem(0) shortcut_keys = item.text(1) # Expected data expected_key = QKeySequence(Qt.CTRL + Qt.Key_N) expected_key_str = expected_key.toString(QKeySequence.NativeText) # Just one shortcut should be loaded self.assertEqual(shorts_count, 1) # The key should be the same as the expected self.assertEqual(shortcut_keys, expected_key_str) def test_save_shortcuts(self): data = [] def called(): data.append(True) actions = gui.FakeActions() setattr(actions, 'update_shortcuts', called) self.patch(shortcut_manager.actions, 'Actions', lambda: actions) self.shortcuts_manager.result_widget.clear() key = QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_N) key_str = key.toString(QKeySequence.NativeText) tree_data = ["New File", key_str, "New-File"] item = QTreeWidgetItem(self.shortcuts_manager.result_widget, tree_data) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # Before save there is nothing in QSettings self.assertEqual(self.settings.value("New-File", None), None) # Save self.shortcuts_manager.save() # After save there is a value for New-File QSettings self.assertEqual(self.settings.values["New-File"], key_str) # After save check if NINJA call the update_shortcuts in actios.Actions self.assertEqual(data, [True]) ninja-ide-2.3/ninja_tests/gui/editor/000077500000000000000000000000001216641277400176215ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/gui/editor/test_highlighter.py000066400000000000000000000100061216641277400235250ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals from ninja_ide.gui.editor import highlighter from PyQt4.QtGui import QFont import unittest class HighlighterFormatTestCase(unittest.TestCase): def setUp(self): self._old_font = "Monospace" highlighter.settings.FONT_FAMILY = self._old_font self._color = "#000000" self._a_format = highlighter.format(self._color) b_i_string = "bold italic" self._bold_italic = highlighter.format(self._color, b_i_string) self._new_font = "Arial" highlighter.settings.FONT_FAMILY = self._new_font self._new_font_format = highlighter.format(self._color) def tearDown(self): reload(highlighter) def test_format_sets_color(self): color_name = self._a_format.foreground().color().name() self.assertEqual(self._color, color_name) def test_format_sets_bold(self): bold = QFont.Bold f_weight = self._bold_italic.fontWeight() self.assertEqual(bold, f_weight) f_weight = self._a_format.fontWeight() self.assertNotEqual(bold, f_weight) def test_format_sets_italic(self): italic = self._bold_italic.fontItalic() self.assertTrue(italic) italic = self._a_format.fontItalic() self.assertFalse(italic) def test_format_set_font(self): font = self._a_format.fontFamily() self.assertEqual(self._old_font, font) font = self._new_font_format.fontFamily() self.assertEqual(self._new_font, font) class HighlighterRestyleTestCase(unittest.TestCase): def setUp(self): highlighter.SDEFAULTS = (("style_key", "scheme_key", "default"),) a_scheme = {"scheme_key": "#BBBBBB"} highlighter.resources.COLOR_SCHEME = a_scheme highlighter.restyle(a_scheme) def tearDown(self): reload(highlighter) def test_scheme_populates_styles(self): scolor = highlighter.STYLES["style_key"].foreground().color().name() self.assertEqual(scolor.lower(), "#bbbbbb") class SyntaxUserDataTestCase(unittest.TestCase): def setUp(self): self.err_sud = highlighter.SyntaxUserData(error=True) self.noerr_sud = highlighter.SyntaxUserData(error=False) def test_initializes_correctly(self): self.assertTrue(self.err_sud.error) self.assertFalse(self.noerr_sud.error) self.assertEqual(self.err_sud.str_groups, []) self.assertEqual(self.noerr_sud.str_groups, []) self.assertEqual(self.err_sud.comment_start, -1) self.assertEqual(self.noerr_sud.comment_start, -1) def test_clears_correctly(self): self.err_sud.error = True self.err_sud.str_groups = [1, 2, 3] self.err_sud.comment_start = 2 self.err_sud.clear_data() self.assertFalse(self.err_sud.error) self.assertEqual(self.err_sud.str_groups, []) self.assertEqual(self.err_sud.comment_start, -1) def test_adds_string(self): self.assertNotIn((2, 2), self.err_sud.str_groups) self.err_sud.add_str_group(1, 2) self.assertIn((2, 2), self.err_sud.str_groups) def test_comment_start(self): self.assertEqual(self.err_sud.comment_start, -1) self.err_sud.comment_start_at(2) self.assertEqual(self.err_sud.comment_start, 2) if __name__ == '__main__': unittest.main() ninja-ide-2.3/ninja_tests/gui/misc/000077500000000000000000000000001216641277400172665ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/gui/misc/__init__.py000066400000000000000000000000001216641277400213650ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/gui/misc/test_console_widget.py000066400000000000000000000340421216641277400237070ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals # Import this before Qt to set the correct API import ninja_ide # lint:ok from PyQt4.QtGui import QApplication from PyQt4.QtGui import QTextCursor from PyQt4.QtGui import QKeyEvent from PyQt4.QtCore import Qt from PyQt4.QtCore import QEvent from ninja_ide.gui.misc import console_widget from ninja_tests import BaseTest class FakeCompleterWidget(object): def __init__(self, *arg): pass def popup(self): return self def isVisible(self): return False class ConsoleWidgetTestCase(BaseTest): @classmethod def setUpClass(cls): cls._app = QApplication([]) @classmethod def tearDownClass(cls): del cls._app def setUp(self): super(ConsoleWidgetTestCase, self).setUp() self.patch(console_widget.completer_widget, "CompleterWidget", FakeCompleterWidget) self.patch(console_widget.ConsoleWidget, "_create_context_menu", lambda *arg: None) self.console_widget = console_widget.ConsoleWidget() def test_menu_cut(self): data = [] def called(event): data.append(event) self.patch(self.console_widget, 'keyPressEvent', called) self.console_widget._cut() self.assertEqual(data[0].type(), QEvent.KeyPress) self.assertEqual(data[0].key(), Qt.Key_X) self.assertEqual(data[0].modifiers(), Qt.ControlModifier) self.assertEqual(data[0].text(), "x") def test_menu_cut_with_multiline_selection(self): self.console_widget._write_command() self.console_widget._write_command() self.console_widget._write_command() self.console_widget.textCursor().insertText("asdqwe") self.console_widget.selectAll() text = self.console_widget.textCursor().selectedText() self.console_widget._cut() self.console_widget.selectAll() text_after = self.console_widget.textCursor().selectedText() self.assertEqual(text, text_after) def test_menu_cut_with_line_selection(self): self.console_widget._write_command() self.console_widget._write_command() self.console_widget._write_command() self.console_widget.selectAll() # The >>> content text = self.console_widget.textCursor().selectedText() self.console_widget.moveCursor(QTextCursor.End) self.console_widget.textCursor().insertText("asdqwe") cursor = self.console_widget.textCursor() cursor.movePosition(QTextCursor.End) cursor.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor) cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, len(self.console_widget.prompt)) # "asdqwe" text_word = cursor.selectedText() self.console_widget.setTextCursor(cursor) self.console_widget._cut() self.console_widget.selectAll() text_after = self.console_widget.textCursor().selectedText() self.assertEqual(text, text_after) self.assertEqual(text_word, "asdqwe") def test_menu_paste_selection(self): self.console_widget._write_command() self.console_widget._write_command() self.console_widget._write_command() self.console_widget.textCursor().insertText("asdqwe") self.console_widget.selectAll() text = self.console_widget.textCursor().selectedText() + "ninja" self._app.clipboard().setText("ninja") self.console_widget._paste() self.console_widget.selectAll() text_after = self.console_widget.textCursor().selectedText() self.assertEqual(text, text_after) def test_menu_paste(self): self.console_widget._write_command() self.console_widget._write_command() self.console_widget._write_command() self.console_widget.textCursor().insertText("asdqwe") self.console_widget.moveCursor(QTextCursor.Left) text = self.console_widget.toPlainText()[:-1] + "ninjae" self._app.clipboard().setText("ninja") self.console_widget._paste() text_after = self.console_widget.toPlainText() self.assertEqual(text, text_after) def test_clean_console(self): self.console_widget._write_command() self.console_widget._write_command() self.console_widget._write_command() self.console_widget._clean_console() self.console_widget.selectAll() text = self.console_widget.textCursor().selectedText() self.assertEqual(text, self.console_widget.prompt) def test_copy_history(self): lines = ("asd", "qwe", "rty") for line in lines: self.console_widget.textCursor().insertText(line) self.console_widget._write_command() self.console_widget._copy_history() paste = self._app.clipboard().text() self.assertEqual(paste, '\n'.join(lines)) def test_copy_console_content(self): self.console_widget._write_command() self.console_widget._write_command() lines = ("print 'ninja'", "q = 3") for line in lines: self.console_widget.textCursor().insertText(line) self.console_widget._write_command() self.console_widget._copy_console_content() paste = self._app.clipboard().text() content = [">>> ", ">>> "] + [">>> " + line for line in lines] content.insert(-1, 'ninja') self.assertEqual(paste, '\n'.join(content + ['>>> '])) def test_check_event_on_selection_all_selected(self): self.console_widget._write_command() self.console_widget._write_command() self.console_widget.textCursor().insertText('a = 5') self.console_widget.selectAll() text = self.console_widget.textCursor().selectedText() + 'a' self.assertTrue(self.console_widget.textCursor().hasSelection()) event = QKeyEvent(QEvent.KeyPress, Qt.Key_A, Qt.NoModifier, "a") self.console_widget.keyPressEvent(event) self.assertFalse(self.console_widget.textCursor().hasSelection()) self.console_widget.selectAll() text_after = self.console_widget.textCursor().selectedText() self.assertEqual(text, text_after) def test_check_event_on_selection_all_selected_no_text(self): self.console_widget._write_command() self.console_widget._write_command() self.console_widget.textCursor().insertText('a = 5') self.console_widget.selectAll() text = self.console_widget.textCursor().selectedText() self.assertTrue(self.console_widget.textCursor().hasSelection()) event = QKeyEvent(QEvent.KeyPress, Qt.Key_A, Qt.NoModifier, "") self.console_widget.keyPressEvent(event) self.assertTrue(self.console_widget.textCursor().hasSelection()) self.console_widget.selectAll() text_after = self.console_widget.textCursor().selectedText() self.assertEqual(text, text_after) def test_check_event_on_selection_last_block_selected(self): self.console_widget._write_command() self.console_widget._write_command() self.console_widget.textCursor().insertText('a = 5') self.console_widget.selectAll() text = self.console_widget.textCursor().selectedText()[:-2] + '2' self.console_widget.moveCursor(QTextCursor.End) self.console_widget.setCursorPosition(3, QTextCursor.KeepAnchor) self.assertTrue(self.console_widget.textCursor().hasSelection()) event = QKeyEvent(QEvent.KeyPress, Qt.Key_2, Qt.NoModifier, "2") self.console_widget.keyPressEvent(event) self.assertFalse(self.console_widget.textCursor().hasSelection()) self.console_widget.selectAll() text_after = self.console_widget.textCursor().selectedText() self.assertEqual(text, text_after) def test_tab_pressed(self): self.console_widget.selectAll() text = self.console_widget.textCursor().selectedText() self.console_widget.moveCursor(QTextCursor.End) self.assertEqual(text, self.console_widget.prompt) self.console_widget._tab_pressed(None) self.console_widget.selectAll() text = self.console_widget.textCursor().selectedText() self.assertEqual( text, self.console_widget.prompt + ' ' * console_widget.settings.INDENT) def test_home_pressed(self): self.console_widget._write_command() self.console_widget._write_command() self.console_widget.textCursor().insertText('a = 5') event = QKeyEvent(QEvent.KeyPress, Qt.Key_Home, Qt.NoModifier, "") self.console_widget._home_pressed(event) self.assertEqual( self.console_widget.textCursor().position(), self.console_widget.document().lastBlock().position() + len(self.console_widget.prompt)) def test_home_pressed_with_shift(self): self.console_widget._write_command() self.console_widget._write_command() self.console_widget.textCursor().insertText('a = 5') event = QKeyEvent(QEvent.KeyPress, Qt.Key_Home, Qt.ShiftModifier, "") self.console_widget._home_pressed(event) text = self.console_widget.textCursor().selectedText() self.assertEqual(text, "a = 5") def test_enter_pressed(self): data = [] self.patch(self.console_widget, "_write_command", lambda: data.append(True)) event = QKeyEvent(QEvent.KeyPress, Qt.Key_Enter, Qt.NoModifier, "") self.console_widget.keyPressEvent(event) self.assertEqual(data, [True]) event = QKeyEvent(QEvent.KeyPress, Qt.Key_Return, Qt.NoModifier, "") self.console_widget.keyPressEvent(event) self.assertEqual(data, [True, True]) def test_left_pressed(self): self.console_widget._write_command() self.console_widget.textCursor().insertText('a = 5') val = self.console_widget._left_pressed(None) self.assertFalse(val) self.console_widget.setCursorPosition(0) val = self.console_widget._left_pressed(None) self.assertTrue(val) def test_backspace(self): self.console_widget._write_command() self.console_widget._write_command() self.console_widget.textCursor().insertText('a = 5') self.console_widget.selectAll() text = self.console_widget.textCursor().selectedText()[:-1] self.console_widget.moveCursor(QTextCursor.End) event = QKeyEvent(QEvent.KeyPress, Qt.Key_Backspace, Qt.NoModifier, "") self.console_widget.keyPressEvent(event) self.console_widget.selectAll() text_after = self.console_widget.textCursor().selectedText() self.assertEqual(text, text_after) def test_backspace_indent(self): self.console_widget._write_command() self.console_widget._write_command() self.console_widget.textCursor().insertText( ' ' * console_widget.settings.INDENT * 2) self.console_widget.selectAll() text = self.console_widget.textCursor().selectedText()[: -console_widget.settings.INDENT] self.console_widget.moveCursor(QTextCursor.End) event = QKeyEvent(QEvent.KeyPress, Qt.Key_Backspace, Qt.NoModifier, "") self.console_widget.keyPressEvent(event) self.console_widget.selectAll() text_after = self.console_widget.textCursor().selectedText() self.assertEqual(text, text_after) def test_backspace_remove_selection(self): self.console_widget._write_command() self.console_widget._write_command() self.console_widget.textCursor().insertText("a = 5") self.console_widget.selectAll() text = self.console_widget.textCursor().selectedText()[:-2] self.console_widget.moveCursor(QTextCursor.End) self.console_widget.setCursorPosition(3, QTextCursor.KeepAnchor) event = QKeyEvent(QEvent.KeyPress, Qt.Key_Backspace, Qt.NoModifier, "") self.console_widget.keyPressEvent(event) self.console_widget.selectAll() text_after = self.console_widget.textCursor().selectedText() self.assertEqual(text, text_after) def test_navigate_history(self): lines = ("print 'ninja'", "print 'ide'") for line in lines: self.console_widget.textCursor().insertText(line) self.console_widget._write_command() current = 'current_command' self.console_widget.textCursor().insertText(current) self.console_widget._up_pressed(None) line = self.console_widget.document().lastBlock().text() self.assertEqual(line, self.console_widget.prompt + lines[1]) self.assertEqual(self.console_widget.history_index, 1) self.console_widget._up_pressed(None) line = self.console_widget.document().lastBlock().text() self.assertEqual(line, self.console_widget.prompt + lines[0]) self.assertEqual(self.console_widget.history_index, 0) self.console_widget._down_pressed(None) line = self.console_widget.document().lastBlock().text() self.assertEqual(line, self.console_widget.prompt + lines[1]) self.assertEqual(self.console_widget.history_index, 2) self.console_widget._down_pressed(None) line = self.console_widget.document().lastBlock().text() self.assertEqual(line, self.console_widget.prompt + current) self.assertEqual(self.console_widget.history_index, 2)ninja-ide-2.3/ninja_tests/gui/test_status_bar.py000066400000000000000000000035151216641277400221170ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import sys # Import this before Qt to set the correct API import ninja_ide # lint:ok from PyQt4.QtGui import QApplication from ninja_ide.gui import status_bar from ninja_ide.gui.main_panel import main_container from ninja_tests import BaseTest from ninja_tests import gui class StatusBarTestCase(BaseTest): def setUp(self): super(StatusBarTestCase, self).setUp() self.app = QApplication(sys.argv) self.parent = gui.FakeParent() self.patch(main_container.editor, 'Editor', gui.FakeEditor) self.main = main_container.MainContainer(None) self.main._parent = self.parent self.status = status_bar.StatusBar() def test_show(self): editor = self.main.add_editor() editor.setPlainText('ninja test') editor.selectAll() data = [] def fake_find_matches(*arg): data.append(arg) self.patch(self.status._searchWidget, 'find_matches', fake_find_matches) self.status.show() expected = [(editor, True)] self.assertEqual(data, expected) ninja-ide-2.3/ninja_tests/tools/000077500000000000000000000000001216641277400167075ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/tools/__init__.py000066400000000000000000000012641216641277400210230ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . ninja-ide-2.3/ninja_tests/tools/completion/000077500000000000000000000000001216641277400210605ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/tools/completion/__init__.py000066400000000000000000000044311216641277400231730ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . import re def get_source_data(code, word=""): clazzes = sorted(set(re.findall("class (\w+?)\(", code))) funcs = sorted(set(re.findall("(\w+?)\(", code))) attrs = sorted(set(re.split('\W+', code))) del attrs[0] filter_attrs = lambda x: (x not in funcs) and not x.isdigit() attrs = filter(filter_attrs, attrs) if word in attrs: attrs.remove(word) funcs = filter(lambda x: x not in clazzes, funcs) data = {'attributes': attrs, 'functions': funcs, 'classes': clazzes} return data SOURCE_COMPLETION = """ a = "ninja-ide" b = a.split() import os r = os.path.sep from PyQt4 import QtGui c = QtGui.Q q = self.wrong_call("home", "cat") mamamia = 2 class MyClass: attr = 'my_name' def __init__(self): self.s = {} self.m = 5 def print_function(self): pass def another(self): l = []""" SOURCE_ANALYZER_NATIVE = """ import os import sys from sys import exit a = 5 b = [] class Test(object): def __init__(self): self.x = {} def my_function(self): code = 'string' self.var = 4.5 if code: my_var = 'inside if' def func_args(self, var, inte, num=5, li='ninja', *arggg, **kwarggg): nothing = False def global_func(): bo = True obj = os.path di = Test() another = obj man = () """ SOURCE_LATE_RESOLUTION = """ import os p = os.path """ SOURCE_INHERITANCE = """ import decimal from threading import Lock class Parent: def __init__(self): self.value = 'string' def function(self): pass """ ninja-ide-2.3/ninja_tests/tools/completion/test_analyzer.py000066400000000000000000000322701216641277400243220ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import _ast import unittest from ninja_ide.tools.completion import analyzer from ninja_ide.tools.completion import model from ninja_tests.tools.completion import SOURCE_ANALYZER_NATIVE class AnalyzerTestCase(unittest.TestCase): def setUp(self): self.analyzer = analyzer.Analyzer() ############################################################################### # SIMPLE NATIVE TYPES ############################################################################### def test_content_value(self): self.assertTrue(self.analyzer.content is None) self.analyzer.analyze(SOURCE_ANALYZER_NATIVE) self.assertTrue(self.analyzer.content is None) def test_imports(self): module = self.analyzer.analyze(SOURCE_ANALYZER_NATIVE) type1 = model._TypeData(None, 'sys', 'import sys', None) type2 = model._TypeData(None, 'os', 'import os', None) type3 = model._TypeData(None, 'sys.exit', 'import sys.exit', None) expected = {'sys': type1, 'os': type2, 'exit': type3} for imp in module.imports: data = expected[imp] impo = module.imports[imp] self.assertEqual(data.data_type, impo.data_type) self.assertEqual(data.line_content, impo.line_content) def test_module_class_func_attrs(self): module = self.analyzer.analyze(SOURCE_ANALYZER_NATIVE) result_f = module.functions.keys() result_f.sort() result_a = module.attributes.keys() result_a.sort() result_c = module.classes.keys() functions = ['global_func'] attrs = ['a', 'b', 'man'] classes = ['Test'] functions.sort() attrs.sort() self.assertEqual(result_f, functions) self.assertEqual(result_a, attrs) self.assertEqual(result_c, classes) def test_attrs_in_module_func(self): module = self.analyzer.analyze(SOURCE_ANALYZER_NATIVE) func = module.functions['global_func'] self.assertEqual(func.args, {}) self.assertEqual(func.decorators, []) self.assertEqual(func.return_type, []) self.assertEqual(func.name, 'global_func') self.assertEqual(func.functions, {}) #Assign: 1 result_a = func.attributes.keys() result_a.sort() expected = ['another', 'bo', 'di', 'obj'] self.assertEqual(result_a, expected) assign = func.attributes['bo'] self.assertEqual(assign.name, 'bo') assign_test1 = model.Assign('bo') assign_test1.add_data(0, '__builtin__.bool', ' bo = True', None) expected_data = assign_test1.data[0] result_data = assign.data[0] self.assertEqual(result_data.data_type, expected_data.data_type) self.assertEqual(result_data.line_content, expected_data.line_content) self.assertEqual(result_data.operation, expected_data.operation) self.assertTrue(result_data.is_native) self.assertFalse(result_data.from_import) #Assign: 2 assign = func.attributes['obj'] self.assertEqual(assign.name, 'obj') assign_test2 = model.Assign('obj') assign_test2.add_data(0, model.late_resolution, ' obj = os.path', _ast.Attribute) expected_data = assign_test2.data[0] result_data = assign.data[0] self.assertEqual(result_data.data_type, expected_data.data_type) self.assertEqual(result_data.line_content, expected_data.line_content) self.assertEqual(result_data.operation, expected_data.operation) self.assertFalse(result_data.is_native) #Assign: 3 assign = func.attributes['di'] self.assertEqual(assign.name, 'di') assign_test2 = model.Assign('di') assign_test2.add_data(0, model.late_resolution, ' di = Test()', _ast.Call) expected_data = assign_test2.data[0] result_data = assign.data[0] self.assertEqual(result_data.data_type, expected_data.data_type) self.assertEqual(result_data.line_content, expected_data.line_content) self.assertEqual(result_data.operation, expected_data.operation) self.assertFalse(result_data.is_native) self.assertFalse(result_data.from_import) #Assign: 4 assign = func.attributes['another'] self.assertEqual(assign.name, 'another') assign_test2 = model.Assign('another') assign_test2.add_data(0, model.late_resolution, ' another = obj', _ast.Name) expected_data = assign_test2.data[0] result_data = assign.data[0] self.assertEqual(result_data.data_type, expected_data.data_type) self.assertEqual(result_data.line_content, expected_data.line_content) self.assertEqual(result_data.operation, expected_data.operation) self.assertFalse(result_data.is_native) def test_simple_class_data(self): module = self.analyzer.analyze(SOURCE_ANALYZER_NATIVE) clazz = module.classes['Test'] result_f = clazz.functions.keys() result_f.sort() result_a = clazz.attributes.keys() result_a.sort() functions = ['__init__', 'my_function', 'func_args'] attrs = ['x', 'var'] functions.sort() attrs.sort() self.assertEqual(result_f, functions) self.assertEqual(result_a, attrs) self.assertEqual(clazz.bases, {'object': None}) def test_simple_class_attrs(self): module = self.analyzer.analyze(SOURCE_ANALYZER_NATIVE) clazz = module.classes['Test'] attr_x = clazz.attributes['x'] attr_var = clazz.attributes['var'] type_x = attr_x.data[0] type_var = attr_var.data[0] self.assertEqual(type_x.data_type, '__builtin__.dict') self.assertEqual(type_x.line_content, ' self.x = {}') self.assertEqual(type_x.operation, None) self.assertEqual(type_x.from_import, False) self.assertEqual(type_x.is_native, True) self.assertEqual(type_var.data_type, '__builtin__.float') self.assertEqual(type_var.line_content, ' self.var = 4.5') self.assertEqual(type_var.operation, None) self.assertEqual(type_var.from_import, False) self.assertEqual(type_var.is_native, True) def test_attrs_in_class_func(self): module = self.analyzer.analyze(SOURCE_ANALYZER_NATIVE) clazz = module.classes['Test'] func = clazz.functions['my_function'] self.assertEqual(func.args, {}) self.assertEqual(func.decorators, []) self.assertEqual(func.return_type, []) self.assertEqual(func.name, 'my_function') self.assertEqual(func.functions, {}) #Assign result_a = func.attributes.keys() result_a.sort() expected = ['code', 'my_var'] self.assertEqual(result_a, expected) #Assing: code assign = func.attributes['code'] self.assertEqual(assign.name, 'code') assign_test1 = model.Assign('code') assign_test1.add_data(0, '__builtin__.str', " code = 'string'", None) expected_data = assign_test1.data[0] result_data = assign.data[0] self.assertEqual(result_data.data_type, expected_data.data_type) self.assertEqual(result_data.line_content, expected_data.line_content) self.assertEqual(result_data.operation, expected_data.operation) self.assertTrue(result_data.is_native) self.assertFalse(result_data.from_import) #Assing: my_var assign = func.attributes['my_var'] self.assertEqual(assign.name, 'my_var') assign_test1 = model.Assign('my_var') assign_test1.add_data(0, '__builtin__.str', " my_var = 'inside if'", None) expected_data = assign_test1.data[0] result_data = assign.data[0] self.assertEqual(result_data.data_type, expected_data.data_type) self.assertEqual(result_data.line_content, expected_data.line_content) self.assertEqual(result_data.operation, expected_data.operation) self.assertTrue(result_data.is_native) self.assertFalse(result_data.from_import) def test_attrs_in_class_func_extended(self): module = self.analyzer.analyze(SOURCE_ANALYZER_NATIVE) clazz = module.classes['Test'] func = clazz.functions['func_args'] #Args args_names = ['var', 'inte', 'num', 'li', 'arggg', 'kwarggg'] args_names.sort() func_args = func.args.keys() func_args.sort() self.assertEqual(func_args, args_names) #For: var type_var = model._TypeData(0, model.late_resolution, None, None) func_arg_obj = func.args['var'] type_arg_func = func_arg_obj.data[0] self.assertEqual(func_arg_obj.name, 'var') self.assertEqual(type_arg_func.data_type, type_var.data_type) self.assertEqual(type_arg_func.line_content, type_var.line_content) self.assertEqual(type_arg_func.operation, type_var.operation) self.assertFalse(type_arg_func.is_native) #For: inte type_var = model._TypeData(0, model.late_resolution, None, None) func_arg_obj = func.args['inte'] type_arg_func = func_arg_obj.data[0] self.assertEqual(func_arg_obj.name, 'inte') self.assertEqual(type_arg_func.data_type, type_var.data_type) self.assertEqual(type_arg_func.line_content, type_var.line_content) self.assertEqual(type_arg_func.operation, type_var.operation) self.assertFalse(type_arg_func.is_native) #For: num type_var = model._TypeData(0, '__builtin__.int', None, None) func_arg_obj = func.args['num'] type_arg_func = func_arg_obj.data[0] self.assertEqual(func_arg_obj.name, 'num') self.assertEqual(type_arg_func.data_type, type_var.data_type) self.assertEqual(type_arg_func.line_content, type_var.line_content) self.assertEqual(type_arg_func.operation, type_var.operation) self.assertTrue(type_arg_func.is_native) #For: li type_var = model._TypeData(0, '__builtin__.str', None, None) func_arg_obj = func.args['li'] type_arg_func = func_arg_obj.data[0] self.assertEqual(func_arg_obj.name, 'li') self.assertEqual(type_arg_func.data_type, type_var.data_type) self.assertEqual(type_arg_func.line_content, type_var.line_content) self.assertEqual(type_arg_func.operation, type_var.operation) self.assertTrue(type_arg_func.is_native) #For: arggg type_var = model._TypeData(0, '__builtin__.list', None, None) func_arg_obj = func.args['arggg'] type_arg_func = func_arg_obj.data[0] self.assertEqual(func_arg_obj.name, 'arggg') self.assertEqual(type_arg_func.data_type, type_var.data_type) self.assertEqual(type_arg_func.line_content, type_var.line_content) self.assertEqual(type_arg_func.operation, type_var.operation) self.assertTrue(type_arg_func.is_native) #For: kwarggg type_var = model._TypeData(0, '__builtin__.dict', None, None) func_arg_obj = func.args['kwarggg'] type_arg_func = func_arg_obj.data[0] self.assertEqual(func_arg_obj.name, 'kwarggg') self.assertEqual(type_arg_func.data_type, type_var.data_type) self.assertEqual(type_arg_func.line_content, type_var.line_content) self.assertEqual(type_arg_func.operation, type_var.operation) self.assertTrue(type_arg_func.is_native) #Decorators self.assertEqual(func.decorators, []) #Return Type self.assertEqual(func.return_type, []) #Attributes self.assertEqual(func.name, 'func_args') self.assertEqual(func.functions, {}) #Assign result_a = func.attributes.keys() expected = ['nothing'] self.assertEqual(result_a, expected) assign = func.attributes['nothing'] self.assertEqual(assign.name, 'nothing') assign_test1 = model.Assign('nothing') assign_test1.add_data(0, '__builtin__.bool', " nothing = False", None) expected_data = assign_test1.data[0] result_data = assign.data[0] self.assertEqual(result_data.data_type, expected_data.data_type) self.assertEqual(result_data.line_content, expected_data.line_content) self.assertEqual(result_data.operation, expected_data.operation) self.assertTrue(result_data.is_native) self.assertFalse(result_data.from_import) if __name__ == '__main__': unittest.main() ninja-ide-2.3/ninja_tests/tools/completion/test_code_completion.py000066400000000000000000000731531216641277400256450ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import unittest from ninja_ide.tools.completion import code_completion from ninja_ide.tools.completion import completion_daemon from ninja_tests.tools.completion import get_source_data, SOURCE_COMPLETION class CodeCompletionTestCase(unittest.TestCase): def setUp(self): code_completion.settings.SYNTAX = {'python': {'keywords': []}} self.cc = code_completion.CodeCompletion() def tearDown(self): completion_daemon.shutdown_daemon() ############################################################################### # TESTS FOR BUILTIN COMPLETION ############################################################################### def test_import_attribute(self): global SOURCE_COMPLETION source_code = SOURCE_COMPLETION + '\n os.' self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) self.assertIn('path', results['modules']) def test_import_double_attribute(self): global SOURCE_COMPLETION source_code = SOURCE_COMPLETION + '\n os.path.' self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) self.assertIn('expanduser', results['functions']) def test_global_attr_in_class(self): global SOURCE_COMPLETION source_code = SOURCE_COMPLETION + '\n mamamia.' self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(int) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_global_attr_not_recognized_in_class(self): global SOURCE_COMPLETION source_code = SOURCE_COMPLETION + '\n cat.' self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = get_source_data(SOURCE_COMPLETION, 'cat') self.assertEqual(expected, results) def test_builtin_list_completion_in_class_not_attr(self): global SOURCE_COMPLETION self.cc.analyze_file('', SOURCE_COMPLETION) source_code = SOURCE_COMPLETION + '\n l.' offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(list) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_dict_completion_in_class_not_attr(self): global SOURCE_COMPLETION new_lines = '\n d = {}\n d.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(dict) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_int_completion_in_class_not_attr(self): global SOURCE_COMPLETION new_lines = '\n i = 4\n i.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(int) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_float_completion_in_class_not_attr(self): global SOURCE_COMPLETION new_lines = '\n i = 4.3\n i.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(float) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_tuple_completion_in_class_not_attr(self): global SOURCE_COMPLETION new_lines = '\n t = ()\n t.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(tuple) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_bool_completion_in_class_not_attr(self): global SOURCE_COMPLETION new_lines = '\n b = True\n b.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(bool) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_str_completion_in_class_not_attr(self): global SOURCE_COMPLETION new_lines = '\n s = "ninja"\n s.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(str) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) @unittest.skip("FIX LATER") def test_builtin_unicode_completion_in_class_not_attr(self): global SOURCE_COMPLETION new_lines = '\n s = u"ninja"\n s.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(unicode) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results) def test_invalid_var_in_class_function(self): global SOURCE_COMPLETION new_lines = '\n s.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = get_source_data(SOURCE_COMPLETION, 's') self.assertEqual(expected, results) def test_builtin_int_completion_in_class_attr(self): global SOURCE_COMPLETION new_lines = '\n self.var = 4\n self.var.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(int) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_float_completion_in_class_attr(self): global SOURCE_COMPLETION new_lines = '\n self.var = 4.3\n self.var.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(float) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_list_completion_in_class_attr(self): global SOURCE_COMPLETION new_lines = '\n self.var = []\n self.var.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(list) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_dict_completion_in_class_attr(self): global SOURCE_COMPLETION new_lines = '\n self.var = {}\n self.var.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(dict) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_tuple_completion_in_class_attr(self): global SOURCE_COMPLETION new_lines = '\n self.var = ()\n self.var.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(tuple) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_bool_completion_in_class_attr(self): global SOURCE_COMPLETION new_lines = '\n self.var = False\n self.var.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(bool) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_str_completion_in_class_attr(self): global SOURCE_COMPLETION new_lines = '\n self.var = "ninja"\n self.var.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(str) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) @unittest.skip("FIX LATER") def test_builtin_unicode_completion_in_class_attr(self): global SOURCE_COMPLETION new_lines = '\n self.var = u"ninja"\n self.var.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(unicode) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results) def test_invalid_var_in_class_function_attr(self): global SOURCE_COMPLETION new_lines = '\n self.invalid.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = get_source_data(source_code, 'self') self.assertEqual(expected, results) def test_builtin_dict_completion_in_class_attr_diff_func(self): """Test the completion of some attribute from another function.""" global SOURCE_COMPLETION new_lines = '\n self.s.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(dict) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_int_completion_in_class_attr_diff_func(self): global SOURCE_COMPLETION new_lines = ('\n self.var = 4' '\n\n def new_func(self):' '\n self.var.') source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(int) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_float_completion_in_class_attr_diff_func(self): global SOURCE_COMPLETION new_lines = ('\n self.var = 4.3' '\n\n def new_func(self):' '\n self.var.') source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(float) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_list_completion_in_class_attr_diff_func(self): global SOURCE_COMPLETION new_lines = ('\n self.var = []' '\n\n def new_func(self):' '\n self.var.') source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(list) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_tuple_completion_in_class_attr_diff_func(self): global SOURCE_COMPLETION new_lines = ('\n self.var = ()' '\n\n def new_func(self):' '\n self.var.') source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(tuple) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_bool_completion_in_class_attr_diff_func(self): global SOURCE_COMPLETION new_lines = ('\n self.var = True' '\n\n def new_func(self):' '\n self.var.') source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(bool) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_str_completion_in_class_attr_diff_func(self): global SOURCE_COMPLETION new_lines = ('\n self.var = "ninja"' '\n\n def new_func(self):' '\n self.var.') source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(str) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) @unittest.skip("FIX LATER") def test_builtin_unicode_completion_in_class_attr_diff_func(self): global SOURCE_COMPLETION new_lines = ('\n self.var = u"ninja"' '\n\n def new_func(self):' '\n self.var.') source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(unicode) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results) def test_module_import_attribute(self): global SOURCE_COMPLETION source_code = SOURCE_COMPLETION + '\n\nos.' self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) self.assertIn('path', results['modules']) def test_module_import_double_attribute(self): global SOURCE_COMPLETION source_code = SOURCE_COMPLETION + '\n\nos.path.' self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) self.assertIn('expanduser', results['functions']) def test_valid_class_attr(self): global SOURCE_COMPLETION new_lines = '\n self.attr.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(str) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_list_completion(self): global SOURCE_COMPLETION new_lines = '\n\nlis = []\nlis.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(list) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_dict_completion(self): global SOURCE_COMPLETION new_lines = '\n\nd = {}\nd.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(dict) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_int_completion(self): global SOURCE_COMPLETION new_lines = '\n\ni = 4\ni.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(int) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_float_completion(self): global SOURCE_COMPLETION new_lines = '\n\ni = 4.3\ni.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(float) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_tuple_completion(self): global SOURCE_COMPLETION new_lines = '\n\nt = ()\nt.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(tuple) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_bool_completion(self): global SOURCE_COMPLETION new_lines = '\n\nbo = True\nbo.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(bool) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_builtin_str_completion(self): global SOURCE_COMPLETION new_lines = '\n\ns = "ninja"\ns.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(str) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) @unittest.skip("FIX LATER") def test_builtin_unicode_completion(self): global SOURCE_COMPLETION new_lines = '\n\ns = u"ninja"\ns.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = dir(unicode) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_invalid_var(self): global SOURCE_COMPLETION new_lines = '\n\ninvalid.' source_code = SOURCE_COMPLETION + new_lines self.cc.analyze_file('', source_code) offset = len(source_code) results = self.cc.get_completion(source_code, offset) expected = get_source_data(source_code, 'invalid') self.assertEqual(expected, results) ############################################################################### # TESTS FOR COMPLETION SEGMENT ############################################################################### def test_completion_segment1(self): source = """var.""" token_code = self.cc._tokenize_text(source) segment = self.cc._search_for_completion_segment(token_code) expected = 'var.' self.assertEqual(expected, segment) def test_completion_segment2(self): source = """var.attr""" token_code = self.cc._tokenize_text(source) segment = self.cc._search_for_completion_segment(token_code) expected = 'var.attr' self.assertEqual(expected, segment) def test_completion_segment3(self): source = """var.attr.""" token_code = self.cc._tokenize_text(source) segment = self.cc._search_for_completion_segment(token_code) expected = 'var.attr.' self.assertEqual(expected, segment) def test_completion_segment4(self): source = """var.func()""" token_code = self.cc._tokenize_text(source) segment = self.cc._search_for_completion_segment(token_code) expected = 'var.func()' self.assertEqual(expected, segment) def test_completion_segment5(self): source = """var.func().""" token_code = self.cc._tokenize_text(source) segment = self.cc._search_for_completion_segment(token_code) expected = 'var.func().' self.assertEqual(expected, segment) def test_completion_segment6(self): source = """var.func('name').""" token_code = self.cc._tokenize_text(source) segment = self.cc._search_for_completion_segment(token_code) expected = 'var.func().' self.assertEqual(expected, segment) def test_completion_segment7(self): source = """q = var.func('name').""" token_code = self.cc._tokenize_text(source) segment = self.cc._search_for_completion_segment(token_code) expected = 'var.func().' self.assertEqual(expected, segment) def test_completion_segment8(self): source = """ f = ase.port("diego", [], {}, "gato")""" token_code = self.cc._tokenize_text(source) segment = self.cc._search_for_completion_segment(token_code) expected = 'ase.port()' self.assertEqual(expected, segment) def test_completion_segment9(self): source = """ f = ase.port("diego", [], {}, "gato").""" token_code = self.cc._tokenize_text(source) segment = self.cc._search_for_completion_segment(token_code) expected = 'ase.port().' self.assertEqual(expected, segment) def test_completion_segment10(self): source = """ f = ase.port("diego", [], {}, "gato").attr""" token_code = self.cc._tokenize_text(source) segment = self.cc._search_for_completion_segment(token_code) expected = 'ase.port().attr' self.assertEqual(expected, segment) def test_completion_segment11(self): source = """ f = ase.port("diego", [], {}, "gato").attr.""" token_code = self.cc._tokenize_text(source) segment = self.cc._search_for_completion_segment(token_code) expected = 'ase.port().attr.' self.assertEqual(expected, segment) def test_completion_segment12(self): source = """ ase.port(var.)""" token_code = self.cc._tokenize_text(source[:len(source) - 1]) segment = self.cc._search_for_completion_segment(token_code) expected = 'var.' self.assertEqual(expected, segment) def test_completion_segment13(self): source = """ ase.port(var.func('name', 3, [yep], (6, 7)).attr)""" token_code = self.cc._tokenize_text(source[:len(source) - 1]) segment = self.cc._search_for_completion_segment(token_code) expected = 'var.func().attr' self.assertEqual(expected, segment) def test_completion_segment14(self): #doesn't have much sense, but i wanted to test it source = """ ase.port{var.func('name', 3, [yep], (6, 7)).attr}""" token_code = self.cc._tokenize_text(source[:len(source) - 1]) segment = self.cc._search_for_completion_segment(token_code) expected = 'var.func().attr' self.assertEqual(expected, segment) def test_completion_segment15(self): #doesn't have much sense, but i wanted to test it source = """ f = ase.port[var.func('name', 3, [yep], (6, 7)).attr]""" token_code = self.cc._tokenize_text(source[:len(source) - 1]) segment = self.cc._search_for_completion_segment(token_code) expected = 'var.func().attr' self.assertEqual(expected, segment) def test_completion_segment16(self): #doesn't have much sense, but i wanted to test it source = """asd.\ ase.port(var.func('name', 3, [yep], (6, 7)).attr).""" token_code = self.cc._tokenize_text(source) segment = self.cc._search_for_completion_segment(token_code) expected = 'asd.ase.port().' self.assertEqual(expected, segment) ############################################################################### # TESTS FOR SCOPE SEARCH ############################################################################### def test_search_for_scope1(self): global SOURCE_COMPLETION new_lines = '\n l.' source_code = SOURCE_COMPLETION + new_lines token_code = self.cc._tokenize_text(source_code) scope = self.cc._search_for_scope(token_code) expected = ['MyClass', 'another'] self.assertEqual(expected, scope) def test_search_for_scope2(self): global SOURCE_COMPLETION new_lines = '\n l.' source_code = SOURCE_COMPLETION + new_lines token_code = self.cc._tokenize_text(source_code) scope = self.cc._search_for_scope(token_code) expected = ['MyClass'] self.assertEqual(expected, scope) def test_search_for_scope3(self): global SOURCE_COMPLETION new_lines = ('\n def new_func():' '\n self.var.') source_code = SOURCE_COMPLETION + new_lines token_code = self.cc._tokenize_text(source_code) scope = self.cc._search_for_scope(token_code) expected = ['MyClass', 'another', 'new_func'] self.assertEqual(expected, scope) def test_search_for_scope(self): global SOURCE_COMPLETION new_lines = ('\nvar.') source_code = SOURCE_COMPLETION + new_lines token_code = self.cc._tokenize_text(source_code) scope = self.cc._search_for_scope(token_code) self.assertEqual(None, scope) if __name__ == '__main__': unittest.main() ninja-ide-2.3/ninja_tests/tools/completion/test_code_completion_late_resolution.py000066400000000000000000000625361216641277400311400ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import import _ast import time import unittest from ninja_ide.tools.completion import analyzer from ninja_ide.tools.completion import model from ninja_ide.tools.completion import code_completion from ninja_ide.tools.completion import completion_daemon from ninja_tests.tools.completion import ( get_source_data, SOURCE_LATE_RESOLUTION, SOURCE_INHERITANCE ) completion_daemon.shutdown_daemon() completion_daemon.WAITING_BEFORE_START = 0 class AnalyzerLateResolutionTestCase(unittest.TestCase): def setUp(self): code_completion.settings.SYNTAX = {'python': {'keywords': []}} self.cc = code_completion.CodeCompletion() self.analyzer = analyzer.Analyzer() def tearDown(self): completion_daemon.shutdown_daemon() ############################################################################### # For Python Imports ############################################################################### def test_var_attribute_assign(self): module = self.analyzer.analyze(SOURCE_LATE_RESOLUTION) type1 = model._TypeData(None, 'os', 'import os', None) type2 = model.Assign('p') type2.add_data(4, model.late_resolution, 'p = os.path', _ast.Attribute) expected = {'os': type1} for imp in module.imports: data = expected[imp] impo = module.imports[imp] self.assertEqual(data.data_type, impo.data_type) self.assertEqual(data.line_content, impo.line_content) self.assertEqual(module.attributes['p'].data[0].lineno, type2.data[0].lineno) self.assertEqual(module.attributes['p'].data[0].data_type, type2.data[0].data_type) self.assertEqual(module.attributes['p'].data[0].operation, type2.data[0].operation) self.assertEqual(module.attributes['p'].data[0].line_content, type2.data[0].line_content) def test_simple_import_late_resolution(self): source_code = SOURCE_LATE_RESOLUTION + '\np.' self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('pathsep', results['attributes']) self.assertIn('expanduser', results['functions']) self.assertIn('sys', results['modules']) def test_simple_import_late_resolution_func(self): new_code = ['def func():', ' p.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('pathsep', results['attributes']) self.assertIn('expanduser', results['functions']) self.assertIn('sys', results['modules']) def test_simple_import_late_resolution_chained_attr(self): new_code = ['import threading', 't = threading.Lock().'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('acquire', results['attributes']) def test_simple_import_late_resolution_chained_attr_2(self): new_code = ['from threading import Lock', 't = Lock().'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('acquire', results['attributes']) def test_simple_import_late_resolution_chained_attr_3(self): new_code = ['from threading import Lock', 't = Lock()', 't.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('acquire', results['attributes']) def test_simple_import_late_resolution_not_outside_func(self): new_code = ['def func():', ' q = os.path', 'q.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) expected = get_source_data(source_code, 'q') self.assertEqual(results, expected) def test_simple_import_late_resolution_chained_func_1(self): new_code = ['import threading', 'import sys', 'def func():', ' q = threading.Lock()', ' def gfunc():', ' a = sys', ' a.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('exit', results['attributes']) def test_simple_import_late_resolution_chained_func_2(self): new_code = ['import threading', 'import sys', 'def func():', ' q = threading.Lock()', ' def gfunc():', ' a = sys', ' q.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('acquire', results['attributes']) def test_simple_import_late_resolution_chained_func_3(self): new_code = ['import threading', 'import sys', 'def func():', ' q = threading.Lock()', ' def gfunc():', ' a = sys', ' p.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('expanduser', results['functions']) def test_simple_import_late_resolution_chained_func_4(self): new_code = ['import threading', 'import sys', 'def func():', ' q = threading.Lock()', ' def gfunc():', ' a = sys', ' a.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) expected = get_source_data(source_code, 'a') self.assertEqual(expected, results) def test_simple_import_late_resolution_chained_func_5(self): new_code = ['import threading', 'import sys', 'def func():', ' q = threading.Lock()', ' def gfunc():', ' a = sys', ' q.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('acquire', results['attributes']) def test_simple_import_late_resolution_chained_func_6(self): new_code = ['import threading', 'import sys', 'def func():', ' q = threading.Lock()', ' def gfunc():', ' a = sys', 'p.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('expanduser', results['functions']) def test_simple_import_late_resolution_chained_func_7(self): new_code = ['import threading', 'import sys', 'def func():', ' q = threading.Lock()', ' def gfunc():', ' a = sys', 'q.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) expected = get_source_data(source_code, 'q') self.assertEqual(expected, results) def test_simple_import_late_resolution_local_symbols(self): new_code = ['class MyClass(object):', ' def __init__(self):', ' self.value1 = True', ' def func(self):', ' self.q = os.path', ' def gfunc(self):', ' import sys', ' self.a = sys', 'mc = MyClass()', 'mc.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) expected = {'attributes': ['a', 'q', 'value1'], 'functions': ['__init__', 'func', 'gfunc']} self.assertEqual(expected, results) def test_late_resolution_own_class_1(self): new_code = ['class MyClass(object):', ' def __init__(self):', ' self.value1 = True', ' def func(self):', ' self.q = os.path', ' def gfunc(self):', ' import sys', ' self.a = sys', 'mc = MyClass()', 'd = mc'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) expected = {'attributes': ['a', 'q', 'value1'], 'functions': ['__init__', 'func', 'gfunc']} self.assertEqual(expected, results) def test_late_resolution_own_class_2(self): new_code = ['class MyClass(object):', ' def __init__(self):', ' self.value1 = "ninja-ide"', ' def func(self):', ' self.q = os.path', ' def gfunc(self):', ' import sys', ' self.a = sys', 'mc = MyClass()', 'd = mc.value1', 'd.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) expected = dir(str) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_late_resolution_return_1(self): new_code = ['def function():', ' return "ninja-ide"', 'f = function()', 'f.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) expected = dir(str) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_late_resolution_return_2(self): new_code = ['def function():', ' return "ninja-ide"', 'f = function().'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) expected = dir(str) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_late_resolution_return_3(self): new_code = ['class Ninja:', ' def __init__(self):', ' self.val = 34', ' def funcion(self):', ' return "ninja"', ' def funcion2(self):', ' return self.val', 'n = Ninja()', 'a = n.funcion()', 'a.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) expected = dir(str) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_late_resolution_return_4(self): new_code = ['class Ninja:', ' def __init__(self):', ' self.val = 34', ' def funcion(self):', ' return "ninja"', ' def funcion2(self):', ' return self.val', 'n = Ninja()', 'a = n.funcion().'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) expected = dir(str) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) @unittest.skip("It happens in the test, but can not reproduce it IRL") def test_late_resolution_return_5(self): #It happens in the test, but can not reproduce it IRL new_code = ['class Ninja:', ' def __init__(self):', ' self.val = 34', ' def funcion(self):', ' return "ninja"', ' def funcion2(self):', ' return self.val', 'n = Ninja()', 'a = n.funcion2()', 'a.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) expected = dir(int) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_late_resolution_return_6(self): new_code = ['class Ninja:', ' def __init__(self):', ' self.val = 34', ' def funcion(self):', ' return "ninja"', ' def funcion2(self):', ' return self.val', 'n = Ninja()', 'a = n.funcion2().'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) expected = dir(int) __attrib = [d for d in expected if d[:2] == '__'] expected = expected[len(__attrib):] + __attrib self.assertEqual(expected, results['attributes']) def test_late_resolution_return_7(self): new_code = ['import threading', 'def func():', ' return threading.Lock()', 'n = func()', 'n.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('acquire', results['attributes']) def test_late_resolution_return_8(self): new_code = ['import threading', 'def func():', ' return threading.Lock()', 'n = func().'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('acquire', results['attributes']) def test_late_resolution_return_9(self): new_code = ['import threading', 'def func():', ' return threading', 'n = func().'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('Lock', results['attributes']) def test_late_resolution_return_10(self): new_code = ['import threading', 'def func():', ' return threading', 'n = func()', 'n.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('Lock', results['attributes']) def test_weird_case_1(self): new_code = ['import threading', 'def func():', ' return threading', 'n = func()', 'b = n.Lock().'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('acquire', results['attributes']) def test_weird_case_2(self): new_code = ['import threading', 'def func():', ' return threading', 'n = func()', 'b = n.Lock()', 'b.'] source_code = SOURCE_LATE_RESOLUTION + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('acquire', results['attributes']) class InheritanceLateResolutionTestCase(unittest.TestCase): def setUp(self): code_completion.settings.SYNTAX = {'python': {'keywords': []}} self.cc = code_completion.CodeCompletion() self.analyzer = analyzer.Analyzer() def tearDown(self): completion_daemon.shutdown_daemon() def test_simple_local_inheritance(self): new_code = ['class Son(Parent):', ' def __init__(self):', ' super(Son, self).__init__()', ' self.x = 3', 'n = Son()', 'n.'] source_code = SOURCE_INHERITANCE + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('value', results['attributes']) self.assertIn('x', results['attributes']) self.assertIn('function', results['functions']) def test_simple_import_inheritance(self): new_code = ['class Son(Lock):', ' def __init__(self):', ' super(Son, self).__init__()', ' self.x = 3', 'n = Son()', 'n.'] source_code = SOURCE_INHERITANCE + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('acquire', results['attributes']) self.assertIn('x', results['attributes']) def test_simple_import_inheritance_2(self): new_code = ['class Son(decimal.Decimal):', ' def __init__(self):', ' super(Son, self).__init__()', ' self.x = 3', 'n = Son()', 'n.'] source_code = SOURCE_INHERITANCE + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('real', results['attributes']) self.assertIn('x', results['attributes']) self.assertIn('to_integral', results['functions']) def test_multiple_import_inheritance(self): new_code = ['class Son(Lock, decimal.Decimal):', ' def __init__(self):', ' super(Son, self).__init__()', ' self.x = 3', 'n = Son()', 'n.'] source_code = SOURCE_INHERITANCE + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('acquire', results['attributes']) self.assertIn('real', results['attributes']) self.assertIn('x', results['attributes']) self.assertIn('to_integral', results['functions']) def test_multiple_mix_inheritance(self): new_code = ['class Parent2(Lock):', ' def __init__(self):', ' self.my_value = "yeah"', ' def function2(self):', ' self.another = "yeah2"', 'class Son(Parent2, decimal.Decimal):', ' def __init__(self):', ' super(Son, self).__init__()', ' self.x = 3', 'n = Son()', 'n.'] source_code = SOURCE_INHERITANCE + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('acquire', results['attributes']) self.assertIn('real', results['attributes']) self.assertIn('another', results['attributes']) self.assertIn('my_value', results['attributes']) self.assertIn('x', results['attributes']) self.assertIn('function2', results['functions']) self.assertIn('to_integral', results['functions']) def test_multiple_mix_inheritance_inside_class(self): new_code = ['class Parent2(Lock):', ' def __init__(self):', ' self.my_value = "yeah"', ' def function2(self):', ' self.another = "yeah2"', 'class Son(Parent2, decimal.Decimal):', ' def __init__(self):', ' super(Son, self).__init__()', ' self.'] source_code = SOURCE_INHERITANCE + '\n'.join(new_code) self.cc.analyze_file('', source_code) offset = len(source_code) time.sleep(1) results = self.cc.get_completion(source_code, offset) self.assertIn('acquire', results['attributes']) self.assertIn('real', results['attributes']) self.assertIn('another', results['attributes']) self.assertIn('my_value', results['attributes']) self.assertIn('function2', results['functions']) self.assertIn('to_integral', results['functions']) if __name__ == '__main__': unittest.main() ninja-ide-2.3/ninja_tests/tools/completion/test_completer.py000066400000000000000000000012641216641277400244660ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . ninja-ide-2.3/ninja_tests/tools/completion/test_model.py000066400000000000000000000012641216641277400235740ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . ninja-ide-2.3/ninja_tests/tools/jsonmanager/000077500000000000000000000000001216641277400212135ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/tools/jsonmanager/__init__.py000066400000000000000000000000001216641277400233120ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/tools/jsonmanager/samples/000077500000000000000000000000001216641277400226575ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/tools/jsonmanager/samples/__init__.py000066400000000000000000000000001216641277400247560ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/tools/jsonmanager/samples/badfiles/000077500000000000000000000000001216641277400244305ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/tools/jsonmanager/samples/badfiles/python.json000066400000000000000000000043001216641277400266410ustar00rootroot00000000000000{ "comment": [ "#" ], "extension": [ "py", "pyw", "rpy", "tac" ], "definition" [ "def" "class" ], "string": [ "'", "\"" ], "properObject": [ "self" ], "operators": [ "=", "==", "!=", "<", ^o^ "<=", ERROR, ERROR, ERROR, BIIIIIIIIIIP ">", ... ">=", ... "\\+", am i alive ? "-", am i a machine ? "\\*", why am i here ? "/", ... "//", ERROR, ERROR, ERROR, ... PUFFFF! "\\%", ... "\\*\\*", "\\+=", \o/ "-=", "\\*=", "/=", "\\%=", "\\^", "\\|", "\\&", "\\~", ">>", "<<" ], "keywords": [ "and", "assert", "break", "class", "continue", "def", "del", "elif", "else", "except", "exec", "finally", "for", "from", "global", "if", "import", "in", "is", "as", "lambda", "not", "or", "pass", "print", "raise", "return", "super", "try", "while", "with", "yield", "None", "True", "False" ], "extras": [ "abs", "all", "any", "basestring", "bin", "bool", "bytearray", "callable", "chr", "classmethod", "cmp", "compile", "complex", "delattr", "dict", "dir", "divmod", "enumerate", "eval", "execfile", "file", "filter", "float", "format", "frozenset", "getattr", "globals", "hasattr", "hash", "help", "hex", "id", "input", "int", "isinstance", "issubclass", "iter", "len", "list", "locals", "long", "map", "max", "memoryview", "min", "next", "object", "oct", "open", "ord", "pow", "property", "range", "raw_input", "reduce", "reload", "repr", "reversed", "round", "set", "setattr", "slice", "sorted", "staticmethod", "str", "sum", "tuple", "type", "unichr", "unicode", "vars", "xrange", "zip", "apply", "buffer", "coerce", "intern" ] } ninja-ide-2.3/ninja_tests/tools/jsonmanager/samples/goodfiles/000077500000000000000000000000001216641277400246325ustar00rootroot00000000000000ninja-ide-2.3/ninja_tests/tools/jsonmanager/samples/goodfiles/python.json000066400000000000000000000037041216641277400270520ustar00rootroot00000000000000{ "comment": [ "#" ], "extension": [ "py", "pyw", "rpy", "tac" ], "definition": [ "def", "class" ], "string": [ "'", "\"" ], "properObject": [ "self" ], "operators": [ "=", "==", "!=", "<", "<=", ">", ">=", "\\+", "-", "\\*", "/", "//", "\\%", "\\*\\*", "\\+=", "-=", "\\*=", "/=", "\\%=", "\\^", "\\|", "\\&", "\\~", ">>", "<<" ], "keywords": [ "and", "assert", "break", "class", "continue", "def", "del", "elif", "else", "except", "exec", "finally", "for", "from", "global", "if", "import", "in", "is", "as", "lambda", "not", "or", "pass", "print", "raise", "return", "super", "try", "while", "with", "yield", "None", "True", "False" ], "extras": [ "abs", "all", "any", "basestring", "bin", "bool", "bytearray", "callable", "chr", "classmethod", "cmp", "compile", "complex", "delattr", "dict", "dir", "divmod", "enumerate", "eval", "execfile", "file", "filter", "float", "format", "frozenset", "getattr", "globals", "hasattr", "hash", "help", "hex", "id", "input", "int", "isinstance", "issubclass", "iter", "len", "list", "locals", "long", "map", "max", "memoryview", "min", "next", "object", "oct", "open", "ord", "pow", "property", "range", "raw_input", "reduce", "reload", "repr", "reversed", "round", "set", "setattr", "slice", "sorted", "staticmethod", "str", "sum", "tuple", "type", "unichr", "unicode", "vars", "xrange", "zip", "apply", "buffer", "coerce", "intern" ] } ninja-ide-2.3/ninja_tests/tools/jsonmanager/test_ninja_project.py000066400000000000000000000065411216641277400254570ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . import os import json import shutil import unittest import tempfile from ninja_ide.tools.json_manager import read_ninja_plugin from ninja_ide.tools.json_manager import read_ninja_project from ninja_ide.tools.json_manager import create_ninja_project class TestNinjaProject(unittest.TestCase): def setUp(self): self.tmpdir = tempfile.mkdtemp() def tearDown(self): shutil.rmtree(self.tmpdir) def test_create_ninja_project(self): path = self.tmpdir project = 'This is My Project' structure = dict(foo='bar') create_ninja_project(path, project, structure) expected_name = 'this_is_my_project.nja' self.assertTrue(expected_name in os.listdir(path)) with open(os.path.join(path, expected_name), 'r') as fp: content = json.load(fp) assert content['foo'] == 'bar' def test_create_ninja_project_with_lot_of_spaces_in_name(self): path = self.tmpdir project = 'This is My Project ' structure = dict(foo='bar') create_ninja_project(path, project, structure) expected_name = 'this_is_my_________project.nja' self.assertTrue(expected_name in os.listdir(path)) with open(os.path.join(path, expected_name), 'r') as fp: content = json.load(fp) assert content['foo'] == 'bar' def test_read_ninja_project(self): path = self.tmpdir project = 'This is My Project' structure = dict(foo='bar') structure = read_ninja_project(path) self.assertEquals(structure, dict()) structure = dict(foo='bar') create_ninja_project(path, project, structure) structure = read_ninja_project(path) assert structure['foo'] == 'bar' def test_read_ninja_bad_file_project(self): path = self.tmpdir frula_file = os.path.join(path, 'frula.nja') with open(frula_file, 'w') as fp: fp.write('frula\n') structure = read_ninja_project(path) assert structure == dict() def test_read_ninja_plugin(self): path = self.tmpdir content = dict(samurai='ki') plugin_file = os.path.join(path, 'samurai.plugin') with open(plugin_file, 'w') as fp: json.dump(content, fp) structure = read_ninja_plugin(path) assert structure['samurai'] == 'ki' def test_read_ninja_bad_file_plugin(self): path = self.tmpdir frula_file = os.path.join(path, 'frula.plugin') with open(frula_file, 'w') as fp: fp.write('frula\n') structure = read_ninja_plugin(path) assert structure == dict() if __name__ == '__main__': unittest.main() ninja-ide-2.3/ninja_tests/tools/jsonmanager/test_syntax_loader.py000066400000000000000000000047571216641277400255150ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . import os import unittest from ninja_tests.tools.jsonmanager import samples from ninja_ide import resources from ninja_ide.core import settings from ninja_ide.tools.json_manager import load_syntax class TestSyntaxLoader(unittest.TestCase): ''' We test the loader method from the json manager that is in charge of loading syntax files ''' def setUp(self): self.samples = os.path.dirname(samples.__file__) self.goodsamples = os.path.join(self.samples, 'goodfiles') self.badsamples = os.path.join(self.samples, 'badfiles') # Clean SYNTAX before each test settings.SYNTAX = {} def test_load_nice_json_syntax_file(self): resources.SYNTAX_FILES = self.goodsamples py_syntax_name = 'python' py_syntax_file = os.path.join(self.goodsamples, py_syntax_name + '.json') assert os.path.isfile(py_syntax_file) is True self.assertTrue(py_syntax_name not in settings.SYNTAX) load_syntax() self.assertTrue(py_syntax_name in settings.SYNTAX) python_syntax = settings.SYNTAX["python"] extensions = python_syntax['extension'] for kw in extensions: self.assertTrue(kw in settings.EXTENSIONS) self.assertEquals(settings.EXTENSIONS[kw], py_syntax_name) def test_load_bad_json_syntax_file(self): resources.SYNTAX_FILES = self.badsamples py_syntax_name = 'python' py_syntax_file = os.path.join(self.goodsamples, py_syntax_name + '.json') assert os.path.isfile(py_syntax_file) is True self.assertTrue(py_syntax_name not in settings.SYNTAX) load_syntax() self.assertTrue(py_syntax_name not in settings.SYNTAX) if __name__ == '__main__': unittest.main() ninja-ide-2.3/ninja_tests/tools/test_locator.py000066400000000000000000000024311216641277400217630ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of NINJA-IDE (http://ninja-ide.org). # # NINJA-IDE 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 # any later version. # # NINJA-IDE 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 NINJA-IDE; If not, see . from __future__ import absolute_import from __future__ import unicode_literals import sys # Import this before Qt to set the correct API import ninja_ide # lint:ok from PyQt4.QtGui import QApplication from ninja_ide.tools import locator from ninja_tests import BaseTest class LocatorTestCase(BaseTest): def setUp(self): super(LocatorTestCase, self).setUp() self.app = QApplication(sys.argv) self.locator = locator.Locator() def test_navigate_to(self): pass def test_load_results(self): pass def test_get_classes_from_project(self): pass ninja-ide-2.3/ninja_translate000066400000000000000000000045531216641277400163340ustar00rootroot00000000000000 SOURCES += \ ninja_ide/gui/updates.py \ ninja_ide/gui/status_bar.py \ ninja_ide/gui/ide.py \ ninja_ide/gui/central_widget.py \ ninja_ide/gui/actions.py \ ninja_ide/tools/ui_tools.py \ ninja_ide/tools/locator.py \ ninja_ide/gui/dialogs/wizard_new_project.py \ ninja_ide/gui/dialogs/themes_manager.py \ ninja_ide/gui/dialogs/traceback_widget.py \ ninja_ide/gui/dialogs/project_properties_widget.py \ ninja_ide/gui/dialogs/preferences.py \ ninja_ide/gui/dialogs/plugins_manager.py \ ninja_ide/gui/dialogs/language_manager.py \ ninja_ide/gui/dialogs/from_import_dialog.py \ ninja_ide/gui/dialogs/about_ninja.py \ ninja_ide/gui/editor/syntax_highlighter.py \ ninja_ide/gui/editor/sidebar_widget.py \ ninja_ide/gui/editor/python_syntax.py \ ninja_ide/gui/editor/pep8_checker.py \ ninja_ide/gui/editor/minimap.py\ ninja_ide/gui/editor/migration_2to3.py \ ninja_ide/gui/editor/highlighter.py \ ninja_ide/gui/editor/helpers.py \ ninja_ide/gui/editor/errors_checker.py \ ninja_ide/gui/editor/editor.py \ ninja_ide/gui/explorer/migration_lists.py \ ninja_ide/gui/explorer/explorer_container.py \ ninja_ide/gui/explorer/tree_projects_widget.py \ ninja_ide/gui/explorer/tree_symbols_widget.py \ ninja_ide/gui/explorer/explorer_container.py \ ninja_ide/gui/explorer/errors_lists.py \ ninja_ide/gui/main_panel/tab_widget.py \ ninja_ide/gui/main_panel/tab_group.py \ ninja_ide/gui/main_panel/start_page.py \ ninja_ide/gui/main_panel/itab_item.py \ ninja_ide/gui/main_panel/image_viewer.py \ ninja_ide/gui/main_panel/class_diagram.py \ ninja_ide/gui/main_panel/browser_widget.py \ ninja_ide/gui/main_panel/main_container.py \ ninja_ide/gui/menus/menu_view.py \ ninja_ide/gui/menus/menu_source.py \ ninja_ide/gui/menus/menu_project.py \ ninja_ide/gui/menus/menu_plugins.py \ ninja_ide/gui/menus/menu_file.py \ ninja_ide/gui/menus/menu_edit.py \ ninja_ide/gui/menus/menu_about.py \ ninja_ide/gui/misc/web_render.py \ ninja_ide/gui/misc/run_widget.py \ ninja_ide/gui/misc/shortcut_manager.py \ ninja_ide/gui/misc/results.py \ ninja_ide/gui/misc/plugin_preferences.py \ ninja_ide/gui/misc/misc_container.py \ ninja_ide/gui/misc/find_in_files.py \ ninja_ide/gui/misc/console_widget.py TRANSLATIONS = ninja.ts ninja-ide-2.3/run-tests000077500000000000000000000001531216641277400151170ustar00rootroot00000000000000FOLDER=$PWD if [ $# -eq 1 ] then FOLDER=$PWD/$1 fi clear python -m unittest discover -v -s $FOLDER ninja-ide-2.3/setup.py000066400000000000000000000105561216641277400147470ustar00rootroot00000000000000#!/usr/bin/env python #-*-coding:utf-8-*- # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser 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 Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . ############################################################################### # DOCS ############################################################################### """Setup for Ninja-ide (http://www.ninja-ide.org) NINJA-IDE is a cross-platform integrated development environment (IDE). NINJA-IDE runs on Linux/X11, Mac OS X and Windows desktop operating systems, and allows developers to create applications for several purposes using all the tools and utilities of NINJA-IDE, making the task of writing software easier and more enjoyable. """ ############################################################################### # IMPORTS ############################################################################### import sys from setuptools import setup, find_packages import ninja_ide ############################################################################### # VALIDATE THE NEEDED MODULES ############################################################################### # This modules can't be easy installed # Syntax: [(module, url of the tutorial)...] if sys.platform == 'win32': NEEDED_MODULES = [("PyQt4", "http://www.riverbankcomputing.co.uk/software/pyqt/intro"), ('win32con', "http://sourceforge.net/projects/pywin32/files/pywin32/")] else: NEEDED_MODULES = [("PyQt4", "http://www.riverbankcomputing.co.uk/software/pyqt/intro"), ] for mn, urlm in NEEDED_MODULES: try: __import__(mn) except ImportError: print("Module '%s' not found. For more details: '%s'.\n" % (mn, urlm)) sys.exit(1) dependencies = [] if sys.platform == 'darwin': dependencies.append("macfsevents") elif sys.platform == 'linux2': dependencies.append("pyinotify") ############################################################################### # PRE-SETUP ############################################################################### # Common params = { "name": ninja_ide.__prj__, "version": ninja_ide.__version__, "description": ninja_ide.__doc__, "author": ninja_ide.__author__, "author_email": ninja_ide.__mail__, "url": ninja_ide.__url__, "license": ninja_ide.__license__, "keywords": "ide python ninja development", "classifiers": ["Development Status :: Development Status :: 4 - Beta", "Topic :: Utilities", "License :: OSI Approved :: GNU General Public License (GPL)", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 2"], # Ninja need: "install_requires": dependencies, # include all resources "include_package_data": True, "package_data": {'': ['*.png', '*.gif', '*.jpg', '*.json', '*.qss', '*.js', '*.html', '*.css', '*.qm', '*.qml']}, # include ninja pkg and setup the run script "packages": find_packages() + [ 'ninja_ide/addins', 'ninja_ide/addins/lang', 'ninja_ide/addins/qml', 'ninja_ide/addins/qml/img', 'ninja_ide/addins/syntax', 'ninja_ide/addins/theme', 'ninja_ide/img'], #auto create scripts "entry_points": { 'console_scripts': [ 'ninja-ide = ninja_ide:setup_and_run', ], 'gui_scripts': [ 'ninja-ide = ninja_ide:setup_and_run', ] } } ############################################################################### # SETUP ############################################################################### setup(**params) ############################################################################### # MAIN ############################################################################### if __name__ == '__main__': print(__doc__)