pax_global_header00006660000000000000000000000064122232424630014512gustar00rootroot0000000000000052 comment=651ed3d3623bec3d73233b84889624fd97d0891a glogg-0.9.2/000077500000000000000000000000001222324246300126215ustar00rootroot00000000000000glogg-0.9.2/.gitignore000066400000000000000000000010511222324246300146060ustar00rootroot00000000000000# git-ls-files --others --exclude-from=.git/info/exclude # Lines that start with '#' are comments. # For a project mostly in C, the following would be a good set of # exclude patterns (uncomment them if you want to use them): *.[oa] *~ moc_*.cpp ui_*.h .*.sw? qrc_glogg.cpp Makefile glogg glogg_tests massif.out* core.* doc/*.html # File generated by Qt Creator glogg.config glogg.creator glogg.creator.user glogg.files glogg.includes glogg.pro.user Makefile.Debug Makefile.Release debug/ release/ object_script.glogg.Debug object_script.glogg.Release glogg-0.9.2/COPYING000066400000000000000000001045131222324246300136600ustar00rootroot00000000000000 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 . glogg-0.9.2/FileAssociation.nsh000066400000000000000000000110501222324246300164040ustar00rootroot00000000000000/* _____________________________________________________________________________ File Association _____________________________________________________________________________ Based on code taken from http://nsis.sourceforge.net/File_Association Usage in script: 1. !include "FileAssociation.nsh" 2. [Section|Function] ${FileAssociationFunction} "Param1" "Param2" "..." $var [SectionEnd|FunctionEnd] FileAssociationFunction=[RegisterExtension|UnRegisterExtension] _____________________________________________________________________________ ${RegisterExtension} "[executable]" "[extension]" "[description]" "[executable]" ; executable which opens the file format ; "[extension]" ; extension, which represents the file format to open ; "[description]" ; description for the extension. This will be display in Windows Explorer. ; ${UnRegisterExtension} "[extension]" "[description]" "[extension]" ; extension, which represents the file format to open ; "[description]" ; description for the extension. This will be display in Windows Explorer. ; _____________________________________________________________________________ Macros _____________________________________________________________________________ Change log window verbosity (default: 3=no script) Example: !include "FileAssociation.nsh" !insertmacro RegisterExtension ${FileAssociation_VERBOSE} 4 # all verbosity !insertmacro UnRegisterExtension ${FileAssociation_VERBOSE} 3 # no script */ !ifndef FileAssociation_INCLUDED !define FileAssociation_INCLUDED !include Util.nsh !verbose push !verbose 3 !ifndef _FileAssociation_VERBOSE !define _FileAssociation_VERBOSE 3 !endif !verbose ${_FileAssociation_VERBOSE} !define FileAssociation_VERBOSE `!insertmacro FileAssociation_VERBOSE` !verbose pop !macro FileAssociation_VERBOSE _VERBOSE !verbose push !verbose 3 !undef _FileAssociation_VERBOSE !define _FileAssociation_VERBOSE ${_VERBOSE} !verbose pop !macroend !macro RegisterExtensionCall _EXECUTABLE _EXTENSION _DESCRIPTION !verbose push !verbose ${_FileAssociation_VERBOSE} Push `${_DESCRIPTION}` Push `${_EXTENSION}` Push `${_EXECUTABLE}` ${CallArtificialFunction} RegisterExtension_ !verbose pop !macroend !macro UnRegisterExtensionCall _EXTENSION _DESCRIPTION !verbose push !verbose ${_FileAssociation_VERBOSE} Push `${_EXTENSION}` Push `${_DESCRIPTION}` ${CallArtificialFunction} UnRegisterExtension_ !verbose pop !macroend !define RegisterExtension `!insertmacro RegisterExtensionCall` !define un.RegisterExtension `!insertmacro RegisterExtensionCall` !macro RegisterExtension !macroend !macro un.RegisterExtension !macroend !macro RegisterExtension_ !verbose push !verbose ${_FileAssociation_VERBOSE} Exch $R2 ;exe Exch Exch $R1 ;ext Exch Exch 2 Exch $R0 ;desc Exch 2 Push $0 Push $1 ReadRegStr $1 HKCR $R1 "" ; read current file association StrCmp "$1" "" NoBackup ; is it empty StrCmp "$1" "$R0" NoBackup ; is it our own WriteRegStr HKCR $R1 "backup_val" "$1" ; backup current value NoBackup: WriteRegStr HKCR $R1 "" "$R0" ; set our file association ReadRegStr $0 HKCR $R0 "" StrCmp $0 "" 0 Skip WriteRegStr HKCR "$R0" "" "$R0" WriteRegStr HKCR "$R0\shell" "" "open" WriteRegStr HKCR "$R0\DefaultIcon" "" "$R2,0" Skip: WriteRegStr HKCR "$R0\shell\open\command" "" '"$R2" "%1"' WriteRegStr HKCR "$R0\shell\edit" "" "Edit $R0" WriteRegStr HKCR "$R0\shell\edit\command" "" '"$R2" "%1"' Pop $1 Pop $0 Pop $R2 Pop $R1 Pop $R0 !verbose pop !macroend !define UnRegisterExtension `!insertmacro UnRegisterExtensionCall` !define un.UnRegisterExtension `!insertmacro UnRegisterExtensionCall` !macro UnRegisterExtension !macroend !macro un.UnRegisterExtension !macroend !macro UnRegisterExtension_ !verbose push !verbose ${_FileAssociation_VERBOSE} Exch $R1 ;desc Exch Exch $R0 ;ext Exch Push $0 Push $1 ReadRegStr $1 HKCR $R0 "" StrCmp $1 $R1 0 NoOwn ; only do this if we own it ReadRegStr $1 HKCR $R0 "backup_val" StrCmp $1 "" 0 Restore ; if backup="" then delete the whole key DeleteRegKey HKCR $R0 Goto NoOwn Restore: WriteRegStr HKCR $R0 "" $1 DeleteRegValue HKCR $R0 "backup_val" DeleteRegKey HKCR $R1 ;Delete key with association name settings NoOwn: Pop $1 Pop $0 Pop $R1 Pop $R0 !verbose pop !macroend !endif # !FileAssociation_INCLUDED glogg-0.9.2/INSTALL.win.md000066400000000000000000000114671222324246300150560ustar00rootroot00000000000000# Building glogg for Windows As often on Windows, building _glogg_ is more complicated than we would like. The method we use to test and generate the builds available on glogg.bonnefon.org is to cross-build _glogg_ from a Debian/Ubuntu machine. The compiler currently used is MinGW-W64 4.6.3, which is available on Debian unstable and Ubuntu 12.04 LTS: apt-get install g++-mingw-w64-i686 There are two dependencies that must be built for Windows: - Qt, we currently use version 4.8.2 - Boost, we currently use version 1.50.0 Once the dependencies are installed (see below), a script takes care of the building and the packaging: ./release-win32-x.sh ## Building Qt on Windows It is allegedly possible to cross-compile Qt from Windows but after a lot of frustration, the _glogg_ team is building it natively on Windows (using a Windows 7 64bit VM in VirtualBox) and then using the binary generated from the Linux machine. Amazingly, it works as long as we are using a similar version of gcc (MinGW-W64) on both machines. Here are instructions to do the build in a Windows 7 VM: - Download the source from qt website (tested 4.8.2), be sure to download it with DOS end-of-line conventions (zip archive instead of tar.bz2 or tar.gz). - Install the .NET Framework 4 (http://www.microsoft.com/en-us/download/details.aspx?id=17718) - Install the Windows SDK (http://www.microsoft.com/en-us/download/details.aspx?id=8279) - Install mingw-w64 from TDM-GCC (tdm64-gcc, tested with 4.6.1). - Extract the Qt source in c:\qt\4.8.2 If building a 32 bits version of Qt (what we do for _glogg_): - Modify qt/4.8.2/mkspecs/win32-g++/qmake.conf to add `-m32` to `QMAKE_CFLAGS` and `QMAKE_LFLAGS` - Modify qt/4.8.2/mkspecs/win32-g++/qmake.conf to replace: `QMAKE_RC = windres -F pe-i386` - (optionally make other changes here to improve performances) Build from the MinGW command prompt: configure.exe -platform win32-g++-4.6 -no-phonon -no-phonon-backend -no-webkit -fast -opensource -shared -no-qt3support -no-sql-sqlite -no-openvg -no-gif -no-opengl -no-scripttools -qt-style-windowsxp -qt-style-windowsvista mingw32-make - copy the whole `qt/4.8.2` to the linux machine in `~/qt-x-win32/qt_win/4.8.2` ### Creating the cross-compiled Qt target - Install ~/qt-x-win32 created before Then create a copy of the standard gcc target to create the new cross-gcc target: cd /usr/share/qt4/mkspecs/ sudo cp -a win32-g++ win32-x-g++ and modify it to point to the cross-compiler and our local version of Windows Qt: sudo sed -i -re 's/ (gcc|g\+\+)/ i686-w64-mingw32-\1/' win32-x-g++/qmake.conf sudo sed -i -re '/QMAKE_SH/iQMAKE_SH=1' win32-x-g++/qmake.conf sudo sed -i -re 's/QMAKE_COPY_DIR.*$/QMAKE_COPY_DIR = cp -r/' win32-x-g++/qmake.conf sudo sed -i -re '/QMAKE_LFLAGS/s/$/ -mwindows -static-libgcc -static-libstdc++/' win32-x-g++/qmake.conf sudo sed -i -re 's/QMAKE_RC.*$/QMAKE_RC = i686-w64-mingw32-windres/' win32-x-g++/qmake.conf sudo sed -i -re 's/QMAKE_STRIP\s.*$/QMAKE_STRIP = i686-w64-mingw32-strip/' win32-x-g++/qmake.conf sudo sed -i -re 's/\.exe//' win32-x-g++/qmake.conf sudo sed -i -re 's/QMAKE_INCDIR\s.*$/QMAKE_INCDIR = \/usr\/i686-w64-mingw32\/include/' win32-x-g++/qmake.conf sudo sed -i -re 's/QMAKE_INCDIR_QT\s.*$/QMAKE_INCDIR_QT = \/home\/$USER\/qt_win\/4.8.2\/include/' win32-x-g++/qmake.conf sudo sed -i -re 's/QMAKE_LIBDIR_QT\s.*$/QMAKE_LIBDIR_QT = \/home\/$USER\/qt_win\/4.8.2\/lib/' win32-x-g++/qmake.conf Now you can build a hello world to test Qt: mkdir /tmp/testqt cd /tmp/testqt echo '#include #include int main(int argc, char *argv[]) { QApplication app(argc, argv); QPushButton hello("Hello world!"); hello.resize(100, 30); hello.show(); return app.exec(); }' >main.cpp qmake -project qmake -spec win32-x-g++ -r CONFIG+=release make ## Building Boost on Windows Download the source from boost.org (tested with 1.50.0), DOS EOL mandatory! Extract it in a Windows VM Edit bootstrap.bat to read: call .\build.bat mingw %*... set toolset=gcc And from the MinGW prompt: bootstrap b2 toolset=gcc address-model=32 variant=debug,release link=static,shared threading=multi install - Copy the whole c:\boost_1_50_0 to the linux machine to ~/qt-x-win32/boost_1_50_0 ## (optional) Install NSIS If _wine_ and the NSIS compiler (available from [here](http://nsis.sourceforge.net/Main_Page)) are available, the script will generate the installer for _glogg_. The NSIS compiler should be installed in `~/qt-x-win32/NSIS`. ## Building _glogg_ From this point, building _glogg_ is hopefully straightforward: ./release-win32-x.sh The `release-win32-x.sh` script might need some changes if you use different paths for the dependencies. The object file is in `./release/` glogg-0.9.2/README000066400000000000000000000031461222324246300135050ustar00rootroot00000000000000glogg - the fast, smart log explorer glogg is a multi-platform GUI application that helps browse and search through long and complex log files. It is designed with programmers and system administrators in mind and can be seen as a graphical, interactive combination of grep and less. ## Main features * Runs on Unix-like systems, Windows and Mac thanks to Qt * Provides a second window showing the result of the current search * Supports grep/egrep like regular expressions * Colorizes the log and search results * Displays a context view of where in the log the lines of interest are * Is fast and reads the file directly from disk, without loading it into memory * Is open source, released under the GPL ## Requirements * Qt libraries (version 4.3.0 or later) * Boost "program-options" development libraries * Markdown HTML processor (optional, to generate HTML documentation) ## Building The build system uses qmake. Building and installation is done this way: tar xzf glogg-X.X.X.tar.gz cd glogg-X.X.X qmake (or qmake-qt4) make make install INSTALL_ROOT=/usr/local (as root if needed) qmake BOOST_PATH=/path/to/boost/ will statically compile the required parts of the Boost libraries whose source are found at the specified path. The path should be the directory where the tarball from www.boost.org is extracted. (use this method on Windows or if Boost is not available on the system) The documentation is built and installed automatically if 'markdown' is found. ## Contact Please visit glogg's website: http://glogg.bonnefon.org/ The development mailing list is hosted at http://groups.google.co.uk/group/glogg-devel glogg-0.9.2/TODO000066400000000000000000000010221222324246300133040ustar00rootroot00000000000000New features: - Line number in filtered view - Matches overview (right bar) - Bookmarks - Multi files - Improved savable search text - Save position with latest files (not just the last one) - Non modal filter dialog - On/off search item window - Drag 'n drop in filters Fixes: - Error in regexp - Yellow background for errors/file changed - Keep colours (partially) of selected text - Greater number of latest files - Long file name makes the status bar extend Perf: - Search slows down when there is a large number of matches glogg-0.9.2/abstractlogdata.cpp000066400000000000000000000042031222324246300164630ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // This file implements AbstractLogData. // This base class is primarily an interface class and should not // implement anything. // It exists so that AbstractLogView can manipulate an abtract set of data // (either full or filtered). #include "abstractlogdata.h" AbstractLogData::AbstractLogData() { } // Simple wrapper in order to use a clean Template Method QString AbstractLogData::getLineString( qint64 line ) const { return doGetLineString(line); } // Simple wrapper in order to use a clean Template Method QString AbstractLogData::getExpandedLineString( qint64 line ) const { return doGetExpandedLineString(line); } // Simple wrapper in order to use a clean Template Method QStringList AbstractLogData::getLines( qint64 first_line, int number ) const { return doGetLines( first_line, number ); } // Simple wrapper in order to use a clean Template Method QStringList AbstractLogData::getExpandedLines( qint64 first_line, int number ) const { return doGetExpandedLines( first_line, number ); } // Simple wrapper in order to use a clean Template Method qint64 AbstractLogData::getNbLine() const { return doGetNbLine(); } // Simple wrapper in order to use a clean Template Method int AbstractLogData::getMaxLength() const { return doGetMaxLength(); } // Simple wrapper in order to use a clean Template Method int AbstractLogData::getLineLength( qint64 line ) const { return doGetLineLength( line ); } glogg-0.9.2/abstractlogdata.h000066400000000000000000000101211222324246300161240ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef ABSTRACTLOGDATA_H #define ABSTRACTLOGDATA_H // #include "log.h" #include #include #include // Base class representing a set of data. // It can be either a full set or a filtered set. class AbstractLogData : public QObject { Q_OBJECT public: AbstractLogData(); // Permit each child to have its destructor virtual ~AbstractLogData() {}; // Returns the line passed as a QString QString getLineString( qint64 line ) const; // Returns the line passed as a QString, with tabs expanded QString getExpandedLineString( qint64 line ) const; // Returns a set of lines as a QStringList QStringList getLines( qint64 first_line, int number ) const; // Returns a set of lines with tabs expanded QStringList getExpandedLines( qint64 first_line, int number ) const; // Returns the total number of lines qint64 getNbLine() const; // Returns the visible length of the longest line // Tabs are expanded int getMaxLength() const; // Returns the visible length of the passed line // Tabs are expanded int getLineLength( qint64 line ) const; // Length of a tab stop static const int tabStop = 8; protected: // Internal function called to get a given line virtual QString doGetLineString( qint64 line ) const = 0; // Internal function called to get a given line virtual QString doGetExpandedLineString( qint64 line ) const = 0; // Internal function called to get a set of lines virtual QStringList doGetLines( qint64 first_line, int number ) const = 0; // Internal function called to get a set of expanded lines virtual QStringList doGetExpandedLines( qint64 first_line, int number ) const = 0; // Internal function called to get the number of lines virtual qint64 doGetNbLine() const = 0; // Internal function called to get the maximum length virtual int doGetMaxLength() const = 0; // Internal function called to get the line length virtual int doGetLineLength( qint64 line ) const = 0; static inline QString untabify( const QString& line ) { QString untabified_line; int total_spaces = 0; for ( int j = 0; j < line.length(); j++ ) { if ( line[j] == '\t' ) { int spaces = tabStop - ( ( j + total_spaces ) % tabStop ); // LOG(logDEBUG4) << "Replacing tab at char " << j << " (" << spaces << " spaces)"; QString blanks( spaces, QChar(' ') ); untabified_line.append( blanks ); total_spaces += spaces - 1; } else { untabified_line.append( line[j] ); } } return untabified_line; } static inline QString untabify( const char* line ) { QString untabified_line; int total_spaces = 0; for ( const char* i = line; *i != '\0'; i++ ) { if ( *i == '\t' ) { int spaces = tabStop - ( ( (i - line) + total_spaces ) % tabStop ); // LOG(logDEBUG4) << "Replacing tab at char " << j << " (" << spaces << " spaces)"; QString blanks( spaces, QChar(' ') ); untabified_line.append( blanks ); total_spaces += spaces - 1; } else { untabified_line.append( *i ); } } return untabified_line; } }; #endif glogg-0.9.2/abstractlogview.cpp000066400000000000000000001341761222324246300165410ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2011, 2012, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // This file implements the AbstractLogView base class. // Most of the actual drawing and event management common to the two views // is implemented in this class. The class only calls protected virtual // functions when view specific behaviour is desired, using the template // pattern. #include #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "persistentinfo.h" #include "filterset.h" #include "logmainview.h" #include "quickfind.h" #include "quickfindpattern.h" #include "overview.h" #include "configuration.h" namespace { int countDigits( quint64 n ) { if (n == 0) return 1; // We must force the compiler to not store intermediate results // in registers because this causes incorrect result on some // systems under optimizations level >0. For the skeptical: // // #include // #include // int main(int argc, char **argv) { // (void)argc; // long long int n = atoll(argv[1]); // return floor( log( n ) / log( 10 ) + 1 ); // } // // This is on Thinkpad T60 (Genuine Intel(R) CPU T2300). // $ g++ --version // g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 // $ g++ -O0 -Wall -W -o math math.cpp -lm; ./math 10; echo $? // 2 // $ g++ -O1 -Wall -W -o math math.cpp -lm; ./math 10; echo $? // 1 // // A fix is to (1) explicitly place intermediate results in // variables *and* (2) [A] mark them as 'volatile', or [B] pass // -ffloat-store to g++ (note that approach [A] is more portable). volatile qreal ln_n = qLn( n ); volatile qreal ln_10 = qLn( 10 ); volatile qreal lg_n = ln_n / ln_10; volatile qreal lg_n_1 = lg_n + 1; volatile qreal fl_lg_n_1 = qFloor( lg_n_1 ); return fl_lg_n_1; } } // anon namespace LineChunk::LineChunk( int first_col, int last_col, ChunkType type ) { // LOG(logDEBUG) << "new LineChunk: " << first_col << " " << last_col; start_ = first_col; end_ = last_col; type_ = type; } QList LineChunk::select( int sel_start, int sel_end ) const { QList list; if ( ( sel_start < start_ ) && ( sel_end < start_ ) ) { // Selection BEFORE this chunk: no change list << LineChunk( *this ); } else if ( sel_start > end_ ) { // Selection AFTER this chunk: no change list << LineChunk( *this ); } else /* if ( ( sel_start >= start_ ) && ( sel_end <= end_ ) ) */ { // We only want to consider what's inside THIS chunk sel_start = qMax( sel_start, start_ ); sel_end = qMin( sel_end, end_ ); if ( sel_start > start_ ) list << LineChunk( start_, sel_start - 1, type_ ); list << LineChunk( sel_start, sel_end, Selected ); if ( sel_end < end_ ) list << LineChunk( sel_end + 1, end_, type_ ); } return list; } inline void LineDrawer::addChunk( int first_col, int last_col, QColor fore, QColor back ) { if ( first_col < 0 ) first_col = 0; int length = last_col - first_col + 1; if ( length > 0 ) { list << Chunk ( first_col, length, fore, back ); } } inline void LineDrawer::addChunk( const LineChunk& chunk, QColor fore, QColor back ) { int first_col = chunk.start(); int last_col = chunk.end(); addChunk( first_col, last_col, fore, back ); } inline void LineDrawer::draw( QPainter& painter, int initialXPos, int initialYPos, int line_width, const QString& line, int leftExtraBackgroundPx ) { QFontMetrics fm = painter.fontMetrics(); const int fontHeight = fm.height(); const int fontAscent = fm.ascent(); // For some reason on Qt 4.8.2 for Win, maxWidth() is wrong but the // following give the right result, not sure why: const int fontWidth = fm.width( QChar('a') ); int xPos = initialXPos; int yPos = initialYPos; foreach ( Chunk chunk, list ) { // Draw each chunk // LOG(logDEBUG) << "Chunk: " << chunk.start() << " " << chunk.length(); QString cutline = line.mid( chunk.start(), chunk.length() ); const int chunk_width = cutline.length() * fontWidth; if ( xPos == initialXPos ) { // First chunk, we extend the left background a bit, // it looks prettier. painter.fillRect( xPos - leftExtraBackgroundPx, yPos, chunk_width + leftExtraBackgroundPx, fontHeight, chunk.backColor() ); } else { // other chunks... painter.fillRect( xPos, yPos, chunk_width, fontHeight, chunk.backColor() ); } painter.setPen( chunk.foreColor() ); painter.drawText( xPos, yPos + fontAscent, cutline ); xPos += chunk_width; } // Draw the empty block at the end of the line int blank_width = line_width - xPos; if ( blank_width > 0 ) painter.fillRect( xPos, yPos, blank_width, fontHeight, backColor_ ); } const int DigitsBuffer::timeout_ = 2000; DigitsBuffer::DigitsBuffer() : QObject() { } void DigitsBuffer::reset() { LOG(logDEBUG) << "DigitsBuffer::reset()"; timer_.stop(); digits_.clear(); } void DigitsBuffer::add( char character ) { LOG(logDEBUG) << "DigitsBuffer::add()"; digits_.append( QChar( character ) ); timer_.start( timeout_ , this ); } int DigitsBuffer::content() { int result = digits_.toInt(); reset(); return result; } void DigitsBuffer::timerEvent( QTimerEvent* event ) { if ( event->timerId() == timer_.timerId() ) { reset(); } else { QObject::timerEvent( event ); } } // Graphic parameters const int AbstractLogView::OVERVIEW_WIDTH = 27; AbstractLogView::AbstractLogView(const AbstractLogData* newLogData, const QuickFindPattern* const quickFindPattern, QWidget* parent) : QAbstractScrollArea( parent ), lineNumbersVisible_( false ), selectionStartPos_(), selectionCurrentEndPos_(), autoScrollTimer_(), selection_(), quickFindPattern_( quickFindPattern ), quickFind_( newLogData, &selection_, quickFindPattern ) { logData = newLogData; followMode_ = false; selectionStarted_ = false; markingClickInitiated_ = false; firstLine = 0; lastLine = 0; firstCol = 0; overview_ = NULL; overviewWidget_ = NULL; // Display nbDigitsInLineNumber_ = 0; leftMarginPx_ = 0; // Fonts charWidth_ = 1; charHeight_ = 1; // Create the viewport QWidget setViewport( 0 ); setAttribute( Qt::WA_StaticContents ); // Does it work? // Hovering setMouseTracking( true ); lastHoveredLine_ = -1; // Init the popup menu createMenu(); // Signals connect( quickFindPattern_, SIGNAL( patternUpdated() ), this, SLOT ( handlePatternUpdated() ) ); connect( verticalScrollBar(), SIGNAL( sliderMoved( int ) ), this, SIGNAL( followDisabled() ) ); connect( &quickFind_, SIGNAL( notify( const QFNotification& ) ), this, SIGNAL( notifyQuickFind( const QFNotification& ) ) ); connect( &quickFind_, SIGNAL( clearNotification() ), this, SIGNAL( clearQuickFindNotification() ) ); } AbstractLogView::~AbstractLogView() { } // // Received events // void AbstractLogView::changeEvent( QEvent* changeEvent ) { QAbstractScrollArea::changeEvent( changeEvent ); // Stop the timer if the widget becomes inactive if ( changeEvent->type() == QEvent::ActivationChange ) { if ( ! isActiveWindow() ) autoScrollTimer_.stop(); } viewport()->update(); } void AbstractLogView::mousePressEvent( QMouseEvent* mouseEvent ) { static Configuration& config = Persistent( "settings" ); if ( mouseEvent->button() == Qt::LeftButton ) { int line = convertCoordToLine( mouseEvent->y() ); if ( mouseEvent->modifiers() & Qt::ShiftModifier ) { selection_.selectRangeFromPrevious( line ); emit updateLineNumber( line ); update(); } else { if ( mouseEvent->x() < bulletZoneWidthPx_ ) { // Mark a line if it is clicked in the left margin // (only if click and release in the same area) markingClickInitiated_ = true; markingClickLine_ = line; } else { // Select the line, and start a selection if ( line < logData->getNbLine() ) { selection_.selectLine( line ); emit updateLineNumber( line ); emit newSelection( line ); } // Remember the click in case we're starting a selection selectionStarted_ = true; selectionStartPos_ = convertCoordToFilePos( mouseEvent->pos() ); selectionCurrentEndPos_ = selectionStartPos_; } } } else if ( mouseEvent->button() == Qt::RightButton ) { // Prepare the popup depending on selection type if ( selection_.isSingleLine() ) { copyAction_->setText( "&Copy this line" ); } else { copyAction_->setText( "&Copy" ); copyAction_->setStatusTip( tr("Copy the selection") ); } if ( selection_.isPortion() ) { findNextAction_->setEnabled( true ); findPreviousAction_->setEnabled( true ); addToSearchAction_->setEnabled( true ); } else { findNextAction_->setEnabled( false ); findPreviousAction_->setEnabled( false ); addToSearchAction_->setEnabled( false ); } // "Add to search" only makes sense in regexp mode if ( config.mainRegexpType() != ExtendedRegexp ) addToSearchAction_->setEnabled( false ); // Display the popup (blocking) popupMenu_->exec( QCursor::pos() ); } } void AbstractLogView::mouseMoveEvent( QMouseEvent* mouseEvent ) { // Selection implementation if ( selectionStarted_ ) { QPoint thisEndPos = convertCoordToFilePos( mouseEvent->pos() ); if ( thisEndPos != selectionCurrentEndPos_ ) { // Are we on a different line? if ( selectionStartPos_.y() != thisEndPos.y() ) { if ( thisEndPos.y() != selectionCurrentEndPos_.y() ) { // This is a 'range' selection selection_.selectRange( selectionStartPos_.y(), thisEndPos.y() ); emit updateLineNumber( thisEndPos.y() ); update(); } } // So we are on the same line. Are we moving horizontaly? else if ( thisEndPos.x() != selectionCurrentEndPos_.x() ) { // This is a 'portion' selection selection_.selectPortion( thisEndPos.y(), selectionStartPos_.x(), thisEndPos.x() ); update(); } // On the same line, and moving vertically then else { // This is a 'line' selection selection_.selectLine( thisEndPos.y() ); emit updateLineNumber( thisEndPos.y() ); update(); } selectionCurrentEndPos_ = thisEndPos; // Do we need to scroll while extending the selection? QRect visible = viewport()->rect(); if ( visible.contains( mouseEvent->pos() ) ) autoScrollTimer_.stop(); else if ( ! autoScrollTimer_.isActive() ) autoScrollTimer_.start( 100, this ); } } else { considerMouseHovering( mouseEvent->x(), mouseEvent->y() ); } } void AbstractLogView::mouseReleaseEvent( QMouseEvent* mouseEvent ) { if ( markingClickInitiated_ ) { markingClickInitiated_ = false; int line = convertCoordToLine( mouseEvent->y() ); if ( line == markingClickLine_ ) emit markLine( line ); } else { selectionStarted_ = false; if ( autoScrollTimer_.isActive() ) autoScrollTimer_.stop(); updateGlobalSelection(); } } void AbstractLogView::mouseDoubleClickEvent( QMouseEvent* mouseEvent ) { if ( mouseEvent->button() == Qt::LeftButton ) { const QPoint pos = convertCoordToFilePos( mouseEvent->pos() ); selectWordAtPosition( pos ); } } void AbstractLogView::timerEvent( QTimerEvent* timerEvent ) { if ( timerEvent->timerId() == autoScrollTimer_.timerId() ) { QRect visible = viewport()->rect(); const QPoint globalPos = QCursor::pos(); const QPoint pos = viewport()->mapFromGlobal( globalPos ); QMouseEvent ev( QEvent::MouseMove, pos, globalPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier ); mouseMoveEvent( &ev ); int deltaX = qMax( pos.x() - visible.left(), visible.right() - pos.x() ) - visible.width(); int deltaY = qMax( pos.y() - visible.top(), visible.bottom() - pos.y() ) - visible.height(); int delta = qMax( deltaX, deltaY ); if ( delta >= 0 ) { if ( delta < 7 ) delta = 7; int timeout = 4900 / ( delta * delta ); autoScrollTimer_.start( timeout, this ); if ( deltaX > 0 ) horizontalScrollBar()->triggerAction( pos.x() 0 ) verticalScrollBar()->triggerAction( pos.y() modifiers() & Qt::ControlModifier) == Qt::ControlModifier; bool shiftModifier = (keyEvent->modifiers() & Qt::ShiftModifier) == Qt::ShiftModifier; if ( keyEvent->key() == Qt::Key_Left ) horizontalScrollBar()->triggerAction(QScrollBar::SliderPageStepSub); else if ( keyEvent->key() == Qt::Key_Right ) horizontalScrollBar()->triggerAction(QScrollBar::SliderPageStepAdd); else if ( keyEvent->key() == Qt::Key_Home && !controlModifier) jumpToStartOfLine(); else if ( keyEvent->key() == Qt::Key_End && !controlModifier) jumpToRightOfScreen(); else if ( (keyEvent->key() == Qt::Key_PageDown && controlModifier) || (keyEvent->key() == Qt::Key_End && controlModifier) ) { emit followDisabled(); // duplicate of 'G' action. selection_.selectLine( logData->getNbLine() - 1 ); emit updateLineNumber( logData->getNbLine() - 1 ); jumpToBottom(); } else if ( (keyEvent->key() == Qt::Key_PageUp && controlModifier) || (keyEvent->key() == Qt::Key_Home && controlModifier) ) { emit followDisabled(); // like 'g' but 0 input first line action. selectAndDisplayLine( 0 ); emit updateLineNumber( 0 ); } else if ( keyEvent->key() == Qt::Key_F3 && !shiftModifier ) searchNext(); // duplicate of 'n' action. else if ( keyEvent->key() == Qt::Key_F3 && shiftModifier ) searchPrevious(); // duplicate of 'N' action. else { const char character = (keyEvent->text())[0].toAscii(); if ( ( character >= '0' ) && ( character <= '9' ) ) { // Adds the digit to the timed buffer digitsBuffer_.add( character ); } else { switch ( (keyEvent->text())[0].toAscii() ) { case 'j': { int delta = qMax( 1, digitsBuffer_.content() ); emit followDisabled(); //verticalScrollBar()->triggerAction( //QScrollBar::SliderSingleStepAdd); moveSelection( delta ); break; } case 'k': { int delta = qMin( -1, - digitsBuffer_.content() ); emit followDisabled(); //verticalScrollBar()->triggerAction( //QScrollBar::SliderSingleStepSub); moveSelection( delta ); break; } case 'h': horizontalScrollBar()->triggerAction( QScrollBar::SliderSingleStepSub); break; case 'l': horizontalScrollBar()->triggerAction( QScrollBar::SliderSingleStepAdd); break; case '0': jumpToStartOfLine(); break; case '$': jumpToEndOfLine(); break; case 'g': { int newLine = qMax( 0, digitsBuffer_.content() - 1 ); if ( newLine >= logData->getNbLine() ) newLine = logData->getNbLine() - 1; emit followDisabled(); selectAndDisplayLine( newLine ); emit updateLineNumber( newLine ); break; } case 'G': emit followDisabled(); selection_.selectLine( logData->getNbLine() - 1 ); emit updateLineNumber( logData->getNbLine() - 1 ); jumpToBottom(); break; case 'n': emit searchNext(); break; case 'N': emit searchPrevious(); break; case '*': // Use the selected 'word' and search forward findNextSelected(); break; case '#': // Use the selected 'word' and search backward findPreviousSelected(); break; default: keyEvent->ignore(); } } } if ( !keyEvent->isAccepted() ) QAbstractScrollArea::keyPressEvent( keyEvent ); } void AbstractLogView::wheelEvent( QWheelEvent* wheelEvent ) { emit followDisabled(); QAbstractScrollArea::wheelEvent( wheelEvent ); } void AbstractLogView::resizeEvent( QResizeEvent* ) { if ( logData == NULL ) return; LOG(logDEBUG) << "resizeEvent received"; updateDisplaySize(); } void AbstractLogView::scrollContentsBy( int dx, int dy ) { LOG(logDEBUG4) << "scrollContentsBy received"; firstLine = (firstLine - dy) > 0 ? firstLine - dy : 0; firstCol = (firstCol - dx) > 0 ? firstCol - dx : 0; lastLine = qMin( logData->getNbLine(), firstLine + getNbVisibleLines() ); // Update the overview if we have one if ( overview_ != NULL ) overview_->updateCurrentPosition( firstLine, lastLine ); // Are we hovering over a new line? const QPoint mouse_pos = mapFromGlobal( QCursor::pos() ); considerMouseHovering( mouse_pos.x(), mouse_pos.y() ); // Redraw update(); } void AbstractLogView::paintEvent( QPaintEvent* paintEvent ) { QRect invalidRect = paintEvent->rect(); if ( (invalidRect.isEmpty()) || (logData == NULL) ) return; LOG(logDEBUG4) << "paintEvent received, firstLine=" << firstLine << " lastLine=" << lastLine << " rect: " << invalidRect.topLeft().x() << ", " << invalidRect.topLeft().y() << ", " << invalidRect.bottomRight().x() << ", " << invalidRect.bottomRight().y(); { // Repaint the viewport QPainter painter( viewport() ); const int fontHeight = charHeight_; const int fontAscent = painter.fontMetrics().ascent(); const int nbCols = getNbVisibleCols(); const QPalette& palette = viewport()->palette(); const FilterSet& filterSet = Persistent( "filterSet" ); QColor foreColor, backColor; static const QBrush normalBulletBrush = QBrush( Qt::white ); static const QBrush matchBulletBrush = QBrush( Qt::red ); static const QBrush markBrush = QBrush( "dodgerblue" ); static const int SEPARATOR_WIDTH = 1; static const int BULLET_AREA_WIDTH = 11; static const int CONTENT_MARGIN_WIDTH = 1; static const int LINE_NUMBER_PADDING = 3; // First check the lines to be drawn are within range (might not be the case if // the file has just changed) const int nbLines = logData->getNbLine(); if ( nbLines == 0 ) { return; } else { if ( firstLine >= nbLines ) firstLine = nbLines - 1; if ( lastLine >= nbLines ) lastLine = nbLines - 1; } // Lines to write const QStringList lines = logData->getExpandedLines( firstLine, lastLine - firstLine + 1 ); // First draw the bullet left margin painter.setPen(palette.color(QPalette::Text)); painter.drawLine( BULLET_AREA_WIDTH, 0, BULLET_AREA_WIDTH, viewport()->height() ); painter.fillRect( 0, 0, BULLET_AREA_WIDTH, viewport()->height(), Qt::darkGray ); // Column at which the content should start (pixels) int contentStartPosX = BULLET_AREA_WIDTH + SEPARATOR_WIDTH; // This is also the bullet zone width, used for marking clicks bulletZoneWidthPx_ = contentStartPosX; // Draw the line numbers area int lineNumberAreaStartX = 0; if ( lineNumbersVisible_ ) { int lineNumberWidth = charWidth_ * nbDigitsInLineNumber_; int lineNumberAreaWidth = 2 * LINE_NUMBER_PADDING + lineNumberWidth; lineNumberAreaStartX = contentStartPosX; painter.setPen(palette.color(QPalette::Text)); /* Not sure if it looks good... painter.drawLine( contentStartPosX + lineNumberAreaWidth, 0, contentStartPosX + lineNumberAreaWidth, viewport()->height() ); */ painter.fillRect( contentStartPosX, 0, lineNumberAreaWidth, viewport()->height(), Qt::lightGray ); // Update for drawing the actual text contentStartPosX += lineNumberAreaWidth; } else { contentStartPosX += SEPARATOR_WIDTH; } // This is the total width of the 'margin' (including line number if any) // used for mouse calculation etc... leftMarginPx_ = contentStartPosX; // Then draw each line for (int i = firstLine; i <= lastLine; i++) { // Position in pixel of the base line of the line to print const int yPos = (i-firstLine) * fontHeight; const int xPos = contentStartPosX + CONTENT_MARGIN_WIDTH; // string to print, cut to fit the length and position of the view const QString line = lines[i - firstLine]; const QString cutLine = line.mid( firstCol, nbCols ); if ( selection_.isLineSelected( i ) ) { // Reverse the selected line foreColor = palette.color( QPalette::HighlightedText ); backColor = palette.color( QPalette::Highlight ); painter.setPen(palette.color(QPalette::Text)); } else if ( filterSet.matchLine( logData->getLineString( i ), &foreColor, &backColor ) ) { // Apply a filter to the line } else { // Use the default colors foreColor = palette.color( QPalette::Text ); backColor = palette.color( QPalette::Base ); } // Is there something selected in the line? int sel_start, sel_end; bool isSelection = selection_.getPortionForLine( i, &sel_start, &sel_end ); // Has the line got elements to be highlighted QList qfMatchList; bool isMatch = quickFindPattern_->matchLine( line, qfMatchList ); if ( isSelection || isMatch ) { // We use the LineDrawer and its chunks because the // line has to be somehow highlighted LineDrawer lineDrawer( backColor ); // First we create a list of chunks with the highlights QList chunkList; int column = 0; // Current column in line space foreach( const QuickFindMatch match, qfMatchList ) { int start = match.startColumn() - firstCol; int end = start + match.length(); // Ignore matches that are *completely* outside view area if ( ( start < 0 && end < 0 ) || start >= nbCols ) continue; if ( start > column ) chunkList << LineChunk( column, start - 1, LineChunk::Normal ); column = qMin( start + match.length() - 1, nbCols ); chunkList << LineChunk( qMax( start, 0 ), column, LineChunk::Highlighted ); column++; } if ( column <= cutLine.length() - 1 ) chunkList << LineChunk( column, cutLine.length() - 1, LineChunk::Normal ); // Then we add the selection if needed QList newChunkList; if ( isSelection ) { sel_start -= firstCol; // coord in line space sel_end -= firstCol; foreach ( const LineChunk chunk, chunkList ) { newChunkList << chunk.select( sel_start, sel_end ); } } else newChunkList = chunkList; foreach ( const LineChunk chunk, newChunkList ) { // Select the colours QColor fore; QColor back; switch ( chunk.type() ) { case LineChunk::Normal: fore = foreColor; back = backColor; break; case LineChunk::Highlighted: fore = QColor( "black" ); back = QColor( "yellow" ); // fore = highlightForeColor; // back = highlightBackColor; break; case LineChunk::Selected: fore = palette.color( QPalette::HighlightedText ), back = palette.color( QPalette::Highlight ); break; } lineDrawer.addChunk ( chunk, fore, back ); } lineDrawer.draw( painter, xPos, yPos, viewport()->width(), cutLine, CONTENT_MARGIN_WIDTH ); } else { // Nothing to be highlighted, we print the whole line! painter.fillRect( xPos - CONTENT_MARGIN_WIDTH, yPos, viewport()->width(), fontHeight, backColor ); // (the rectangle is extended on the left to cover the small // margin, it looks better (LineDrawer does the same) ) painter.setPen( foreColor ); painter.drawText( xPos, yPos + fontAscent, cutLine ); } // Then draw the bullet painter.setPen( palette.color( QPalette::Text ) ); const int circleSize = 3; const int arrowHeight = 4; const int middleXLine = BULLET_AREA_WIDTH / 2; const int middleYLine = yPos + (fontHeight / 2); const LineType line_type = lineType( i ); if ( line_type == Marked ) { // A pretty arrow if the line is marked const QPoint points[7] = { QPoint(1, middleYLine - 2), QPoint(middleXLine, middleYLine - 2), QPoint(middleXLine, middleYLine - arrowHeight), QPoint(BULLET_AREA_WIDTH - 2, middleYLine), QPoint(middleXLine, middleYLine + arrowHeight), QPoint(middleXLine, middleYLine + 2), QPoint(1, middleYLine + 2 ), }; painter.setBrush( markBrush ); painter.drawPolygon( points, 7 ); } else { if ( lineType( i ) == Match ) painter.setBrush( matchBulletBrush ); else painter.setBrush( normalBulletBrush ); painter.drawEllipse( middleXLine - circleSize, middleYLine - circleSize, circleSize * 2, circleSize * 2 ); } // Draw the line number if ( lineNumbersVisible_ ) { static const QString lineNumberFormat( "%1" ); const QString& lineNumberStr = lineNumberFormat.arg( displayLineNumber( i ), nbDigitsInLineNumber_ ); painter.setPen( palette.color( QPalette::Text ) ); painter.drawText( lineNumberAreaStartX + LINE_NUMBER_PADDING, yPos + fontAscent, lineNumberStr ); } } // For each line } LOG(logDEBUG4) << "End of repaint"; } // These two functions are virtual and this implementation is clearly // only valid for a non-filtered display. // We count on the 'filtered' derived classes to override them. qint64 AbstractLogView::displayLineNumber( int lineNumber ) const { return lineNumber + 1; // show a 1-based index } qint64 AbstractLogView::maxDisplayLineNumber() const { return logData->getNbLine(); } void AbstractLogView::setOverview( Overview* overview, OverviewWidget* overview_widget ) { overview_ = overview; overviewWidget_ = overview_widget; if ( overviewWidget_ ) { connect( overviewWidget_, SIGNAL( lineClicked ( int ) ), this, SIGNAL( followDisabled() ) ); connect( overviewWidget_, SIGNAL( lineClicked ( int ) ), this, SLOT( jumpToLine( int ) ) ); } refreshOverview(); } void AbstractLogView::searchUsingFunction( qint64 (QuickFind::*search_function)() ) { emit followDisabled(); int line = (quickFind_.*search_function)(); if ( line >= 0 ) { LOG(logDEBUG) << "search " << line; displayLine( line ); emit updateLineNumber( line ); } } void AbstractLogView::searchForward() { searchUsingFunction( &QuickFind::searchForward ); } void AbstractLogView::searchBackward() { searchUsingFunction( &QuickFind::searchBackward ); } void AbstractLogView::incrementallySearchForward() { searchUsingFunction( &QuickFind::incrementallySearchForward ); } void AbstractLogView::incrementallySearchBackward() { searchUsingFunction( &QuickFind::incrementallySearchBackward ); } void AbstractLogView::incrementalSearchAbort() { quickFind_.incrementalSearchAbort(); emit changeQuickFind( "", QuickFindMux::Forward ); } void AbstractLogView::incrementalSearchStop() { quickFind_.incrementalSearchStop(); } void AbstractLogView::followSet( bool checked ) { followMode_ = checked; if ( checked ) jumpToBottom(); } void AbstractLogView::refreshOverview() { assert( overviewWidget_ ); // Create space for the Overview if needed if ( ( getOverview() != NULL ) && getOverview()->isVisible() ) { setViewportMargins( 0, 0, OVERVIEW_WIDTH, 0 ); overviewWidget_->show(); } else { setViewportMargins( 0, 0, 0, 0 ); overviewWidget_->hide(); } } // Reset the QuickFind when the pattern is changed. void AbstractLogView::handlePatternUpdated() { LOG(logDEBUG) << "AbstractLogView::handlePatternUpdated()"; quickFind_.resetLimits(); update(); } // OR the current with the current search expression void AbstractLogView::addToSearch() { if ( selection_.isPortion() ) { LOG(logDEBUG) << "AbstractLogView::addToSearch()"; emit addToSearch( selection_.getSelectedText( logData ) ); } else { LOG(logERROR) << "AbstractLogView::addToSearch called for a wrong type of selection"; } } // Find next occurence of the selected text (*) void AbstractLogView::findNextSelected() { // Use the selected 'word' and search forward if ( selection_.isPortion() ) { emit changeQuickFind( selection_.getSelectedText( logData ), QuickFindMux::Forward ); emit searchNext(); } } // Find next previous of the selected text (#) void AbstractLogView::findPreviousSelected() { if ( selection_.isPortion() ) { emit changeQuickFind( selection_.getSelectedText( logData ), QuickFindMux::Backward ); emit searchNext(); } } // Copy the selection to the clipboard void AbstractLogView::copy() { static QClipboard* clipboard = QApplication::clipboard(); clipboard->setText( selection_.getSelectedText( logData ) ); } // // Public functions // void AbstractLogView::updateData() { LOG(logDEBUG) << "AbstractLogView::updateData"; // Check the top Line is within range if ( firstLine >= logData->getNbLine() ) { firstLine = 0; firstCol = 0; verticalScrollBar()->setValue( 0 ); horizontalScrollBar()->setValue( 0 ); } // Crop selection if it become out of range selection_.crop( logData->getNbLine() - 1 ); // Adapt the scroll bars to the new content verticalScrollBar()->setRange( 0, logData->getNbLine()-1 ); const int hScrollMaxValue = ( logData->getMaxLength() - getNbVisibleCols() + 1 ) > 0 ? ( logData->getMaxLength() - getNbVisibleCols() + 1 ) : 0; horizontalScrollBar()->setRange( 0, hScrollMaxValue ); lastLine = qMin( logData->getNbLine(), firstLine + getNbVisibleLines() ); // Reset the QuickFind in case we have new stuff to search into quickFind_.resetLimits(); if ( followMode_ ) jumpToBottom(); // Update the overview if we have one if ( overview_ != NULL ) overview_->updateCurrentPosition( firstLine, lastLine ); // Update the length of line numbers nbDigitsInLineNumber_ = countDigits( maxDisplayLineNumber() ); // Repaint! update(); } void AbstractLogView::updateDisplaySize() { // Font is assumed to be mono-space (is restricted by options dialog) QFontMetrics fm = fontMetrics(); charHeight_ = fm.height(); // For some reason on Qt 4.8.2 for Win, maxWidth() is wrong but the // following give the right result, not sure why: charWidth_ = fm.width( QChar('a') ); // Calculate the index of the last line shown lastLine = qMin( logData->getNbLine(), firstLine + getNbVisibleLines() ); // Update the scroll bars verticalScrollBar()->setPageStep( getNbVisibleLines() ); const int hScrollMaxValue = ( logData->getMaxLength() - getNbVisibleCols() + 1 ) > 0 ? ( logData->getMaxLength() - getNbVisibleCols() + 1 ) : 0; horizontalScrollBar()->setRange( 0, hScrollMaxValue ); LOG(logDEBUG) << "viewport.width()=" << viewport()->width(); LOG(logDEBUG) << "viewport.height()=" << viewport()->height(); LOG(logDEBUG) << "width()=" << width(); LOG(logDEBUG) << "height()=" << height(); if ( overviewWidget_ ) overviewWidget_->setGeometry( viewport()->width() + 2, 1, OVERVIEW_WIDTH - 1, viewport()->height() ); } int AbstractLogView::getTopLine() const { return firstLine; } QString AbstractLogView::getSelection() const { return selection_.getSelectedText( logData ); } void AbstractLogView::selectAll() { selection_.selectRange( 0, logData->getNbLine() - 1 ); update(); } void AbstractLogView::selectAndDisplayLine( int line ) { emit followDisabled(); selection_.selectLine( line ); displayLine( line ); emit updateLineNumber( line ); } // The difference between this function and displayLine() is quite // subtle: this one always jump, even if the line passed is visible. void AbstractLogView::jumpToLine( int line ) { // Put the selected line in the middle if possible int newTopLine = line - ( getNbVisibleLines() / 2 ); if ( newTopLine < 0 ) newTopLine = 0; // This will also trigger a scrollContents event verticalScrollBar()->setValue( newTopLine ); } void AbstractLogView::setLineNumbersVisible( bool lineNumbersVisible ) { lineNumbersVisible_ = lineNumbersVisible; } // // Private functions // // Returns the number of lines visible in the viewport int AbstractLogView::getNbVisibleLines() const { return viewport()->height() / charHeight_ + 1; } // Returns the number of columns visible in the viewport int AbstractLogView::getNbVisibleCols() const { return ( viewport()->width() - leftMarginPx_ ) / charWidth_ + 1; } // Converts the mouse x, y coordinates to the line number in the file int AbstractLogView::convertCoordToLine(int yPos) const { int line = firstLine + yPos / charHeight_; return line; } // Converts the mouse x, y coordinates to the char coordinates (in the file) // This function ensure the pos exists in the file. QPoint AbstractLogView::convertCoordToFilePos( const QPoint& pos ) const { int line = firstLine + pos.y() / charHeight_; if ( line >= logData->getNbLine() ) line = logData->getNbLine() - 1; if ( line < 0 ) line = 0; // Determine column in screen space and convert it to file space int column = firstCol + ( pos.x() - leftMarginPx_ ) / charWidth_; QString this_line = logData->getExpandedLineString( line ); const int length = this_line.length(); if ( column >= length ) column = length - 1; if ( column < 0 ) column = 0; LOG(logDEBUG4) << "AbstractLogView::convertCoordToFilePos col=" << column << " line=" << line; QPoint point( column, line ); return point; } // Makes the widget adjust itself to display the passed line. // Doing so, it will throw itself a scrollContents event. void AbstractLogView::displayLine( int line ) { // If the line is already the screen if ( ( line >= firstLine ) && ( line < ( firstLine + getNbVisibleLines() ) ) ) { // ... don't scroll and just repaint update(); } else { jumpToLine( line ); } } // Move the selection up and down by the passed number of lines void AbstractLogView::moveSelection( int delta ) { LOG(logDEBUG) << "AbstractLogView::moveSelection delta=" << delta; QList selection = selection_.getLines(); int new_line; // If nothing is selected, do as if line -1 was. if ( selection.isEmpty() ) selection.append( -1 ); if ( delta < 0 ) new_line = selection.first() + delta; else new_line = selection.last() + delta; if ( new_line < 0 ) new_line = 0; else if ( new_line >= logData->getNbLine() ) new_line = logData->getNbLine() - 1; // Select and display the new line selection_.selectLine( new_line ); displayLine( new_line ); emit updateLineNumber( new_line ); } // Make the start of the lines visible void AbstractLogView::jumpToStartOfLine() { horizontalScrollBar()->setValue( 0 ); } // Make the end of the lines in the selection visible void AbstractLogView::jumpToEndOfLine() { QList selection = selection_.getLines(); // Search the longest line in the selection int max_length = 0; foreach ( int line, selection ) { int length = logData->getLineLength( line ); if ( length > max_length ) max_length = length; } horizontalScrollBar()->setValue( max_length - getNbVisibleCols() ); } // Make the end of the lines on the screen visible void AbstractLogView::jumpToRightOfScreen() { QList selection = selection_.getLines(); // Search the longest line on screen int max_length = 0; for ( int i = firstLine; i <= ( firstLine + getNbVisibleLines() ); i++ ) { int length = logData->getLineLength( i ); if ( length > max_length ) max_length = length; } horizontalScrollBar()->setValue( max_length - getNbVisibleCols() ); } // Jump to the first line void AbstractLogView::jumpToTop() { // This will also trigger a scrollContents event verticalScrollBar()->setValue( 0 ); update(); // in case the screen hasn't moved } // Jump to the last line void AbstractLogView::jumpToBottom() { const int new_top_line = qMax( logData->getNbLine() - getNbVisibleLines() + 1, 0LL ); // This will also trigger a scrollContents event verticalScrollBar()->setValue( new_top_line ); update(); // in case the screen hasn't moved } // Returns whether the character passed is a 'word' character inline bool AbstractLogView::isCharWord( char c ) { if ( ( ( c >= 'A' ) && ( c <= 'Z' ) ) || ( ( c >= 'a' ) && ( c <= 'z' ) ) || ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c == '_' ) ) ) return true; else return false; } // Select the word under the given position void AbstractLogView::selectWordAtPosition( const QPoint& pos ) { const int x = pos.x(); const QString line = logData->getExpandedLineString( pos.y() ); if ( isCharWord( line[x].toLatin1() ) ) { // Search backward for the first character in the word int currentPos = x; for ( ; currentPos > 0; currentPos-- ) if ( ! isCharWord( line[currentPos].toLatin1() ) ) break; // Exclude the first char of the line if needed if ( ! isCharWord( line[currentPos].toLatin1() ) ) currentPos++; int start = currentPos; // Now search for the end currentPos = x; for ( ; currentPos < line.length() - 1; currentPos++ ) if ( ! isCharWord( line[currentPos].toLatin1() ) ) break; // Exclude the last char of the line if needed if ( ! isCharWord( line[currentPos].toLatin1() ) ) currentPos--; int end = currentPos; selection_.selectPortion( pos.y(), start, end ); updateGlobalSelection(); update(); } } // Update the system global (middle click) selection (X11 only) void AbstractLogView::updateGlobalSelection() { static QClipboard* const clipboard = QApplication::clipboard(); // Updating it only for "non-trivial" (range or portion) selections if ( ! selection_.isSingleLine() ) clipboard->setText( selection_.getSelectedText( logData ), QClipboard::Selection ); } // Create the pop-up menu void AbstractLogView::createMenu() { copyAction_ = new QAction( tr("&Copy"), this ); // No text as this action title depends on the type of selection connect( copyAction_, SIGNAL(triggered()), this, SLOT(copy()) ); // For '#' and '*', shortcuts doesn't seem to work but // at least it displays them in the menu, we manually handle those keys // as keys event anyway (in keyPressEvent). findNextAction_ = new QAction(tr("Find &next"), this); findNextAction_->setShortcut( Qt::Key_Asterisk ); findNextAction_->setStatusTip( tr("Find the next occurence") ); connect( findNextAction_, SIGNAL(triggered()), this, SLOT( findNextSelected() ) ); findPreviousAction_ = new QAction( tr("Find &previous"), this ); findPreviousAction_->setShortcut( tr("#") ); findPreviousAction_->setStatusTip( tr("Find the previous occurence") ); connect( findPreviousAction_, SIGNAL(triggered()), this, SLOT( findPreviousSelected() ) ); addToSearchAction_ = new QAction( tr("&Add to search"), this ); addToSearchAction_->setStatusTip( tr("Add the selection to the current search") ); connect( addToSearchAction_, SIGNAL( triggered() ), this, SLOT( addToSearch() ) ); popupMenu_ = new QMenu( this ); popupMenu_->addAction( copyAction_ ); popupMenu_->addSeparator(); popupMenu_->addAction( findNextAction_ ); popupMenu_->addAction( findPreviousAction_ ); popupMenu_->addAction( addToSearchAction_ ); } void AbstractLogView::considerMouseHovering( int x_pos, int y_pos ) { int line = convertCoordToLine( y_pos ); if ( ( x_pos < leftMarginPx_ ) && ( line >= 0 ) && ( line < logData->getNbLine() ) ) { // Mouse moved in the margin, send event up // (possibly to highlight the overview) if ( line != lastHoveredLine_ ) { LOG(logDEBUG) << "Mouse moved in margin line: " << line; emit mouseHoveredOverLine( line ); lastHoveredLine_ = line; } } else { if ( lastHoveredLine_ != -1 ) { emit mouseLeftHoveringZone(); lastHoveredLine_ = -1; } } } glogg-0.9.2/abstractlogview.h000066400000000000000000000264161222324246300162030ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2011, 2012, 2013 Nicolas Bonnefon * and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef ABSTRACTLOGVIEW_H #define ABSTRACTLOGVIEW_H #include #include #include "abstractlogdata.h" #include "selection.h" #include "quickfind.h" #include "overviewwidget.h" #include "quickfindmux.h" class QMenu; class QAction; class LineChunk { public: enum ChunkType { Normal, Highlighted, Selected, }; LineChunk( int first_col, int end_col, ChunkType type ); int start() const { return start_; } int end() const { return end_; } ChunkType type() const { return type_; } // Returns 'true' if the selection is part of this chunk // (at least partially), if so, it should be replaced by the list returned QList select( int selection_start, int selection_end ) const; private: int start_; int end_; ChunkType type_; }; // Utility class for syntax colouring. // It stores the chunks of line to draw // each chunk having a different colour class LineDrawer { public: LineDrawer( const QColor& back_color) : list(), backColor_( back_color ) { }; // Add a chunk of line using the given colours. // Both first_col and last_col are included // An empty chunk will be ignored. // the first column will be set to 0 if negative // The column are relative to the screen void addChunk( int first_col, int last_col, QColor fore, QColor back ); void addChunk( const LineChunk& chunk, QColor fore, QColor back ); // Draw the current line of text using the given painter, // in the passed block (in pixels) // The line must be cut to fit on the screen. // leftExtraBackgroundPx is the an extra margin to start drawing // the coloured // background, going all the way to the element // left of the line looks better. void draw( QPainter& painter, int xPos, int yPos, int line_width, const QString& line, int leftExtraBackgroundPx ); private: class Chunk { public: // Create a new chunk Chunk( int start, int length, QColor fore, QColor back ) : start_( start ), length_( length ), foreColor_ ( fore ), backColor_ ( back ) { }; // Accessors int start() const { return start_; } int length() const { return length_; } const QColor& foreColor() const { return foreColor_; } const QColor& backColor() const { return backColor_; } private: int start_; int length_; QColor foreColor_; QColor backColor_; }; QList list; QColor backColor_; }; // Utility class representing a buffer for number entered on the keyboard // The buffer keep at most 7 digits, and reset itself after a timeout. class DigitsBuffer : public QObject { Q_OBJECT public: DigitsBuffer(); // Reset the buffer. void reset(); // Add a single digit to the buffer (discarded if it's not a digit), // the timeout timer is reset. void add( char character ); // Get the content of the buffer (0 if empty) and reset it. int content(); protected: void timerEvent( QTimerEvent* event ); private: // Duration of the timeout in milliseconds. static const int timeout_; QString digits_; QBasicTimer timer_; }; class Overview; // Base class representing the log view widget. // It can be either the top (full) or bottom (filtered) view. class AbstractLogView : public QAbstractScrollArea, public SearchableWidgetInterface { Q_OBJECT public: // Constructor of the widget, the data set is passed. // The caller retains ownership of the data set. // The pointer to the QFP is used for colouring and QuickFind searches AbstractLogView( const AbstractLogData* newLogData, const QuickFindPattern* const quickFind, QWidget* parent=0 ); ~AbstractLogView(); // Refresh the widget when the data set has changed. void updateData(); // Instructs the widget to update it's content geometry, // used when the font is changed. void updateDisplaySize(); // Return the line number of the top line of the view int getTopLine() const; // Return the text of the current selection. QString getSelection() const; // Instructs the widget to select the whole text. void selectAll(); protected: void mousePressEvent( QMouseEvent* mouseEvent ); void mouseMoveEvent( QMouseEvent* mouseEvent ); void mouseReleaseEvent( QMouseEvent* ); void mouseDoubleClickEvent( QMouseEvent* mouseEvent ); void timerEvent( QTimerEvent* timerEvent ); void changeEvent( QEvent* changeEvent ); void paintEvent( QPaintEvent* paintEvent ); void resizeEvent( QResizeEvent* resizeEvent ); void scrollContentsBy( int dx, int dy ); void keyPressEvent( QKeyEvent* keyEvent ); void wheelEvent( QWheelEvent* wheelEvent ); // Must be implemented to return wether the line number is // a match, a mark or just a normal line (used for coloured bullets) enum LineType { Normal, Marked, Match }; virtual LineType lineType( int lineNumber ) const = 0; // Line number to display for line at the given index virtual qint64 displayLineNumber( int lineNumber ) const; virtual qint64 maxDisplayLineNumber() const; // Get the overview associated with this view, or NULL if there is none Overview* getOverview() const { return overview_; } // Set the Overview and OverviewWidget void setOverview( Overview* overview, OverviewWidget* overview_widget ); signals: // Sent when a new line has been selected by the user. void newSelection(int line); // Sent up to the MainWindow to disable the follow mode void followDisabled(); // Sent when the view wants the QuickFind widget pattern to change. void changeQuickFind( const QString& newPattern, QuickFindMux::QFDirection newDirection ); // Sent up when the current line number is updated void updateLineNumber( int line ); // Sent up when quickFind wants to show a message to the user. void notifyQuickFind( const QFNotification& message ); // Sent up when quickFind wants to clear the notification. void clearQuickFindNotification(); // Sent when the view ask for a line to be marked // (click in the left margin). void markLine( qint64 line ); // Sent up when the user wants to add the selection to the search void addToSearch( const QString& selection ); // Sent up when the mouse is hovered over a line's margin void mouseHoveredOverLine( qint64 line ); // Sent up when the mouse leaves a line's margin void mouseLeftHoveringZone(); // Sent up for view initiated quickfind searches void searchNext(); void searchPrevious(); public slots: // Makes the widget select and display the passed line. // Scrolling as necessary void selectAndDisplayLine( int line ); // Use the current QFP to go and select the next match. virtual void searchForward(); // Use the current QFP to go and select the previous match. virtual void searchBackward(); // Use the current QFP to go and select the next match (incremental) virtual void incrementallySearchForward(); // Use the current QFP to go and select the previous match (incremental) virtual void incrementallySearchBackward(); // Stop the current incremental search (typically when user press return) virtual void incrementalSearchStop(); // Abort the current incremental search (typically when user press esc) virtual void incrementalSearchAbort(); // Signals the follow mode has been enabled. void followSet( bool checked ); // Signal the on/off status of the overview has been changed. void refreshOverview(); // Make the view jump to the specified line, regardless of weither it // is on the screen or not. // (does NOT emit followDisabled() ) void jumpToLine( int line ); // Configure the setting of whether to show line number margin void setLineNumbersVisible( bool lineNumbersVisible ); private slots: void handlePatternUpdated(); void addToSearch(); void findNextSelected(); void findPreviousSelected(); void copy(); private: // Constants static const int OVERVIEW_WIDTH; // Width of the bullet zone, including decoration int bulletZoneWidthPx_; // Total size of all margins and decorations in pixels int leftMarginPx_; // Number of digits to display in line numbers int nbDigitsInLineNumber_; // Digits buffer (for numeric keyboard entry) DigitsBuffer digitsBuffer_; // Follow mode bool followMode_; // Whether to show line numbers or not bool lineNumbersVisible_; // Pointer to the CrawlerWidget's data set const AbstractLogData* logData; // Pointer to the Overview object Overview* overview_; // Pointer to the OverviewWidget, this class doesn't own it, // but is responsible for displaying it (for purely aesthetic // reasons). OverviewWidget* overviewWidget_; bool selectionStarted_; // Start of the selection (characters) QPoint selectionStartPos_; // Current end of the selection (characters) QPoint selectionCurrentEndPos_; QBasicTimer autoScrollTimer_; // Hovering state // Last line that has been hoovered on, -1 if none qint64 lastHoveredLine_; // Marks (left margin click) bool markingClickInitiated_; qint64 markingClickLine_; Selection selection_; qint64 firstLine; qint64 lastLine; int firstCol; // Text handling int charWidth_; int charHeight_; // Popup menu QMenu* popupMenu_; QAction* copyAction_; QAction* findNextAction_; QAction* findPreviousAction_; QAction* addToSearchAction_; // Pointer to the CrawlerWidget's QFP object const QuickFindPattern* const quickFindPattern_; // Our own QuickFind object QuickFind quickFind_; int getNbVisibleLines() const; int getNbVisibleCols() const; QPoint convertCoordToFilePos( const QPoint& pos ) const; int convertCoordToLine( int yPos ) const; int convertCoordToColumn( int xPos ) const; void displayLine( int line ); void moveSelection( int y ); void jumpToStartOfLine(); void jumpToEndOfLine(); void jumpToRightOfScreen(); void jumpToTop(); void jumpToBottom(); void selectWordAtPosition( const QPoint& pos ); void createMenu(); void considerMouseHovering( int x_pos, int y_pos ); // Search functions (for n/N) void searchUsingFunction ( qint64 (QuickFind::*search_function)() ); // Utils functions bool isCharWord( char c ); void updateGlobalSelection(); }; #endif glogg-0.9.2/configuration.cpp000066400000000000000000000073371222324246300162060ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #include #include "log.h" #include "configuration.h" Configuration::Configuration() { // Should have some sensible default values. mainFont_ = QFont("mono", 10); mainFont_.setStyleHint( QFont::Courier, QFont::PreferOutline ); mainRegexpType_ = ExtendedRegexp; quickfindRegexpType_ = FixedString; quickfindIncremental_ = true; overviewVisible_ = true; lineNumbersVisibleInMain_ = false; lineNumbersVisibleInFiltered_ = true; QFontInfo fi(mainFont_); LOG(logDEBUG) << "Default font is " << fi.family().toStdString(); } // Accessor functions QFont Configuration::mainFont() const { return mainFont_; } void Configuration::setMainFont( QFont newFont ) { LOG(logDEBUG) << "Configuration::setMainFont"; mainFont_ = newFont; } void Configuration::retrieveFromStorage( QSettings& settings ) { LOG(logDEBUG) << "Configuration::retrieveFromStorage"; // Fonts QString family = settings.value( "mainFont.family" ).toString(); int size = settings.value( "mainFont.size" ).toInt(); // If no config read, keep the default if ( !family.isNull() ) mainFont_ = QFont( family, size ); // Regexp types mainRegexpType_ = static_cast( settings.value( "regexpType.main", mainRegexpType_ ).toInt() ); quickfindRegexpType_ = static_cast( settings.value( "regexpType.quickfind", quickfindRegexpType_ ).toInt() ); if ( settings.contains( "quickfind.incremental" ) ) quickfindIncremental_ = settings.value( "quickfind.incremental" ).toBool(); // View settings if ( settings.contains( "view.overviewVisible" ) ) overviewVisible_ = settings.value( "view.overviewVisible" ).toBool(); if ( settings.contains( "view.lineNumbersVisibleInMain" ) ) lineNumbersVisibleInMain_ = settings.value( "view.lineNumbersVisibleInMain" ).toBool(); if ( settings.contains( "view.lineNumbersVisibleInFiltered" ) ) lineNumbersVisibleInFiltered_ = settings.value( "view.lineNumbersVisibleInFiltered" ).toBool(); // Some sanity check (mainly for people upgrading) if ( quickfindIncremental_ ) quickfindRegexpType_ = FixedString; } void Configuration::saveToStorage( QSettings& settings ) const { LOG(logDEBUG) << "Configuration::saveToStorage"; QFontInfo fi(mainFont_); settings.setValue( "mainFont.family", fi.family() ); settings.setValue( "mainFont.size", fi.pointSize() ); settings.setValue( "regexpType.main", static_cast( mainRegexpType_ ) ); settings.setValue( "regexpType.quickfind", static_cast( quickfindRegexpType_ ) ); settings.setValue( "quickfind.incremental", quickfindIncremental_ ); settings.setValue( "view.overviewVisible", overviewVisible_ ); settings.setValue( "view.lineNumbersVisibleInMain", lineNumbersVisibleInMain_ ); settings.setValue( "view.lineNumbersVisibleInFiltered", lineNumbersVisibleInFiltered_ ); } glogg-0.9.2/configuration.h000066400000000000000000000055131222324246300156450ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2011, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef CONFIGURATION_H #define CONFIGURATION_H #include #include #include "persistable.h" // Type of regexp to use for searches enum SearchRegexpType { ExtendedRegexp, Wildcard, FixedString, }; // Configuration class containing everything in the "Settings" dialog class Configuration : public Persistable { public: Configuration(); // Accesses the main font used for display QFont mainFont() const; void setMainFont( QFont newFont ); // Accesses the regexp types SearchRegexpType mainRegexpType() const { return mainRegexpType_; } SearchRegexpType quickfindRegexpType() const { return quickfindRegexpType_; } bool isQuickfindIncremental() const { return quickfindIncremental_; } void setMainRegexpType( SearchRegexpType type ) { mainRegexpType_ = type; } void setQuickfindRegexpType( SearchRegexpType type ) { quickfindRegexpType_ = type; } void setQuickfindIncremental( bool is_incremental ) { quickfindIncremental_ = is_incremental; } // View settings bool isOverviewVisible() const { return overviewVisible_; } void setOverviewVisible( bool isVisible ) { overviewVisible_ = isVisible; } bool mainLineNumbersVisible() const { return lineNumbersVisibleInMain_; } bool filteredLineNumbersVisible() const { return lineNumbersVisibleInFiltered_; } void setMainLineNumbersVisible( bool lineNumbersVisible ) { lineNumbersVisibleInMain_ = lineNumbersVisible; } void setFilteredLineNumbersVisible( bool lineNumbersVisible ) { lineNumbersVisibleInFiltered_ = lineNumbersVisible; } // Reads/writes the current config in the QSettings object passed virtual void saveToStorage( QSettings& settings ) const; virtual void retrieveFromStorage( QSettings& settings ); private: // Configuration settings QFont mainFont_; SearchRegexpType mainRegexpType_; SearchRegexpType quickfindRegexpType_; bool quickfindIncremental_; // View settings bool overviewVisible_; bool lineNumbersVisibleInMain_; bool lineNumbersVisibleInFiltered_; }; #endif glogg-0.9.2/crawlerwidget.cpp000066400000000000000000000650241222324246300161770ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2011, 2012, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // This file implements the CrawlerWidget class. // It is responsible for creating and managing the two views and all // the UI elements. It implements the connection between the UI elements. // It also owns the sets of data (full and filtered). #include "log.h" #include #include #include #include #include #include #include #include #include #include "crawlerwidget.h" #include "quickfindpattern.h" #include "overview.h" #include "infoline.h" #include "savedsearches.h" #include "quickfindwidget.h" #include "persistentinfo.h" #include "configuration.h" // Palette for error signaling (yellow background) const QPalette CrawlerWidget::errorPalette( QColor( "yellow" ) ); // Constructor makes all the child widgets and set up connections. CrawlerWidget::CrawlerWidget(SavedSearches* searches, QWidget *parent) : QSplitter(parent) { setOrientation(Qt::Vertical); // Initialise internal data (with empty file and search) logData_ = new LogData(); logFilteredData_ = logData_->getNewFilteredData(); // The matches overview overview_ = new Overview(); // Initialise the QF Mux to send requests from the QFWidget // to the right window quickFindMux_ = new QuickFindMux( this ); // The views bottomWindow = new QWidget; overviewWidget_ = new OverviewWidget(); logMainView = new LogMainView( logData_, quickFindMux_->getPattern(), overview_, overviewWidget_ ); filteredView = new FilteredView( logFilteredData_, quickFindMux_->getPattern() ); overviewWidget_->setOverview( overview_ ); overviewWidget_->setParent( logMainView ); savedSearches = searches; quickFindMux_->registerSearchable( logMainView ); quickFindMux_->registerSearchable( filteredView ); // Construct the visibility button visibilityModel_ = new QStandardItemModel( this ); QStandardItem *marksAndMatchesItem = new QStandardItem( tr( "Marks and matches" ) ); QPixmap marksAndMatchesPixmap( 16, 10 ); marksAndMatchesPixmap.fill( Qt::gray ); marksAndMatchesItem->setIcon( QIcon( marksAndMatchesPixmap ) ); marksAndMatchesItem->setData( FilteredView::MarksAndMatches ); visibilityModel_->appendRow( marksAndMatchesItem ); QStandardItem *marksItem = new QStandardItem( tr( "Marks" ) ); QPixmap marksPixmap( 16, 10 ); marksPixmap.fill( Qt::blue ); marksItem->setIcon( QIcon( marksPixmap ) ); marksItem->setData( FilteredView::MarksOnly ); visibilityModel_->appendRow( marksItem ); QStandardItem *matchesItem = new QStandardItem( tr( "Matches" ) ); QPixmap matchesPixmap( 16, 10 ); matchesPixmap.fill( Qt::red ); matchesItem->setIcon( QIcon( matchesPixmap ) ); matchesItem->setData( FilteredView::MatchesOnly ); visibilityModel_->appendRow( matchesItem ); QListView *visibilityView = new QListView( this ); visibilityView->setMovement( QListView::Static ); visibilityView->setMinimumWidth( 170 ); // Only needed with custom style-sheet visibilityBox = new QComboBox(); visibilityBox->setModel( visibilityModel_ ); visibilityBox->setView( visibilityView ); // Select "Marks and matches" by default (same default as the filtered view) visibilityBox->setCurrentIndex( 0 ); // TODO: Maybe there is some way to set the popup width to be // sized-to-content (as it is when the stylesheet is not overriden) in the // stylesheet as opposed to setting a hard min-width on the view above. visibilityBox->setStyleSheet( " \ QComboBox:on {\ padding: 1px 2px 1px 6px;\ width: 19px;\ } \ QComboBox:!on {\ padding: 1px 2px 1px 7px;\ width: 19px;\ height: 16px;\ border: 1px solid gray;\ } \ QComboBox::drop-down::down-arrow {\ width: 0px;\ border-width: 0px;\ } \ " ); // Construct the Search Info line searchInfoLine = new InfoLine(); searchInfoLine->setFrameStyle( QFrame::WinPanel | QFrame::Sunken ); searchInfoLine->setLineWidth( 1 ); searchInfoLineDefaultPalette = searchInfoLine->palette(); ignoreCaseCheck = new QCheckBox( "Ignore &case" ); searchRefreshCheck = new QCheckBox( "Auto-&refresh" ); // Construct the Search line searchLabel = new QLabel(tr("&Text: ")); searchLineEdit = new QComboBox; searchLineEdit->setEditable( true ); searchLineEdit->setCompleter( 0 ); searchLineEdit->addItems( savedSearches->recentSearches() ); searchLineEdit->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum ); searchLineEdit->setSizeAdjustPolicy( QComboBox::AdjustToMinimumContentsLengthWithIcon ); searchLabel->setBuddy( searchLineEdit ); searchButton = new QToolButton(); searchButton->setText( tr("&Search") ); searchButton->setAutoRaise( true ); stopButton = new QToolButton(); stopButton->setIcon( QIcon(":/images/stop16.png") ); stopButton->setAutoRaise( true ); stopButton->setEnabled( false ); // Construct the QuickFind bar quickFindWidget_ = new QuickFindWidget(); QHBoxLayout* searchLineLayout = new QHBoxLayout; searchLineLayout->addWidget(searchLabel); searchLineLayout->addWidget(searchLineEdit); searchLineLayout->addWidget(searchButton); searchLineLayout->addWidget(stopButton); searchLineLayout->setContentsMargins(6, 0, 6, 0); stopButton->setSizePolicy( QSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum ) ); searchButton->setSizePolicy( QSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum ) ); QHBoxLayout* searchInfoLineLayout = new QHBoxLayout; searchInfoLineLayout->addWidget( visibilityBox ); searchInfoLineLayout->addWidget( searchInfoLine ); searchInfoLineLayout->addWidget( ignoreCaseCheck ); searchInfoLineLayout->addWidget( searchRefreshCheck ); // Construct the bottom window quickFindWidget_->hide(); QVBoxLayout* bottomMainLayout = new QVBoxLayout; bottomMainLayout->addLayout(searchLineLayout); bottomMainLayout->addLayout(searchInfoLineLayout); bottomMainLayout->addWidget(filteredView); bottomMainLayout->addWidget(quickFindWidget_); bottomMainLayout->setContentsMargins(2, 1, 2, 1); bottomWindow->setLayout(bottomMainLayout); addWidget( logMainView ); addWidget( bottomWindow ); // Default splitter position (usually overridden by the config file) QList splitterSizes; splitterSizes += 400; splitterSizes += 100; setSizes( splitterSizes ); // Connect the signals connect(searchLineEdit->lineEdit(), SIGNAL( returnPressed() ), searchButton, SIGNAL( clicked() )); connect(searchLineEdit->lineEdit(), SIGNAL( textEdited( const QString& ) ), this, SLOT( searchTextChangeHandler() )); connect(searchButton, SIGNAL( clicked() ), this, SLOT( startNewSearch() ) ); connect(stopButton, SIGNAL( clicked() ), this, SLOT( stopSearch() ) ); connect(visibilityBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( changeFilteredViewVisibility( int ) ) ); connect(logMainView, SIGNAL( newSelection( int ) ), logMainView, SLOT( update() ) ); connect(filteredView, SIGNAL( newSelection( int ) ), this, SLOT( jumpToMatchingLine( int ) ) ); connect(filteredView, SIGNAL( newSelection( int ) ), filteredView, SLOT( update() ) ); connect(logMainView, SIGNAL( updateLineNumber( int ) ), this, SIGNAL( updateLineNumber( int ) ) ); connect(logMainView, SIGNAL( markLine( qint64 ) ), this, SLOT( markLineFromMain( qint64 ) ) ); connect(filteredView, SIGNAL( markLine( qint64 ) ), this, SLOT( markLineFromFiltered( qint64 ) ) ); connect(logMainView, SIGNAL( addToSearch( const QString& ) ), this, SLOT( addToSearch( const QString& ) ) ); connect(filteredView, SIGNAL( addToSearch( const QString& ) ), this, SLOT( addToSearch( const QString& ) ) ); connect(filteredView, SIGNAL( mouseHoveredOverLine( qint64 ) ), this, SLOT( mouseHoveredOverMatch( qint64 ) ) ); connect(filteredView, SIGNAL( mouseLeftHoveringZone() ), overviewWidget_, SLOT( removeHighlight() ) ); // Follow option (up and down) connect(this, SIGNAL( followSet( bool ) ), logMainView, SLOT( followSet( bool ) ) ); connect(this, SIGNAL( followSet( bool ) ), filteredView, SLOT( followSet( bool ) ) ); connect(logMainView, SIGNAL( followDisabled() ), this, SIGNAL( followDisabled() ) ); connect(filteredView, SIGNAL( followDisabled() ), this, SIGNAL( followDisabled() ) ); connect( logFilteredData_, SIGNAL( searchProgressed( int, int ) ), this, SLOT( updateFilteredView( int, int ) ) ); // QuickFind connect( quickFindWidget_, SIGNAL( close() ), this, SLOT( hideQuickFindBar() ) ); connect( quickFindWidget_, SIGNAL( patternConfirmed( const QString&, bool ) ), quickFindMux_, SLOT( confirmPattern( const QString&, bool ) ) ); connect( quickFindWidget_, SIGNAL( patternUpdated( const QString&, bool ) ), quickFindMux_, SLOT( setNewPattern( const QString&, bool ) ) ); connect( quickFindWidget_, SIGNAL( cancelSearch() ), quickFindMux_, SLOT( cancelSearch() ) ); connect( quickFindWidget_, SIGNAL( searchForward() ), quickFindMux_, SLOT( searchForward() ) ); connect( quickFindWidget_, SIGNAL( searchBackward() ), quickFindMux_, SLOT( searchBackward() ) ); connect( quickFindWidget_, SIGNAL( searchNext() ), quickFindMux_, SLOT( searchNext() ) ); // QuickFind changes coming from the views connect( quickFindMux_, SIGNAL( patternChanged( const QString& ) ), this, SLOT( changeQFPattern( const QString& ) ) ); connect( quickFindMux_, SIGNAL( notify( const QFNotification& ) ), quickFindWidget_, SLOT( notify( const QFNotification& ) ) ); connect( quickFindMux_, SIGNAL( clearNotification() ), quickFindWidget_, SLOT( clearNotification() ) ); // Sent load file update to MainWindow (for status update) connect( logData_, SIGNAL( loadingProgressed( int ) ), this, SIGNAL( loadingProgressed( int ) ) ); connect( logData_, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinishedHandler( bool ) ) ); connect( logData_, SIGNAL( fileChanged( LogData::MonitoredFileStatus ) ), this, SLOT( fileChangedHandler( LogData::MonitoredFileStatus ) ) ); // Search auto-refresh connect( searchRefreshCheck, SIGNAL( stateChanged( int ) ), this, SLOT( searchRefreshChangedHandler( int ) ) ); } // Start the asynchronous loading of a file. bool CrawlerWidget::readFile( const QString& fileName, int ) { QFileInfo fileInfo( fileName ); if ( fileInfo.isReadable() ) { LOG(logDEBUG) << "Entering readFile " << fileName.toStdString(); // First we cancel any in progress search and loading stopLoading(); // The file exist, so we invalidate the search, remove all marks // and redraw the screen. replaceCurrentSearch( "" ); logFilteredData_->clearMarks(); logData_->attachFile( fileName ); logMainView->updateData(); // Forbid starting a search when loading in progress // searchButton->setEnabled( false ); return true; } else { return false; } } void CrawlerWidget::stopLoading() { logFilteredData_->interruptSearch(); logData_->interruptLoading(); } void CrawlerWidget::getFileInfo( qint64* fileSize, int* fileNbLine, QDateTime* lastModified ) const { *fileSize = logData_->getFileSize(); *fileNbLine = logData_->getNbLine(); *lastModified = logData_->getLastModifiedDate(); } // The top line is first one on the main display int CrawlerWidget::getTopLine() const { return logMainView->getTopLine(); } QString CrawlerWidget::getSelectedText() const { if ( filteredView->hasFocus() ) return filteredView->getSelection(); else return logMainView->getSelection(); } void CrawlerWidget::selectAll() { activeView()->selectAll(); } // Return a pointer to the view in which we should do the QuickFind SearchableWidgetInterface* CrawlerWidget::getActiveSearchable() const { QWidget* searchableWidget; // Search in the window that has focus, or the window where 'Find' was // called from, or the main window. if ( filteredView->hasFocus() || logMainView->hasFocus() ) searchableWidget = QApplication::focusWidget(); else searchableWidget = qfSavedFocus_; if ( AbstractLogView* view = qobject_cast( searchableWidget ) ) return view; else return logMainView; } // // Events handlers // void CrawlerWidget::keyPressEvent( QKeyEvent* keyEvent ) { LOG(logDEBUG4) << "keyPressEvent received"; switch ( (keyEvent->text())[0].toAscii() ) { case '/': displayQuickFindBar( QuickFindMux::Forward ); break; case '?': displayQuickFindBar( QuickFindMux::Backward ); break; default: keyEvent->ignore(); } if ( !keyEvent->isAccepted() ) QSplitter::keyPressEvent( keyEvent ); } // // Slots // void CrawlerWidget::startNewSearch() { // Record the search line in the recent list // (reload the list first in case another glogg changed it) GetPersistentInfo().retrieve( "savedSearches" ); savedSearches->addRecent( searchLineEdit->currentText() ); GetPersistentInfo().save( "savedSearches" ); // Update the SearchLine (history) updateSearchCombo(); // Call the private function to do the search replaceCurrentSearch( searchLineEdit->currentText() ); } void CrawlerWidget::stopSearch() { logFilteredData_->interruptSearch(); searchState_.stopSearch(); printSearchInfoMessage(); } // When receiving the 'newDataAvailable' signal from LogFilteredData void CrawlerWidget::updateFilteredView( int nbMatches, int progress ) { LOG(logDEBUG) << "updateFilteredView received."; if ( progress == 100 ) { // Searching done printSearchInfoMessage( nbMatches ); searchInfoLine->hideGauge(); // De-activate the stop button stopButton->setEnabled( false ); } else { // Search in progress // We ignore 0% and 100% to avoid a flash when the search is very short if ( progress > 0 ) { searchInfoLine->setText( tr("Search in progress (%1 %)... %2 match%3 found so far.") .arg( progress ) .arg( nbMatches ) .arg( nbMatches > 1 ? "es" : "" ) ); searchInfoLine->displayGauge( progress ); } } // Recompute the content of the filtered window. filteredView->updateData(); // Update the match overview overview_->updateData( logData_->getNbLine() ); // Also update the top window for the coloured bullets. update(); } void CrawlerWidget::jumpToMatchingLine(int filteredLineNb) { int mainViewLine = logFilteredData_->getMatchingLineNumber(filteredLineNb); logMainView->selectAndDisplayLine(mainViewLine); // FIXME: should be done with a signal. } void CrawlerWidget::markLineFromMain( qint64 line ) { if ( logFilteredData_->isLineMarked( line ) ) logFilteredData_->deleteMark( line ); else logFilteredData_->addMark( line ); // Recompute the content of the filtered window. filteredView->updateData(); // Update the match overview overview_->updateData( logData_->getNbLine() ); // Also update the top window for the coloured bullets. update(); } void CrawlerWidget::markLineFromFiltered( qint64 line ) { qint64 line_in_file = logFilteredData_->getMatchingLineNumber( line ); if ( logFilteredData_->filteredLineTypeByIndex( line ) == LogFilteredData::Mark ) logFilteredData_->deleteMark( line_in_file ); else logFilteredData_->addMark( line_in_file ); // Recompute the content of the filtered window. filteredView->updateData(); // Update the match overview overview_->updateData( logData_->getNbLine() ); // Also update the top window for the coloured bullets. update(); } void CrawlerWidget::applyConfiguration() { Configuration& config = Persistent( "settings" ); QFont font = config.mainFont(); LOG(logDEBUG) << "CrawlerWidget::applyConfiguration"; // Whatever font we use, we should NOT use kerning font.setKerning( false ); font.setFixedPitch( true ); #if QT_VERSION > 0x040700 // Necessary on systems doing subpixel positionning (e.g. Ubuntu 12.04) font.setStyleStrategy( QFont::ForceIntegerMetrics ); #endif logMainView->setFont(font); filteredView->setFont(font); logMainView->setLineNumbersVisible( config.mainLineNumbersVisible() ); filteredView->setLineNumbersVisible( config.filteredLineNumbersVisible() ); overview_->setVisible( config.isOverviewVisible() ); logMainView->refreshOverview(); logMainView->updateDisplaySize(); logMainView->update(); filteredView->updateDisplaySize(); filteredView->update(); // Update the SearchLine (history) updateSearchCombo(); } void CrawlerWidget::loadingFinishedHandler( bool success ) { // We need to refresh the main window because the view lines on the // overview have probably changed. overview_->updateData( logData_->getNbLine() ); // FIXME, handle topLine // logMainView->updateData( logData_, topLine ); logMainView->updateData(); // searchButton->setEnabled( true ); // See if we need to auto-refresh the search if ( searchState_.isAutorefreshAllowed() ) { LOG(logDEBUG) << "Refreshing the search"; logFilteredData_->updateSearch(); } emit loadingFinished( success ); } void CrawlerWidget::fileChangedHandler( LogData::MonitoredFileStatus status ) { // Handle the case where the file has been truncated if ( status == LogData::Truncated ) { // Clear all marks (TODO offer the option to keep them) logFilteredData_->clearMarks(); if ( ! searchInfoLine->text().isEmpty() ) { // Invalidate the search logFilteredData_->clearSearch(); filteredView->updateData(); searchState_.truncateFile(); printSearchInfoMessage(); } } } void CrawlerWidget::displayQuickFindBar( QuickFindMux::QFDirection direction ) { LOG(logDEBUG) << "CrawlerWidget::displayQuickFindBar"; // Remember who had the focus qfSavedFocus_ = QApplication::focusWidget(); quickFindMux_->setDirection( direction ); quickFindWidget_->userActivate(); } void CrawlerWidget::hideQuickFindBar() { // Restore the focus once the QFBar has been hidden qfSavedFocus_->setFocus(); } void CrawlerWidget::changeQFPattern( const QString& newPattern ) { quickFindWidget_->changeDisplayedPattern( newPattern ); } // Returns a pointer to the window in which the search should be done AbstractLogView* CrawlerWidget::activeView() const { QWidget* activeView; // Search in the window that has focus, or the window where 'Find' was // called from, or the main window. if ( filteredView->hasFocus() || logMainView->hasFocus() ) activeView = QApplication::focusWidget(); else activeView = qfSavedFocus_; if ( AbstractLogView* view = qobject_cast( activeView ) ) return view; else return logMainView; } void CrawlerWidget::searchForward() { LOG(logDEBUG) << "CrawlerWidget::searchForward"; activeView()->searchForward(); } void CrawlerWidget::searchBackward() { LOG(logDEBUG) << "CrawlerWidget::searchBackward"; activeView()->searchBackward(); } void CrawlerWidget::searchRefreshChangedHandler( int state ) { searchState_.setAutorefresh( state == Qt::Checked ); printSearchInfoMessage( logFilteredData_->getNbMatches() ); } void CrawlerWidget::searchTextChangeHandler() { // We suspend auto-refresh searchState_.changeExpression(); printSearchInfoMessage( logFilteredData_->getNbMatches() ); } void CrawlerWidget::changeFilteredViewVisibility( int index ) { QStandardItem* item = visibilityModel_->item( index ); FilteredView::Visibility visibility = static_cast< FilteredView::Visibility>( item->data().toInt() ); filteredView->setVisibility( visibility ); } void CrawlerWidget::addToSearch( const QString& string ) { QString text = searchLineEdit->currentText(); if ( text.isEmpty() ) text = string; else { // Escape the regexp chars from the string before adding it. text += ( '|' + QRegExp::escape( string ) ); } searchLineEdit->setEditText( text ); // Set the focus to lineEdit so that the user can press 'Return' immediately searchLineEdit->lineEdit()->setFocus(); } void CrawlerWidget::mouseHoveredOverMatch( qint64 line ) { qint64 line_in_mainview = logFilteredData_->getMatchingLineNumber( line ); overviewWidget_->highlightLine( line_in_mainview ); } // // Private functions // // Create a new search using the text passed, replace the currently // used one and destroy the old one. void CrawlerWidget::replaceCurrentSearch( const QString& searchText ) { // Interrupt the search if it's ongoing logFilteredData_->interruptSearch(); // We have to wait for the last search update (100%) // before clearing/restarting to avoid having remaining results. // FIXME: this is a bit of a hack, we call processEvents // for Qt to empty its event queue, including (hopefully) // the search update event sent by logFilteredData_. It saves // us the overhead of having proper sync. QApplication::processEvents( QEventLoop::ExcludeUserInputEvents ); if ( !searchText.isEmpty() ) { // Determine the type of regexp depending on the config QRegExp::PatternSyntax syntax; static Configuration& config = Persistent( "settings" ); switch ( config.mainRegexpType() ) { case Wildcard: syntax = QRegExp::Wildcard; break; case FixedString: syntax = QRegExp::FixedString; break; default: syntax = QRegExp::RegExp2; break; } // Set the pattern case insensitive if needed Qt::CaseSensitivity case_sensitivity = Qt::CaseSensitive; if ( ignoreCaseCheck->checkState() == Qt::Checked ) case_sensitivity = Qt::CaseInsensitive; // Constructs the regexp QRegExp regexp( searchText, case_sensitivity, syntax ); if ( regexp.isValid() ) { // Activate the stop button stopButton->setEnabled( true ); // Start a new asynchronous search logFilteredData_->runSearch( regexp ); // Accept auto-refresh of the search searchState_.startSearch(); } else { // The regexp is wrong logFilteredData_->clearSearch(); filteredView->updateData(); searchState_.resetState(); // Inform the user QString errorMessage = tr("Error in expression: "); errorMessage += regexp.errorString(); searchInfoLine->setPalette( errorPalette ); searchInfoLine->setText( errorMessage ); } } else { logFilteredData_->clearSearch(); filteredView->updateData(); searchState_.resetState(); printSearchInfoMessage(); } // Connect the search to the top view logMainView->useNewFiltering( logFilteredData_ ); } // Updates the content of the drop down list for the saved searches, // called when the SavedSearch has been changed. void CrawlerWidget::updateSearchCombo() { const QString text = searchLineEdit->lineEdit()->text(); searchLineEdit->clear(); searchLineEdit->addItems( savedSearches->recentSearches() ); // In case we had something that wasn't added to the list (blank...): searchLineEdit->lineEdit()->setText( text ); } // Print the search info message. void CrawlerWidget::printSearchInfoMessage( int nbMatches ) { QString text; switch ( searchState_.getState() ) { case SearchState::NoSearch: // Blank text is fine break; case SearchState::Static: text = tr("%1 match%2 found.").arg( nbMatches ) .arg( nbMatches > 1 ? "es" : "" ); break; case SearchState::Autorefreshing: text = tr("%1 match%2 found. Search is auto-refreshing...").arg( nbMatches ) .arg( nbMatches > 1 ? "es" : "" ); break; case SearchState::FileTruncated: text = tr("File truncated on disk, previous search results are not valid anymore."); break; } searchInfoLine->setPalette( searchInfoLineDefaultPalette ); searchInfoLine->setText( text ); } // // SearchState implementation // void CrawlerWidget::SearchState::resetState() { state_ = NoSearch; } void CrawlerWidget::SearchState::setAutorefresh( bool refresh ) { autoRefreshRequested_ = refresh; if ( refresh ) { if ( state_ == Static ) state_ = Autorefreshing; } else { if ( state_ == Autorefreshing ) state_ = Static; } } void CrawlerWidget::SearchState::truncateFile() { state_ = FileTruncated; } void CrawlerWidget::SearchState::changeExpression() { if ( state_ == Autorefreshing ) state_ = Static; } void CrawlerWidget::SearchState::stopSearch() { if ( state_ == Autorefreshing ) state_ = Static; } void CrawlerWidget::SearchState::startSearch() { if ( autoRefreshRequested_ ) state_ = Autorefreshing; else state_ = Static; } glogg-0.9.2/crawlerwidget.h000066400000000000000000000167761222324246300156560ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2011, 2013 Nicolas Bonnefon * and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef CRAWLERWIDGET_H #define CRAWLERWIDGET_H #include #include #include #include #include #include #include #include #include "logmainview.h" #include "filteredview.h" #include "logdata.h" #include "logfiltereddata.h" #include "quickfindwidget.h" #include "quickfindmux.h" class InfoLine; class QuickFindPattern; class QuickFindWidget; class SavedSearches; class Overview; class QStandardItemModel; class OverviewWidget; // Implements the central widget of the application. // It includes both windows, the search line, the info // lines and various buttons. class CrawlerWidget : public QSplitter, public QuickFindMuxSelectorInterface { Q_OBJECT public: CrawlerWidget( SavedSearches* searches, QWidget *parent=0 ); // Loads the passed file and reports success. bool readFile( const QString& fileName, int topLine ); // Stop the loading of the file if one is in progress void stopLoading(); // Get the size (in bytes) and number of lines in the current file. void getFileInfo( qint64* fileSize, int* fileNbLine, QDateTime* lastModified ) const; // Get the line number of the first line displayed. int getTopLine() const; // Get the selected text as a string (from the main window) QString getSelectedText() const; // Display the QFB at the bottom, remembering where the focus was void displayQuickFindBar( QuickFindMux::QFDirection direction ); // Instructs the widget to select all the text in the window the user // is interacting with void selectAll(); // Implementation on the mux selector interface // (for dispatching QuickFind to the right widget) virtual SearchableWidgetInterface* getActiveSearchable() const; protected: void keyPressEvent( QKeyEvent* keyEvent ); signals: // Sent to signal the client load has progressed, // passing the completion percentage. void loadingProgressed( int progress ); // Sent to the client when the loading has finished // weither succesfull or not. void loadingFinished( bool success ); // Sent when follow mode is enabled/disabled void followSet( bool checked ); // Sent up to the MainWindow to disable the follow mode void followDisabled(); // Sent up when the current line number is updated void updateLineNumber( int line ); private slots: // Instructs the widget to start a search using the current search line. void startNewSearch(); // Stop the currently ongoing search (if one exists) void stopSearch(); // Instructs the widget to reconfigure itself because Config() has changed. void applyConfiguration(); // Called when new data must be displayed in the filtered window. void updateFilteredView( int nbMatches, int progress ); // Called when a new line has been selected in the filtered view, // to instruct the main view to jump to the matching line. void jumpToMatchingLine( int filteredLineNb ); // Mark a line that has been clicked on the main (top) view. void markLineFromMain( qint64 line ); // Mark a line that has been clicked on the filtered (bottom) view. void markLineFromFiltered( qint64 line ); void loadingFinishedHandler( bool success ); // Manages the info lines to inform the user the file has changed. void fileChangedHandler( LogData::MonitoredFileStatus ); void hideQuickFindBar(); // Instructs the widget to change the pattern in the QuickFind widget // and confirm it. void changeQFPattern( const QString& newPattern ); void searchForward(); void searchBackward(); // Called when the checkbox for search auto-refresh is changed void searchRefreshChangedHandler( int state ); // Called when the text on the search line is modified void searchTextChangeHandler(); // Called when the user change the visibility combobox void changeFilteredViewVisibility( int index ); // Called when the user add the string to the search void addToSearch( const QString& string ); // Called when a match is hovered on in the filtered view void mouseHoveredOverMatch( qint64 line ); private: // State machine holding the state of the search, used to allow/disallow // auto-refresh and inform the user via the info line. class SearchState { public: enum State { NoSearch, Static, Autorefreshing, FileTruncated, }; SearchState() { state_ = NoSearch; autoRefreshRequested_ = false; } // Reset the state (no search active) void resetState(); // The user changed auto-refresh request void setAutorefresh( bool refresh ); // The file has been truncated (stops auto-refresh) void truncateFile(); // The expression has been changed (stops auto-refresh) void changeExpression(); // The search has been stopped (stops auto-refresh) void stopSearch(); // The search has been started (enable auto-refresh) void startSearch(); // Get the state in order to display the proper message State getState() const { return state_; } // Is auto-refresh allowed bool isAutorefreshAllowed() const { return ( state_ == Autorefreshing ); } private: State state_; bool autoRefreshRequested_; }; // Private functions void replaceCurrentSearch( const QString& searchText ); void updateSearchCombo(); AbstractLogView* activeView() const; void printSearchInfoMessage( int nbMatches = 0 ); // Palette for error notification (yellow background) static const QPalette errorPalette; LogMainView* logMainView; QWidget* bottomWindow; QLabel* searchLabel; QComboBox* searchLineEdit; QToolButton* searchButton; QToolButton* stopButton; FilteredView* filteredView; QComboBox* visibilityBox; InfoLine* searchInfoLine; QCheckBox* ignoreCaseCheck; QCheckBox* searchRefreshCheck; QuickFindWidget* quickFindWidget_; OverviewWidget* overviewWidget_; QVBoxLayout* bottomMainLayout; QHBoxLayout* searchLineLayout; QHBoxLayout* searchInfoLineLayout; // Default palette to be remembered QPalette searchInfoLineDefaultPalette; SavedSearches* savedSearches; QuickFindMux* quickFindMux_; LogData* logData_; LogFilteredData* logFilteredData_; qint64 logFileSize_; QWidget* qfSavedFocus_; // Search state (for auto-refresh and truncation) SearchState searchState_; // Matches overview Overview* overview_; // Model for the visibility selector QStandardItemModel* visibilityModel_; }; #endif glogg-0.9.2/doc/000077500000000000000000000000001222324246300133665ustar00rootroot00000000000000glogg-0.9.2/doc/documentation.markdown000066400000000000000000000113121222324246300200010ustar00rootroot00000000000000 ## Getting started _glogg_ can be started from the command line, optionally passing the file to open as an argument, or via the desktop environment's menu or file association. If no file name is passed, _glogg_ will initially open the last used file. The main window is divided in three parts : the top displays the log file. The bottom part, called the "filtered view", shows the results of the search. The line separating the two contains the regular expression used as a filter. Entering a new regular expression, or a simple search term, will update the bottom view, displaying the results of the search. The lines matching the search criteria are listed in order in the results, and are marked with a red circle in both windows. ## Exploring log files Regular expressions are a powerful way to extract the information you are interested in from the log file. _glogg_ uses _extended regular expressions_. One of the most useful regexp feature when exploring logs is the _alternation_, using parentheses and the | operator. It searches for several alternatives, permitting to display several line types in the filtered window, in the same order they appear in the log file. For example, to check that every connection opened is also closed, one can use an expression similar to: `Entering (Open|Close)Connection` Any 'open' call without a matching 'close' will immediately be obvious in the filtered window. The alternation also works with the whole search line. For example if you also want to know what kind of connection has been opened: `Entering (Open|Close)Connection|Created a .* connection` `.*` will match any sequence of character on a single line, but _glogg_ will only display lines with a space and the word `connection` somewhere after `Created a` In addition to the filtered window, the match overview on the right hand side of the screen offers a view of the position of matches in the log file. Matches are showed as small red lines. ## Using filters _Filters_ can colorize some lines of the log being displayed, for example to draw attention to lines indicating an error, or to associate a color with each sort of event. Any number of filter can be defined in the 'Filters' configuration dialog, each using a regexp against which lines will be matched. For each line, all filters are tried in order and the fore and back colors of the first successful filter are applied. ## Marking lines in the log file In addition to regexp matches, _glogg_ enable the user to mark any interesting line in the log. To do this, click on the round bullet in the left margin in front of the line that needs to be marked. Marks are combined with matches and showed in the filtered window. They also appears as blue lines in the match overview. ## Browsing changing log files _glogg_ can display and search through logs while they are written to disk, as it might be the case when debugging a running program or server. The log is automatically updated when it grows, but the 'Auto-refresh' option must be enabled if you want the search results to be automatically refreshed as well. The 'f' key might be used to follow the end of the file as it grows (_a la_ `tail -f`). ## Settings ### Font The font used to display the log file. A clear, monospace font (like the free, open source, [DejaVu Mono](http://www.dejavu-fonts.org/) for example) is recommended. ### Search options Determines which type of regular expression _glogg_ will use when filtering lines for the bottom window, and when using QuickFind. * Extended Regexp: the default, uses regular expressions similar to those used by Perl * Wildcards: uses wildcards (\*, ? and []) in a similar fashion as a Unix shell * Fixed Strings: searches for the text exactly as it is written, no character is special ## Keyboard commands _glogg_ keyboard commands try to approximatively emulate the default bindings used by the classic Unix utilities _vi_ and _less_. The main commands are:
arrows scroll one line up/down or one column left/right
[number] j/k move the selection 'number' (or one) line down/up
h/l scroll left/right
[number] g jump to the line number given or the first one if no number is entered
G jump to the last line of the file (selecting it)
/ start a quickfind search in the current screen
n/N repeat the previous quickfind search forward/backward
*/# search for the next/previous occurence of the currently selected text
f activate 'follow' mode, which keep the display as the tail of the file (like "tail -f")
glogg-0.9.2/filewatcher.cpp000066400000000000000000000112601222324246300156220ustar00rootroot00000000000000/* * Copyright (C) 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #include "log.h" #include "filewatcher.h" #include #include FileWatcher::FileWatcher() : qtFileWatcher_( this ) { monitoringState_ = None; connect( &qtFileWatcher_, SIGNAL( fileChanged( const QString& ) ), this, SLOT( fileChangedOnDisk( const QString& ) ) ); connect( &qtFileWatcher_, SIGNAL( directoryChanged( const QString& ) ), this, SLOT( directoryChangedOnDisk( const QString& ) ) ); } FileWatcher::~FileWatcher() { disconnect( &qtFileWatcher_ ); } void FileWatcher::addFile( const QString& fileName ) { LOG(logDEBUG) << "FileWatcher::addFile " << fileName.toStdString(); QFileInfo fileInfo = QFileInfo( fileName ); if ( fileMonitored_.isEmpty() ) { fileMonitored_ = fileName; // Initialise the Qt file watcher qtFileWatcher_.addPath( fileInfo.path() ); if ( fileInfo.exists() ) { LOG(logDEBUG) << "FileWatcher::addFile: file exists."; qtFileWatcher_.addPath( fileName ); monitoringState_ = FileExists; } else { LOG(logDEBUG) << "FileWatcher::addFile: file doesn't exist."; monitoringState_ = FileRemoved; } } else { LOG(logWARNING) << "FileWatcher::addFile " << fileName.toStdString() << "- Already watching a file (" << fileMonitored_.toStdString() << ")!"; } } void FileWatcher::removeFile( const QString& fileName ) { LOG(logDEBUG) << "FileWatcher::removeFile " << fileName.toStdString(); QFileInfo fileInfo = QFileInfo( fileName ); if ( fileName == fileMonitored_ ) { if ( monitoringState_ == FileExists ) qtFileWatcher_.removePath( fileName ); qtFileWatcher_.removePath( fileInfo.path() ); fileMonitored_.clear(); monitoringState_ = None; } else { LOG(logWARNING) << "FileWatcher::removeFile - The file is not watched!"; } // For debug purpose: foreach (QString str, qtFileWatcher_.files()) { LOG(logERROR) << "File still watched: " << str.toStdString(); } foreach (QString str, qtFileWatcher_.directories()) { LOG(logERROR) << "Directories still watched: " << str.toStdString(); } } // // Slots // void FileWatcher::fileChangedOnDisk( const QString& filename ) { LOG(logDEBUG) << "FileWatcher::fileChangedOnDisk " << filename.toStdString(); if ( ( monitoringState_ == FileExists ) && ( filename == fileMonitored_ ) ) { emit fileChanged( filename ); // If the file has been removed... if ( !QFileInfo( filename ).exists() ) monitoringState_ = FileRemoved; } else LOG(logWARNING) << "FileWatcher::fileChangedOnDisk - call from Qt but no file monitored"; } void FileWatcher::directoryChangedOnDisk( const QString& filename ) { LOG(logDEBUG) << "FileWatcher::directoryChangedOnDisk " << filename.toStdString(); if ( monitoringState_ == FileRemoved ) { if ( QFileInfo( fileMonitored_ ).exists() ) { LOG(logDEBUG) << "FileWatcher::directoryChangedOnDisk - our file reappeared!"; // The file has been recreated, we have to watch it again. monitoringState_ = FileExists; // Restore the Qt file watcher (automatically cancelled // when the file is deleted) qtFileWatcher_.addPath( fileMonitored_ ); emit fileChanged( fileMonitored_ ); } else { LOG(logWARNING) << "FileWatcher::directoryChangedOnDisk - not the file we are watching"; } } else if ( monitoringState_ == FileExists ) { if ( ! QFileInfo( fileMonitored_ ).exists() ) { LOG(logDEBUG) << "FileWatcher::directoryChangedOnDisk - our file disappeared!"; monitoringState_ = FileRemoved; emit fileChanged( filename ); } else { LOG(logWARNING) << "FileWatcher::directoryChangedOnDisk - not the file we are watching"; } } } glogg-0.9.2/filewatcher.h000066400000000000000000000035741222324246300153000ustar00rootroot00000000000000/* * Copyright (C) 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef FILEWATCHER_H #define FILEWATCHER_H #include #include // This class encapsulate Qt's QFileSystemWatcher and additionally support // watching a file that doesn't exist yet (the class will watch the owning // directory) // Only supports one file at the moment. class FileWatcher : public QObject { Q_OBJECT public: // Create an empty object FileWatcher(); // Destroy the object ~FileWatcher(); // Adds the file to the list of file to watch // (do nothing if a file is already monitored) void addFile( const QString& fileName ); // Removes the file to the list of file to watch // (do nothing if said file is not monitored) void removeFile( const QString& fileName ); signals: // Sent when the file on disk has changed in any way. void fileChanged( const QString& ); private slots: void fileChangedOnDisk( const QString& filename ); void directoryChangedOnDisk( const QString& filename ); private: enum MonitoringState { None, FileExists, FileRemoved }; QFileSystemWatcher qtFileWatcher_; QString fileMonitored_; MonitoringState monitoringState_; }; #endif glogg-0.9.2/filteredview.cpp000066400000000000000000000047371222324246300160310ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2012 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // This file implements the FilteredView concrete class. // Most of the actual drawing and event management is done in AbstractLogView // Only behaviour specific to the filtered (bottom) view is implemented here. #include #include "filteredview.h" FilteredView::FilteredView( LogFilteredData* newLogData, const QuickFindPattern* const quickFindPattern, QWidget* parent ) : AbstractLogView( newLogData, quickFindPattern, parent ) { // We keep a copy of the filtered data for fast lookup of the line type logFilteredData_ = newLogData; } void FilteredView::setVisibility( Visibility visi ) { assert( logFilteredData_ ); LogFilteredData::Visibility data_visibility = LogFilteredData::MarksAndMatches; switch ( visi ) { case MarksOnly: data_visibility = LogFilteredData::MarksOnly; break; case MatchesOnly: data_visibility = LogFilteredData::MatchesOnly; break; case MarksAndMatches: data_visibility = LogFilteredData::MarksAndMatches; break; }; logFilteredData_->setVisibility( data_visibility ); updateData(); } // For the filtered view, a line is always matching! AbstractLogView::LineType FilteredView::lineType( int lineNumber ) const { LogFilteredData::FilteredLineType type = logFilteredData_->filteredLineTypeByIndex( lineNumber ); if ( type == LogFilteredData::Mark ) return Marked; else return Match; } qint64 FilteredView::displayLineNumber( int lineNumber ) const { // Display a 1-based index return logFilteredData_->getMatchingLineNumber( lineNumber ) + 1; } qint64 FilteredView::maxDisplayLineNumber() const { return logFilteredData_->getNbTotalLines(); } glogg-0.9.2/filteredview.h000066400000000000000000000030331222324246300154620ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2012 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef FILTEREDVIEW_H #define FILTEREDVIEW_H #include "abstractlogview.h" #include "logfiltereddata.h" // Class implementing the filtered (bottom) view widget. class FilteredView : public AbstractLogView { public: FilteredView( LogFilteredData* newLogData, const QuickFindPattern* const quickFindPattern, QWidget* parent = 0 ); // What is visible in the view. enum Visibility { MatchesOnly, MarksOnly, MarksAndMatches }; void setVisibility( Visibility visi ); protected: virtual LineType lineType( int lineNumber ) const; // Number of the filtered line relative to the unfiltered source virtual qint64 displayLineNumber( int lineNumber ) const; virtual qint64 maxDisplayLineNumber() const; private: LogFilteredData* logFilteredData_; }; #endif glogg-0.9.2/filtersdialog.cpp000066400000000000000000000220751222324246300161630ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #include "log.h" #include "configuration.h" #include "persistentinfo.h" #include "filterset.h" #include "filtersdialog.h" static const QString DEFAULT_PATTERN = "New Filter"; static const QString DEFAULT_FORE_COLOUR = "black"; static const QString DEFAULT_BACK_COLOUR = "white"; // Construct the box, including a copy of the global FilterSet // to handle ok/cancel/apply FiltersDialog::FiltersDialog( QWidget* parent ) : QDialog( parent ) { setupUi( this ); // Reload the filter list from disk (in case it has been changed // by another glogg instance) and copy it to here. GetPersistentInfo().retrieve( "filterSet" ); filterSet = Persistent( "filterSet" ); populateColors(); populateFilterList(); // Start with all buttons disabled except 'add' removeFilterButton->setEnabled(false); upFilterButton->setEnabled(false); downFilterButton->setEnabled(false); // Default to black on white int index = foreColorBox->findText( DEFAULT_FORE_COLOUR ); foreColorBox->setCurrentIndex( index ); index = backColorBox->findText( DEFAULT_BACK_COLOUR ); backColorBox->setCurrentIndex( index ); // No filter selected by default selectedRow_ = -1; connect( filterListWidget, SIGNAL( itemSelectionChanged() ), this, SLOT( updatePropertyFields() ) ); connect( patternEdit, SIGNAL( textEdited( const QString& ) ), this, SLOT( updateFilterProperties() ) ); connect( foreColorBox, SIGNAL( activated( int ) ), this, SLOT( updateFilterProperties() ) ); connect( backColorBox, SIGNAL( activated( int ) ), this, SLOT( updateFilterProperties() ) ); } // // Slots // void FiltersDialog::on_addFilterButton_clicked() { LOG(logDEBUG) << "on_addFilterButton_clicked()"; Filter newFilter = Filter( DEFAULT_PATTERN, DEFAULT_FORE_COLOUR, DEFAULT_BACK_COLOUR ); filterSet.filterList << newFilter; // Add and select the newly created filter filterListWidget->addItem( DEFAULT_PATTERN ); filterListWidget->setCurrentRow( filterListWidget->count() - 1 ); } void FiltersDialog::on_removeFilterButton_clicked() { int index = filterListWidget->currentRow(); LOG(logDEBUG) << "on_removeFilterButton_clicked() index " << index; if ( index >= 0 ) { filterSet.filterList.removeAt( index ); filterListWidget->setCurrentRow( -1 ); delete filterListWidget->takeItem( index ); // Select the new item at the same index filterListWidget->setCurrentRow( index ); } } void FiltersDialog::on_upFilterButton_clicked() { int index = filterListWidget->currentRow(); LOG(logDEBUG) << "on_upFilterButton_clicked() index " << index; if ( index > 0 ) { filterSet.filterList.move( index, index - 1 ); QListWidgetItem* item = filterListWidget->takeItem( index ); filterListWidget->insertItem( index - 1, item ); filterListWidget->setCurrentRow( index - 1 ); } } void FiltersDialog::on_downFilterButton_clicked() { int index = filterListWidget->currentRow(); LOG(logDEBUG) << "on_downFilterButton_clicked() index " << index; if ( ( index >= 0 ) && ( index < ( filterListWidget->count() - 1 ) ) ) { filterSet.filterList.move( index, index + 1 ); QListWidgetItem* item = filterListWidget->takeItem( index ); filterListWidget->insertItem( index + 1, item ); filterListWidget->setCurrentRow( index + 1 ); } } void FiltersDialog::on_buttonBox_clicked( QAbstractButton* button ) { LOG(logDEBUG) << "on_buttonBox_clicked()"; QDialogButtonBox::ButtonRole role = buttonBox->buttonRole( button ); if ( ( role == QDialogButtonBox::AcceptRole ) || ( role == QDialogButtonBox::ApplyRole ) ) { // Copy the filter set and persist it to disk Persistent( "filterSet" ) = filterSet; GetPersistentInfo().save( "filterSet" ); emit optionsChanged(); } if ( role == QDialogButtonBox::AcceptRole ) accept(); else if ( role == QDialogButtonBox::RejectRole ) reject(); } void FiltersDialog::updatePropertyFields() { if ( filterListWidget->selectedItems().count() >= 1 ) selectedRow_ = filterListWidget->row( filterListWidget->selectedItems().at(0) ); else selectedRow_ = -1; LOG(logDEBUG) << "updatePropertyFields(), row = " << selectedRow_; if ( selectedRow_ >= 0 ) { const Filter& currentFilter = filterSet.filterList.at( selectedRow_ ); patternEdit->setText( currentFilter.pattern() ); patternEdit->setEnabled( true ); int index = foreColorBox->findText( currentFilter.foreColorName() ); if ( index != -1 ) { LOG(logDEBUG) << "fore index = " << index; foreColorBox->setCurrentIndex( index ); foreColorBox->setEnabled( true ); } index = backColorBox->findText( currentFilter.backColorName() ); if ( index != -1 ) { LOG(logDEBUG) << "back index = " << index; backColorBox->setCurrentIndex( index ); backColorBox->setEnabled( true ); } // Enable the buttons if needed removeFilterButton->setEnabled( true ); upFilterButton->setEnabled( ( selectedRow_ > 0 ) ? true : false ); downFilterButton->setEnabled( ( selectedRow_ < ( filterListWidget->count() - 1 ) ) ? true : false ); } else { // Nothing is selected, greys the buttons patternEdit->setEnabled( false ); foreColorBox->setEnabled( false ); backColorBox->setEnabled( false ); } } void FiltersDialog::updateFilterProperties() { LOG(logDEBUG) << "updateFilterProperties()"; // If a row is selected if ( selectedRow_ >= 0 ) { Filter& currentFilter = filterSet.filterList[selectedRow_]; // Update the internal data currentFilter.setPattern( patternEdit->text() ); currentFilter.setForeColor( foreColorBox->currentText() ); currentFilter.setBackColor( backColorBox->currentText() ); // Update the entry in the filterList widget filterListWidget->currentItem()->setText( patternEdit->text() ); filterListWidget->currentItem()->setForeground( QBrush( QColor( currentFilter.foreColorName() ) ) ); filterListWidget->currentItem()->setBackground( QBrush( QColor( currentFilter.backColorName() ) ) ); } } // // Private functions // // Fills the color selection combo boxes void FiltersDialog::populateColors() { const QStringList colorNames = QStringList() // Basic 16 HTML colors (minus greys): << "black" << "white" << "maroon" << "red" << "purple" << "fuchsia" << "green" << "lime" << "olive" << "yellow" << "navy" << "blue" << "teal" << "aqua" // Greys << "gainsboro" << "lightgrey" << "silver" << "darkgrey" << "grey" << "dimgrey" // Reds << "tomato" << "orangered" << "orange" << "crimson" << "darkred" // Greens << "greenyellow" << "lightgreen" << "darkgreen" << "lightseagreen" // Blues << "lightcyan" << "darkturquoise" << "steelblue" << "lightblue" << "royalblue" << "darkblue" << "midnightblue" // Browns << "bisque" << "tan" << "sandybrown" << "chocolate"; for ( QStringList::const_iterator i = colorNames.constBegin(); i != colorNames.constEnd(); ++i ) { QPixmap solidPixmap( 20, 10 ); solidPixmap.fill( QColor( *i ) ); QIcon* solidIcon = new QIcon( solidPixmap ); foreColorBox->addItem( *solidIcon, *i ); backColorBox->addItem( *solidIcon, *i ); } } void FiltersDialog::populateFilterList() { filterListWidget->clear(); foreach ( Filter filter, filterSet.filterList ) { QListWidgetItem* new_item = new QListWidgetItem( filter.pattern() ); // new_item->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled ); new_item->setForeground( QBrush( QColor( filter.foreColorName() ) ) ); new_item->setBackground( QBrush( QColor( filter.backColorName() ) ) ); filterListWidget->addItem( new_item ); } } glogg-0.9.2/filtersdialog.h000066400000000000000000000035521222324246300156270ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef FILTERSDIALOG_H #define FILTERSDIALOG_H #include #include "filterset.h" #if QT_VERSION > 0x040400 #include "ui_filtersdialog.h" #else #include "ui_filtersdialog_old.h" #endif class FiltersDialog : public QDialog, public Ui::FiltersDialog { Q_OBJECT public: FiltersDialog( QWidget* parent = 0 ); signals: // Is emitted when new settings must be used void optionsChanged(); private slots: void on_addFilterButton_clicked(); void on_removeFilterButton_clicked(); void on_buttonBox_clicked( QAbstractButton* button ); void on_upFilterButton_clicked(); void on_downFilterButton_clicked(); // Update the property (pattern, color...) fields from the // selected Filter. void updatePropertyFields(); // Update the selected Filter from the values in the property fields. void updateFilterProperties(); private: // Temporary filterset modified by the dialog // it is copied from the one in Config() FilterSet filterSet; // Index of the row currently selected or -1 if none. int selectedRow_; void populateColors(); void populateFilterList(); }; #endif glogg-0.9.2/filtersdialog.ui000066400000000000000000000127351222324246300160200ustar00rootroot00000000000000 FiltersDialog 0 0 526 290 Filters QLayout::SetNoConstraint 6 New Filter :/images/plus.png:/images/plus.png Delete Filter :/images/minus.png:/images/minus.png Qt::Horizontal 40 20 Move Filter Up :/images/up.png:/images/up.png Move Filter Down :/images/down.png:/images/down.png Matching Pattern: false Fore Color: false false Back Color: Qt::Horizontal QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok glogg-0.9.2/filtersdialog_old.ui000066400000000000000000000130051222324246300166450ustar00rootroot00000000000000 FiltersDialog 0 0 526 290 Filters QLayout::SetNoConstraint 6 New Filter :/images/plus.png:/images/plus.png Delete Filter :/images/minus.png:/images/minus.png Qt::Horizontal 40 20 Move Filter Up :/images/up.png:/images/up.png Move Filter Down :/images/down.png:/images/down.png Fore Color: Back Color: Matching Pattern: Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok glogg-0.9.2/filterset.cpp000066400000000000000000000134601222324246300153320ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2011 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // This file implements classes Filter and FilterSet #include #include "log.h" #include "filterset.h" const int FilterSet::FILTERSET_VERSION = 1; Filter::Filter() { } Filter::Filter( const QString& pattern, const QString& foreColorName, const QString& backColorName ) : regexp_( pattern ), foreColorName_( foreColorName ), backColorName_( backColorName ), enabled_( true ) { LOG(logDEBUG) << "New Filter, fore: " << foreColorName_.toStdString() << " back: " << backColorName_.toStdString(); } QString Filter::pattern() const { return regexp_.pattern(); } void Filter::setPattern( const QString& pattern ) { regexp_.setPattern( pattern ); } const QString& Filter::foreColorName() const { return foreColorName_; } void Filter::setForeColor( const QString& foreColorName ) { foreColorName_ = foreColorName; } const QString& Filter::backColorName() const { return backColorName_; } void Filter::setBackColor( const QString& backColorName ) { backColorName_ = backColorName; } int Filter::indexIn( const QString& string ) const { return regexp_.indexIn( string ); } // // Operators for serialization // QDataStream& operator<<( QDataStream& out, const Filter& object ) { LOG(logDEBUG) << "<>( QDataStream& in, Filter& object ) { LOG(logDEBUG) << ">>operator from Filter"; in >> object.regexp_; in >> object.foreColorName_; in >> object.backColorName_; return in; } // Default constructor FilterSet::FilterSet() { qRegisterMetaTypeStreamOperators( "Filter" ); qRegisterMetaTypeStreamOperators( "FilterSet" ); qRegisterMetaTypeStreamOperators( "FilterSet::FilterList" ); } bool FilterSet::matchLine( const QString& line, QColor* foreColor, QColor* backColor ) const { for ( QList::const_iterator i = filterList.constBegin(); i != filterList.constEnd(); i++ ) { if ( i->indexIn( line ) != -1 ) { foreColor->setNamedColor( i->foreColorName() ); backColor->setNamedColor( i->backColorName() ); return true; } } return false; } // // Operators for serialization // QDataStream& operator<<( QDataStream& out, const FilterSet& object ) { LOG(logDEBUG) << "<>( QDataStream& in, FilterSet& object ) { LOG(logDEBUG) << ">>operator from FilterSet"; in >> object.filterList; return in; } // // Persistable virtual functions implementation // void Filter::saveToStorage( QSettings& settings ) const { LOG(logDEBUG) << "Filter::saveToStorage"; settings.setValue( "regexp", regexp_.pattern() ); settings.setValue( "fore_colour", foreColorName_ ); settings.setValue( "back_colour", backColorName_ ); } void Filter::retrieveFromStorage( QSettings& settings ) { LOG(logDEBUG) << "Filter::retrieveFromStorage"; regexp_ = QRegExp( settings.value( "regexp" ).toString() ); foreColorName_ = settings.value( "fore_colour" ).toString(); backColorName_ = settings.value( "back_colour" ).toString(); } void FilterSet::saveToStorage( QSettings& settings ) const { LOG(logDEBUG) << "FilterSet::saveToStorage"; settings.beginGroup( "FilterSet" ); // Remove everything in case the array is shorter than the previous one settings.remove(""); settings.setValue( "version", FILTERSET_VERSION ); settings.beginWriteArray( "filters" ); for (int i = 0; i < filterList.size(); ++i) { settings.setArrayIndex(i); filterList[i].saveToStorage( settings ); } settings.endArray(); settings.endGroup(); } void FilterSet::retrieveFromStorage( QSettings& settings ) { LOG(logDEBUG) << "FilterSet::retrieveFromStorage"; filterList.clear(); if ( settings.contains( "FilterSet/version" ) ) { settings.beginGroup( "FilterSet" ); if ( settings.value( "version" ) == FILTERSET_VERSION ) { int size = settings.beginReadArray( "filters" ); for (int i = 0; i < size; ++i) { settings.setArrayIndex(i); Filter filter; filter.retrieveFromStorage( settings ); filterList.append( filter ); } settings.endArray(); } else { LOG(logERROR) << "Unknown version of FilterSet, ignoring it..."; } settings.endGroup(); } else { LOG(logWARNING) << "Trying to import legacy (<=0.8.2) filters..."; FilterSet tmp_filter_set = settings.value( "filterSet" ).value(); *this = tmp_filter_set; LOG(logWARNING) << "...imported filterset: " << filterList.count() << " elements"; // Remove the old key once migration is done settings.remove( "filterSet" ); // And replace it with the new one saveToStorage( settings ); settings.sync(); } } glogg-0.9.2/filterset.h000066400000000000000000000067611222324246300150050ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef FILTERSET_H #define FILTERSET_H #include #include #include #include "persistable.h" // Represents a filter, i.e. a regexp and the colors matching text // should be rendered in. class Filter { public: // Construct an uninitialized Filter (when reading from a config file) Filter(); Filter( const QString& pattern, const QString& foreColor, const QString& backColor ); // Tests the string passed for a match, returns a value just like // QRegExp::indexIn (i.e. -1 if no match) int indexIn( const QString& string ) const; // Accessor functions QString pattern() const; void setPattern( const QString& pattern ); const QString& foreColorName() const; void setForeColor( const QString& foreColorName ); const QString& backColorName() const; void setBackColor( const QString& backColorName ); // Operators for serialization // (must be kept to migrate filters from <=0.8.2) friend QDataStream& operator<<( QDataStream& out, const Filter& object ); friend QDataStream& operator>>( QDataStream& in, Filter& object ); // Reads/writes the current config in the QSettings object passed void saveToStorage( QSettings& settings ) const; void retrieveFromStorage( QSettings& settings ); private: QRegExp regexp_; QString foreColorName_; QString backColorName_; bool enabled_; }; // Represents an ordered set of filters to be applied to each line displayed. class FilterSet : public Persistable { public: // Construct an empty filter set FilterSet(); // Returns weither the passed line match a filter of the set, // if so, it returns the fore/back colors the line should use. // Ownership of the colors is transfered to the caller. bool matchLine( const QString& line, QColor* foreColor, QColor* backColor ) const; // Reads/writes the current config in the QSettings object passed virtual void saveToStorage( QSettings& settings ) const; virtual void retrieveFromStorage( QSettings& settings ); // Should be private really, but I don't know how to have // it recognised by QVariant then. typedef QList FilterList; // Operators for serialization // (must be kept to migrate filters from <=0.8.2) friend QDataStream& operator<<( QDataStream& out, const FilterSet& object ); friend QDataStream& operator>>( QDataStream& in, FilterSet& object ); private: static const int FILTERSET_VERSION; FilterList filterList; // To simplify this class interface, FilterDialog can access our // internal structure directly. friend class FiltersDialog; }; Q_DECLARE_METATYPE(Filter) Q_DECLARE_METATYPE(FilterSet) Q_DECLARE_METATYPE(FilterSet::FilterList) #endif glogg-0.9.2/finish.sed000066400000000000000000000007241222324246300146010ustar00rootroot00000000000000# Add header/footer around the Markdown generated documentation # Copied from Damian Cugley (http://www.alleged.org.uk/2005/marky/) 1 { h i\ \ \ s/h2>/title>/g rmetadata.inc a\ \ rheader.inc } 2 { x p x } $ { rfooter.inc a\ \ } glogg-0.9.2/glogg.desktop000066400000000000000000000003441222324246300153140ustar00rootroot00000000000000[Desktop Entry] Name=glogg GenericName=Log file browser # %f because glogg supports only one file for now Exec=glogg %f Icon=glogg Type=Application Comment=A smart interactive log explorer. Terminal=false Categories=Qt;Utility; glogg-0.9.2/glogg.nsi000066400000000000000000000122701222324246300144350ustar00rootroot00000000000000# NSIS script creating the Windows installer for glogg # Is passed to the script using -DVERSION=$(git describe) on the command line !ifndef VERSION !define VERSION 'anonymous-build' !endif # Headers !include "MUI2.nsh" !include "FileAssociation.nsh" # General OutFile "glogg-${VERSION}-setup.exe" XpStyle on SetCompressor /SOLID lzma ; Registry key to keep track of the directory we are installed in InstallDir "$PROGRAMFILES\glogg" InstallDirRegKey HKLM Software\glogg "" ; glogg icon ; !define MUI_ICON glogg.ico RequestExecutionLevel admin Name "glogg" Caption "glogg ${VERSION} Setup" # Pages !define MUI_WELCOMEPAGE_TITLE "Welcome to the glogg ${VERSION} Setup Wizard" !define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of glogg\ , a fast, advanced log explorer.$\r$\n$\r$\n\ glogg and the Qt libraries are released under the GPL, see \ the COPYING file.$\r$\n$\r$\n$_CLICK" ; MUI_FINISHPAGE_LINK_LOCATION "http://nsis.sf.net/" !insertmacro MUI_PAGE_WELCOME ;!insertmacro MUI_PAGE_LICENSE "COPYING" # !ifdef VER_MAJOR & VER_MINOR & VER_REVISION & VER_BUILD... !insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH !insertmacro MUI_UNPAGE_WELCOME !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES !insertmacro MUI_UNPAGE_FINISH # Languages !insertmacro MUI_LANGUAGE "English" # Installer sections Section "glogg" glogg ; Prevent this section from being unselected SectionIn RO SetOutPath $INSTDIR File release\glogg.exe File COPYING File README ; Create the 'sendto' link CreateShortCut "$SENDTO\glogg.lnk" "$INSTDIR\glogg,exe" "" "$INSTDIR\glogg.exe" 0 ; Register as an otion (but not main handler) for some files (.txt, .Log, .cap) WriteRegStr HKCR "Applications\glogg.exe" "" "" WriteRegStr HKCR "Applications\glogg.exe\shell" "" "open" WriteRegStr HKCR "Applications\glogg.exe\shell\open" "FriendlyAppName" "glogg" WriteRegStr HKCR "Applications\glogg.exe\shell\open\command" "" '"$INSTDIR\glogg.exe" "%1"' WriteRegStr HKCR "*\OpenWithList\glogg.exe" "" "" WriteRegStr HKCR ".txt\OpenWithList\glogg.exe" "" "" WriteRegStr HKCR ".Log\OpenWithList\glogg.exe" "" "" WriteRegStr HKCR ".cap\OpenWithList\glogg.exe" "" "" ; Register uninstaller WriteRegExpandStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\glogg"\ "UninstallString" '"$INSTDIR\Uninstall.exe"' WriteRegExpandStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\glogg"\ "InstallLocation" "$INSTDIR" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\glogg" "DisplayName" "glogg" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\glogg" "DisplayVersion" "${VERSION}" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\glogg" "NoModify" "1" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\glogg" "NoRepair" "1" ; Create uninstaller WriteUninstaller "$INSTDIR\Uninstall.exe" SectionEnd Section "Qt4 Runtime libraries" qtlibs SetOutPath $INSTDIR File release\QtCore4.dll File release\QtGui4.dll SectionEnd Section "Create Start menu shortcut" shortcut SetShellVarContext all CreateShortCut "$SMPROGRAMS\glogg.lnk" "$INSTDIR\glogg.exe" "" "$INSTDIR\glogg.exe" 0 SectionEnd Section /o "Associate with .log files" associate ${registerExtension} "$INSTDIR\glogg.exe" ".log" "Log file" SectionEnd # Descriptions !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_DESCRIPTION_TEXT ${glogg} "The core files required to use glogg." !insertmacro MUI_DESCRIPTION_TEXT ${qtlibs} "Needed by glogg, you have to install these unless \ you already have the Qt4 development kit installed." !insertmacro MUI_DESCRIPTION_TEXT ${shortcut} "Create a shortcut in the Start menu for glogg." !insertmacro MUI_DESCRIPTION_TEXT ${associate} "Make glogg the default viewer for .log files." !insertmacro MUI_FUNCTION_DESCRIPTION_END # Uninstaller Section "Uninstall" Delete "$INSTDIR\Uninstall.exe" Delete "$INSTDIR\glogg.exe" Delete "$INSTDIR\README" Delete "$INSTDIR\COPYING" Delete "$INSTDIR\mingwm10.dll" Delete "$INSTDIR\libgcc_s_dw2-1.dll" Delete "$INSTDIR\QtCore4.dll" Delete "$INSTDIR\QtGui4.dll" RMDir "$INSTDIR" ; Remove settings in %appdata% Delete "$APPDATA\glogg\glogg.ini" RMDir "$APPDATA\glogg" DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\glogg" ; Remove settings in the registry (from glogg < 0.9) DeleteRegKey HKCU "Software\glogg" ; Remove the file associations ${unregisterExtension} ".log" "Log file" DeleteRegKey HKCR "*\OpenWithList\glogg.exe" DeleteRegKey HKCR ".txt\OpenWithList\glogg.exe" DeleteRegKey HKCR ".Log\OpenWithList\glogg.exe" DeleteRegKey HKCR ".cap\OpenWithList\glogg.exe" DeleteRegKey HKCR "Applications\glogg.exe\shell\open\command" DeleteRegKey HKCR "Applications\glogg.exe\shell\open" DeleteRegKey HKCR "Applications\glogg.exe\shell" DeleteRegKey HKCR "Applications\glogg.exe" ; Remove the shortcut, if any SetShellVarContext all Delete "$SMPROGRAMS\glogg.lnk" SectionEnd glogg-0.9.2/glogg.pro000066400000000000000000000107011222324246300144410ustar00rootroot00000000000000# ------------------------------------------------- # glogg # ------------------------------------------------- TARGET = glogg TEMPLATE = app win32:Debug:CONFIG += console # Necessary when cross-compiling: win32:Release:QMAKE_LFLAGS += "-Wl,-subsystem,windows" # Input SOURCES += main.cpp \ mainwindow.cpp \ crawlerwidget.cpp \ abstractlogdata.cpp \ logdata.cpp \ logfiltereddata.cpp \ abstractlogview.cpp \ logmainview.cpp \ filteredview.cpp \ optionsdialog.cpp \ persistentinfo.cpp \ configuration.cpp \ filtersdialog.cpp \ filterset.cpp \ savedsearches.cpp \ infoline.cpp \ logdataworkerthread.cpp \ logfiltereddataworkerthread.cpp \ filewatcher.cpp \ selection.cpp \ quickfind.cpp \ quickfindpattern.cpp \ quickfindwidget.cpp \ sessioninfo.cpp \ recentfiles.cpp \ menuactiontooltipbehavior.cpp \ overview.cpp \ overviewwidget.cpp \ marks.cpp \ quickfindmux.cpp HEADERS += \ mainwindow.h \ crawlerwidget.h \ logmainview.h \ log.h \ filteredview.h \ abstractlogdata.h \ logdata.h \ logfiltereddata.h \ abstractlogview.h \ optionsdialog.h \ persistentinfo.h \ configuration.h \ filtersdialog.h \ filterset.h \ savedsearches.h \ infoline.h \ logdataworkerthread.h \ logfiltereddataworkerthread.h \ filewatcher.h \ selection.h \ quickfind.h \ quickfindpattern.h \ quickfindwidget.h \ sessioninfo.h \ persistable.h \ recentfiles.h \ menuactiontooltipbehavior.h \ overview.h \ overviewwidget.h \ marks.h \ qfnotifications.h \ quickfindmux.h isEmpty(BOOST_PATH) { message(Building using system dynamic Boost libraries) LIBS += -lboost_program_options } else { message(Building using static Boost libraries at $$BOOST_PATH) SOURCES += $$BOOST_PATH/libs/program_options/src/*.cpp \ $$BOOST_PATH/libs/smart_ptr/src/*.cpp INCLUDEPATH += $$BOOST_PATH } FORMS += optionsdialog.ui greaterThan(QT_VERSION, "4.4.0") { FORMS += filtersdialog.ui } else { message(Using old FiltersDialog) FORMS += filtersdialog_old.ui } # For Windows icon RC_FILE = glogg.rc RESOURCES = glogg.qrc # Build HTML documentation (if 'markdown' is available) system(type markdown >/dev/null) { MARKDOWN += doc/documentation.markdown } else { message("markdown not found, HTML doc will not be generated") } doc_processor.name = markdown doc_processor.input = MARKDOWN doc_processor.output = doc/${QMAKE_FILE_BASE}.html doc_processor.commands = markdown ${QMAKE_FILE_NAME} | \ sed -f finish.sed >${QMAKE_FILE_OUT} doc_processor.CONFIG += target_predeps doc_processor.variable_out = doc.files QMAKE_EXTRA_COMPILERS += doc_processor # Install (for unix) icon16.path = $$PREFIX/share/icons/hicolor/16x16/apps icon16.files = images/hicolor/16x16/glogg.png icon32.path = $$PREFIX/share/icons/hicolor/32x32/apps icon32.files = images/hicolor/32x32/glogg.png icon_svg.path = $$PREFIX/share/icons/hicolor/scalable/apps icon_svg.files = images/hicolor/scalable/glogg.svg doc.path = $$PREFIX/share/doc/glogg doc.files += README COPYING desktop.path = $$PREFIX/share/applications desktop.files = glogg.desktop target.path = $$PREFIX/bin INSTALLS = target icon16 icon32 icon_svg doc desktop # Build directories debug:OBJECTS_DIR = $${OUT_PWD}/.obj/debug-shared release:OBJECTS_DIR = $${OUT_PWD}/.obj/release-shared debug:MOC_DIR = $${OUT_PWD}/.moc/debug-shared release:MOC_DIR = $${OUT_PWD}/.moc/release-shared # Debug symbols in debug builds debug:QMAKE_CXXFLAGS += -g # Extra compiler arguments # QMAKE_CXXFLAGS += -Weffc++ QMAKE_CXXFLAGS += -Wextra GPROF { QMAKE_CXXFLAGS += -pg QMAKE_LFLAGS += -pg } isEmpty(LOG_LEVEL) { Release:DEFINES += FILELOG_MAX_LEVEL=\"logERROR\" Debug:DEFINES += FILELOG_MAX_LEVEL=\"logDEBUG4\" } else { message("Using specified log level: $$LOG_LEVEL") DEFINES += FILELOG_MAX_LEVEL=\"$$LOG_LEVEL\" } # Official builds can be generated with `qmake VERSION="1.2.3"' isEmpty(VERSION):system(date >/dev/null) { system([ -f .tarball-version ]) { QMAKE_CXXFLAGS += -DGLOGG_VERSION=\\\"`cat .tarball-version`\\\" } else { QMAKE_CXXFLAGS += -DGLOGG_DATE=\\\"`date +'\"%F\"'`\\\" QMAKE_CXXFLAGS += -DGLOGG_VERSION=\\\"`git describe`\\\" QMAKE_CXXFLAGS += -DGLOGG_COMMIT=\\\"`git rev-parse --short HEAD`\\\" } } else { QMAKE_CXXFLAGS += -DGLOGG_VERSION=\\\"$$VERSION\\\" } glogg-0.9.2/glogg.qrc000066400000000000000000000012161222324246300144270ustar00rootroot00000000000000 images/hicolor/16x16/glogg.png images/hicolor/24x24/glogg.png images/hicolor/32x32/glogg.png images/hicolor/48x48/glogg.png images/open16.png images/open.xpm images/reload.png images/reload16.png images/stop16.png images/plus.png images/minus.png images/down.png images/up.png images/arrowdown.png images/arrowup.png images/darkclosebutton.png glogg-0.9.2/glogg.rc000066400000000000000000000000621222324246300142440ustar00rootroot00000000000000IDI_ICON1 ICON DISCARDABLE "glogg48.ico" glogg-0.9.2/glogg48.ico000066400000000000000000000226761222324246300146050ustar00rootroot0000000000000000 ¨%(0` ÿÿÿÿÿÿÿÿÿd î!ÿÊ@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#^ZZÿ÷÷÷ÿöööÿ¾½½ÿûLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿrÕÓÓÿäââÿâááÿº¸¸ÿ…ƒƒÿüWÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtÒÐÐÿåääÿßÞÞÿ‰ˆˆÿtssÿˆ††ÿþbÿÿÿÿÿÿÿÿÿÿÿÿ,,& `& `& `& `& `& `& `& `& `& `& `& `& `& `& `& `& `)/)‰*3-Qÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>;;ùêêêÿ†……ÿywwÿywwÿsqqÿ‰ˆˆÿ þnÿÿÿÿÿÿÿÿÿ263¹†‹†ö˜››ö•›˜ö•š˜õ˜›˜õ˜›šõ˜š˜õ˜›šõš›šõš›šõšž›õšž›õ˜›šõ•˜—ôŽ’‘ô…Œ‹ôƒ€ôYaZù;;ÿëÿÿÿÿº¸¸ÿTQQÿa^^ÿ†……ÿ§¦¦ÿ žžÿ|zzÿ^ZZÿmkkÿ¡  ÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿnwqÿ°¯¯ÿš˜˜ÿ‰ˆˆÿqppÿKMJþ9B9ݩӧ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿl‰úgddÿEBBÿdaaÿˆ††ÿqppÿ­¬¬ÿ ÿebbÿ|zzÿëëëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäââÿ”’’ÿdaaÿäââÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿswsÿÇÆÆÿ¾½½ÿ­¬¬ÿ‰ˆˆÿqppÿDHBþ?DAyÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿDñöö¶ÿÿÿÜÜÛÛàýhggÿB??ÿ_\\ÿŒ‹‹ÿ¾½½ÿwvvÿÊÉÉÿÿÿÿÿÿÿÿÿüüüÿäÛÓÿÛ€ÿÄ …ÿåÜÙÿüüüÿÿÿÿÿÿÿÿÿÇÆÆÿZWWÿ·µµÿÿÿÿÿÿÿÿÿÿÿÿÿíðíÿJPHÿnspÿgmjÿadaÿ^aZÿ_d^ÿKPJÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ùùù½½½½õÿÿÿüÉÉÉÿÿÿÿÿÿÿÿÿÿÿÿÿèèèÿŽŒŒÿ}}ÿa^^ÿÿÿÿÿâÖÐÿ€ÿè­yÿ䲃ÿ庑ÿêÀšÿmJÿ_?!ÿZ<#ÿW<#ÿZÿÿÿÅÃÃÃÿÿÿÿÿÉÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿ½»»ÿZWWÿêêêÿýýýÿ›Hÿ”Dÿíµƒÿä²…ÿè»’ÿêÞÿíʬÿðÓ¸ÿñÛÆÿñÛÆÿðÕ»ÿëÊ­ÿèÁ ÿ庒ÿ»q0ÿ¦\!ÿüüüÿÞÜÜÿebbÿùùùÿùùùÿùùùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtywî, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿÉÃÃÃÿÿÿÿÿÉÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿmkkÿÿÿÿÿϵ¡ÿš3ÿ¡g-ÿíµƒÿä´†ÿè½—ÿêÞÿßÀ ÿÜÀ¦ÿÜíÿÜÆ²ÿÛÁ§ÿÛºšÿÖ²‘ÿÖ¬…ÿÕ¤wÿŽ'ÿÕõÿýýýÿTQQÿØÙÙÿÜÛÛÿÜÛÛÿÜÛÛÿäââÿÿÿÿÿÿÿÿÿÿÿÿÿw}wï, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿÍÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»ººÿ”’’ÿÿÿÿÿ°wGÿ°Wÿ¦p9ÿùϧÿùÙºÿÿèÏÿÿèÏÿ°˜ÿwZBÿM0ÿJ-ÿM/ÿW3ÿ_5ÿh;ÿs?ÿ€)ÿºˆaÿ÷÷÷ÿnmmÿÿÿÿÿ)&&ÿüüüÿÿÿÿÿÿÿÿÿ|€ï, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿÒÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿèèèÿtssÿº¸¸ÿöööÿ˜Bÿªa ÿ»Œ^ÿÿâÇÿÿäÊÿÿëÕÿÿðÛÿßʺÿ̺§ÿº¤’ÿ†jNÿzV9ÿ€W6ÿ‚Z5ÿˆW/ÿ\-ÿŽGÿ£Vÿðððÿ’‘‘ÿJGGÿMJJÿMJJÿMJJÿ^ZZÿùùùÿùùùÿÿÿÿÿƒð, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿÖÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ žžÿÖÕÕÿëëëÿ‰#ÿØ—ZÿÒ©…ÿÿèÊÿÿëÐÿÿðÙÿÿöáÿÿ÷âÿÿ÷èÿÿ÷èÿÿóáÿöÓµÿêÁ›ÿè»’ÿ䵈ÿâ¬}ÿÛžgÿ,ÿëëëÿµ´´ÿ˜——ÿüýýÿüüüÿüüüÿùüüÿùùùÿùùùÿÿÿÿÿ‚†ƒð, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿÚÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´²²ÿÜÛÛÿåääÿ‰!ÿê§pÿääÿÿíÐÿÿðÖÿÿóÜÿÿ÷âÿÿùÛÿ÷üÓÿöüÐÿóùÍÿíñÁÿÃÃqÿ½¸aÿ½´Zÿ¾¬Tÿ¾¤Jÿˆ!ÿëëëÿÇÆÆÿq”pÿ­ÿ¬ÿßÿÞÿüüüÿüüüÿüüüÿùùùÿÿÿÿÿ†‹ˆð, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿÞÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿóóóÿˆ††ÿÁÀÀÿëëëÿŒ)ÿ¾z<ÿåǬÿÿðÕÿÿñÛÿÿ÷âÿÿüêÿñÿÍÿßÿ»ÿÛýµÿÖü­ÿÏ÷¤ÿ°ávÿw¸ÿ²ÿ…¬ÿ‰’ÿ9ÿóóóÿ©§§ÿ)À&ÿÿÿ¦ÿ¤ÿýýýÿüýýÿüüüÿüüüÿÿÿÿÿŒ‘Œñ, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿâÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿèèèÿ…ƒƒÿ žžÿ÷÷÷ÿ¡Vÿ©WÿÞ»›ÿÿóÛÿÿöáÿÿüêÿÿÿíÿÿÿáÿüÿÙÿ÷ýÓÿóüÊÿíöÁÿêð¸ÿº»Wÿ­§;ÿ¯¡6ÿ¡kÿ©g3ÿÿÿÿÿ€ÿqápÿ|ÿzÿÌÿÍÿýýýÿýýýÿüýýÿüüüÿÿÿÿÿ”’ñ, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿ·†aÿµ\ÿÕ¬†ÿÿ÷áÿÿùäÿÿÿðÿÿÿóÿÿÿöÿÿÿóÿÿÿíÿÿùäÿÿóÜÿÿëÓÿðÚÿÞ dÿÜš^ÿ§Hÿ¾˜yÿÿÿÿÿYVVÿùüüÿÿÿÿÿÿÿÿÿýÿÿÿýÿÿÿýýýÿýýýÿÿÿÿÿ’—”ñ, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ†……ÿÿÿÿÿÜÐÉÿŒ ÿ´yAÿÿùâÿÿýíÿÿÿöÿÿÿüÿÿÿöÿÿ÷êÿÿðÞÿüèÓÿöÞÇÿñÕ»ÿÞµŽÿ°p3ÿ­k-ÿ†ÿäÜÖÿÿÿÿÿKHHÿ}}ÿ|}}ÿ|}}ÿ|zzÿ÷÷÷ÿýÿÿÿýýýÿÿÿÿÿ’—”ñ, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿèèèÿŽŒŒÿtssÿÌÊÊÿÿÿÿÿ·€WÿŒ/ÿýÓ­ÿÿÿíÿÿÿ÷ÿÿÿÿÿÿÿöÿÿñâÿùèÕÿñÛÇÿëкÿäǬÿÖ²’ÿMÿ…#ÿ½”sÿÿÿÿÿ¯­­ÿ)&&ÿÿÿÿÿñóóÿÿÿÿÿýÿÿÿÿÿÿÿ’—”ñ, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿóóóÿ©§§ÿ©§§ÿkjjÿÿÿÿÿðííÿ‰&ÿÏ…DÿÿÜÀÿÿÿöÿÿÿüÿÿÿöÿÿüëÿÿóáÿÿëÖÿÿâÊÿóÓµÿÌ’\ÿ°aÿ•9ÿóóóÿÿÿÿÿ_\\ÿ´²²ÿ´²²ÿ°²²ÿ°²²ÿ°¯¯ÿüýýÿÿÿÿÿÿÿÿÿÿÿÿÿ’—”ñ, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿš˜˜ÿÿÿÿÿÖÇ»ÿ,ÿµ\ÿóÚÿÿùäÿÿÿñÿÿýëÿÿùäÿÿñÙÿýÕ´ÿâ¤mÿ¯T ÿ’/ÿÛÏÇÿÿÿÿÿwvvÿäääÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’—”ñ, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ÿ÷÷÷ÿ÷÷÷ÿ¡  ÿÞÜÜÿÿÿÿÿåâáÿ£Wÿ•/ÿ¾k#ÿ»zgÿÁŽ’ÿ·€…ÿWQÿ˜B!ÿ…ÿ©j6ÿèèäÿÿÿÿÿÄÃÃÿGD¡ÿMJÿÿMJÿÿKJÿÿKJÿÿKJÿÿKHÿÿZZÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’—”ñ, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿèèèÿŽŒŒÿŽŒŒÿŽŒŒÿŽŒŒÿmkkÿ¯­­ÿÿÿÿÿöööÿÊ­˜ÿªj5ÿŽ-ÿ€ÿÿ0ÿ­m;ÿÌ´¡ÿ÷÷÷ÿÿÿÿÿ•”—ÿ/-§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”—”ñ, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÏÏÿÐÏÏÿÐÏÏÿÐÏÏÿÐÏÏÿMPMÿ†……ÿÿÿÿÿÿÿÿÿÿÿÿÿóóóÿåääÿåääÿöööÿÿÿÿÿÿÿÿÿÿÿÿÿmmmÿ¾½Õÿêëÿÿêëÿÿêëÿÿêëÿÿèêÿÿèêÿÿèêÿÿåèÿÿèêÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”˜”ñ, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^b^ÿÿÿÿÿ•””ÿˆ††ÿ»ººÿíííÿÿÿÿÿÿÿÿÿèèèÿµ´´ÿ€ÿŽŒŒÿ÷ùùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”˜”ñ, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿíííÿ›ššÿ›ššÿ›ššÿ›ššÿ›ššÿEJGÿÿÿÿÿÿÿÿÿÿÿÿÿóööÿ^ZZÿ200ÿ200ÿ,))ÿ$##ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ¯¯¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”˜”ñ, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿêêêÿ’‘‘ÿ’‘‘ÿ’‘‘ÿ’‘‘ÿ’‘‘ÿDHDÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿnmmÿ)''ÿ)''ÿ)''ÿ)''ÿ)''ÿ)''ÿ)''ÿ)''ÿ)''ÿ)''ÿ)''ÿ)''ÿ)''ÿ)''ÿ)''ÿ´´´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”˜”ñ, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^b^ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”˜”ñ, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞÜÜÿÞÜÜÿÞÜÜÿÞÜÜÿÞÜÜÿQVQÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”˜”ñ, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿèèèÿŽŒŒÿŽŒŒÿŽŒŒÿŽŒŒÿŽŒŒÿDHDÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”˜—ñ, ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèèèÿèèèÿèèèÿèèèÿèèèÿTZVÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿnsqô$!!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðññÿwzyÿwzyÿwzyÿwzyÿwzyÿwzyÿwzyÿwzyÿwzyÿwzyÿwzyÿwzyÿwzyÿwzyÿwzyÿTWVê265À6;9»6;9»6;9»6;9»6;9»6;9»6;9»6;9»050€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿêêêÿ’‘‘ÿ’‘‘ÿ’‘‘ÿ’‘‘ÿ’‘‘ÿ’‘‘ÿ’‘‘ÿ’‘‘ÿ’‘‘ÿ’‘‘ÿ’‘‘ÿ’‘‘ÿ’‘‘ÿ’‘‘ÿ’‘‘ÿ’‘‘ÿ’‘‘ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÍͱÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿíííÿ›ššÿ›ššÿ›ššÿ›ššÿ›ššÿ›ššÿ›ššÿ›ššÿ›ššÿ›ššÿ›ššÿ›ššÿ›ššÿ›ššÿ›ššÿ›ššÿ›ššÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÍͱÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÍͱÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÌÍÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ÿÿÿçš›› ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÃÃÃÿÿÿÿÿÿÿÿÿ½½½ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸ÿ·¸¸þ^^^Ç_<ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÌÍÍÿ÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòÿÿÿ×qss” ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿåÿÿÿÿÛÙÙÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿ‚……ÃK$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ.£¤¤¢âááµâááµâááµâááµâááµâááµâááµâááµâááµâááµâááµâááµâááµâááµâááµâááµâááµâááµâááµâááµâááµâááµâááµâááµYZZŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ $$$$$$$$$$$$$$$$$$$$$$$$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿÿÿÿÿáÿÿÿÿÿàÿÿÿÿÿàÿÿÿÿð?ÿÿÿÿøÿüÿþÿÿÿÿ€ÿÿÀþþøøààààààààààààààààààààààààÿÿàÿÿàÿÿàÿÿàÿÿàÿÿàÿÿàÿÿÿÿÿÿÿÿÿÿÿÿÿÿglogg-0.9.2/images/000077500000000000000000000000001222324246300140665ustar00rootroot00000000000000glogg-0.9.2/images/arrowdown.png000066400000000000000000000005521222324246300166200ustar00rootroot00000000000000‰PNG  IHDRóÿasBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î<çIDAT8Í’=N1 FŸ#Џ£¢äÓ~:F¢G4HÜ€Pq .°³ ;J—«pƒ¤Ól¤Ñ쀶X\E±¿çωÅÌ8$ÜAê8©‡a^3U}lš&/§”4çüff_!„§¹ƒsà.ç¼N)éâ5p+"§{#¨ê=°B)¥1úš‹1úRJ`«ª5'ÓoL)i)¥7³ `ãœëÆq\W"ò齿™Ž(ó=ØY]—fö "×À‡ªvó÷ÙT˵ëîjãœëÚ¶-óÚEÀòàœ{^ÿ økø›úl”o)IEND®B`‚glogg-0.9.2/images/arrowup.png000066400000000000000000000005401222324246300162720ustar00rootroot00000000000000‰PNG  IHDRóÿasBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î<ÝIDAT8Ý’1nÃ0 E?…ä‘´ pàÍî}z€Þ¡{Ñ¥S)E hãUŠ^€šÄ.va j†xÐ?RâÓÿÉݱGaW÷ÿ<üusæZë3„žRJ¥uZ¿°4è—Ògá±¹ˆ ªRkôî~r÷€¾Ö:©ª\u ªRJ™Üý°¾ «":3óc´ €ªŠ™tDt&¢aµœsfw?.àYD†òÁÌ^tff·ySJ…™G3€ÎÌ^[3øð¾¥oc4¼¹ûWs·èþ›¸ð±×q’ÿcö;IEND®B`‚glogg-0.9.2/images/darkclosebutton.png000066400000000000000000000004771222324246300200070ustar00rootroot00000000000000‰PNG  IHDRóÿasRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÙ\éšg¿IDAT8Ë­Ó1Š1‡ñŸËöž@X½ƒï ³ØX[í ½€s!+±‹E±œÆÈv[ˆM„A“XèkB ÿ/¼ï%­²,½R^¬·:O ½ ƒ ? H%ú)À k "cñÛ|ÞÝR…uØØBø€þs€{H_©pn ê>c çE0þ‡6¦©³)Û·ž'X%ÄFa1±— ‡ï„°&dÆý¨±À>!¬ áM®…Ý“§¿yûgºì¸+?%Ëò¿IEND®B`‚glogg-0.9.2/images/down.png000066400000000000000000000011221222324246300155370ustar00rootroot00000000000000‰PNG  IHDRÄ´l;gAMAÖØÔOX2tEXtSoftwareAdobe ImageReadyqÉe<äIDAT8½ÔÏOÓ`Çñµ[i;7Ì–hB¦‚˜¡8˜àÍ‹(¤£HH nåÄÅB#:¢ÆcÆ9T³ò@<)—rñ/ûð}jKê¶g<à“¼}õÉÓoŸ€ØYt&èÿ‡i•)H´F%(EFýÏ lxO…-Õo…xšãÒð¦÷ËlPØ3ûzwQ†4¼åMá=»)ì¹á^êœ4ìx“øÈ„ÍÚ½!ÜÇCþá=Æ'Ö/¬hwŸv½¾°¼°9û’LK£VÂQÚ=x„¯¬GX©r9:v¿©Q*Ž^ž³váçÁ¿úþm¬²œ°²›ÃÚîTö®b¦˜áÀ/>”ÚëÔúebÕ½ˆu־ϵ ˜_Ì`´D*­r`:˜éx#¬P)ªv×2Pq³¨²æ¾íd±âd°°Ü‰;#4Má©•lÚq€óß3K9÷¬l¸il²+×RXx—ÄØ¤ÝðÑ*<×ÑtÆ­ða+-ׄÃþVuLØ%Cà ˜¦¾n…¶·#|ÄRñ}GCu[CéCã*%Ýi¦m{mFq뾂7oUÆúP>úJ´S©û8Àù%³m1躾<=Žà|Z®Q7‚o‡JÁ #˜÷–—û‰áÓtV ±ªàBIEND®B`‚glogg-0.9.2/images/hicolor/000077500000000000000000000000001222324246300155255ustar00rootroot00000000000000glogg-0.9.2/images/hicolor/16x16/000077500000000000000000000000001222324246300163125ustar00rootroot00000000000000glogg-0.9.2/images/hicolor/16x16/glogg.png000066400000000000000000000020671222324246300201240ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA±Ž|ûQ“ cHRMz%€ƒùÿ€èu0ê`:—o—©™ÔÂIDATxœb477ssvv±ÿñãÇ/†ÿ@À ¤Yi666Ö+W®ÜܲeËr ÷¬œ4ƒ¢’ÃçÏ_Î9Ï@ µµµSAºþýûÆØÀׯ_ÿt±²²rKËH2Ø9X3€lc M¿ß½{ÇÀÍÍÍðúõkûÇŸ >¼g`bbbøûç/ƒœ‚s}}}ñ‰“'¶?|toÿû÷ïÙÿüù÷ä"€b9äTs~ýúÅðàá=~VV þôé#ÃÏŸ?˜ÊJKæ|ùúõ3P9Ï©3§wé€@—5ÿfxüø #Ðf77w†³gN3|ùò…áÛ—ï Ÿ¾þ`àUÒ14fPUP`àæçÕé ¸ ðíÛW°&†ç/^1lÜzXoøØþ0ðýúÇ´á—¦ƒjf&ÃFÆ =7 ìì b" ÿ 1LÞö˜á›)Ãiv-~›3Ä_¾f`¹}‡áä)v6°>€‚ °Ÿ?¾2¼ùÎɰç#C[–ƒ#ÃÅ»¿Zœb`´÷aøvÿ8Ã÷¿2Í͘¶¬ë &X ‚\ÀÃÅÁð샇½,Xó{†¿ ,ò?XMþ0´nºÌÀ¤ÍÉ`“j Öø÷ï_0 @ ü†¹‚èŠ_ß1|þúŸá-0YÝ}úŒáøÛ ?ž½úŸ‰áñå Þb±;Øb€¥`2ƒ½ðíûw`ÔýeØ~òƒœ´(ÃÁ­Ç=~Êðþ5ƒ‘Šß— Ç×bPÐd`bf@L‡>yæÌ™W ΋Wï„?dÐågØ|à6Ù;o®<ú )ÂÏì*Âðæí+1I1†ï@‹¾|úÂÒ@Œ BPPÐ~Μ¹sÎ;¯ÂÂÌÈ`aeÅpé;Ã7øÙþ1x› 0p²ýeÐÒÑe¸tùÃׯ_¦Ï™¾qýŠõŽ…OŸ>œ|x½ûÝ»—p9m{ff†ÿÿ1¼xö’áõë« ÄâååÔ××Qöùó†oß~´XYYÜÝýN:¹zÛ¶UKñ© –ÿÿ¾zõ…á÷ïï _¾|c`bbb`ccJý+øÿSÓ·oÿÌÌô噘r&ðð°)®Zµ´¨ò76 ˆåÿÿÿ@†|ûö“A]]žáÓ§o ÇŽ]ül;;ŠÚÿþ1ó2xxØŠÈÊÊnݺ¥‹‹[(ÇÈðçÏo°šÿP º Í?þ‡õïßvî8Âðõû`Ø2#Yð—áÉ“§ ²² 7oÞÖÖ±LebfýóïïFøûï/ÓkÆ@åwÃff«ÙÁøì¹‹  66¦ ÿþþe`aa…«YrŒ¡¡†|T”÷FF&†_¿AcdxûæCd\f9PY@aXRÀËËŽÈ¿@XÙØ¸¹8¾}ñîÝ; 838¥€,#H=ÐpPÊd`cge`gãfàáç› @üøñ“áÆ{Àˆfgxþì0L4þþþ˰bÙf†¹ó2üùý›áÅó· ¢bB l@‡üúqÔ×ïßÚšêüÝ­ÀAø÷ï?p$†ì JJr@_p3¼|õ’áñƒç —…î1ˆËª1Õ0ücúÏðï÷GÆ¿$Y™dØ~ýý6T‚„?2SÜ<€Â°ÿ¬ —/ßf‘eà’ghZü’áó‡Ÿ b?¾0¼øËÌ ¤*Ê(ÃðïÐ.æ•Ô¤Å!±ÿñ CGƒ®6<}† qP*’—føË(ÊP<ùCR‚C0ÃW ¯¯?üÁ0ãC†øÜƒ sÛíö1³3ˆñó2ü&OqaAs#-†/?áæº tâøE6.>†ªîó )©Z þ’ 2Âl œì Â< žJ ÿ¹¸šú®0([24ÏXÎ0gÕ†5§Î#„‰äxh©@>ø LŽÒR çïübxüŸÁÚ\ŠáPÃ_Æ Ÿ¹~1ü‘úÃðþç{^n†Ã;Ÿ2ܽûÁ&A—A?V—Áç¯Ø pÎ…@|ðÙ‚W¯Þ}ÁÁðäî;%eq†ï?™ÞƒöÍßÿ o™~3|bÿÃpâÓ†_b¯¸5žÜúÌÀ$ÊÂpáïu~np Šðßþ‚Í þ"[ÊÁ << /¾cxüâ/Çû_n_¾Åpöò#†÷¿03q2||ÌÄðG hæ +ü`¨çŸÀÄ ** ,2 ưøÇÂÂÌ+ÜddÄåÑg=1†÷7Ÿ2ºøžáâÍß §.¿e8vâ>Ã맯x™žþýƒáç“W Ɔ Ÿ¿|d`bøÍðæÍk°AÔ`ˆéÓ§/¿>þ Lšœ` ~þüÍðüåA¦× Q.² ‡>a¸òøÃÃgï¹÷+ƒˆ/¯7Çw, )‰¶ ÿ?Üa¸~ó6ƒ ˆ??Ãq`ùõë7Èp ¦%Km¬«+ûùóçŒÆÂ )Éð{× †Î'`ß¿óå˯N^~ͰãÀC†u+/0˜H²2D¸ðKÞ‹ ¦¦ê ß¿}üX^11|æêï_Š‚Ì `}ð÷Ö’% «¿|ùütÆŒiq7î–éé™Ç¢©©Ëpæø†DV†ëO9ž¾fc¸#)Á +ÎLe\ j"Ž>Á–,UEÖoÞÏÀ, ÿùÃÐÜ>X£=}² €! ö?Èfq))ÕùÜÜŠž’’r ŠŠ /øíìlÏhmiÁ ¬„¾ÿbf`eüÉðõËW†ßÿ˜¼¼l„„øÞ¼}ÏÐÙ³—ƒŸáÜù‹ ÷žÝ¾ÏÉÇæxrÏú‡Ä\^NN™V!! wYYE`*f8ztë•Îoœ=»·ZII‘áÅ‹· ïß}aäeba¤ÜÿŸá°˜¸wë1Ã…Kgh›kGqð²>É(™r::úÎQR2 {,Çyy9XsáÞ½‹!¼|²¼{ö(6ûüã°ø†&0³}ýÄðè ¸ìÿ÷˜¥€ÅôÕ›÷X/]½ÄðêÅóí>¾«fbf¹þç${ 33«VMM½§ŠŠ*C}}/Ãöí‹v¿|ù (÷Xö³5778þýÏ Lbÿÿ12 µ: ÉX3þûÿõÓÞ¬œôWG¾¾»üþóOäŠ €Xþþý}£­­q«µÇ¾}ËV ¯Š{áÿ¯Ÿ¿¾ž¥:F-PMLÜ BüüÀxÄ,œ Þ‹í[Ô´vëIEND®B`‚glogg-0.9.2/images/hicolor/32x32/000077500000000000000000000000001222324246300163065ustar00rootroot00000000000000glogg-0.9.2/images/hicolor/32x32/glogg.png000066400000000000000000000056501222324246300201210ustar00rootroot00000000000000‰PNG  IHDR szzôgAMA±Ž|ûQ“ cHRMz%€ƒùÿ€èu0ê`:—o—©™Ô 3IDATxœbäää‘1006àæfgüùóïFF’ 3ÓӧϿ޹sý4;;÷§oß>EÿƒåØÙ¹44­þýûÇÀÄÄÈðýûO†×¯^1¼{w,@,¦¦¦îë{ÿþ'ï_þ±±±Á5 89ÙX<ófÖ¬ÉÍÏŸ?^vüøþoÄê öOŸ~ð~þüh;ÃýûOÀŒ$…œœ¨\FFVkQQ±;ˆ¿£ €Xþþý÷÷û÷_ ?~üdÙ) À *&&&0M,`fffÒ›3gJáºuä¦OŸÜðùó»g„ô ȧ¬¬, _¾ü†ƒ’’XâÉ“× oß~Å/˜R‡+TþýûË *"È &ÎÇ .n $((j½aÃZn då´þÿÇ¥Ä"@ò ƒhbE‡g8sæØQÌÌL ¿~þaxÿñІ€BÏÖÖ!0Ðì™/^üåã“æ”ffe`dãüú’$€!Êøó×ï¿@k¾2@@± rÈ£>>V NNÆ ll¬ ?ýb8xàíÛ88Ø1C¨éÓç ËWl{äíÛwJ:º ´u5¿U2Á”£‰åéÓ—ŸOŸ>[ä^‰†`fŸ>}“áÊ•{Àx`¸tù:й_jkrøùxq'àb#lÇŽ_aˆMLÓ†9 €p†€––<ƒ„„77/+Õk7þýýGŒåX,jA‰•‰n@a #nÞ|ÄpëÖC~~^†ë×o3üþóœ@àôé« MM} þ|4l`C¹‰á÷ï? ȱó÷ï_`š`g()Ég°³3„ˆì$†`Ž¥~P¡ÄÁÁÆ¿ éõ› O¿fxñì%Ða\@u¬ ½:ö8´Ô5~üûÏðfÐ2&6†ìl8C €pF¬¬Ðì ""@š‘aõš ï?fàæáeRPf¨¨¬+deæžß_>~øÀð÷ë/=AÆÿ ¼‰0ˆÿ¿}ÏÀ(,ˆá€Â™ïßÊpïÞS`ŠþÌðòÕk†ô$F)†É«2½p‡ëÉ †ß¿~3ü:ÐÍAž!%FƒáÛ—; ›³ëÜ®^aæ°#þüf`ø ¤Ê²b€b¨öÎpp0Æ›0ûe–±b¸ñ€!©ä(ƒ¾™Cm¾ƒ?3Ã{`Årâúg†Õ;Ÿ1lN?Ä0·ÝŽA¦¦€aÛŠí aî6 ì ó!?10¨Ë"ÛgÎXµj?Ãѣ缼Ìî?ýÏZz˜!.ߎ¡9IœÇ@õÝ³ï ¢ªÀz˜cÚ[w3¤F­dX½1‚ᯧ Cúѳ ìlÿµßw}=-†b ;Œà€bB€•š::Š ’ J*Ê }gÔ%J£å¸€r¿>øòˆßþcøüî;ƒ Ë_ †'™ú&ap0ÔbxvîÕígî¾ÂðøÜM$¢Ú@XsØe@§ÉH 1¿ö‘áЫ 51ª  î5Ð`Mÿx$ÿ1üdføõ†•Y‘—A1HŸa÷•ç ç€ÑâìeÉ ¤Å -§Æ óG\_011cØ@ % XΖÿöì9ÃÀÄÌÂðèîv`­(-ÁÏð XÁ~ûK`â:Ëðá,ãs†ËìO6}?ΰóß&†ʧ>p=fxyï¯?CÈû †™L«XYX¶@¼*þþC¤D€bAr3ÂŒÀ(Pfàâ`a¸yï50*Dþë•߀… #0xä9˜ù^|ÿËpþù/Gn†ß¼ì ç¸ï1¼ÿðŽAKeor’BÀµ¨ VðÀíØ0@0übxi Pá".ÂËÀ/!Ìp÷þU† —%4äÔ8y>¼øÆpëÒ}†;·2œ¹øˆáõG fv†ŸÙ>>ùËÀæÂËðð›.1¼\ô„á3°Š `ðõudø ,-ÿýEä€Bx"5DÌÍuÎ¾È §¤ÌÀô÷7ÃÙóO45äd…Y%á;†ÛÎ0<¿›áÇï_ÀÐådøÇÌÉðö#çOn-Q†Ý«Ÿ0|øõŒáÓ½ç@_£î ¤ôÿÿ!Ĭ>988ÿCâ7ÿ€Eêå«w ½íüõ\}ËpíÑw†gÏ?3p³1™ƒ¯3Ã`ܾÖîo¾þeX»åƒž83ƒšØ?†½Ll }ýÍ RRbÀ:ã°]Á 1÷ÿ_†[@,Ož<û°}û¶'ªªêê||ÀÒ\H0]ü˜ÙvoÝËPSêÀp­p?ÃåsOì^<½Ë0gÁ<†??20³³3|ÿÉÌðæ 3ƒ†ºC[{,ÆÕ˦Ϙðhñ|p}òíÛ/UUm†åË'3pÛÌL,Ü0óË—/ž<¸û¬¬œ’ŒŒ¼´€8 ÄÄxŽ;ÉðøÉG†èp{==)†½[/3œºöŽá°håæ`WdøÇ&Åðô+ƒ­Ž ì9‘ Oï]Ö–W€qî,ļ<œœmÜÝìLLõ9ɰs÷Á¥…éGA  ­¤_?ïîٳ뾬¬¿µµ¥Ì£GÏþååÕ±¬Y³Xr#Æ £&Äì¡ÂðíÝg&`è¼ù+ÀÀÂ&Ä`¥&ÍàëgÊÐ]gÏpóÜ1†.0¤§‡3xx80pñ sÄ`;QœX7;qaѲµ ·¯]™QS]tä€b„ä>HTRR´ÖÔÔÊüö#àÁƒ÷Ü’ ÊÊ*Àª—áòå3 .ή ‘ <¼< ÷^ü§X1v†À¶â¦Í‡ØØ9ÂÂ\¤¥EÁæÍ_´Ø®¼É /-ÍpïÁ†KW®2<~~ÿ3Ë?¯7Žß© ˜åŒŒ,ÀÚïþ÷ï ¥¥µ¹@˜‘‘‰aÍšo,-µ>u^µz7#¨œ¦†ŸÀJfï×/àv£¹¹ƒ¥•.°Â oýðñq1p{F_¿þfxýú ÃÓ‡¾yÆ9wܸpë>, ¼(æg99³¹²²êò<<\@_È[»_­ãÍ_¾¼Õ.-ã_¢¥¥ b` LÕ& –´´F-àaÖ·ï¾0<|ô”áÏÏŸ ¯žùòëÿïBaá…Èõ@ÀÅÅãeo4…EPüçÏ@Cå>|xL„›Ï¾xq/‘‘ïÞ«×_*¯]¿TËn‚,ÿ ¬ëoß~ µRˆ€ú€ÿ€Ùù°Ü>vâÃ[W¹àÓ¥Ï_>Õ±²poeuä~@ YÐÝ=Q|ýú­ §NÝ`xùØè8ºqׇ/óò7øøxY?ÜsÕXnüðìKpŽeDr¸“òÿïŸ?ÿ~üúÍõ˜˜""ü.nذc'0*ï2`rÀff6))N†Ã‡+¢åËþü\”{5ý÷—ÏoV<{|o0vñ7Á](†ÿL̬,\l\ŒNvV¿×­ßS9@ðz͚œ½ZM...΃wNZÞÁisÀ(ÈY9¹þaTè˜.†(2ÿa`bÿw¼: òŽ9† µÍ2IEND®B`‚glogg-0.9.2/images/hicolor/48x48/000077500000000000000000000000001222324246300163245ustar00rootroot00000000000000glogg-0.9.2/images/hicolor/48x48/glogg.png000066400000000000000000000073721222324246300201420ustar00rootroot00000000000000‰PNG  IHDR00Wù‡gAMA±Ž|ûQ“ cHRMz%€ƒùÿ€èu0ê`:—o—©™Ô…IDATxœbüÿÿ?022*)Ú‚Ï@û^RÓ@€;èx½%K—ØÚÚ SÓptPYY~hW#Щe&@ÁB\ ::ÆHKݺu‡áïßßÔ2hkkò) 'n=q‡f4 Ùé§gÏžùÿýû÷ÿ4Ÿ€øzqqá < »)Å„’æ¿}ûÎÀÁÁfïÛ·á÷ï_T $$À Äê¯_¿ý¤ƒ1qèˆ#”@(1pøðap0b‚ÆÍÏgÏžµh·%1@LØ<õ÷ï?Š…Šf ##SA Õ Ä€"ªØ|÷îÃÚµë(± RSS(6Qøû÷/0sSÝrj€b¥#`fååGŽ‘²±±a8}ú4ƒ©©)AÍŸ>}dèèl'Ûò»wï¾¾ÿÞ5V¼åö÷_¿Žž?u¾›@]ó¾|‰Yæå0,_¼’‰‰ìd, Äö„•ä[*)©Î½wïöct9€"Ê Ç/Y² CüÝ»”8žh ¦ªö÷÷flrD”ÄÅÅ0Äž:L¡ÓˆñÈÑIHYYCŒ—‡—÷PQxýú5Ãòå+0ÄŸ={†!–——ϰk×n²ÔÛÛÍàííM´z€"ÊÌÌÌ œœœ˜âLŒ(üïß¿0¼¶`jþüÅðë×p1L,øý›´†$@å!!!¬•Б<0uê4`‘ú™ÁÄĘaòä~°zeþ3g΂‹e^^>†ÜÜL66’HÑyàÂ…‹bïß½ghkkg¨ªª„‹}ùò…áéSHÒ&/0¼¼<†Œ€@-=Tƒþ#5]þc‹ XàˆŠå.€"ºÝ»wŠØýû÷b“‚‚Àµô¢íWæî}Àpúð†§Ÿ@®b``°¶•cHs—gsÖf˜4ià `l]*(dˆxûê`dS¡IMèøË<ˆÛmDt1š ç¯\¹ \¬‚J¦GŸ1Ø–îcxtヲ&Cº¯ ƒ¶ÃçOßÎ\Æpáêk†ø7¦úÜeXWgÏ‘ÅpD_ŸaQc3C†/@L!~4àn\ÑIÖO?~‚¿çÄu×Ô Bj ³¦0x™ñ0ðÝô hßÏ? !?n?ùǰóüS†yÎ2ȸ¯b8¾À“ÁÆÆša³‘ÃÓ¼\iiib€Q¥ë-[¶Ù‡fÚ)ÀZø#ƒküf—x7†šp3ePx‚º@¿€Qþ÷‹ñ21xè ó‹,Ã~^Fˬ] ŸwI0tvv0ÄÆ&0„……Bmù&A–ŸŸ°†ÇZñb€"Ê_¿~f̧ ÏŸ?cHHH‹eM>ÎÀÀÉÌé§Ç  t<¨@…Yùý+ÐÓß1|úüƒáó×_À<ò“ÁL‹aÛîû ¯¾14,<ÅГíÄÀPRRÊðþý{$Ûþ3¸¸ÜbàáuŸAIS €ˆÎÀ~,¸Ä155f8uñ.ÃÊW#5LÕ˜ÀöügBTùÀ¨øóŸ ( ô3+#+ãw¿ÉNZî/CoÛI†dup0{ö ™& 0Ý `aa;þÐ\BM-€"iè×/HyÆ¶Û BÀ ëj¬.¿cŸ™ âP,p³ 3³cˆ“›“ç'Ãýwÿ ô€füc¸|÷Ãâ=·Ú”¥øøøêë«€•%j†þGd§ €=€³º£³gÏe—Wfà ;/¼b`fä`åçdøt<ÐÕ¿þ‚jl`ÒªÖ½ÀŒü›á;ó &!Ö¿ {\`xÂþ™á7#‹ò/†]ç_3´Õjjª3”<­`à’ãff`0øgµÔç…z OM@ÈøƒK777¸9¡¤¤ÎÐÏ.¿f° 4bäa;hŒŒŽîæO ï€ðи @O0þd¸ôñ!Ãî¿À<à TLN ê, g—³ÍÒÐÐd¨¿ßÀðNîƒ0¢8~áñ@åp‘jß0üøË`¢«ÃðãÏ/†ß@³!ÏÕÍ÷—#lÀ8øn}&»³¾0ø3¸1üd•RŒ ߀-m/NcS^^ŽÁ뜃½ ƒþGr9H?(àþüú…³* ¢<oÞ¼ç~pÁðêÃ;†‡L¿€ ip €Òþ ÁdóþƒÔ ºàÅ£ï ZÿÔIé÷¿¾¿úõ¦ hÀ@Y!ðŸ?R¨ÿe@ôß¿q; €ˆò(\½z…AJJŠAQQAžŸáØ©K ,Ú‚ N¿t~=ÄÊ©îßzÀðöÃ[†×¯Þ1ì9v†áõ›/ Ù€žc&`ÉÄÊÎpëõ)-QVVVP¿˜aÛ¶ ›7o…Û' È0þl„Ãð$!€BöÎv,¨uwwg¸qã&vep7•`8ñT˾fxðöƒ0ÐÀ¢”Žž»ÈpøÐq†×®AÒ.¨BbábøÇ.–€ln†Ÿ¯xÜãuØÙÙîܹÃpùòepr‚pÒa0øûg 1€©ÞÕÕ…aãÆM`v”<ÃÎ=§¾ðì Ãýÿß9YØÙ˜5ôÔ5€ŽÚÍ Ä_ÿgø L1ßüæ†O?þ3t6ïd±–6­ÙžHg‡˜ÿؾþføüý/ãÏ¿ «^cPÆ »¥:°EûAPPAAÑ]eƒ7ð]†;î`ýÄß@ÌL( P¾xñ"ƒšš*°ê/gX¸pÃÉ>wûy ߀!ËÌÿÿÿÚÃÄ0oñ"†ë—.00Cí/¸6þÏ LBÌìÀLÌÁðæ&?ÓÉàž2e2¸³£«k›p°°°€iccc†¹sgAÝ;ÌÏ´´4ýøñýåíÛ·Aã4RÈŠ`M  Zzܤ(®·ch¯_ÔãÄ & ,>ÿÛM¿>ùÁðïÏp Ü&cúÆðö7Ð#À´ßÖéÄ ,'ŰgÏ>†ÛX ƒ<ÊÔ ä¹OŸ>!R°d”2 { €À–½w€û€L`•.ýåË'P9r`EV¬¡¡Íà !!æ÷d;2˜¨‰0Dfnc0 6b·WcˆˆŽa`‰g`…¶a@õÄìÍ—Þl»Ì°u¥ƒ° J’ 6Ж-›€u JXawÜådæ`–Ã&@ŒÈÐÀv%8Y«¼xñÂòò qP äxOOw†«ÿÆÆpzõî?~ÍàP²“áÑÙ— \l üê îà?†7^ƒÒ&ƒŽ4þNOQ~p¹ßÜÜÂ`nnÁ·û10&¸y8Á•&´]ôôå3†ù f3ìØ¹óÿý›OŸ>}„15@(@òˆË·o‹89ùÔîܹËâàà$ëããÅtãÆ-YYY}}]###†;w23˜™AÆQA!{ýþ †µî“#Cˆ"ƒ²‚8¼D9~üÃĉ“±Ëàåå·ïÝÇw Ñ ± Ê*@ÿþØ/†Ÿ@³X˜ÁùïÓ§Ïï=Œµç@¸Z£¹¸øA=˜ËÒ…>>>L·oß¶…”TUUÀéVQQè˜)@ Þ‹×ÂÂ|pù­£" ÆÈàñã§ 6l`8þCww0Påy8y´Õ5‚ýCÀ± ê€ê`*³Oÿ³21OÁáN€Âê`¬¼ÆÂyEE•>77Ö»w¨(.Fåääúû'CüÇ`I!²sçn†´´L^^^uuU°ç@ÝN#@INPÜæŸ3g&Ö±TPRü¬GA5þË—¯>|xvü­»·þÿõõ~ÞĂɸ<@X=JBêêZÛY@³–jj* bbâ`ǃtóç/Öž¦šY9•‹¸¸81€0¨­tóæ-pó€Øl ç¬e;ÍϽÿîø»÷î0¼yÿú“£µ³U„NØ7\úÃ@Çûéë­v¼™€éìPÛd°ŒŒ,Ãôé3€¼Õ Œ¥ s+ã+ÈzAi]KKŒQšÀ­;7ÁéþÕ«—àäyëîM†÷ŸÞûÅü׸££ ïÄ8@¡xèø33Ëeææ¦Œ 4rh˜Ôn==½ÀÐyZt|+L¬É ó9àÎ}È”ñ£Ç®Þ¼òÿÛ÷ï7-D‡ó§NœÕ ¸€Ž°¶¶[¸~ýFPÍÅÅ e*qq1†––¶ÿ_¾|,:~Âñ O»ú»”D„@ `p?Ô9¥òÀ 2t IóŒP¿ýÿûäéÿ Òæ×¯ï _!ëÞ-ÖG³þúýë/0N<ì| ±ž x1 ô@É… —ºED„ÁiyÕªUà4/,,ÂP] ô?)@b²f]]!¡/¿xýˆµ àääÖdaaa ÊQQqà¥ß¾}û¿wï> ¨EæCÉD4­1@¡rRØØ8ßÉÉ)‚8§€Øa HD®ç)ÈœIEND®B`‚glogg-0.9.2/images/hicolor/scalable/000077500000000000000000000000001222324246300172735ustar00rootroot00000000000000glogg-0.9.2/images/hicolor/scalable/glogg.svg000066400000000000000000002026521222324246300211220ustar00rootroot00000000000000 image/svg+xml Jakub Steiner input keyboard keys peripheral HID http://jimmac.musichall.cz/ image/svg+xml glogg-0.9.2/images/minus.png000066400000000000000000000006551222324246300157350ustar00rootroot00000000000000‰PNG  IHDRàw=øsBIT|dˆ pHYsvv}Õ‚ÌtEXtSoftwarewww.inkscape.org›î<*IDATHÇí”1KA…¿É…Hb“üí‚¢½„T¶þÁ_&é"ha‘Jý'áÂÝγػÛK›˜Êx0»÷fß ó8“Ä!£Ç£èö>€™YÿUH*L/—O_“ñܤrG¸ÇÜjå9T<¹ƒ„ar¦£ãåôõíÚ{˜Íüf±€²„"ªâRºa›[–`¶uþ¸»åìýÓúî!εZAQDÔ´j”eâôz0ÄófQ(„´w‡,ƒ c #D7C65F", ", c #ECE0AC", "' c #FCEFBD", ") c #F5DA8E", "! c #DAD499", "~ c #FEF6C5", "{ c #FEF6B7", "] c #FEF6B1", "^ c #FEF6B2", "/ c #FCF1AC", "( c #F8EDA6", "_ c #F6EAA0", ": c #F3E59B", "< c #F0E295", "[ c #ECDD94", "} c #DED5B0", "| c #B4A155", "1 c #E7DDAD", "2 c #F8E7AF", "3 c #EFD488", "4 c #DBD59C", "5 c #F1EDC2", "6 c #FEF5BB", "7 c #FEF5AB", "8 c #FEF4A4", "9 c #FEF4A5", "0 c #FEF4A6", "a c #FBF0A0", "b c #F7EB9A", "c c #F5E795", "d c #F1E390", "e c #EEE08D", "f c #EFE39C", "g c #B5A255", "h c #DBCF8E", "i c #F4E4AC", "j c #E1C57B", "k c #DAD398", "l c #F8EB9B", "m c #F5E898", "n c #F2E594", "o c #EFE190", "p c #EDDE8C", "q c #EDDF93", "r c #BEAC5C", "s c #CAB964", "t c #EEDDA5", "u c #D4B66B", "v c #FBF0A1", "w c #F7EC9C", "x c #F4E898", "y c #F2E495", "z c #EFE293", "A c #EFE192", "B c #ECDE8E", "C c #E8DA87", "D c #D7C875", "E c #B19C4C", "F c #E1CA90", "G c #CDAE60", "H c #FEF4A7", "I c #FEF4A8", "J c #FBEFA3", "K c #F7ED9E", "L c #F4E89A", "M c #F2E597", "N c #EFE397", "O c #EFE296", "P c #EEE095", "Q c #EDDE93", "R c #E9D989", "S c #E9D98E", "T c #B29D4B", "U c #D6BF80", "V c #C9AA63", "W c #E3DB95", "X c #FEF4A9", "Y c #FAF1A4", "Z c #F7ED9F", "` c #F6E89C", " . c #F2E697", ".. c #F1E296", "+. c #EFE396", "@. c #EEE197", "#. c #EEE098", "$. c #ECE097", "%. c #E9DA8D", "&. c #E8D98E", "*. c #C0AB54", "=. c #BDA057", "-. c #C1A35A", ";. c #E4DC98", ">. c #FBF098", ",. c #FAF1A6", "'. c #F9EDA3", "). c #F5EBA0", "!. c #F3E79C", "~. c #F2E499", "{. c #EFE297", "]. c #EDDF96", "^. c #ECE198", "/. c #ECDE98", "(. c #E7D788", "_. c #E2D38B", ":. c #AF9044", "<. c #B59956", "[. c #F8EB8E", "}. c #F9EDA1", "|. c #F5E99E", "1. c #F3E799", "2. c #EFE092", "3. c #EEE192", "4. c #EBDE8F", "5. c #E9DC8D", "6. c #EADA8F", "7. c #E4D488", "8. c #E1D07C", "9. c #E2D179", "0. c #A28C50", "a. c #AC9054", "b. c #E9D752", "c. c #EED661", "d. c #E6D15C", "e. c #E3CA56", "f. c #E0C656", "g. c #D9C150", "h. c #D8BE4E", "i. c #D5BA49", "j. c #D5BA48", "k. c #D4B847", "l. c #D3B745", "m. c #D1B441", "n. c #CFB03C", "o. c #CCAB35", "p. c #C8A72F", "q. c #989898", "r. c #909090", "s. c #898989", "t. c #828282", "u. c #7D7D7D", "v. c #7A7A7A", "w. c #787878", "x. c #767676", " ", " . . . . . . ", " . + + + + + + . ", " . @ @ @ @ @ @ @ . . # . . . $ ", " . + + + + + + + + + + + + + % ", " . + + + + + + + + + & & + + * = ", ". . . . - - - - - - - - - ; > , ' ' ) = ", "! ~ { ] ] ] ] ^ / ( _ : < [ } | 1 2 3 = ", "4 5 6 7 8 8 8 9 0 a b c d e f g h i j = ", " k 0 8 8 8 9 0 a l m n o p q r s t u = ", " 4 8 8 8 9 0 v w x y z A B C D E F G = ", " 8 0 H I J K L M N O P Q R S T U V = ", " W I X Y Z ` ...+.@.#.$.%.&.*.=.-.= ", " ;.>.,.'.).!.~.{.P ].^./.%.(._.:.<.= ", " [.}.|.1.2.3.4.5.%.6.7.8.9.8.0.a.= ", " b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.a.= ", " q.r.s.t.u.v.w.x.x.x.x.x.x.x.x.= ", " "}; glogg-0.9.2/images/open16.png000066400000000000000000000011371222324246300157060ustar00rootroot00000000000000‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs  šœtIMEÕ1$² ìIDAT8ËÅ“¿kTA€¿}w¹w¹$"Á#rX™â ZD¬ M-XØ 4–Gj;ùÄF°{ R "BÀ4¢ø#xg./wïÇîÎŽEBÌ% ¦Ø¯øf‡™ slo.Ÿ.eÖÊË¿lo.OTªç•jcá(ï÷Ö—­•G§ ÊÇÞµ¨\_¨M]‚¥Jcµ³5v¿žPm~¨4ïÝ.ŸPj¾;„âxb<ž¹5Ñ@I¾½Ÿl·Ûóå­Ç7€ËIà]ÚÝ`o¯ƒ‰ª§ö<5}…¸Ra§—6;e€jµº:s¶1}T¯üÄ Þâ]Ž·Þ9’tœõ·¯h]œ'ÏR¢Ù¹Åµ@ü4Ë‘9ÈÈEž( ‡Y‹û8û“AÿI’ì l¾ûĹ] îp8â=Î9¼¼@Ð 9##†î÷/¤ý#‚Ù¹Å5ÐR(!>àœGœ ª%`„È”©×Ïô¶És÷[ÐÝñ7½/Q¼‚WBP4(ª5%Ô”Ç)ƒA6¼£±¬ÛÙ L¶œ·ø¢@B@¼G x7@ƒCTH33,h^XâóúÝgŸ¾žç9Ef)¬EÄ£"¨*ªà"žn6}r_¼¾þPDÞdYFš¦XkDä— ùïñ ÷ì ŽZ›IEND®B`‚glogg-0.9.2/images/plus.png000066400000000000000000000013051222324246300155560ustar00rootroot00000000000000‰PNG  IHDRàw=øsBIT|dˆ pHYsvv}Õ‚ÌtEXtSoftwarewww.inkscape.org›î<BIDATHǵ–½NQ†Ÿo2Å+ÁŠÂðè”X°X›˜xt–ö&Þ‚ZàRAˆ­&&F1¢îº‘(°3s~æç|³‹ËO`Aö$'“™3ßyæ=ï{N&VUFi""Ã÷:ba<ÊKÑJôjâÅDSQ‚‚dEZ:§ ÿ ™|9Ùü4ÿ¯–‚œR n-Ï6EDÎS2’¥]~& =\0£–]àIu£)Ž1 rR,)NíÕ‚”z2œø1P2RŒdøËŽç|8¥ìõ’bÅàƒ‡_õ˜ˆ„ÓŠéŠâÕ¸5µ45?ÈøaÞ èžÒøÝ@"0Q†ÕŒ<*ˆv"ÂÓPêõÓS:Ø'±ˆÈôÒôü›»¯±j¨4§”’J*”€Öòо'§–ýÇØ ]6˯løM¶üÛ¡c:t²íÍvSDäp‰\Èø’¯“i‚‘£õrXɰ‘ÅFõÕápê¹ofyç¿óÃíCοîaFfNzPhA0¤Øa$ÈÁa0ýÉc©ƒ•ÌÙÿ ì|bwÄUYa4…h½è+Ë÷Až‹ @üë÷/†÷>0Ü}õ‰aÛƒ_ 3®ýg8õòß?ÀxÇãÿ ¯@ÂäL¹¿ F’ÿ|6gCÍf F ‹Äž}f<÷üÿ #‰¿à0yéó—/ ÷Þþ`è¸#°þjbã:)HîÃÊ, F" ‡| ^ùùçÃŒ ‹/33\=¹O}SµË-€Ù"v÷ƒ‰Ç? Æ¿`—~éù7¿ãâpÃÕ€n*úŸAá0',¸ÃÂðýÉ— ï~ ƒ,ß_eÁ œ¢ ¾ l@ „ß~gÐRðë÷_†w>1d]’`xù‘A Ùç‚þ3\ýÏpÔ÷ÃBû¿ ¬LßóÃÎ'Hü¿ÿäxeå±?@”2øÉ ÇÍò4_¿}cØõš‡áæ'&! k÷½o ÌŽAá{™~£åÙU÷Áá Òÿ÷ß?  cÙ¸µAÈÿÿ¿þdcgú L)Ì@üa8ø–¬1Iý?ƒ($µüŠÛK01ìõúÃpï3ÃïŒ >}÷–‘áÂ[ppBàÐlÀTÇÌ) "€Yð$ ²ùß&F&&†—?™Á(ó1Åþƒ ÿþó+0R™ŒEÿ1Ë0f^ Òÿ¨DÿýÇÈðë/Øf€Yðèågï¾3(ñ]ËÌÌ Hª¸ÿ ’þ™–þB ÌÊÌÌÀ,€@4а@®9â7PdÁ‹/Ì ?¾}y2 €@qð¨ææó/L ¿ÿþe`bfb°ý¶`ám&†×ßÿ fge»úPÍ÷_¿ÁIdø×À¼òõ×?Áb€ÜûÀÄðíãÛÛ ( ŸY˜NÝýÈÌ<¿.cb–›\ÿ©©û˜f. ¡_¾ÿdøò ”ÁŠN²‚Ûƒ œ“A©†¯¿eaøôòþYPð(ˆ¾ª 3ìÚq›µ^S‘AWâ??Ã<« !8޼db°ÜÊÉ`*ÂÆ Éù—á,Póõtúþ3m ¸ó•áÂ+V†×g7ìÕöA€Ãßÿ—~ÂÎðñë&fs±ÿ Ç}~SÎ?†O¿ö>gfXr l8PW“ñ†i6ÿÁÁ ûÿ`|ð ÃíS»ûžï›rd@1B ;î/¿X<—0¼÷WþÄà¯òƒAˆ—›•…™‰‰‘áîgf† ï9˜‘A˜‚Aùœ|ðù»Oà ù`Û†Õ·øÎe³ëþÿó T } `ÄíøÿX™°°þù¤¶á.Ï­?@—ø)}fà†¯/ƒ*°Tçg„$Mpòd`xÿå;зßÁJi[†o¹ÏÃð`Y¶ÐðçЪ” €+Pâÿ¯ÙõJ‘•W쎽ÌW;é¯ ê‚À>aF>#$½ƒ‚”Z@ßþÀÆpè)7Ã'Ü ÏVfû½?4í$Мw ™ @èU&#ÈGrƬ2雳ÅÄÅúõD~-ùÉ Ïû‹AŒ ’|_}caxø™áÆ;v†Ko8ŸÛÝ÷t–ÿüÿÁ.ÿ3ˆO%Å$³G…UHÁ—‰Ç–X¶0±rJƒ²à¿Ÿ_^üýöööŸ·÷Ï~¹´aÏÇCàý ”’ €‰¨ Y •”͌ԄùmÆüBv52 P 3ÐÍð¹]ú‚yIEND®B`‚glogg-0.9.2/images/reload16.png000066400000000000000000000022631222324246300162140ustar00rootroot00000000000000‰PNG  IHDRVÎŽWgAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<EIDATxÚblhh`¨¯¯ge``bõo ž~b0ûø“AûË/ùŸþÿþÇpã÷_†¿þ2¬ÿ¶§ñ$êg Æÿÿÿƒ ‘b½ÓOB¾þfˆãf`áb`àçŠþg`xýáþ{ % Oßÿ,Ò~¿iÒµk×þ"@,P—{ÌÎÇÎà®äq³¡Ú(ÃÁ†’ _°÷~裭ÅÀ4ìL @1]ävêÉÿe_üûÿëÏßÿÿýûÿçï_0þö‚Alø»ÿþÿøóïÿ™§ÿþWoy[ ÔÏ   Äpýõÿª=÷þýÿðýР?ÿüúõÏã?ÿM×ýûÏ0ó?ƒØÛþýo¹áÿÿ»†ýþ÷ö™ÿ£f\³ €bzò‰ÁT”ó'ó†ßþ2lzÈÈಙáëoF†Z£ÿ µ†ÿ¾Ù±û™Ž¿d`¸ +Vf5a`à±ó …1@±¼ùÊ ¡"ð—áןÿ ŸQ”uŒ›ÁH„áï á @Ë®`bFpP\Ò^rÿ$yþ2ücå± Bô@±|øÁ ÃÍò— Ç^33¼ùÁÀ0Ãh0@Nþ´ÀIŠÁˆ¯ddãþŽI^Ö¿ ™¹U¡1Ëç_Àtò÷óþ'$äyþ3Öá/s0ýgH*gffb`b„¸êË_ ?~ÿaøþ—$Šy€búõáÞ«¯ÿÁ TùþéM^úóèÝ? ?~ýÒ@Û– þøõÃçï?î¾ûÇðõÓ‡û°Ø & ü‰›ï˜¶iñÿcp‘úËÐy‰‰aæ F†Ÿ`Ãþ1̽Šä3]ùó÷o†ï?3\zÅÌðéõãs@3þ€  &~v†m'Ÿ²2<ûô—™‰‰ašÅ/KÑ …'YD—s1H¬âa(:Åʰá!0¼€f1<úÄÄpú9ûG÷€d@Ò€pÀÒŸ)Æ’;âôþ0ðs² ddØñ”…áÔkK,Å|äÿ3üf¸o>1,¸Âɰq籩·'¸uÍx4ã@b†óÎ;ìÆS¾ô×íúúÿÜÃÿ?}ûþÿË÷Ÿÿ¿ÿüõÿç¯ßÿþþæŸ}ðáõίÿU+O­  ļ° @ ÿ¥¬¬Ìp6‡±ø[×ëgWÞòuYJþd0øË Ë ‰„ÇŸο`f8ö”“áÜñƒSMq›~ Ä_Y¬ €˜üßÁÁØÿ×T{Ÿø¡µïÌ3†Ûî²³.¿Åóü*Û¯M—¿ÝüДzfhØåDúÝ㢵Ï=Ch~?jÀOí·E̪U[Ã?‘@q:YÏçiè:ö™™þŠÙüB†=ºî3~¬\ŽâwßãÛùz$–ø ƹ ø'ä¿þÏGÓU^ÅVOo¹²]ú”`f.‡¨*( ùC_ÜóÎî.ô‰«>ÀýãO8W« àRU"} j£š¹‚è:¢ë¸r+põ¼þÚFDs)¼‡ÿ››­‚%¢(6²h•J´-¤ ž¿@`fwk+ÎÿˆÙ‡÷öÿm  ©Ô­&5‘¬RÖÔé’i–DQ$>ˆúö[ð /Á‹/ß0±ßÓÝ‚érQ0­êÅ NIœ>aÂ'ËŠPîîÆzæé›÷¿K¡^§pç…“¬/¤‘»1v&XV„‡v$- ÀÔèx h6§ÛÇÆzõö6ôXŒF&³©#z,Fef–•“'³?tv<~)š¾ñÊߎïè6S¾xüvÏÀ"7‹m*óóS©ì±hdï¯Ñð±d2Y¾ ;¸ãþŽ˜Q{Õív?¡ßó8ýêÅß3ÕB}íÈ©HøÀ¥Hè|2™,oj#Àää¤~וlgo¹²Ý!Òg‰(5‘ìÅ .-ùd2Ùø‡ÿ ‡*2“kIEND®B`‚glogg-0.9.2/images/up.png000066400000000000000000000012641222324246300152230ustar00rootroot00000000000000‰PNG  IHDRÄ´l;gAMAÖØÔOX2tEXtSoftwareAdobe ImageReadyqÉe<FIDAT8˵ԿOAÀñï›YA 9-‰QŒÁÂÆ†š¨Ä!D œÆBˆJ!+ÿ+mÁ’ÂB ¥! Ƙ%TH¬ŒâÉÁqpÞí¹ƒ[³yÉd¶˜ùÌ›Ù7ã©*;;±aÙˆª.ÿ7XDNÕï²/EDF€û@FUƒmÃ"r¡9™¿¾cx$"A~òìiÎgŽ$ù]ÌÔD|?¨< ÏQÜf¢ð+` hÒûš[ù‘žY?× ¨‘’à¢ø, QX"°BU%”Ñò¬Tæe]˾ªVÃZzê ¬(žÑš¨°ë)缪bÝ<㲪À®/Ÿ·'â6¹Å+mY@6,bnïuK/Ì0ÿeýŒËeW†×V+C} Œ ܺÁ`ŒWõ°æþM,x¸4 ›Œõºâ¾cÁ›T–©*ÛJ„nœ;cWz~4“¸ñØ|Ôzw€ŽIEND®B`‚glogg-0.9.2/infoline.cpp000066400000000000000000000041631222324246300151340ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #include "log.h" #include "infoline.h" #include // This file implements InfoLine. It is responsible for decorating the // widget and managing the completion gauge. InfoLine::InfoLine() : QLabel(), origPalette_( palette() ), backgroundColor_( origPalette_.color( QPalette::Button ) ), darkBackgroundColor_( origPalette_.color( QPalette::Dark ) ) { setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); } void InfoLine::displayGauge( int completion ) { int changeoverX = width() * completion / 100; // Create a gradient for the progress bar QLinearGradient linearGrad( changeoverX - 1, 0, changeoverX + 1, 0 ); linearGrad.setColorAt( 0, darkBackgroundColor_ ); linearGrad.setColorAt( 1, backgroundColor_ ); // Apply the gradient to the current palette (background) QPalette newPalette = origPalette_; newPalette.setBrush( backgroundRole(), QBrush( linearGrad ) ); setPalette( newPalette ); } void InfoLine::hideGauge() { setPalette( origPalette_ ); } // Custom painter: draw the background then call QLabel's painter void InfoLine::paintEvent( QPaintEvent* paintEvent ) { // Fill the widget background { QPainter painter( this ); painter.fillRect( 0, 0, this->width(), this->height(), palette().brush( backgroundRole() ) ); } // Call the parent's painter QLabel::paintEvent( paintEvent ); } glogg-0.9.2/infoline.h000066400000000000000000000030631222324246300145770ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef INFOLINE_H #define INFOLINE_H #include #include // Information line with integrated completion gauge // used for the file name and the search results. class InfoLine : public QLabel { public: // Default constructor InfoLine(); // Display the gauge in the background with the passed value (0-100) // This function doesn't change the text of the widget. void displayGauge( int completion ); // Hide the gauge and make the widget like a normal QLabel void hideGauge(); protected: void paintEvent(QPaintEvent* paintEvent); private: // The original palette of the QLabel QPalette origPalette_; // Color of the background const QColor backgroundColor_; // Color of the darkened background (left part of the gauge) const QColor darkBackgroundColor_; }; #endif glogg-0.9.2/log.h000066400000000000000000000117351222324246300135620ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef __LOG_H__ #define __LOG_H__ #include #include #include // Modify here! //#define FILELOG_MAX_LEVEL logDEBUG inline std::string NowTime(); enum TLogLevel {logERROR, logWARNING, logINFO, logDEBUG, logDEBUG1, logDEBUG2, logDEBUG3, logDEBUG4}; template class Log { public: Log(); virtual ~Log(); std::ostringstream& Get(TLogLevel level = logINFO, const std::string& sourceFile = "", int lineNumber = 0); public: static TLogLevel ReportingLevel() { return reportingLevel; } static std::string ToString(TLogLevel level); static TLogLevel FromString(const std::string& level); static void setReportingLevel(TLogLevel level) { reportingLevel = level; } template friend class Log; protected: std::ostringstream os; private: static TLogLevel reportingLevel; Log(const Log&); Log& operator =(const Log&); }; template TLogLevel Log::reportingLevel = logDEBUG4; template Log::Log() { } template std::ostringstream& Log::Get(TLogLevel level, const std::string& sourceFile, int lineNumber) { os << "- " << NowTime(); os << " " << ToString(level); os << " " << sourceFile << ":" << lineNumber << ": "; os << std::string(level > logDEBUG ? level - logDEBUG : 0, '\t'); return os; } template Log::~Log() { os << std::endl; T::Output(os.str()); } template std::string Log::ToString(TLogLevel level) { static const char* const buffer[] = {"ERROR", "WARNING", "INFO", "DEBUG", "DEBUG1", "DEBUG2", "DEBUG3", "DEBUG4"}; return buffer[level]; } template TLogLevel Log::FromString(const std::string& level) { if (level == "DEBUG4") return logDEBUG4; if (level == "DEBUG3") return logDEBUG3; if (level == "DEBUG2") return logDEBUG2; if (level == "DEBUG1") return logDEBUG1; if (level == "DEBUG") return logDEBUG; if (level == "INFO") return logINFO; if (level == "WARNING") return logWARNING; if (level == "ERROR") return logERROR; Log().Get(logWARNING) << "Unknown logging level '" << level << "'. Using INFO level as default."; return logINFO; } class Output2FILE { public: static FILE*& Stream(); static void Output(const std::string& msg); }; inline FILE*& Output2FILE::Stream() { static FILE* pStream = stderr; return pStream; } inline void Output2FILE::Output(const std::string& msg) { FILE* pStream = Stream(); if (!pStream) return; fprintf(pStream, "%s", msg.c_str()); fflush(pStream); } #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) # if defined (BUILDING_FILELOG_DLL) # define FILELOG_DECLSPEC __declspec (dllexport) # elif defined (USING_FILELOG_DLL) # define FILELOG_DECLSPEC __declspec (dllimport) # else # define FILELOG_DECLSPEC # endif // BUILDING_DBSIMPLE_DLL #else # define FILELOG_DECLSPEC #endif // _WIN32 //class FILELOG_DECLSPEC FILELog : public Log {}; typedef Log FILELog; #ifndef FILELOG_MAX_LEVEL #define FILELOG_MAX_LEVEL logDEBUG #endif #define FILE_LOG(level) \ if (level > FILELOG_MAX_LEVEL) ;\ else if (level > FILELog::ReportingLevel() || !Output2FILE::Stream()) ; \ else FILELog().Get(level, __FILE__, __LINE__) #define LOG(level) FILE_LOG(level) #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) #include inline std::string NowTime() { const int MAX_LEN = 200; char buffer[MAX_LEN]; if (GetTimeFormatA(LOCALE_USER_DEFAULT, 0, 0, "HH':'mm':'ss", buffer, MAX_LEN) == 0) return "Error in NowTime()"; char result[100] = {0}; static DWORD first = GetTickCount(); std::sprintf(result, "%s.%03ld", buffer, (long)(GetTickCount() - first) % 1000); return result; } #else #include inline std::string NowTime() { char buffer[11]; time_t t; time(&t); tm r; strftime(buffer, sizeof(buffer), "%T", localtime_r(&t, &r)); struct timeval tv; gettimeofday(&tv, 0); char result[100] = {0}; std::sprintf(result, "%s.%03ld", buffer, (long)tv.tv_usec / 1000); return result; } #endif //WIN32 #endif //__LOG_H__ glogg-0.9.2/logdata.cpp000066400000000000000000000272141222324246300147460ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // This file implements LogData, the content of a log file. #include #include #include "log.h" #include "logdata.h" #include "logfiltereddata.h" // Implementation of the 'start' functions for each operation void LogData::AttachOperation::doStart( LogDataWorkerThread& workerThread ) const { LOG(logDEBUG) << "Attaching " << filename_.toStdString(); workerThread.attachFile( filename_ ); workerThread.indexAll(); } void LogData::FullIndexOperation::doStart( LogDataWorkerThread& workerThread ) const { LOG(logDEBUG) << "Reindexing (full)"; workerThread.indexAll(); } void LogData::PartialIndexOperation::doStart( LogDataWorkerThread& workerThread ) const { LOG(logDEBUG) << "Reindexing (partial)"; workerThread.indexAdditionalLines( filesize_ ); } // Constructs an empty log file. // It must be displayed without error. LogData::LogData() : AbstractLogData(), fileWatcher_(), linePosition_(), fileMutex_(), dataMutex_(), workerThread_() { // Start with an "empty" log file_ = NULL; fileSize_ = 0; nbLines_ = 0; maxLength_ = 0; currentOperation_ = NULL; nextOperation_ = NULL; // Initialise the file watcher connect( &fileWatcher_, SIGNAL( fileChanged( const QString& ) ), this, SLOT( fileChangedOnDisk() ) ); // Forward the update signal connect( &workerThread_, SIGNAL( indexingProgressed( int ) ), this, SIGNAL( loadingProgressed( int ) ) ); connect( &workerThread_, SIGNAL( indexingFinished( bool ) ), this, SLOT( indexingFinished( bool ) ) ); // Starts the worker thread workerThread_.start(); } LogData::~LogData() { if ( file_ ) delete file_; } // // Public functions // void LogData::attachFile( const QString& fileName ) { LOG(logDEBUG) << "LogData::attachFile " << fileName.toStdString(); if ( file_ ) { // Remove the current file from the watch list fileWatcher_.removeFile( file_->fileName() ); } workerThread_.interrupt(); LogDataOperation* newOperation = new AttachOperation( fileName ); // If an attach operation is already in progress, the new one will // be delayed untilthe current one is finished (canceled) enqueueOperation( newOperation ); } void LogData::interruptLoading() { workerThread_.interrupt(); } qint64 LogData::getFileSize() const { return fileSize_; } QDateTime LogData::getLastModifiedDate() const { return lastModifiedDate_; } // Return an initialised LogFilteredData. The search is not started. LogFilteredData* LogData::getNewFilteredData() const { LogFilteredData* newFilteredData = new LogFilteredData( this ); return newFilteredData; } // Add an operation to the queue and perform it immediately if // there is none ongoing. void LogData::enqueueOperation( const LogDataOperation* newOperation ) { if ( currentOperation_ == NULL ) { // We do it immediately currentOperation_ = newOperation; startOperation(); } else { // An operation is in progress... // ... we schedule the attach op for later if ( nextOperation_ != NULL ) delete nextOperation_; nextOperation_ = newOperation; } } // Performs the current operation asynchronously, a indexingFinished // signal will be received when it's finished. void LogData::startOperation() { if ( currentOperation_ ) { LOG(logDEBUG) << "startOperation found something to do."; // If it's a full indexing ... // ... we invalidate the non indexed data if ( currentOperation_->isFull() ) { fileSize_ = 0; nbLines_ = 0; maxLength_ = 0; } // And let the operation do its stuff currentOperation_->start( workerThread_ ); } } // // Slots // void LogData::fileChangedOnDisk() { LOG(logDEBUG) << "signalFileChanged"; fileWatcher_.removeFile( file_->fileName() ); const QString name = file_->fileName(); QFileInfo info( name ); LogDataOperation* newOperation = NULL; LOG(logDEBUG) << "current fileSize=" << fileSize_; LOG(logDEBUG) << "info file_->size()=" << info.size(); if ( info.size() < fileSize_ ) { fileChangedOnDisk_ = Truncated; LOG(logINFO) << "File truncated"; newOperation = new FullIndexOperation(); } else if ( fileChangedOnDisk_ != DataAdded ) { fileChangedOnDisk_ = DataAdded; LOG(logINFO) << "New data on disk"; newOperation = new PartialIndexOperation( fileSize_ ); } if ( newOperation ) enqueueOperation( newOperation ); lastModifiedDate_ = info.lastModified(); emit fileChanged( fileChangedOnDisk_ ); // TODO: fileChangedOnDisk_, fileSize_ } void LogData::indexingFinished( bool success ) { LOG(logDEBUG) << "Entering LogData::indexingFinished."; // We use the newly created file data or restore the old ones. // (Qt implicit copy makes this fast!) { QMutexLocker locker( &dataMutex_ ); workerThread_.getIndexingData( &fileSize_, &maxLength_, &linePosition_ ); nbLines_ = linePosition_.size(); } LOG(logDEBUG) << "indexingFinished: " << success << ", found " << nbLines_ << " lines."; if ( success ) { // Use the new filename if needed if ( !currentOperation_->getFilename().isNull() ) { QString newFileName = currentOperation_->getFilename(); if ( file_ ) { QMutexLocker locker( &fileMutex_ ); file_->setFileName( newFileName ); } else { QMutexLocker locker( &fileMutex_ ); file_ = new QFile( newFileName ); } } // Update the modified date/time if the file exists lastModifiedDate_ = QDateTime(); QFileInfo fileInfo( *file_ ); if ( fileInfo.exists() ) lastModifiedDate_ = fileInfo.lastModified(); } if ( file_ ) { // And we watch the file for updates fileChangedOnDisk_ = Unchanged; fileWatcher_.addFile( file_->fileName() ); } emit loadingFinished( success ); // So now the operation is done, let's see if there is something // else to do, in which case, do it! if ( currentOperation_ ) { delete currentOperation_; currentOperation_ = nextOperation_; nextOperation_ = NULL; } else { LOG(logERROR) << "currentOperation_ is NULL in indexingFinished()"; } if ( currentOperation_ ) { LOG(logDEBUG) << "indexingFinished is performing the next operation"; startOperation(); } } // // Implementation of virtual functions // qint64 LogData::doGetNbLine() const { return nbLines_; } int LogData::doGetMaxLength() const { return maxLength_; } int LogData::doGetLineLength( qint64 line ) const { if ( line >= nbLines_ ) { return 0; /* exception? */ } int length = doGetExpandedLineString( line ).length(); return length; } QString LogData::doGetLineString( qint64 line ) const { if ( line >= nbLines_ ) { return QString(); /* exception? */ } dataMutex_.lock(); fileMutex_.lock(); file_->open( QIODevice::ReadOnly ); file_->seek( (line == 0) ? 0 : linePosition_[line-1] ); QString string = QString( file_->readLine() ); file_->close(); fileMutex_.unlock(); dataMutex_.unlock(); string.chop( 1 ); return string; } QString LogData::doGetExpandedLineString( qint64 line ) const { if ( line >= nbLines_ ) { return QString(); /* exception? */ } dataMutex_.lock(); fileMutex_.lock(); file_->open( QIODevice::ReadOnly ); file_->seek( (line == 0) ? 0 : linePosition_[line-1] ); QByteArray rawString = file_->readLine(); file_->close(); fileMutex_.unlock(); dataMutex_.unlock(); QString string = QString( untabify( rawString.constData() ) ); string.chop( 1 ); return string; } // Note this function is also called from the LogFilteredDataWorker thread, so // data must be protected because they are changed in the main thread (by // indexingFinished). QStringList LogData::doGetLines( qint64 first_line, int number ) const { QStringList list; const qint64 last_line = first_line + number - 1; // LOG(logDEBUG) << "LogData::doGetLines first_line:" << first_line << " nb:" << number; if ( number == 0 ) { return QStringList(); } if ( last_line >= nbLines_ ) { LOG(logWARNING) << "LogData::doGetLines Lines out of bound asked for"; return QStringList(); /* exception? */ } dataMutex_.lock(); fileMutex_.lock(); file_->open( QIODevice::ReadOnly ); const qint64 first_byte = (first_line == 0) ? 0 : linePosition_[first_line-1]; const qint64 last_byte = linePosition_[last_line]; // LOG(logDEBUG) << "LogData::doGetLines first_byte:" << first_byte << " last_byte:" << last_byte; file_->seek( first_byte ); QByteArray blob = file_->read( last_byte - first_byte ); file_->close(); fileMutex_.unlock(); qint64 beginning = 0; qint64 end = 0; for ( qint64 line = first_line; (line <= last_line); line++ ) { end = linePosition_[line] - first_byte; // LOG(logDEBUG) << "Getting line " << line << " beginning " << beginning << " end " << end; QByteArray this_line = blob.mid( beginning, end - beginning - 1 ); // LOG(logDEBUG) << "Line is: " << QString( this_line ).toStdString(); list.append( QString( this_line ) ); beginning = end; } dataMutex_.unlock(); return list; } QStringList LogData::doGetExpandedLines( qint64 first_line, int number ) const { QStringList list; const qint64 last_line = first_line + number - 1; if ( number == 0 ) { return QStringList(); } if ( last_line >= nbLines_ ) { LOG(logWARNING) << "LogData::doGetExpandedLines Lines out of bound asked for"; return QStringList(); /* exception? */ } dataMutex_.lock(); fileMutex_.lock(); file_->open( QIODevice::ReadOnly ); const qint64 first_byte = (first_line == 0) ? 0 : linePosition_[first_line-1]; const qint64 last_byte = linePosition_[last_line]; // LOG(logDEBUG) << "LogData::doGetExpandedLines first_byte:" << first_byte << " last_byte:" << last_byte; file_->seek( first_byte ); QByteArray blob = file_->read( last_byte - first_byte ); file_->close(); fileMutex_.unlock(); qint64 beginning = 0; qint64 end = 0; for ( qint64 line = first_line; (line <= last_line); line++ ) { end = linePosition_[line] - first_byte; // LOG(logDEBUG) << "Getting line " << line << " beginning " << beginning << " end " << end; QByteArray this_line = blob.mid( beginning, end - beginning - 1 ); // LOG(logDEBUG) << "Line is: " << QString( this_line ).toStdString(); list.append( untabify( this_line.constData() ) ); beginning = end; } dataMutex_.unlock(); return list; } glogg-0.9.2/logdata.h000066400000000000000000000136311222324246300144110ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef LOGDATA_H #define LOGDATA_H #include #include #include #include #include #include #include "abstractlogdata.h" #include "logdataworkerthread.h" #include "filewatcher.h" class LogFilteredData; // Represents a complete set of data to be displayed (ie. a log file content) // This class is thread-safe. class LogData : public AbstractLogData { Q_OBJECT public: // Creates an empty LogData LogData(); // Destroy an object ~LogData(); enum MonitoredFileStatus { Unchanged, DataAdded, Truncated }; // Attaches (or reattaches) the LogData to a file on disk // It starts the asynchronous indexing and returns (almost) immediately // Replace the ongoing loading if necessary. // Attaching to a non existant file works and the file is reported // to be empty. void attachFile( const QString& fileName ); // Interrupt the loading and restore the previous file. // Does nothing if no loading in progress. void interruptLoading(); // Creates a new filtered data using the passed regexp // ownership is passed to the caller LogFilteredData* getNewFilteredData() const; // Returns the size if the file in bytes qint64 getFileSize() const; // Returns the last modification date for the file. // Null if the file is not on disk. QDateTime getLastModifiedDate() const; signals: // Sent during the 'attach' process to signal progress // percent being the percentage of completion. void loadingProgressed( int percent ); // Signal the client the file is fully loaded and available. void loadingFinished( bool success ); // Sent when the file on disk has changed, will be followed // by loadingProgressed if needed and then a loadingFinished. void fileChanged( LogData::MonitoredFileStatus status ); private slots: // Consider reloading the file when it changes on disk updated void fileChangedOnDisk(); // Called when the worker thread signals the current operation ended void indexingFinished( bool success ); private: // This class models an indexing operation. // It exists to permit LogData to delay the operation if another // one is ongoing (operations are asynchronous) class LogDataOperation { public: LogDataOperation( const QString& fileName ) : filename_( fileName ) {} // Permit each child to have its destructor virtual ~LogDataOperation() {}; void start( LogDataWorkerThread& workerThread ) const { doStart( workerThread ); } const QString& getFilename() const { return filename_; } virtual bool isFull() const { return true; } protected: virtual void doStart( LogDataWorkerThread& workerThread ) const = 0; QString filename_; }; // Attaching a new file (change name + full index) class AttachOperation : public LogDataOperation { public: AttachOperation( const QString& fileName ) : LogDataOperation( fileName ) {} ~AttachOperation() {}; bool isFull() const { return true; } protected: void doStart( LogDataWorkerThread& workerThread ) const; }; // Reindexing the current file class FullIndexOperation : public LogDataOperation { public: FullIndexOperation() : LogDataOperation( QString() ) {} ~FullIndexOperation() {}; bool isFull() const { return false; } protected: void doStart( LogDataWorkerThread& workerThread ) const; }; // Indexing part of the current file (from fileSize) class PartialIndexOperation : public LogDataOperation { public: PartialIndexOperation( qint64 fileSize ) : LogDataOperation( QString() ), filesize_( fileSize ) {} ~PartialIndexOperation() {}; bool isFull() const { return false; } protected: void doStart( LogDataWorkerThread& workerThread ) const; private: qint64 filesize_; }; FileWatcher fileWatcher_; MonitoredFileStatus fileChangedOnDisk_; // Implementation of virtual functions virtual QString doGetLineString( qint64 line ) const; virtual QString doGetExpandedLineString( qint64 line ) const; virtual QStringList doGetLines( qint64 first, int number ) const; virtual QStringList doGetExpandedLines( qint64 first, int number ) const; virtual qint64 doGetNbLine() const; virtual int doGetMaxLength() const; virtual int doGetLineLength( qint64 line ) const; void enqueueOperation( const LogDataOperation* newOperation ); void startOperation(); QString indexingFileName_; QFile* file_; LinePositionArray linePosition_; qint64 fileSize_; qint64 nbLines_; int maxLength_; QDateTime lastModifiedDate_; const LogDataOperation* currentOperation_; const LogDataOperation* nextOperation_; // To protect the file: mutable QMutex fileMutex_; // To protect linePosition_, fileSize_ and maxLength_: mutable QMutex dataMutex_; // (are mutable to allow 'const' function to touch it, // while remaining const) // When acquiring both, data should be help before locking file. LogDataWorkerThread workerThread_; }; #endif glogg-0.9.2/logdataworkerthread.cpp000066400000000000000000000232671222324246300173740ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #include #include "log.h" #include "logdata.h" #include "logdataworkerthread.h" // Size of the chunk to read (5 MiB) const int IndexOperation::sizeChunk = 5*1024*1024; void IndexingData::getAll( qint64* size, int* length, LinePositionArray* linePosition ) { QMutexLocker locker( &dataMutex_ ); *size = indexedSize_; *length = maxLength_; *linePosition = linePosition_; } void IndexingData::setAll( qint64 size, int length, const LinePositionArray& linePosition ) { QMutexLocker locker( &dataMutex_ ); indexedSize_ = size; maxLength_ = length; linePosition_ = linePosition; } void IndexingData::addAll( qint64 size, int length, const LinePositionArray& linePosition ) { QMutexLocker locker( &dataMutex_ ); indexedSize_ += size; maxLength_ = qMax( maxLength_, length ); linePosition_ += linePosition; } LogDataWorkerThread::LogDataWorkerThread() : QThread(), mutex_(), operationRequestedCond_(), nothingToDoCond_(), fileName_(), indexingData_() { terminate_ = false; interruptRequested_ = false; operationRequested_ = NULL; } LogDataWorkerThread::~LogDataWorkerThread() { { QMutexLocker locker( &mutex_ ); terminate_ = true; operationRequestedCond_.wakeAll(); } wait(); } void LogDataWorkerThread::attachFile( const QString& fileName ) { QMutexLocker locker( &mutex_ ); // to protect fileName_ fileName_ = fileName; } void LogDataWorkerThread::indexAll() { QMutexLocker locker( &mutex_ ); // to protect operationRequested_ LOG(logDEBUG) << "FullIndex requested"; // If an operation is ongoing, we will block while ( (operationRequested_ != NULL) ) nothingToDoCond_.wait( &mutex_ ); interruptRequested_ = false; operationRequested_ = new FullIndexOperation( fileName_, &interruptRequested_ ); operationRequestedCond_.wakeAll(); } void LogDataWorkerThread::indexAdditionalLines( qint64 position ) { QMutexLocker locker( &mutex_ ); // to protect operationRequested_ LOG(logDEBUG) << "AddLines requested"; // If an operation is ongoing, we will block while ( (operationRequested_ != NULL) ) nothingToDoCond_.wait( &mutex_ ); interruptRequested_ = false; operationRequested_ = new PartialIndexOperation( fileName_, &interruptRequested_, position ); operationRequestedCond_.wakeAll(); } void LogDataWorkerThread::interrupt() { LOG(logDEBUG) << "Load interrupt requested"; // No mutex here, setting a bool is probably atomic! interruptRequested_ = true; } // This will do an atomic copy of the object // (hopefully fast as we use Qt containers) void LogDataWorkerThread::getIndexingData( qint64* indexedSize, int* maxLength, LinePositionArray* linePosition ) { indexingData_.getAll( indexedSize, maxLength, linePosition ); } // This is the thread's main loop void LogDataWorkerThread::run() { QMutexLocker locker( &mutex_ ); forever { while ( (terminate_ == false) && (operationRequested_ == NULL) ) operationRequestedCond_.wait( &mutex_ ); LOG(logDEBUG) << "Worker thread signaled"; // Look at what needs to be done if ( terminate_ ) return; // We must die if ( operationRequested_ ) { connect( operationRequested_, SIGNAL( indexingProgressed( int ) ), this, SIGNAL( indexingProgressed( int ) ) ); // Run the operation if ( operationRequested_->start( indexingData_ ) ) { LOG(logDEBUG) << "... finished copy in workerThread."; emit indexingFinished( true ); } else { emit indexingFinished( false ); } delete operationRequested_; operationRequested_ = NULL; nothingToDoCond_.wakeAll(); } } } // // Operations implementation // IndexOperation::IndexOperation( QString& fileName, bool* interruptRequest ) : fileName_( fileName ) { interruptRequest_ = interruptRequest; } PartialIndexOperation::PartialIndexOperation( QString& fileName, bool* interruptRequest, qint64 position ) : IndexOperation( fileName, interruptRequest ) { initialPosition_ = position; } qint64 IndexOperation::doIndex( LinePositionArray& linePosition, int* maxLength, qint64 initialPosition ) { int max_length = *maxLength; qint64 pos = initialPosition; // Absolute position of the start of current line qint64 end = 0; // Absolute position of the end of current line int additional_spaces = 0; // Additional spaces due to tabs QFile file( fileName_ ); if ( file.open( QIODevice::ReadOnly ) ) { // Count the number of lines and max length // (read big chunks to speed up reading from disk) file.seek( pos ); while ( !file.atEnd() ) { if ( *interruptRequest_ ) // a bool is always read/written atomically isn't it? break; // Read a chunk of 5MB const qint64 block_beginning = file.pos(); const QByteArray block = file.read( sizeChunk ); // Count the number of lines in each chunk qint64 pos_within_block = 0; while ( pos_within_block != -1 ) { pos_within_block = qMax( pos - block_beginning, 0LL); // Looking for the next \n, expanding tabs in the process do { if ( pos_within_block < block.length() ) { const char c = block.at(pos_within_block); if ( c == '\n' ) break; else if ( c == '\t' ) additional_spaces += AbstractLogData::tabStop - ( ( ( block_beginning - pos ) + pos_within_block + additional_spaces ) % AbstractLogData::tabStop ) - 1; pos_within_block++; } else { pos_within_block = -1; } } while ( pos_within_block != -1 ); // When a end of line has been found... if ( pos_within_block != -1 ) { end = pos_within_block + block_beginning; const int length = end-pos + additional_spaces; if ( length > max_length ) max_length = length; pos = end + 1; additional_spaces = 0; linePosition.append( pos ); } } // Update the caller for progress indication int progress = ( file.size() > 0 ) ? pos*100 / file.size() : 100; emit indexingProgressed( progress ); } // Check if there is a non LF terminated line at the end of the file if ( file.size() > pos ) { LOG( logWARNING ) << "Non LF terminated file, adding a fake end of line"; linePosition.append( file.size() + 1 ); linePosition.setFakeFinalLF(); } } else { // TODO: Check that the file is seekable? // If the file cannot be open, we do as if it was empty LOG(logWARNING) << "Cannot open file " << fileName_.toStdString(); emit indexingProgressed( 100 ); } *maxLength = max_length; return file.size(); } // Called in the worker thread's context // Should not use any shared variable bool FullIndexOperation::start( IndexingData& sharedData ) { LOG(logDEBUG) << "FullIndexOperation::start(), file " << fileName_.toStdString(); LOG(logDEBUG) << "FullIndexOperation: Starting the count..."; int maxLength = 0; LinePositionArray linePosition = LinePositionArray(); emit indexingProgressed( 0 ); qint64 size = doIndex( linePosition, &maxLength, 0 ); if ( *interruptRequest_ == false ) { // Commit the results to the shared data (atomically) sharedData.setAll( size, maxLength, linePosition ); } LOG(logDEBUG) << "FullIndexOperation: ... finished counting." "interrupt = " << *interruptRequest_; return ( *interruptRequest_ ? false : true ); } bool PartialIndexOperation::start( IndexingData& sharedData ) { LOG(logDEBUG) << "PartialIndexOperation::start(), file " << fileName_.toStdString(); LOG(logDEBUG) << "PartialIndexOperation: Starting the count at " << initialPosition_ << " ..."; int maxLength = 0; LinePositionArray linePosition = LinePositionArray(); emit indexingProgressed( 0 ); qint64 size = doIndex( linePosition, &maxLength, initialPosition_ ); if ( *interruptRequest_ == false ) { // Commit the results to the shared data (atomically) sharedData.addAll( size - initialPosition_, maxLength, linePosition ); } LOG(logDEBUG) << "PartialIndexOperation: ... finished counting."; return ( *interruptRequest_ ? false : true ); } glogg-0.9.2/logdataworkerthread.h000066400000000000000000000140521222324246300170310ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef LOGDATAWORKERTHREAD_H #define LOGDATAWORKERTHREAD_H #include #include #include #include #include // This class is a list of end of lines position, // in addition to a list of qint64 (positions within the files) // it can keep track of whether the final LF was added (for non-LF terminated // files) and remove it when more data are added. class LinePositionArray { public: // Default constructor LinePositionArray() : array() { fakeFinalLF_ = false; } // Copy constructor inline LinePositionArray( const LinePositionArray& orig ) : array(orig.array) { fakeFinalLF_ = orig.fakeFinalLF_; } // Add a new line position at the given position inline void append( qint64 pos ) { array.append( pos ); } // Size of the array inline int size() { return array.size(); } // Extract an element inline qint64 at( int i ) const { return array.at( i ); } inline qint64 operator[]( int i ) const { return array.at( i ); } // Set the presence of a fake final LF // Must be used after 'append'-ing a fake LF at the end. void setFakeFinalLF( bool finalLF=true ) { fakeFinalLF_ = finalLF; } // Add another list to this one, removing any fake LF on this list. LinePositionArray& operator+= ( const LinePositionArray& other ) { // If our final LF is fake, we remove it if ( fakeFinalLF_ ) this->array.pop_back(); // Append the arrays this->array += other.array; // In case the 'other' object has a fake LF this->fakeFinalLF_ = other.fakeFinalLF_; return *this; } private: QVector array; bool fakeFinalLF_; }; // This class is a mutex protected set of indexing data. // It is thread safe. class IndexingData { public: IndexingData() : dataMutex_(), linePosition_(), maxLength_(0), indexedSize_(0) { } // Atomically get all the indexing data void getAll( qint64* size, int* length, LinePositionArray* linePosition ); // Atomically set all the indexing data // (overwriting the existing) void setAll( qint64 size, int length, const LinePositionArray& linePosition ); // Atomically add to all the existing // indexing data. void addAll( qint64 size, int length, const LinePositionArray& linePosition ); private: QMutex dataMutex_; LinePositionArray linePosition_; int maxLength_; qint64 indexedSize_; }; class IndexOperation : public QObject { Q_OBJECT public: IndexOperation( QString& fileName, bool* interruptRequest ); virtual ~IndexOperation() { } // Start the indexing operation, returns true if it has been done // and false if it has been cancelled (results not copied) virtual bool start( IndexingData& result ) = 0; signals: void indexingProgressed( int ); protected: static const int sizeChunk; // Returns the total size indexed qint64 doIndex( LinePositionArray& linePosition, int* maxLength, qint64 initialPosition ); QString fileName_; bool* interruptRequest_; }; class FullIndexOperation : public IndexOperation { public: FullIndexOperation( QString& fileName, bool* interruptRequest ) : IndexOperation( fileName, interruptRequest ) { } virtual bool start( IndexingData& result ); }; class PartialIndexOperation : public IndexOperation { public: PartialIndexOperation( QString& fileName, bool* interruptRequest, qint64 position ); virtual bool start( IndexingData& result ); private: qint64 initialPosition_; }; // Create and manage the thread doing loading/indexing for // the creating LogData. One LogDataWorkerThread is used // per LogData instance. // Note everything except the run() function is in the LogData's // thread. class LogDataWorkerThread : public QThread { Q_OBJECT public: LogDataWorkerThread(); ~LogDataWorkerThread(); // Attaches to a file on disk. Attaching to a non existant file // will work, it will just appear as an empty file. void attachFile( const QString& fileName ); // Instructs the thread to start a new full indexing of the file, sending // signals as it progresses. void indexAll(); // Instructs the thread to start a partial indexing (starting at // the index passed). void indexAdditionalLines( qint64 position ); // Interrupts the indexing if one is in progress void interrupt(); // Returns a copy of the current indexing data void getIndexingData( qint64* indexedSize, int* maxLength, LinePositionArray* linePosition ); signals: // Sent during the indexing process to signal progress // percent being the percentage of completion. void indexingProgressed( int percent ); // Sent when indexing is finished, signals the client // to copy the new data back. void indexingFinished( bool success ); protected: void run(); private: void doIndexAll(); // Mutex to protect operationRequested_ and friends QMutex mutex_; QWaitCondition operationRequestedCond_; QWaitCondition nothingToDoCond_; QString fileName_; // Set when the thread must die bool terminate_; bool interruptRequested_; IndexOperation* operationRequested_; // Shared indexing data IndexingData indexingData_; }; #endif glogg-0.9.2/logfiltereddata.cpp000066400000000000000000000265001222324246300164620ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2011, 2012 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // This file implements LogFilteredData // It stores a pointer to the LogData that created it, // so should always be destroyed before the LogData. #include "log.h" #include #include #include #include "utils.h" #include "logdata.h" #include "marks.h" #include "logfiltereddata.h" // Creates an empty set. It must be possible to display it without error. // FIXME LogFilteredData::LogFilteredData() : AbstractLogData(), matchingLineList( QList() ), currentRegExp_(), visibility_(), filteredItemsCache_() { matchingLineList = QList(); /* Prevent any more searching */ maxLength_ = 0; maxLengthMarks_ = 0; searchDone_ = true; visibility_ = MarksAndMatches; workerThread_ = new LogFilteredDataWorkerThread( NULL ); marks_ = new Marks(); filteredItemsCacheDirty_ = true; } // Usual constructor: just copy the data, the search is started by runSearch() LogFilteredData::LogFilteredData( const LogData* logData ) : AbstractLogData(), matchingLineList( SearchResultArray() ), currentRegExp_(), visibility_(), filteredItemsCache_() { // Starts with an empty result list maxLength_ = 0; maxLengthMarks_ = 0; nbLinesProcessed_ = 0; sourceLogData_ = logData; searchDone_ = false; visibility_ = MarksAndMatches; workerThread_ = new LogFilteredDataWorkerThread( logData ); marks_ = new Marks(); filteredItemsCacheDirty_ = true; // Forward the update signal connect( workerThread_, SIGNAL( searchProgressed( int, int ) ), this, SLOT( handleSearchProgressed( int, int ) ) ); // Starts the worker thread workerThread_->start(); } // // Public functions // // Run the search and send newDataAvailable() signals. void LogFilteredData::runSearch( const QRegExp& regExp ) { LOG(logDEBUG) << "Entering runSearch"; // Reset the search currentRegExp_ = regExp; matchingLineList.clear(); maxLength_ = 0; maxLengthMarks_ = 0; workerThread_->search( currentRegExp_ ); } void LogFilteredData::updateSearch() { LOG(logDEBUG) << "Entering updateSearch"; workerThread_->updateSearch( currentRegExp_, nbLinesProcessed_ ); } void LogFilteredData::interruptSearch() { LOG(logDEBUG) << "Entering interruptSearch"; workerThread_->interrupt(); } void LogFilteredData::clearSearch() { currentRegExp_ = QRegExp(); matchingLineList.clear(); maxLength_ = 0; filteredItemsCacheDirty_ = true; } qint64 LogFilteredData::getMatchingLineNumber( int matchNum ) const { qint64 matchingLine = findLogDataLine( matchNum ); return matchingLine; } // Scan the list for the 'lineNumber' passed bool LogFilteredData::isLineInMatchingList( qint64 lineNumber ) { int index; // Not used return lookupLineNumber( matchingLineList, lineNumber, &index); } qint64 LogFilteredData::getNbTotalLines() const { return sourceLogData_->getNbLine(); } int LogFilteredData::getNbMatches() const { return matchingLineList.size(); } int LogFilteredData::getNbMarks() const { return marks_->size(); } LogFilteredData::FilteredLineType LogFilteredData::filteredLineTypeByIndex( int index ) const { // If we are only showing one type, the line is there because // it is of this type. if ( visibility_ == MatchesOnly ) return Match; else if ( visibility_ == MarksOnly ) return Mark; else { // If it is MarksAndMatches, we have to look. // Regenerate the cache if needed if ( filteredItemsCacheDirty_ ) regenerateFilteredItemsCache(); return filteredItemsCache_[ index ].type(); } } // Delegation to our Marks object void LogFilteredData::addMark( qint64 line, QChar mark ) { assert( marks_ ); if ( ( line >= 0 ) && ( line < sourceLogData_->getNbLine() ) ) { marks_->addMark( line, mark ); maxLengthMarks_ = qMax( maxLengthMarks_, sourceLogData_->getLineLength( line ) ); filteredItemsCacheDirty_ = true; } else LOG(logERROR) << "LogFilteredData::addMark\ trying to create a mark outside of the file."; } qint64 LogFilteredData::getMark( QChar mark ) const { assert( marks_ ); return marks_->getMark( mark ); } bool LogFilteredData::isLineMarked( qint64 line ) const { assert( marks_ ); return marks_->isLineMarked( line ); } void LogFilteredData::deleteMark( QChar mark ) { assert( marks_ ); marks_->deleteMark( mark ); filteredItemsCacheDirty_ = true; // FIXME: maxLengthMarks_ } void LogFilteredData::deleteMark( qint64 line ) { assert( marks_ ); marks_->deleteMark( line ); filteredItemsCacheDirty_ = true; // Now update the max length if needed if ( sourceLogData_->getLineLength( line ) >= maxLengthMarks_ ) { LOG(logDEBUG) << "deleteMark recalculating longest mark"; maxLengthMarks_ = 0; for ( Marks::const_iterator i = marks_->begin(); i != marks_->end(); ++i ) { LOG(logDEBUG) << "line " << i->lineNumber(); maxLengthMarks_ = qMax( maxLengthMarks_, sourceLogData_->getLineLength( i->lineNumber() ) ); } } } void LogFilteredData::clearMarks() { assert( marks_ ); marks_->clear(); filteredItemsCacheDirty_ = true; maxLengthMarks_ = 0; } void LogFilteredData::setVisibility( Visibility visi ) { visibility_ = visi; } // // Slots // void LogFilteredData::handleSearchProgressed( int nbMatches, int progress ) { LOG(logDEBUG) << "LogFilteredData::handleSearchProgressed matches=" << nbMatches << " progress=" << progress; // searchDone_ = true; workerThread_->getSearchResult( &maxLength_, &matchingLineList, &nbLinesProcessed_ ); filteredItemsCacheDirty_ = true; emit searchProgressed( nbMatches, progress ); } qint64 LogFilteredData::findLogDataLine( qint64 lineNum ) const { qint64 line = 0; if ( visibility_ == MatchesOnly ) { if ( lineNum < matchingLineList.size() ) { line = matchingLineList[lineNum].lineNumber(); } else { LOG(logERROR) << "Index too big in LogFilteredData: " << lineNum; } } else if ( visibility_ == MarksOnly ) { if ( lineNum < marks_->size() ) line = marks_->getLineMarkedByIndex( lineNum ); else LOG(logERROR) << "Index too big in LogFilteredData: " << lineNum; } else { // Regenerate the cache if needed if ( filteredItemsCacheDirty_ ) regenerateFilteredItemsCache(); if ( lineNum < filteredItemsCache_.size() ) line = filteredItemsCache_[ lineNum ].lineNumber(); else LOG(logERROR) << "Index too big in LogFilteredData: " << lineNum; } return line; } // Implementation of the virtual function. QString LogFilteredData::doGetLineString( qint64 lineNum ) const { qint64 line = findLogDataLine( lineNum ); QString string = sourceLogData_->getLineString( line ); return string; } // Implementation of the virtual function. QString LogFilteredData::doGetExpandedLineString( qint64 lineNum ) const { qint64 line = findLogDataLine( lineNum ); QString string = sourceLogData_->getExpandedLineString( line ); return string; } // Implementation of the virtual function. QStringList LogFilteredData::doGetLines( qint64 first_line, int number ) const { QStringList list; for ( int i = first_line; i < first_line + number; i++ ) { list.append( doGetLineString( i ) ); } return list; } // Implementation of the virtual function. QStringList LogFilteredData::doGetExpandedLines( qint64 first_line, int number ) const { QStringList list; for ( int i = first_line; i < first_line + number; i++ ) { list.append( doGetExpandedLineString( i ) ); } return list; } // Implementation of the virtual function. qint64 LogFilteredData::doGetNbLine() const { qint64 nbLines; if ( visibility_ == MatchesOnly ) nbLines = matchingLineList.size(); else if ( visibility_ == MarksOnly ) nbLines = marks_->size(); else { // Regenerate the cache if needed (hopefully most of the time // it won't be necessarily) if ( filteredItemsCacheDirty_ ) regenerateFilteredItemsCache(); nbLines = filteredItemsCache_.size(); } return nbLines; } // Implementation of the virtual function. int LogFilteredData::doGetMaxLength() const { int max_length; if ( visibility_ == MatchesOnly ) max_length = maxLength_; else if ( visibility_ == MarksOnly ) max_length = maxLengthMarks_; else max_length = qMax( maxLength_, maxLengthMarks_ ); return max_length; } // Implementation of the virtual function. int LogFilteredData::doGetLineLength( qint64 lineNum ) const { qint64 line = findLogDataLine( lineNum ); return sourceLogData_->getExpandedLineString( line ).length(); } // TODO: We might be a bit smarter and not regenerate the whole thing when // e.g. stuff is added at the end of the search. void LogFilteredData::regenerateFilteredItemsCache() const { LOG(logDEBUG) << "regenerateFilteredItemsCache"; filteredItemsCache_.clear(); filteredItemsCache_.reserve( matchingLineList.size() + marks_->size() ); // (it's an overestimate but probably not by much so it's fine) QList::const_iterator i = matchingLineList.begin(); Marks::const_iterator j = marks_->begin(); while ( ( i != matchingLineList.end() ) || ( j != marks_->end() ) ) { qint64 next_mark = ( j != marks_->end() ) ? j->lineNumber() : std::numeric_limits::max(); qint64 next_match = ( i != matchingLineList.end() ) ? i->lineNumber() : std::numeric_limits::max(); // We choose a Mark over a Match if a line is both, just an arbitrary choice really. if ( next_mark <= next_match ) { // LOG(logDEBUG) << "Add mark at " << next_mark; filteredItemsCache_.append( FilteredItem( next_mark, Mark ) ); if ( j != marks_->end() ) ++j; if ( ( next_mark == next_match ) && ( i != matchingLineList.end() ) ) ++i; // Case when it's both match and mark. } else { // LOG(logDEBUG) << "Add match at " << next_match; filteredItemsCache_.append( FilteredItem( next_match, Match ) ); if ( i != matchingLineList.end() ) ++i; } } filteredItemsCacheDirty_ = false; LOG(logDEBUG) << "finished regenerateFilteredItemsCache"; } glogg-0.9.2/logfiltereddata.h000066400000000000000000000141111222324246300161220ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2011, 2012 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef LOGFILTEREDDATA_H #define LOGFILTEREDDATA_H #include #include #include #include #include #include #include "abstractlogdata.h" #include "logfiltereddataworkerthread.h" class LogData; class Marks; // A list of matches found in a LogData, it stores all the matching lines, // which can be accessed using the AbstractLogData interface, together with // the original line number where they were found. // Constructing such objet does not start the search. // This object should be constructed by a LogData. class LogFilteredData : public AbstractLogData { Q_OBJECT public: // Creates an empty LogFilteredData LogFilteredData(); // Constructor used by LogData LogFilteredData( const LogData* logData ); // Starts the async search, sending newDataAvailable() when new data found. // If a search is already in progress this function will block until // it is done, so the application should call interruptSearch() first. void runSearch( const QRegExp& regExp ); // Add to the existing search, starting at the line when the search was // last stopped. Used when the file on disk has been added too. void updateSearch(); // Interrupt the running search if one is in progress. // Nothing is done if no search is in progress. void interruptSearch(); // Clear the search and the list of results. void clearSearch(); // Returns the line number in the original LogData where the element // 'index' was found. qint64 getMatchingLineNumber( int index ) const; // Returns whether the line number passed is in our list of matching ones. bool isLineInMatchingList( qint64 lineNumber ); // Returns the number of lines in the source log data qint64 getNbTotalLines() const; // Returns the number of matches (independently of the visibility) int getNbMatches() const; // Returns the number of marks (independently of the visibility) int getNbMarks() const; // Returns the reason why the line at the passed index is in the filtered // data. It can be because it is either a mark or a match. enum FilteredLineType { Match, Mark }; FilteredLineType filteredLineTypeByIndex( int index ) const; // Marks interface (delegated to a Marks object) // Add a mark at the given line, optionally identified by the given char // If a mark for this char already exist, the previous one is replaced. void addMark( qint64 line, QChar mark = QChar() ); // Get the (unique) mark identified by the passed char. qint64 getMark( QChar mark ) const; // Returns wheither the passed line has a mark on it. bool isLineMarked( qint64 line ) const; // Delete the mark identified by the passed char. void deleteMark( QChar mark ); // Delete the mark present on the passed line or do nothing if there is // none. void deleteMark( qint64 line ); // Completely clear the marks list. void clearMarks(); // Changes what the AbstractLogData returns via its getXLines/getNbLines // API. enum Visibility { MatchesOnly, MarksOnly, MarksAndMatches }; void setVisibility( Visibility visibility ); signals: // Sent when the search has progressed, give the number of matches (so far) // and the percentage of completion void searchProgressed( int nbMatches, int progress ); private slots: void handleSearchProgressed( int NbMatches, int progress ); private: class FilteredItem; // Implementation of virtual functions QString doGetLineString( qint64 line ) const; QString doGetExpandedLineString( qint64 line ) const; QStringList doGetLines( qint64 first, int number ) const; QStringList doGetExpandedLines( qint64 first, int number ) const; qint64 doGetNbLine() const; int doGetMaxLength() const; int doGetLineLength( qint64 line ) const; QList matchingLineList; const LogData* sourceLogData_; QRegExp currentRegExp_; bool searchDone_; int maxLength_; int maxLengthMarks_; // Number of lines of the LogData that has been searched for: qint64 nbLinesProcessed_; Visibility visibility_; // Cache used to combine Marks and Matches // when visibility_ == MarksAndMatches // (QVector store actual objects instead of pointers) mutable QVector filteredItemsCache_; mutable bool filteredItemsCacheDirty_; LogFilteredDataWorkerThread* workerThread_; Marks* marks_; // Utility functions qint64 findLogDataLine( qint64 lineNum ) const; void regenerateFilteredItemsCache() const; }; // A class representing a Mark or Match. // Conceptually it should be a base class for Mark and MatchingLine, // but we implement it this way for performance reason as we create plenty of // those everytime we refresh the cache. // Specifically it allows to store this in the cache by value instead // of pointer (less small allocations and no RTTI). class LogFilteredData::FilteredItem { public: // A default ctor seems to be necessary for QVector FilteredItem() { lineNumber_ = 0; } FilteredItem( qint64 lineNumber, FilteredLineType type ) { lineNumber_ = lineNumber; type_ = type; } qint64 lineNumber() const { return lineNumber_; } FilteredLineType type() const { return type_; } private: qint64 lineNumber_; FilteredLineType type_; }; #endif glogg-0.9.2/logfiltereddataworkerthread.cpp000066400000000000000000000174351222324246300211130ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #include #include "log.h" #include "logfiltereddataworkerthread.h" #include "logdata.h" // Number of lines in each chunk to read const int SearchOperation::nbLinesInChunk = 5000; void SearchData::getAll( int* length, SearchResultArray* matches, qint64* lines) const { QMutexLocker locker( &dataMutex_ ); *length = maxLength_; *matches = matches_; *lines = nbLinesProcessed_; } void SearchData::setAll( int length, const SearchResultArray& matches ) { QMutexLocker locker( &dataMutex_ ); maxLength_ = length; matches_ = matches; } void SearchData::addAll( int length, const SearchResultArray& matches, qint64 lines ) { QMutexLocker locker( &dataMutex_ ); maxLength_ = qMax( maxLength_, length ); matches_ += matches; nbLinesProcessed_ = lines; } int SearchData::getNbMatches() const { QMutexLocker locker( &dataMutex_ ); return matches_.count(); } // This function starts searching from the end since we use it // to remove the final match. void SearchData::deleteMatch( qint64 line ) { QMutexLocker locker( &dataMutex_ ); SearchResultArray::iterator i = matches_.end(); while ( i != matches_.begin() ) { i--; const int this_line = i->lineNumber(); if ( this_line == line ) { matches_.erase(i); break; } // Exit if we have passed the line number to look for. if ( this_line < line ) break; } } void SearchData::clear() { QMutexLocker locker( &dataMutex_ ); maxLength_ = 0; matches_.clear(); } LogFilteredDataWorkerThread::LogFilteredDataWorkerThread( const LogData* sourceLogData ) : QThread(), mutex_(), operationRequestedCond_(), nothingToDoCond_(), searchData_() { terminate_ = false; interruptRequested_ = false; operationRequested_ = NULL; sourceLogData_ = sourceLogData; } LogFilteredDataWorkerThread::~LogFilteredDataWorkerThread() { { QMutexLocker locker( &mutex_ ); terminate_ = true; operationRequestedCond_.wakeAll(); } wait(); } void LogFilteredDataWorkerThread::search( const QRegExp& regExp ) { QMutexLocker locker( &mutex_ ); // to protect operationRequested_ LOG(logDEBUG) << "Search requested"; // If an operation is ongoing, we will block while ( (operationRequested_ != NULL) ) nothingToDoCond_.wait( &mutex_ ); interruptRequested_ = false; operationRequested_ = new FullSearchOperation( sourceLogData_, regExp, &interruptRequested_ ); operationRequestedCond_.wakeAll(); } void LogFilteredDataWorkerThread::updateSearch( const QRegExp& regExp, qint64 position ) { QMutexLocker locker( &mutex_ ); // to protect operationRequested_ LOG(logDEBUG) << "Search requested"; // If an operation is ongoing, we will block while ( (operationRequested_ != NULL) ) nothingToDoCond_.wait( &mutex_ ); interruptRequested_ = false; operationRequested_ = new UpdateSearchOperation( sourceLogData_, regExp, &interruptRequested_, position ); operationRequestedCond_.wakeAll(); } void LogFilteredDataWorkerThread::interrupt() { LOG(logDEBUG) << "Search interruption requested"; // No mutex here, setting a bool is probably atomic! interruptRequested_ = true; // We wait for the interruption to be done { QMutexLocker locker( &mutex_ ); while ( (operationRequested_ != NULL) ) nothingToDoCond_.wait( &mutex_ ); } } // This will do an atomic copy of the object // (hopefully fast as we use Qt containers) void LogFilteredDataWorkerThread::getSearchResult( int* maxLength, SearchResultArray* searchMatches, qint64* nbLinesProcessed ) { searchData_.getAll( maxLength, searchMatches, nbLinesProcessed ); } // This is the thread's main loop void LogFilteredDataWorkerThread::run() { QMutexLocker locker( &mutex_ ); forever { while ( (terminate_ == false) && (operationRequested_ == NULL) ) operationRequestedCond_.wait( &mutex_ ); LOG(logDEBUG) << "Worker thread signaled"; // Look at what needs to be done if ( terminate_ ) return; // We must die if ( operationRequested_ ) { connect( operationRequested_, SIGNAL( searchProgressed( int, int ) ), this, SIGNAL( searchProgressed( int, int ) ) ); // Run the search operation operationRequested_->start( searchData_ ); LOG(logDEBUG) << "... finished copy in workerThread."; emit searchFinished(); delete operationRequested_; operationRequested_ = NULL; nothingToDoCond_.wakeAll(); } } } // // Operations implementation // SearchOperation::SearchOperation( const LogData* sourceLogData, const QRegExp& regExp, bool* interruptRequest ) : regexp_( regExp ), sourceLogData_( sourceLogData ) { interruptRequested_ = interruptRequest; } void SearchOperation::doSearch( SearchData& searchData, qint64 initialLine ) { const qint64 nbSourceLines = sourceLogData_->getNbLine(); int maxLength = 0; int nbMatches = searchData.getNbMatches(); SearchResultArray currentList = SearchResultArray(); for ( qint64 i = initialLine; i < nbSourceLines; i += nbLinesInChunk ) { if ( *interruptRequested_ ) break; const int percentage = ( i - initialLine ) * 100 / ( nbSourceLines - initialLine ); emit searchProgressed( nbMatches, percentage ); const QStringList lines = sourceLogData_->getLines( i, qMin( nbLinesInChunk, (int) ( nbSourceLines - i ) ) ); LOG(logDEBUG) << "Chunk starting at " << i << ", " << lines.size() << " lines read."; int j = 0; for ( ; j < lines.size(); j++ ) { if ( regexp_.indexIn( lines[j] ) != -1 ) { const int length = sourceLogData_->getExpandedLineString(i+j).length(); if ( length > maxLength ) maxLength = length; MatchingLine match( i+j ); currentList.append( match ); nbMatches++; } } // After each block, copy the data to shared data // and update the client searchData.addAll( maxLength, currentList, i+j ); currentList.clear(); } emit searchProgressed( nbMatches, 100 ); } // Called in the worker thread's context void FullSearchOperation::start( SearchData& searchData ) { // Clear the shared data searchData.clear(); doSearch( searchData, 0 ); } // Called in the worker thread's context void UpdateSearchOperation::start( SearchData& searchData ) { qint64 initial_line = initialPosition_; if ( initial_line >= 1 ) { // We need to re-search the last line because it might have // been updated (if it was not LF-terminated) --initial_line; // In case the last line matched, we don't want it to match twice. searchData.deleteMatch( initial_line ); } doSearch( searchData, initial_line ); } glogg-0.9.2/logfiltereddataworkerthread.h000066400000000000000000000123721222324246300205530ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef LOGFILTEREDDATAWORKERTHREAD_H #define LOGFILTEREDDATAWORKERTHREAD_H #include #include #include #include #include #include class LogData; // Class encapsulating a single matching line // Contains the line number the line was found in and its content. class MatchingLine { public: MatchingLine( int line ) { lineNumber_ = line; }; // Accessors int lineNumber() const { return lineNumber_; } private: int lineNumber_; }; typedef QList SearchResultArray; // This class is a mutex protected set of search result data. // It is thread safe. class SearchData { public: SearchData() : dataMutex_(), matches_(), maxLength_(0) { } // Atomically get all the search data void getAll( int* length, SearchResultArray* matches, qint64* nbLinesProcessed ) const; // Atomically set all the search data // (overwriting the existing) void setAll( int length, const SearchResultArray& matches ); // Atomically add to all the existing search data. void addAll( int length, const SearchResultArray& matches, qint64 nbLinesProcessed ); // Get the number of matches int getNbMatches() const; // Delete the match for the passed line (if it exist) void deleteMatch( qint64 line ); // Atomically clear the data. void clear(); private: mutable QMutex dataMutex_; SearchResultArray matches_; int maxLength_; qint64 nbLinesProcessed_; }; class SearchOperation : public QObject { Q_OBJECT public: SearchOperation( const LogData* sourceLogData, const QRegExp& regExp, bool* interruptRequest ); virtual ~SearchOperation() { } // Start the search operation, returns true if it has been done // and false if it has been cancelled (results not copied) virtual void start( SearchData& result ) = 0; signals: void searchProgressed( int percent, int nbMatches ); protected: static const int nbLinesInChunk; // Implement the common part of the search, passing // the shared results and the line to begin the search from. void doSearch( SearchData& result, qint64 initialLine ); bool* interruptRequested_; const QRegExp regexp_; const LogData* sourceLogData_; }; class FullSearchOperation : public SearchOperation { public: FullSearchOperation( const LogData* sourceLogData, const QRegExp& regExp, bool* interruptRequest ) : SearchOperation( sourceLogData, regExp, interruptRequest ) {} virtual void start( SearchData& result ); }; class UpdateSearchOperation : public SearchOperation { public: UpdateSearchOperation( const LogData* sourceLogData, const QRegExp& regExp, bool* interruptRequest, qint64 position ) : SearchOperation( sourceLogData, regExp, interruptRequest ), initialPosition_( position ) {} virtual void start( SearchData& result ); private: qint64 initialPosition_; }; // Create and manage the thread doing loading/indexing for // the creating LogData. One LogDataWorkerThread is used // per LogData instance. // Note everything except the run() function is in the LogData's // thread. class LogFilteredDataWorkerThread : public QThread { Q_OBJECT public: LogFilteredDataWorkerThread( const LogData* sourceLogData ); ~LogFilteredDataWorkerThread(); // Start the search with the passed regexp void search( const QRegExp& regExp ); // Continue the previous search starting at the passed position // in the source file (line number) void updateSearch( const QRegExp& regExp, qint64 position ); // Interrupts the search if one is in progress void interrupt(); // Returns a copy of the current indexing data void getSearchResult( int* maxLength, SearchResultArray* searchMatches, qint64* nbLinesProcessed ); signals: // Sent during the indexing process to signal progress // percent being the percentage of completion. void searchProgressed( int percent, int nbMatches ); // Sent when indexing is finished, signals the client // to copy the new data back. void searchFinished(); protected: void run(); private: const LogData* sourceLogData_; // Mutex to protect operationRequested_ and friends QMutex mutex_; QWaitCondition operationRequestedCond_; QWaitCondition nothingToDoCond_; // Set when the thread must die bool terminate_; bool interruptRequested_; SearchOperation* operationRequested_; // Shared indexing data SearchData searchData_; }; #endif glogg-0.9.2/logmainview.cpp000066400000000000000000000040701222324246300156470ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2011, 2013 Nicolas Bonnefon * and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // This file implements the LogMainView concrete class. // Most of the actual drawing and event management is done in AbstractLogView // Only behaviour specific to the main (top) view is implemented here. #include "logmainview.h" #include "logfiltereddata.h" #include "overview.h" LogMainView::LogMainView( const LogData* newLogData, const QuickFindPattern* const quickFindPattern, Overview* overview, OverviewWidget* overview_widget, QWidget* parent) : AbstractLogView( newLogData, quickFindPattern, parent ) { filteredData_ = NULL; // The main data has a real (non NULL) Overview setOverview( overview, overview_widget ); } // Just update our internal record. void LogMainView::useNewFiltering( LogFilteredData* filteredData ) { filteredData_ = filteredData; if ( getOverview() != NULL ) getOverview()->setFilteredData( filteredData_ ); } AbstractLogView::LineType LogMainView::lineType( int lineNumber ) const { if ( filteredData_ != NULL ) { LineType line_type; if ( filteredData_->isLineMarked( lineNumber ) ) line_type = Marked; else if ( filteredData_->isLineInMatchingList( lineNumber ) ) line_type = Match; else line_type = Normal; return line_type; } else return Normal; } glogg-0.9.2/logmainview.h000066400000000000000000000030221222324246300153100ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2011, 2013 Nicolas Bonnefon * and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef LOGMAINVIEW_H #define LOGMAINVIEW_H #include "abstractlogview.h" #include "logdata.h" // Class implementing the main (top) view widget. class LogMainView : public AbstractLogView { public: LogMainView( const LogData* newLogData, const QuickFindPattern* const quickFindPattern, Overview* overview, OverviewWidget* overview_widget, QWidget* parent = 0 ); // Configure the view to use the passed filtered list // (used for couloured bullets) // Should be NULL or the empty LFD if no filtering is used void useNewFiltering( LogFilteredData* filteredData ); protected: // Implements the virtual function virtual LineType lineType( int lineNumber ) const; private: LogFilteredData* filteredData_; }; #endif glogg-0.9.2/main.cpp000066400000000000000000000114531222324246300142550ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2011 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #include #include namespace po = boost::program_options; #include using namespace std; #include "persistentinfo.h" #include "sessioninfo.h" #include "configuration.h" #include "filterset.h" #include "recentfiles.h" #include "mainwindow.h" #include "savedsearches.h" #include "log.h" static void print_version(); int main(int argc, char *argv[]) { QApplication app(argc, argv); string filename = ""; TLogLevel logLevel = logWARNING; try { po::options_description desc("Usage: glogg [options] [file]"); desc.add_options() ("help,h", "print out program usage (this message)") ("version,v", "print glogg's version information") ("debug,d", "output more debug (include multiple times for more verbosity e.g. -dddd") ; po::options_description desc_hidden("Hidden options"); // For -dd, -ddd... for ( string s = "dd"; s.length() <= 10; s.append("d") ) desc_hidden.add_options()(s.c_str(), "debug"); desc_hidden.add_options() ("input-file", po::value(), "input file") ; po::options_description all_options("all options"); all_options.add(desc).add(desc_hidden); po::positional_options_description positional; positional.add("input-file", 1); int command_line_style = (((po::command_line_style::unix_style ^ po::command_line_style::allow_guessing) | po::command_line_style::allow_long_disguise) ^ po::command_line_style::allow_sticky); po::variables_map vm; po::store(po::command_line_parser(argc, argv). options(all_options). positional(positional). style(command_line_style).run(), vm); po::notify(vm); if ( vm.count("help") ) { desc.print(cout); return 0; } if ( vm.count("version") ) { print_version(); return 0; } if ( vm.count( "debug" ) ) { logLevel = logINFO; } for ( string s = "dd"; s.length() <= 10; s.append("d") ) if ( vm.count( s ) ) logLevel = (TLogLevel) (logWARNING + s.length()); if ( vm.count("input-file") ) filename = vm["input-file"].as(); } catch(exception& e) { cerr << "Option processing error: " << e.what() << endl; return 1; } catch(...) { cerr << "Exception of unknown type!\n"; } #if 0 FILE* file = fopen("glogg.log", "w"); Output2FILE::Stream() = file; #endif FILELog::setReportingLevel( logLevel ); // Register the configuration items GetPersistentInfo().migrateAndInit(); GetPersistentInfo().registerPersistable( new SessionInfo, QString( "session" ) ); GetPersistentInfo().registerPersistable( new Configuration, QString( "settings" ) ); GetPersistentInfo().registerPersistable( new FilterSet, QString( "filterSet" ) ); GetPersistentInfo().registerPersistable( new SavedSearches, QString( "savedSearches" ) ); GetPersistentInfo().registerPersistable( new RecentFiles, QString( "recentFiles" ) ); // FIXME: should be replaced by a two staged init of MainWindow GetPersistentInfo().retrieve( QString( "settings" ) ); MainWindow* mw = new MainWindow(); LOG(logDEBUG) << "MainWindow created."; mw->show(); mw->loadInitialFile( QString::fromStdString( filename ) ); return app.exec(); } static void print_version() { cout << "glogg " GLOGG_VERSION "\n"; #ifdef GLOGG_COMMIT cout << "Built " GLOGG_DATE " from " GLOGG_COMMIT "\n"; #endif cout << "Copyright (C) 2009, 2010, 2011 Nicolas Bonnefon and other contributors\n"; cout << "This is free software. You may redistribute copies of it under the terms of\n"; cout << "the GNU General Public License .\n"; cout << "There is NO WARRANTY, to the extent permitted by law.\n"; } glogg-0.9.2/mainwindow.cpp000066400000000000000000000467021222324246300155120ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2011, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // This file implements MainWindow. It is responsible for creating and // managing the menus, the toolbar, and the CrawlerWidget. It also // load/save the settings on opening/closing of the app #include #include #include "log.h" #include "mainwindow.h" #include "sessioninfo.h" #include "recentfiles.h" #include "crawlerwidget.h" #include "filtersdialog.h" #include "optionsdialog.h" #include "persistentinfo.h" #include "savedsearches.h" #include "menuactiontooltipbehavior.h" MainWindow::MainWindow() : recentFiles( Persistent( "recentFiles" ) ), mainIcon_() { createActions(); createMenus(); // createContextMenu(); createToolBars(); // createStatusBar(); createCrawler(); setAcceptDrops( true ); // Default geometry const QRect geometry = QApplication::desktop()->availableGeometry( this ); setGeometry( geometry.x() + 20, geometry.y() + 40, geometry.width() - 140, geometry.height() - 140 ); // Send actions to the crawlerwidget connect( this, SIGNAL( followSet( bool ) ), crawlerWidget, SIGNAL( followSet( bool ) ) ); connect( this, SIGNAL( optionsChanged() ), crawlerWidget, SLOT( applyConfiguration() ) ); // Actions from the CrawlerWidget connect( crawlerWidget, SIGNAL( followDisabled() ), this, SLOT( disableFollow() ) ); connect( crawlerWidget, SIGNAL( updateLineNumber( int ) ), this, SLOT( lineNumberHandler( int ) ) ); readSettings(); emit optionsChanged(); // We start with the empty file setCurrentFile( "" ); mainIcon_.addFile( ":/images/hicolor/16x16/glogg.png" ); mainIcon_.addFile( ":/images/hicolor/24x24/glogg.png" ); mainIcon_.addFile( ":/images/hicolor/32x32/glogg.png" ); mainIcon_.addFile( ":/images/hicolor/48x48/glogg.png" ); // Register for progress status bar connect( crawlerWidget, SIGNAL( loadingProgressed( int ) ), this, SLOT( updateLoadingProgress( int ) ) ); connect( crawlerWidget, SIGNAL( loadingFinished( bool ) ), this, SLOT( displayNormalStatus( bool ) ) ); setWindowIcon( mainIcon_ ); setCentralWidget(crawlerWidget); } void MainWindow::loadInitialFile( QString fileName ) { LOG(logDEBUG) << "loadInitialFile"; // Is there a file passed as argument? if ( !fileName.isEmpty() ) loadFile( fileName ); else if ( !previousFile.isEmpty() ) loadFile( previousFile ); } // // Private functions // void MainWindow::createCrawler() { // First get the global search history savedSearches = &(Persistent( "savedSearches" )); crawlerWidget = new CrawlerWidget( savedSearches ); } // Menu actions void MainWindow::createActions() { Configuration& config = Persistent( "settings" ); openAction = new QAction(tr("&Open..."), this); openAction->setShortcut(QKeySequence::Open); openAction->setIcon( QIcon(":/images/open16.png") ); openAction->setStatusTip(tr("Open a file")); connect(openAction, SIGNAL(triggered()), this, SLOT(open())); // Recent files for (int i = 0; i < MaxRecentFiles; ++i) { recentFileActions[i] = new QAction(this); recentFileActions[i]->setVisible(false); connect(recentFileActions[i], SIGNAL(triggered()), this, SLOT(openRecentFile())); } exitAction = new QAction(tr("E&xit"), this); exitAction->setShortcut(tr("Ctrl+Q")); exitAction->setStatusTip(tr("Exit the application")); connect( exitAction, SIGNAL(triggered()), this, SLOT(close()) ); copyAction = new QAction(tr("&Copy"), this); copyAction->setShortcut(QKeySequence::Copy); copyAction->setStatusTip(tr("Copy the selection")); connect( copyAction, SIGNAL(triggered()), this, SLOT(copy()) ); selectAllAction = new QAction(tr("Select &All"), this); selectAllAction->setShortcut(tr("Ctrl+A")); selectAllAction->setStatusTip(tr("Select all the text")); connect( selectAllAction, SIGNAL(triggered()), this, SLOT( selectAll() ) ); findAction = new QAction(tr("&Find..."), this); findAction->setShortcut(QKeySequence::Find); findAction->setStatusTip(tr("Find the text")); connect( findAction, SIGNAL(triggered()), this, SLOT( find() ) ); overviewVisibleAction = new QAction( tr("Matches &overview"), this ); overviewVisibleAction->setCheckable( true ); overviewVisibleAction->setChecked( config.isOverviewVisible() ); connect( overviewVisibleAction, SIGNAL( toggled( bool ) ), this, SLOT( toggleOverviewVisibility( bool )) ); lineNumbersVisibleInMainAction = new QAction( tr("Line &numbers in main view"), this ); lineNumbersVisibleInMainAction->setCheckable( true ); lineNumbersVisibleInMainAction->setChecked( config.mainLineNumbersVisible() ); connect( lineNumbersVisibleInMainAction, SIGNAL( toggled( bool ) ), this, SLOT( toggleMainLineNumbersVisibility( bool )) ); lineNumbersVisibleInFilteredAction = new QAction( tr("Line &numbers in filtered view"), this ); lineNumbersVisibleInFilteredAction->setCheckable( true ); lineNumbersVisibleInFilteredAction->setChecked( config.filteredLineNumbersVisible() ); connect( lineNumbersVisibleInFilteredAction, SIGNAL( toggled( bool ) ), this, SLOT( toggleFilteredLineNumbersVisibility( bool )) ); followAction = new QAction( tr("&Follow File"), this ); followAction->setShortcut(Qt::Key_F); followAction->setCheckable(true); connect( followAction, SIGNAL(toggled( bool )), this, SIGNAL(followSet( bool )) ); reloadAction = new QAction( tr("&Reload"), this ); reloadAction->setShortcut(QKeySequence::Refresh); reloadAction->setIcon( QIcon(":/images/reload16.png") ); connect( reloadAction, SIGNAL(triggered()), this, SLOT(reload()) ); stopAction = new QAction( tr("&Stop"), this ); stopAction->setIcon( QIcon(":/images/stop16.png") ); stopAction->setEnabled( false ); connect( stopAction, SIGNAL(triggered()), this, SLOT(stop()) ); filtersAction = new QAction(tr("&Filters..."), this); filtersAction->setStatusTip(tr("Show the Filters box")); connect( filtersAction, SIGNAL(triggered()), this, SLOT(filters()) ); optionsAction = new QAction(tr("&Options..."), this); optionsAction->setStatusTip(tr("Show the Options box")); connect( optionsAction, SIGNAL(triggered()), this, SLOT(options()) ); aboutAction = new QAction(tr("&About"), this); aboutAction->setStatusTip(tr("Show the About box")); connect( aboutAction, SIGNAL(triggered()), this, SLOT(about()) ); aboutQtAction = new QAction(tr("About &Qt"), this); aboutAction->setStatusTip(tr("Show the Qt library's About box")); connect( aboutQtAction, SIGNAL(triggered()), this, SLOT(aboutQt()) ); } void MainWindow::createMenus() { fileMenu = menuBar()->addMenu( tr("&File") ); fileMenu->addAction( openAction ); fileMenu->addSeparator(); for (int i = 0; i < MaxRecentFiles; ++i) { fileMenu->addAction( recentFileActions[i] ); recentFileActionBehaviors[i] = new MenuActionToolTipBehavior(recentFileActions[i], fileMenu, this); } fileMenu->addSeparator(); fileMenu->addAction( exitAction ); editMenu = menuBar()->addMenu( tr("&Edit") ); editMenu->addAction( copyAction ); editMenu->addAction( selectAllAction ); editMenu->addSeparator(); editMenu->addAction( findAction ); viewMenu = menuBar()->addMenu( tr("&View") ); viewMenu->addAction( overviewVisibleAction ); viewMenu->addSeparator(); viewMenu->addAction( lineNumbersVisibleInMainAction ); viewMenu->addAction( lineNumbersVisibleInFilteredAction ); viewMenu->addSeparator(); viewMenu->addAction( followAction ); viewMenu->addSeparator(); viewMenu->addAction( reloadAction ); toolsMenu = menuBar()->addMenu( tr("&Tools") ); toolsMenu->addAction( filtersAction ); toolsMenu->addSeparator(); toolsMenu->addAction( optionsAction ); menuBar()->addSeparator(); helpMenu = menuBar()->addMenu( tr("&Help") ); helpMenu->addAction( aboutAction ); } void MainWindow::createToolBars() { infoLine = new InfoLine(); infoLine->setFrameStyle( QFrame::WinPanel | QFrame::Sunken ); infoLine->setLineWidth( 0 ); lineNbField = new QLabel( ); lineNbField->setText( "Line 0" ); lineNbField->setAlignment( Qt::AlignLeft | Qt::AlignVCenter ); lineNbField->setMinimumSize( lineNbField->fontMetrics().size( 0, "Line 0000000") ); toolBar = addToolBar( tr("&Toolbar") ); toolBar->setIconSize( QSize( 16, 16 ) ); toolBar->setMovable( false ); toolBar->addAction( openAction ); toolBar->addAction( reloadAction ); toolBar->addWidget( infoLine ); toolBar->addAction( stopAction ); toolBar->addWidget( lineNbField ); } // // Slots // // Opens the file selection dialog to select a new log file void MainWindow::open() { QString defaultDir = "."; // Default to the path of the current file if there is one if ( !currentFile.isEmpty() ) { QFileInfo fileInfo = QFileInfo( currentFile ); defaultDir = fileInfo.path(); } QString fileName = QFileDialog::getOpenFileName(this, tr("Open file"), defaultDir, tr("All files (*)")); if (!fileName.isEmpty()) loadFile(fileName); } // Opens a log file from the recent files list void MainWindow::openRecentFile() { QAction* action = qobject_cast(sender()); if (action) loadFile(action->data().toString()); } // Select all the text in the currently selected view void MainWindow::selectAll() { crawlerWidget->selectAll(); } // Copy the currently selected line into the clipboard void MainWindow::copy() { static QClipboard* clipboard = QApplication::clipboard(); clipboard->setText( crawlerWidget->getSelectedText() ); // Put it in the global selection as well (X11 only) clipboard->setText( crawlerWidget->getSelectedText(), QClipboard::Selection ); } // Display the QuickFind bar void MainWindow::find() { crawlerWidget->displayQuickFindBar( QuickFindMux::Forward ); } // Reload the current log file void MainWindow::reload() { if ( !currentFile.isEmpty() ) loadFile( currentFile ); } // Stop the loading operation void MainWindow::stop() { crawlerWidget->stopLoading(); } // Opens the 'Filters' dialog box void MainWindow::filters() { FiltersDialog dialog(this); connect(&dialog, SIGNAL( optionsChanged() ), crawlerWidget, SLOT( applyConfiguration() )); dialog.exec(); } // Opens the 'Options' modal dialog box void MainWindow::options() { OptionsDialog dialog(this); connect(&dialog, SIGNAL( optionsChanged() ), crawlerWidget, SLOT( applyConfiguration() )); dialog.exec(); } // Opens the 'About' dialog box. void MainWindow::about() { QMessageBox::about(this, tr("About glogg"), tr("

glogg " GLOGG_VERSION "

" "

A fast, advanced log explorer." #ifdef GLOGG_COMMIT "

Built " GLOGG_DATE " from " GLOGG_COMMIT #endif "

Copyright © 2009, 2010, 2011, 2012, 2013 Nicolas Bonnefon and other contributors" "

You may modify and redistribute the program under the terms of the GPL (version 3 or later)." ) ); } // Opens the 'About Qt' dialog box. void MainWindow::aboutQt() { } void MainWindow::toggleOverviewVisibility( bool isVisible ) { Configuration& config = Persistent( "settings" ); config.setOverviewVisible( isVisible ); emit optionsChanged(); } void MainWindow::toggleMainLineNumbersVisibility( bool isVisible ) { Configuration& config = Persistent( "settings" ); config.setMainLineNumbersVisible( isVisible ); emit optionsChanged(); } void MainWindow::toggleFilteredLineNumbersVisibility( bool isVisible ) { Configuration& config = Persistent( "settings" ); config.setFilteredLineNumbersVisible( isVisible ); emit optionsChanged(); } void MainWindow::disableFollow() { followAction->setChecked( false ); } void MainWindow::lineNumberHandler( int line ) { // The line number received is the internal (starts at 0) lineNbField->setText( tr( "Line %1" ).arg( line + 1 ) ); } void MainWindow::updateLoadingProgress( int progress ) { LOG(logDEBUG) << "Loading progress: " << progress; // We ignore 0% and 100% to avoid a flash when the file (or update) // is very short. if ( progress > 0 && progress < 100 ) { infoLine->setText( loadingFileName + tr( " - Indexing lines... (%1 %)" ).arg( progress ) ); infoLine->displayGauge( progress ); stopAction->setEnabled( true ); } } void MainWindow::displayNormalStatus( bool success ) { QLocale defaultLocale; LOG(logDEBUG) << "displayNormalStatus"; if ( success ) setCurrentFile( loadingFileName ); qint64 fileSize; int fileNbLine; QDateTime lastModified; crawlerWidget->getFileInfo( &fileSize, &fileNbLine, &lastModified ); if ( lastModified.isValid() ) { const QString date = #if QT_VERSION > 0x040500 defaultLocale.toString( lastModified, QLocale::NarrowFormat ); #else defaultLocale.toString( lastModified.date(), QLocale::ShortFormat ) .append( " " ).append( defaultLocale.toString( lastModified.time(), QLocale::ShortFormat ) ); #endif infoLine->setText( tr( "%1 (%2 - %3 lines - modified on %4)" ) .arg(currentFile).arg(readableSize(fileSize)) .arg(fileNbLine).arg( date ) ); } else { infoLine->setText( tr( "%1 (%2 - %3 lines)" ) .arg(currentFile).arg(readableSize(fileSize)) .arg(fileNbLine) ); } infoLine->hideGauge(); stopAction->setEnabled( false ); } // // Events // // Closes the application void MainWindow::closeEvent( QCloseEvent *event ) { writeSettings(); event->accept(); } // Accepts the drag event if it looks like a filename void MainWindow::dragEnterEvent( QDragEnterEvent* event ) { if ( event->mimeData()->hasFormat( "text/uri-list" ) ) event->acceptProposedAction(); } // Tries and loads the file if the URL dropped is local void MainWindow::dropEvent( QDropEvent* event ) { QList urls = event->mimeData()->urls(); if ( urls.isEmpty() ) return; QString fileName = urls.first().toLocalFile(); if ( fileName.isEmpty() ) return; loadFile( fileName ); } // // Private functions // // Loads the passed file into the CrawlerWidget and update the title bar. // The loading is done asynchronously. bool MainWindow::loadFile( const QString& fileName ) { LOG(logDEBUG) << "loadFile ( " << fileName.toStdString() << " )"; int topLine = 0; // If we're loading the same file, put the same line on top. if ( fileName == currentFile ) topLine = crawlerWidget->getTopLine(); // Load the file loadingFileName = fileName; if ( crawlerWidget->readFile( fileName, topLine ) ) { LOG(logDEBUG) << "Success loading file " << fileName.toStdString(); return true; } else { LOG(logWARNING) << "Cannot load file " << fileName.toStdString(); displayNormalStatus( false ); return false; } } // Strips the passed filename from its directory part. QString MainWindow::strippedName( const QString& fullFileName ) const { return QFileInfo( fullFileName ).fileName(); } // Add the filename to the recent files list and update the title bar. void MainWindow::setCurrentFile( const QString& fileName ) { // Change the current file currentFile = fileName; QString shownName = tr( "Untitled" ); if ( !currentFile.isEmpty() ) { // (reload the list first in case another glogg changed it) GetPersistentInfo().retrieve( "recentFiles" ); recentFiles.addRecent( currentFile ); GetPersistentInfo().save( "recentFiles" ); updateRecentFileActions(); shownName = strippedName( currentFile ); } setWindowTitle( tr("%1 - %2").arg(shownName).arg(tr("glogg")) #ifdef GLOGG_COMMIT + " (dev build " GLOGG_VERSION ")" #endif ); } // Updates the actions for the recent files. // Must be called after having added a new name to the list. void MainWindow::updateRecentFileActions() { QStringList recent_files = recentFiles.recentFiles(); for ( int j = 0; j < MaxRecentFiles; ++j ) { if ( j < recent_files.count() ) { QString text = tr("&%1 %2").arg(j + 1).arg(strippedName(recent_files[j])); recentFileActions[j]->setText( text ); recentFileActions[j]->setToolTip( recent_files[j] ); recentFileActions[j]->setData( recent_files[j] ); recentFileActions[j]->setVisible( true ); } else { recentFileActions[j]->setVisible( false ); } } // separatorAction->setVisible(!recentFiles.isEmpty()); } // Write settings to permanent storage void MainWindow::writeSettings() { // Save the session SessionInfo& session = Persistent( "session" ); session.setGeometry( saveGeometry() ); session.setCrawlerState( crawlerWidget->saveState() ); session.setCurrentFile( currentFile ); GetPersistentInfo().save( QString( "session" ) ); // User settings GetPersistentInfo().save( QString( "settings" ) ); } // Read settings from permanent storage void MainWindow::readSettings() { // Get and restore the session GetPersistentInfo().retrieve( QString( "session" ) ); SessionInfo session = Persistent( "session" ); restoreGeometry( session.geometry() ); crawlerWidget->restoreState( session.crawlerState() ); previousFile = session.currentFile(); // History of recent files GetPersistentInfo().retrieve( QString( "recentFiles" ) ); updateRecentFileActions(); GetPersistentInfo().retrieve( QString( "savedSearches" ) ); GetPersistentInfo().retrieve( QString( "settings" ) ); GetPersistentInfo().retrieve( QString( "filterSet" ) ); } // Returns the size in human readable format QString MainWindow::readableSize( qint64 size ) const { static const QString sizeStrs[] = { tr("B"), tr("KiB"), tr("MiB"), tr("GiB"), tr("TiB") }; QLocale defaultLocale; unsigned int i; double humanSize = size; for ( i=0; i+1 < (sizeof(sizeStrs)/sizeof(QString)) && (humanSize/1024.0) >= 1024.0; i++ ) humanSize /= 1024.0; if ( humanSize >= 1024.0 ) { humanSize /= 1024.0; i++; } QString output; if ( i == 0 ) // No decimal part if we display straight bytes. output = defaultLocale.toString( (int) humanSize ); else output = defaultLocale.toString( humanSize, 'f', 1 ); output += QString(" ") + sizeStrs[i]; return output; } glogg-0.9.2/mainwindow.h000066400000000000000000000100711222324246300151450ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2011, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include "crawlerwidget.h" #include "infoline.h" class QAction; class RecentFiles; class MenuActionToolTipBehavior; // Main window of the application, creates menus, toolbar and // the CrawlerWidget class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(); // Loads the initial file (parameter passed or from config file) void loadInitialFile( QString fileName ); protected: void closeEvent( QCloseEvent* event ); // Drag and drop support void dragEnterEvent( QDragEnterEvent* event ); void dropEvent( QDropEvent* event ); private slots: void open(); void openRecentFile(); void selectAll(); void copy(); void find(); void reload(); void stop(); void filters(); void options(); void about(); void aboutQt(); // Change the view settings void toggleOverviewVisibility( bool isVisible ); void toggleMainLineNumbersVisibility( bool isVisible ); void toggleFilteredLineNumbersVisibility( bool isVisible ); // Disable the follow mode checkbox and send the followSet signal down void disableFollow(); // Update the line number displayed in the status bar. // Must be passed as the internal (starts at 0) line number. void lineNumberHandler( int line ); // Instructs the widget to update the loading progress gauge void updateLoadingProgress( int progress ); // Instructs the widget to display the 'normal' status bar, // without the progress gauge and with file info void displayNormalStatus( bool success ); signals: // Is emitted when new settings must be used void optionsChanged(); // Is emitted when the 'follow' option is enabled/disabled void followSet( bool checked ); private: void createActions(); void createMenus(); void createContextMenu(); void createToolBars(); void createStatusBar(); void createCrawler(); void createRecentFileToolTipTimer(); void readSettings(); void writeSettings(); bool loadFile( const QString& fileName ); void setCurrentFile( const QString& fileName ); void updateRecentFileActions(); QString strippedName( const QString& fullFileName ) const; // Returns the size in human readable format QString readableSize( qint64 size ) const; SavedSearches *savedSearches; CrawlerWidget *crawlerWidget; RecentFiles& recentFiles; QString loadingFileName; QString currentFile; QString previousFile; enum { MaxRecentFiles = 5 }; QAction *recentFileActions[MaxRecentFiles]; MenuActionToolTipBehavior *recentFileActionBehaviors[MaxRecentFiles]; QAction *separatorAction; QMenu *fileMenu; QMenu *editMenu; QMenu *viewMenu; QMenu *toolsMenu; QMenu *helpMenu; InfoLine *infoLine; QLabel* lineNbField; QToolBar *toolBar; QAction *openAction; QAction *exitAction; QAction *copyAction; QAction *selectAllAction; QAction *findAction; QAction *overviewVisibleAction; QAction *lineNumbersVisibleInMainAction; QAction *lineNumbersVisibleInFilteredAction; QAction *followAction; QAction *reloadAction; QAction *stopAction; QAction *filtersAction; QAction *optionsAction; QAction *aboutAction; QAction *aboutQtAction; QIcon mainIcon_; }; #endif glogg-0.9.2/marks.cpp000066400000000000000000000042451222324246300144470ustar00rootroot00000000000000/* * Copyright (C) 2011 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #include "marks.h" #include "log.h" #include "utils.h" // This file implements the list of marks for a file. // It is implemented as a QList which is kept in order when inserting, // it is simpler than something fancy like a heap, and insertion are // not done very often anyway. Oh and we need to iterate through the // list, disqualifying a straight heap. Marks::Marks() : marks_() { } void Marks::addMark( qint64 line, QChar mark ) { // Look for the index immediately before int index; if ( ! lookupLineNumber< QList >( marks_, line, &index ) ) { // If a mark is not already set for this line LOG(logDEBUG) << "Inserting mark at line " << line << " (index " << index << ")"; marks_.insert( index, Mark( line ) ); } else { LOG(logERROR) << "Trying to add an existing mark at line " << line; } // 'mark' is not used yet mark = mark; } qint64 Marks::getMark( QChar mark ) const { // 'mark' is not used yet mark = mark; return 0; } bool Marks::isLineMarked( qint64 line ) const { int index; return lookupLineNumber< QList >( marks_, line, &index ); } void Marks::deleteMark( QChar mark ) { // 'mark' is not used yet mark = mark; } void Marks::deleteMark( qint64 line ) { int index; if ( lookupLineNumber< QList >( marks_, line, &index ) ) { marks_.removeAt( index ); } } void Marks::clear() { marks_.clear(); } glogg-0.9.2/marks.h000066400000000000000000000062271222324246300141160ustar00rootroot00000000000000/* * Copyright (C) 2011 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef MARKS_H #define MARKS_H #include #include // Class encapsulating a single mark // Contains the line number the mark is identifying. class Mark { public: Mark( int line ) { lineNumber_ = line; }; // Accessors int lineNumber() const { return lineNumber_; } private: int lineNumber_; }; // A list of marks, i.e. line numbers optionally associated to an // identifying character. class Marks { public: // Create an empty Marks Marks(); // Add a mark at the given line, optionally identified by the given char // If a mark for this char already exist, the previous one is replaced. // It will happily add marks anywhere, even at stupid indexes. void addMark( qint64 line, QChar mark = QChar() ); // Get the (unique) mark identified by the passed char. qint64 getMark( QChar mark ) const; // Returns wheither the passed line has a mark on it. bool isLineMarked( qint64 line ) const; // Delete the mark identified by the passed char. void deleteMark( QChar mark ); // Delete the mark present on the passed line or do nothing if there is // none. void deleteMark( qint64 line ); // Get the line marked identified by the index (in this list) passed. qint64 getLineMarkedByIndex( int index ) const { return marks_[index].lineNumber(); } // Return the total number of marks int size() const { return marks_.size(); } // Completely clear the marks list. void clear(); // Iterator // Provide a const_iterator for the client to iterate through the marks. class const_iterator { public: const_iterator( QList::const_iterator iter ) { internal_iter_ = iter; } const_iterator( const const_iterator& original ) { internal_iter_ = original.internal_iter_; } const Mark& operator*() { return *internal_iter_; } const Mark* operator->() { return &(*internal_iter_); } bool operator!=( const const_iterator& other ) const { return ( internal_iter_ != other.internal_iter_ ); } const_iterator& operator++() { ++internal_iter_ ; return *this; } private: QList::const_iterator internal_iter_; }; const_iterator begin() const { return const_iterator( marks_.begin() ); } const_iterator end() const { return const_iterator( marks_.end() ); } private: // List of marks. QList marks_; }; #endif glogg-0.9.2/menuactiontooltipbehavior.cpp000066400000000000000000000073311222324246300206260ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #include #include #include #include #include "menuactiontooltipbehavior.h" // It would be nice to only need action, and have action be the parent, // however implementation needs the parent menu (see showToolTip), and // since neither action nor parent is guaranteed to die before the other, // for memory-management purposes the parent will have to be specified // explicity (probably, the window owning the action and the menu). MenuActionToolTipBehavior::MenuActionToolTipBehavior(QAction *action, QMenu *parentMenu, QObject *parent = 0) : QObject(parent), action(action), parentMenu(parentMenu), toolTipDelayMs(1000), timerId(0), hoverPoint() { connect(action, SIGNAL(hovered()), this, SLOT(onActionHovered())); } int MenuActionToolTipBehavior::toolTipDelay() { return toolTipDelayMs; } void MenuActionToolTipBehavior::setToolTipDelay(int delayMs) { toolTipDelayMs = delayMs; } void MenuActionToolTipBehavior::timerEvent(QTimerEvent *event) { // Not ours, don't touch if (event->timerId() != timerId) { QObject::timerEvent(event); return; } killTimer(timerId); // interested in a single shot timerId = 0; // Has the mouse waited unmoved in one location for 'delay' ms? const QPoint &mousePos = QCursor::pos(); if (hoverPoint == mousePos) showToolTip(hoverPoint); } void MenuActionToolTipBehavior::onActionHovered() { const QPoint &mousePos = QCursor::pos(); // Hover is fired on keyboard focus over action in menu, ignore it const QPoint &relativeMousePos = parentMenu->mapFromGlobal(mousePos); if (!parentMenu->actionGeometry(action).contains(relativeMousePos)) { if (timerId != 0) { // once timer expires its check will fail anyway killTimer(timerId); timerId = 0; } QToolTip::hideText(); // there might be one currently shown return; } // Record location hoverPoint = mousePos; // Restart timer if (timerId != 0) killTimer(timerId); timerId = startTimer(toolTipDelayMs); } void MenuActionToolTipBehavior::showToolTip(const QPoint &position) { const QString &toolTip = action->toolTip(); // Show tooltip until mouse moves at all // NOTE: using action->parentWidget() which is the MainWindow, // does not work (tooltip is not cleared when upon leaving the // region). This is the only reason we need parentMenu here. Just // a wild guess: maybe it isn't cleared because it would be // cleared on a mouse move over the designated widget, but mouse // move doesn't happen over MainWindow, since the mouse is over // the menu even when out of the activeRegion. QPoint relativePos = parentMenu->mapFromGlobal(position); QRect activeRegion(relativePos.x(), relativePos.y(), 1, 1); QToolTip::showText(position, toolTip, parentMenu, activeRegion); } glogg-0.9.2/menuactiontooltipbehavior.h000066400000000000000000000032511222324246300202700ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef MANUACTIONTOOLTIPBEHAVIOR_H #define MANUACTIONTOOLTIPBEHAVIOR_H #include #include class QAction; class QMenu; class QTimerEvent; // Provides a behavior to show an action's tooltip after mouse is unmoved for // a specified number of 'ms'. E.g. used for tooltips with full-path for recent // files in the file menu. Not thread-safe. class MenuActionToolTipBehavior : public QObject { Q_OBJECT; public: MenuActionToolTipBehavior(QAction *action, QMenu *parentMenu, QObject *parent); // Time in ms that mouse needs to stay unmoved for tooltip to be shown int toolTipDelay(); /* ms */ void setToolTipDelay(int ms); private: void timerEvent(QTimerEvent *event); void showToolTip(const QPoint &position); private slots: void onActionHovered(); private: QAction *action; QMenu *parentMenu; int toolTipDelayMs; int timerId; QPoint hoverPoint; }; #endif glogg-0.9.2/optionsdialog.cpp000066400000000000000000000121741222324246300162050ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2011, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #include #include "optionsdialog.h" #include "log.h" #include "persistentinfo.h" #include "configuration.h" // Constructor OptionsDialog::OptionsDialog( QWidget* parent ) : QDialog(parent) { setupUi( this ); setupFontList(); setupRegexp(); connect(buttonBox, SIGNAL( clicked( QAbstractButton* ) ), this, SLOT( onButtonBoxClicked( QAbstractButton* ) ) ); connect(fontFamilyBox, SIGNAL( currentIndexChanged(const QString& ) ), this, SLOT( updateFontSize( const QString& ) )); connect(incrementalCheckBox, SIGNAL( toggled( bool ) ), this, SLOT( onIncrementalChanged() ) ); updateDialogFromConfig(); setupIncremental(); } // // Private functions // // Populates the 'family' ComboBox void OptionsDialog::setupFontList() { QFontDatabase database; // We only show the fixed fonts foreach ( const QString &str, database.families() ) { if ( database.isFixedPitch( str ) ) fontFamilyBox->addItem( str ); } } // Populate the regexp ComboBoxes void OptionsDialog::setupRegexp() { QStringList regexpTypes; regexpTypes << tr("Extended Regexp") << tr("Wildcards") << tr("Fixed Strings"); mainSearchBox->addItems( regexpTypes ); quickFindSearchBox->addItems( regexpTypes ); } // Enable/disable the QuickFind options depending on the state // of the "incremental" checkbox. void OptionsDialog::setupIncremental() { if ( incrementalCheckBox->isChecked() ) { quickFindSearchBox->setCurrentIndex( getRegexpIndex( FixedString ) ); quickFindSearchBox->setEnabled( false ); } else { quickFindSearchBox->setEnabled( true ); } } // Convert a regexp type to its index in the list int OptionsDialog::getRegexpIndex( SearchRegexpType syntax ) const { return static_cast( syntax ); } // Convert the index of a regexp type to its type SearchRegexpType OptionsDialog::getRegexpTypeFromIndex( int index ) const { return static_cast( index );; } // Updates the dialog box using values in global Config() void OptionsDialog::updateDialogFromConfig() { Configuration& config = Persistent( "settings" ); // Main font QFontInfo fontInfo = QFontInfo( config.mainFont() ); int familyIndex = fontFamilyBox->findText( fontInfo.family() ); if ( familyIndex != -1 ) fontFamilyBox->setCurrentIndex( familyIndex ); int sizeIndex = fontSizeBox->findText( QString::number(fontInfo.pointSize()) ); if ( sizeIndex != -1 ) fontSizeBox->setCurrentIndex( sizeIndex ); // Regexp types mainSearchBox->setCurrentIndex( getRegexpIndex( config.mainRegexpType() ) ); quickFindSearchBox->setCurrentIndex( getRegexpIndex( config.quickfindRegexpType() ) ); incrementalCheckBox->setChecked( config.isQuickfindIncremental() ); } // // Slots // void OptionsDialog::updateFontSize(const QString& fontFamily) { QFontDatabase database; QString oldFontSize = fontSizeBox->currentText(); QList sizes = database.pointSizes( fontFamily, "" ); fontSizeBox->clear(); foreach (int size, sizes) { fontSizeBox->addItem( QString::number(size) ); } // Now restore the size we had before int i = fontSizeBox->findText(oldFontSize); if ( i != -1 ) fontSizeBox->setCurrentIndex(i); } void OptionsDialog::updateConfigFromDialog() { Configuration& config = Persistent( "settings" ); QFont font = QFont( fontFamilyBox->currentText(), (fontSizeBox->currentText()).toInt() ); config.setMainFont(font); config.setMainRegexpType( getRegexpTypeFromIndex( mainSearchBox->currentIndex() ) ); config.setQuickfindRegexpType( getRegexpTypeFromIndex( quickFindSearchBox->currentIndex() ) ); config.setQuickfindIncremental( incrementalCheckBox->isChecked() ); emit optionsChanged(); } void OptionsDialog::onButtonBoxClicked( QAbstractButton* button ) { QDialogButtonBox::ButtonRole role = buttonBox->buttonRole( button ); if ( ( role == QDialogButtonBox::AcceptRole ) || ( role == QDialogButtonBox::ApplyRole ) ) { updateConfigFromDialog(); } if ( role == QDialogButtonBox::AcceptRole ) accept(); else if ( role == QDialogButtonBox::RejectRole ) reject(); } void OptionsDialog::onIncrementalChanged() { setupIncremental(); } glogg-0.9.2/optionsdialog.h000066400000000000000000000035431222324246300156520ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef OPTIONSDIALOG_H #define OPTIONSDIALOG_H #include #include "configuration.h" #include "ui_optionsdialog.h" // Implements the main option dialog box class OptionsDialog : public QDialog, public Ui::OptionsDialog { Q_OBJECT public: OptionsDialog(QWidget* parent = 0); signals: // Is emitted when new settings must be used void optionsChanged(); private slots: // Clears and updates the font size box with the sizes allowed // by the passed font family. void updateFontSize(const QString& fontFamily); // Update the content of the global Config() using parameters // from the dialog box. void updateConfigFromDialog(); // Called when a ok/cancel/apply button is clicked. void onButtonBoxClicked( QAbstractButton* button ); // Called when the 'incremental' button is toggled. void onIncrementalChanged(); private: void setupFontList(); void setupRegexp(); void setupIncremental(); int getRegexpIndex( SearchRegexpType syntax ) const; SearchRegexpType getRegexpTypeFromIndex( int index ) const; void updateDialogFromConfig(); }; #endif glogg-0.9.2/optionsdialog.ui000066400000000000000000000075051222324246300160420ustar00rootroot00000000000000 OptionsDialog 0 0 411 303 0 0 Options 60 260 341 32 Qt::Horizontal QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok 11 89 389 161 Search options 10 30 371 111 Main search type: QuickFind search type: Qt::LeftToRight Incremental QuickFind 11 11 389 71 Font 10 30 371 31 Family: Size: 10 10 true true true glogg-0.9.2/overview.cpp000066400000000000000000000101311222324246300151670ustar00rootroot00000000000000/* * Copyright (C) 2011, 2012 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // This file implements the Overview class. // It provides support for drawing the match overview sidebar but // the actual drawing is done in AbstractLogView which uses this class. #include "log.h" #include "overview.h" Overview::Overview() : matchLines_(), markLines_() { logFilteredData_ = NULL; linesInFile_ = 0; topLine_ = 0; nbLines_ = 0; height_ = 0; dirty_ = true; visible_ = false; } Overview::~Overview() { } void Overview::setFilteredData( const LogFilteredData* logFilteredData ) { logFilteredData_ = logFilteredData; } void Overview::updateData( int totalNbLine ) { LOG(logDEBUG) << "OverviewWidget::updateData " << totalNbLine; linesInFile_ = totalNbLine; dirty_ = true; } void Overview::updateView( int height ) { // We don't touch the cache if the height hasn't changed if ( ( height != height_ ) || ( dirty_ == true ) ) { height_ = height; recalculatesLines(); } } const QVector* Overview::getMatchLines() const { return &matchLines_; } const QVector* Overview::getMarkLines() const { return &markLines_; } std::pair Overview::getViewLines() const { int top = 0; int bottom = height_ - 1; if ( linesInFile_ > 0 ) { top = (int)((qint64)topLine_ * height_ / linesInFile_); bottom = (int)((qint64)top + nbLines_ * height_ / linesInFile_); } return std::pair(top, bottom); } int Overview::fileLineFromY( int position ) const { int line = (int)((qint64)position * linesInFile_ / height_); return line; } int Overview::yFromFileLine( int file_line ) const { int position = 0; if ( linesInFile_ > 0 ) position = (int)((qint64)file_line * height_ / linesInFile_); return position; } // Update the internal cache void Overview::recalculatesLines() { LOG(logDEBUG) << "OverviewWidget::recalculatesLines"; if ( logFilteredData_ != NULL ) { matchLines_.clear(); markLines_.clear(); for ( int i = 0; i < logFilteredData_->getNbLine(); i++ ) { LogFilteredData::FilteredLineType line_type = logFilteredData_->filteredLineTypeByIndex( i ); int line = (int) logFilteredData_->getMatchingLineNumber( i ); int position = (int)( (qint64)line * height_ / linesInFile_ ); if ( line_type == LogFilteredData::Match ) { if ( ( ! matchLines_.isEmpty() ) && matchLines_.last().position() == position ) { // If the line is already there, we increase its weight matchLines_.last().load(); } else { // If not we just add it matchLines_.append( WeightedLine( position ) ); } } else { if ( ( ! markLines_.isEmpty() ) && markLines_.last().position() == position ) { // If the line is already there, we increase its weight markLines_.last().load(); } else { // If not we just add it markLines_.append( WeightedLine( position ) ); } } } } else LOG(logERROR) << "Overview::recalculatesLines: logFilteredData_ == NULL"; dirty_ = false; } glogg-0.9.2/overview.h000066400000000000000000000076721222324246300146540ustar00rootroot00000000000000/* * Copyright (C) 2011, 2012 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef OVERVIEW_H #define OVERVIEW_H #include #include "logfiltereddata.h" // Class implementing the logic behind the matches overview bar. // This class converts the matches found in a LogFilteredData in // a screen dependent set of coloured lines, which is cached. // This class is not a UI class, actual display is left to the client. // // This class is NOT thread-safe. class Overview { public: // A line with a position in pixel and a weight (darkness) class WeightedLine { public: static const int WEIGHT_STEPS = 3; WeightedLine() { pos_ = 0; weight_ = 0; } // (Necessary for QVector) WeightedLine( int pos ) { pos_ = pos; weight_ = 0; } int position() const { return pos_; } int weight() const { return weight_; } void load() { weight_ = qMin( weight_ + 1, WEIGHT_STEPS - 1 ); } private: int pos_; int weight_; }; Overview(); ~Overview(); // Associate the passed filteredData to this Overview void setFilteredData( const LogFilteredData* logFilteredData ); // Signal the overview its attached LogFilteredData has been changed and // the overview must be updated with the provided total number // of line of the file. void updateData( int totalNbLine ); // Set the visibility flag of this overview. void setVisible( bool visible ) { visible_ = visible; dirty_ = visible; } // Update the current position in the file (to draw the view line) void updateCurrentPosition( int firstLine, int lastLine ) { topLine_ = firstLine; nbLines_ = lastLine - firstLine; } // Returns weither this overview is visible. bool isVisible() { return visible_; } // Signal the overview the height of the display has changed, triggering // an update of its cache. void updateView( int height ); // Returns a list of lines (between 0 and 'height') representing matches. // (pointer returned is valid until next call to update*() const QVector* getMatchLines() const; // Returns a list of lines (between 0 and 'height') representing marks. // (pointer returned is valid until next call to update*() const QVector* getMarkLines() const; // Return a pair of lines (between 0 and 'height') representing the current view. std::pair getViewLines() const; // Return the line number corresponding to the passed overview y coordinate. int fileLineFromY( int y ) const; // Return the y coordinate corresponding to the passed line number. int yFromFileLine( int file_line ) const; private: // List of matches associated with this Overview. const LogFilteredData* logFilteredData_; // Total number of lines in the file. int linesInFile_; // Whether the overview is visible. bool visible_; // First and last line currently viewed. int topLine_; int nbLines_; // Current height of view window. int height_; // Does the cache (matchesLines, markLines) need to be recalculated. int dirty_; // List of lines representing matches and marks (are shared with the client) QVector matchLines_; QVector markLines_; void recalculatesLines(); }; #endif glogg-0.9.2/overviewwidget.cpp000066400000000000000000000204471222324246300164060ustar00rootroot00000000000000/* * Copyright (C) 2011, 2012, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // This file implements OverviewWidget. This class is responsable for // managing and painting the matches overview widget. #include #include #include #include "log.h" #include "overviewwidget.h" #include "overview.h" // Graphic parameters const int OverviewWidget::LINE_MARGIN = 4; const int OverviewWidget::STEP_DURATION_MS = 30; const int OverviewWidget::INITIAL_TTL_VALUE = 5; #define HIGHLIGHT_XPM_WIDTH 27 #define HIGHLIGHT_XPM_HEIGHT 9 #define S(x) #x #define SX(x) S(x) // width height colours char/pixel // Colours #define HIGHLIGHT_XPM_LEAD_LINE SX(HIGHLIGHT_XPM_WIDTH) " " SX(HIGHLIGHT_XPM_HEIGHT) " 2 1",\ " s mask c none",\ "x c #572F80" const char* const highlight_xpm[][14] = { { HIGHLIGHT_XPM_LEAD_LINE, " ", " ", " xxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxx ", " xx xx ", " xx xx ", " xx xx ", " xxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxx ", " ", " ", }, { HIGHLIGHT_XPM_LEAD_LINE, " ", " xxxxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxxxx ", " xxx xxx ", " xxx xxx ", " xxx xxx ", " xxxxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxxxx ", " ", }, { HIGHLIGHT_XPM_LEAD_LINE, " xxxxxxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxxxxxx ", " xxxx xxxx ", " xxxx xxxx ", " xxxx xxxx ", " xxxxxxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxxxxxx ", }, { HIGHLIGHT_XPM_LEAD_LINE, " ", " xxxxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxxxx ", " xxx xxx ", " xxx xxx ", " xxx xxx ", " xxxxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxxxx ", " ", }, { HIGHLIGHT_XPM_LEAD_LINE, " ", " ", " xxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxx ", " xx xx ", " xx xx ", " xx xx ", " xxxxxxxxxxxxxxxxxxxxx ", " xxxxxxxxxxxxxxxxxxxxx ", " ", " ", }, { HIGHLIGHT_XPM_LEAD_LINE, " ", " ", " ", " xxxxxxxxxxxxxxxxxxx ", " x x ", " x x ", " x x ", " xxxxxxxxxxxxxxxxxxx ", " ", " ", " ", }, }; OverviewWidget::OverviewWidget( QWidget* parent ) : QWidget( parent ), highlightTimer_() { overview_ = NULL; setBackgroundRole( QPalette::Window ); // Highlight highlightedLine_ = -1; highlightedTTL_ = 0; // We should be hidden by default (e.g. for the FilteredView) hide(); } void OverviewWidget::paintEvent( QPaintEvent* paintEvent ) { LOG(logDEBUG) << "OverviewWidget::paintEvent"; static const QColor match_color("red"); static const QColor mark_color("dodgerblue"); static const QPixmap highlight_pixmap[] = { QPixmap( highlight_xpm[0] ), QPixmap( highlight_xpm[1] ), QPixmap( highlight_xpm[2] ), QPixmap( highlight_xpm[3] ), QPixmap( highlight_xpm[4] ), QPixmap( highlight_xpm[5] ), }; // We must be hidden until we have an Overview assert( overview_ != NULL ); overview_->updateView( height() ); { QPainter painter( this ); // The line separating from the main view painter.setPen( palette().color(QPalette::Text) ); painter.drawLine( 0, 0, 0, height() ); // The 'match' lines painter.setPen( match_color ); foreach (Overview::WeightedLine line, *(overview_->getMatchLines()) ) { painter.setOpacity( ( 1.0 / Overview::WeightedLine::WEIGHT_STEPS ) * ( line.weight() + 1 ) ); // (allow multiple matches to look 'darker' than a single one.) painter.drawLine( 1 + LINE_MARGIN, line.position(), width() - LINE_MARGIN - 1, line.position() ); } // The 'mark' lines painter.setPen( mark_color ); foreach (Overview::WeightedLine line, *(overview_->getMarkLines()) ) { painter.setOpacity( ( 1.0 / Overview::WeightedLine::WEIGHT_STEPS ) * ( line.weight() + 1 ) ); // (allow multiple matches to look 'darker' than a single one.) painter.drawLine( 1 + LINE_MARGIN, line.position(), width() - LINE_MARGIN - 1, line.position() ); } // The 'view' lines painter.setOpacity( 1 ); painter.setPen( palette().color(QPalette::Text) ); std::pair view_lines = overview_->getViewLines(); painter.drawLine( 1, view_lines.first, width(), view_lines.first ); painter.drawLine( 1, view_lines.second, width(), view_lines.second ); // The highlight if ( highlightedLine_ >= 0 ) { /* QPen highlight_pen( palette().color(QPalette::Text) ); highlight_pen.setWidth( 4 - highlightedTTL_ ); painter.setOpacity( 1 ); painter.setPen( highlight_pen ); painter.drawRect( 2, position - 2, width() - 2 - 2, 4 ); */ int position = overview_->yFromFileLine( highlightedLine_ ); painter.drawPixmap( ( width() - HIGHLIGHT_XPM_WIDTH ) / 2, position - ( HIGHLIGHT_XPM_HEIGHT / 2 ), highlight_pixmap[ INITIAL_TTL_VALUE - highlightedTTL_ ] ); } } } void OverviewWidget::mousePressEvent( QMouseEvent* mouseEvent ) { if ( mouseEvent->button() == Qt::LeftButton ) handleMousePress( mouseEvent->y() ); } void OverviewWidget::mouseMoveEvent( QMouseEvent* mouseEvent ) { if ( mouseEvent->buttons() |= Qt::LeftButton ) handleMousePress( mouseEvent->y() ); } void OverviewWidget::handleMousePress( int position ) { int line = overview_->fileLineFromY( position ); LOG(logDEBUG) << "OverviewWidget::handleMousePress y=" << position << " line=" << line; emit lineClicked( line ); } void OverviewWidget::highlightLine( qint64 line ) { highlightTimer_.stop(); highlightedLine_ = line; highlightedTTL_ = INITIAL_TTL_VALUE; update(); highlightTimer_.start( STEP_DURATION_MS, this ); } void OverviewWidget::removeHighlight() { highlightTimer_.stop(); highlightedLine_ = -1; update(); } void OverviewWidget::timerEvent( QTimerEvent* event ) { if ( event->timerId() == highlightTimer_.timerId() ) { LOG(logDEBUG) << "OverviewWidget::timerEvent"; if ( highlightedTTL_ > 0 ) { --highlightedTTL_; update(); } else { highlightTimer_.stop(); } } else { QObject::timerEvent( event ); } } glogg-0.9.2/overviewwidget.h000066400000000000000000000037171222324246300160540ustar00rootroot00000000000000/* * Copyright (C) 2011, 2012, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef OVERVIEWWIDGET_H #define OVERVIEWWIDGET_H #include #include class Overview; class OverviewWidget : public QWidget { Q_OBJECT public: OverviewWidget( QWidget* parent = 0 ); // Associate the widget with an Overview object. void setOverview( Overview* overview ) { overview_ = overview; } public slots: // Sent when a match at the line passed must be highlighted in // the overview void highlightLine( qint64 line ); void removeHighlight(); protected: void paintEvent( QPaintEvent* paintEvent ); void mousePressEvent( QMouseEvent* mouseEvent ); void mouseMoveEvent( QMouseEvent* mouseEvent ); void timerEvent( QTimerEvent* event ); signals: // Sent when the user click on a line in the Overview. void lineClicked( int line ); private: // Constants static const int LINE_MARGIN; static const int STEP_DURATION_MS; static const int INITIAL_TTL_VALUE; Overview* overview_; // Highlight: // Which line is higlighted, or -1 if none int highlightedLine_; // Number of step until the highlight become static int highlightedTTL_; QBasicTimer highlightTimer_; void handleMousePress( int position ); }; #endif glogg-0.9.2/persistable.h000066400000000000000000000022051222324246300153060ustar00rootroot00000000000000/* * Copyright (C) 2011 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef PERSISTABLE_H #define PERSISTABLE_H class QSettings; // Must be implemented by classes which could be saved to persistent // storage by PersistentInfo. class Persistable { public: virtual ~Persistable() {} // Must be implemented to save/retrieve from Qt Settings virtual void saveToStorage( QSettings& settings ) const = 0; virtual void retrieveFromStorage( QSettings& settings ) = 0; }; #endif glogg-0.9.2/persistentinfo.cpp000066400000000000000000000062201222324246300164010ustar00rootroot00000000000000/* * Copyright (C) 2011 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // Implements PersistentInfo, a singleton class which store/retrieve objects // to persistent storage. #include "persistentinfo.h" #include #include #include "log.h" #include "persistable.h" PersistentInfo::PersistentInfo() { settings_ = NULL; initialised_ = false; } PersistentInfo::~PersistentInfo() { if ( initialised_ ) delete settings_; } void PersistentInfo::migrateAndInit() { assert( initialised_ == false ); #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) // On Windows, we use .ini files and import from the registry if no // .ini file is found (glogg <= 0.9 used the registry). // This store the config file in %appdata% settings_ = new QSettings( QSettings::IniFormat, QSettings::UserScope, "glogg", "glogg" ); if ( settings_->childKeys().count() == 0 ) { LOG(logWARNING) << "INI file empty, trying to import from registry"; QSettings registry( "glogg", "glogg" ); foreach ( QString key, registry.allKeys() ) { settings_->setValue( key, registry.value( key ) ); } } #else // We use default Qt storage on proper OSes settings_ = new QSettings( "glogg", "glogg" ); #endif initialised_ = true; } void PersistentInfo::registerPersistable( Persistable* object, const QString& name ) { assert( initialised_ ); objectList_.insert( name, object ); } Persistable* PersistentInfo::getPersistable( const QString& name ) { assert( initialised_ ); Persistable* object = objectList_.value( name, NULL ); return object; } void PersistentInfo::save( const QString& name ) { assert( initialised_ ); if ( objectList_.contains( name ) ) objectList_.value( name )->saveToStorage( *settings_ ); else LOG(logERROR) << "Unregistered persistable " << name.toStdString(); // Sync to ensure it is propagated to other processes settings_->sync(); } void PersistentInfo::retrieve( const QString& name ) { assert( initialised_ ); // Sync to ensure it has been propagated from other processes settings_->sync(); if ( objectList_.contains( name ) ) objectList_.value( name )->retrieveFromStorage( *settings_ ); else LOG(logERROR) << "Unregistered persistable " << name.toStdString(); } // Friend function to construct/get the singleton PersistentInfo& GetPersistentInfo() { static PersistentInfo pInfo; return pInfo; } glogg-0.9.2/persistentinfo.h000066400000000000000000000044651222324246300160570ustar00rootroot00000000000000/* * Copyright (C) 2011 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef PERSISTENTINFO_H #define PERSISTENTINFO_H #include #include class Persistable; // Singleton class managing the saving of persistent data to permanent storage // Clients must implement Persistable and register with this object, they can // then be saved/loaded. class PersistentInfo { public: // Initialise the storage backend for the Persistable, migrating the settings // if needed. Must be called before any other function. void migrateAndInit(); // Register a Persistable void registerPersistable( Persistable* object, const QString& name ); // Get a Persistable (or NULL if it doesn't exist) Persistable* getPersistable( const QString& name ); // Save a persistable to its permanent storage void save( const QString& name ); // Retrieve a persistable from permanent storage void retrieve( const QString& name ); private: // Can't be constructed or copied (singleton) PersistentInfo(); PersistentInfo( const PersistentInfo& ); ~PersistentInfo(); // Has migrateAndInit() been called? bool initialised_; // List of persistables QHash objectList_; // Qt setting object QSettings* settings_; // allow this function to create one instance friend PersistentInfo& GetPersistentInfo(); }; PersistentInfo& GetPersistentInfo(); // Global function used to get an object from the PersistentInfo store template T& Persistent( const char* name ) { Persistable* p = GetPersistentInfo().getPersistable( QString( name ) ); return dynamic_cast(*p); } #endif glogg-0.9.2/qfnotifications.h000066400000000000000000000040501222324246300161710ustar00rootroot00000000000000/* * Copyright (C) 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef QFNOTIFICATIONS_H #define QFNOTIFICATIONS_H #include #include #include // Notifications sent by the QF for displaying to the user // and their translation in UI text. class QFNotification { public: virtual QString message() const = 0; // Max width of the message (in pixels) static int maxWidth( const QWidget* widget ) { QFontMetrics fm = widget->fontMetrics(); return qMax( fm.size( Qt::TextSingleLine, REACHED_BOF ).width(), fm.size( Qt::TextSingleLine, REACHED_EOF ).width() ); } protected: static const QString REACHED_EOF; static const QString REACHED_BOF; }; class QFNotificationReachedEndOfFile : public QFNotification { QString message() const { return REACHED_EOF; } }; class QFNotificationReachedBegininningOfFile : public QFNotification { QString message() const { return REACHED_BOF; } }; class QFNotificationProgress : public QFNotification { public: // Constructor taking the progress (in percent) QFNotificationProgress( int progress_percent ) { progressPercent_ = progress_percent; } QString message() const { return QString( QObject::tr("Searching (position %1 %)") .arg( progressPercent_ ) ); } private: int progressPercent_; }; #endif glogg-0.9.2/quickfind.cpp000066400000000000000000000261551222324246300153130ustar00rootroot00000000000000/* * Copyright (C) 2010, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // This file implements QuickFind. // This class implements the Quick Find mechanism using references // to the logData, the QFP and the selection passed. // Search is started just after the selection and the selection is updated // if a match is found. #include #include "log.h" #include "quickfindpattern.h" #include "selection.h" #include "quickfind.h" void SearchingNotifier::reset() { dotToDisplay_ = 0; startTime_ = QTime::currentTime(); } void SearchingNotifier::sendNotification( qint64 current_line, qint64 nb_lines ) { LOG( logDEBUG ) << "Emitting Searching...."; qint64 progress; if ( current_line < 0 ) progress = ( nb_lines + current_line ) * 100 / nb_lines; else progress = current_line * 100 / nb_lines; emit notify( QFNotificationProgress( progress ) ); QApplication::processEvents( QEventLoop::ExcludeUserInputEvents ); startTime_ = QTime::currentTime().addMSecs( -800 ); } void QuickFind::LastMatchPosition::set( int line, int column ) { if ( ( line_ == -1 ) || ( ( line <= line_ ) && ( column < column_ ) ) ) { line_ = line; column_ = column; } } void QuickFind::LastMatchPosition::set( const FilePosition &position ) { set( position.line(), position.column() ); } bool QuickFind::LastMatchPosition::isLater( int line, int column ) const { if ( line_ == -1 ) return false; else if ( ( line == line_ ) && ( column >= column_ ) ) return true; else if ( line > line_ ) return true; else return false; } bool QuickFind::LastMatchPosition::isLater( const FilePosition &position ) const { return isLater( position.line(), position.column() ); } bool QuickFind::LastMatchPosition::isSooner( int line, int column ) const { if ( line_ == -1 ) return false; else if ( ( line == line_ ) && ( column <= column_ ) ) return true; else if ( line < line_ ) return true; else return false; } bool QuickFind::LastMatchPosition::isSooner( const FilePosition &position ) const { return isSooner( position.line(), position.column() ); } QuickFind::QuickFind( const AbstractLogData* const logData, Selection* selection, const QuickFindPattern* const quickFindPattern ) : logData_( logData ), selection_( selection ), quickFindPattern_( quickFindPattern ), lastMatch_(), firstMatch_(), searchingNotifier_(), incrementalSearchStatus_() { connect( &searchingNotifier_, SIGNAL( notify( const QFNotification& ) ), this, SIGNAL( notify( const QFNotification& ) ) ); } void QuickFind::incrementalSearchStop() { if ( incrementalSearchStatus_.isOngoing() ) { if ( selection_->isEmpty() ) { // Nothing found? // We reset the selection to what it was *selection_ = incrementalSearchStatus_.initialSelection(); } incrementalSearchStatus_ = IncrementalSearchStatus(); } } void QuickFind::incrementalSearchAbort() { if ( incrementalSearchStatus_.isOngoing() ) { // We reset the selection to what it was *selection_ = incrementalSearchStatus_.initialSelection(); incrementalSearchStatus_ = IncrementalSearchStatus(); } } qint64 QuickFind::incrementallySearchForward() { LOG( logDEBUG ) << "QuickFind::incrementallySearchForward"; // Position where we start the search from FilePosition start_position = selection_->getNextPosition(); if ( incrementalSearchStatus_.direction() == Forward ) { // An incremental search is active, we restart the search // from the initial point LOG( logDEBUG ) << "Restart search from initial point"; start_position = incrementalSearchStatus_.position(); } else { // It's a new search so we search from the selection incrementalSearchStatus_ = IncrementalSearchStatus( Forward, start_position, *selection_ ); } qint64 line_found = doSearchForward( start_position ); if ( line_found >= 0 ) { // We have found a result... // ... the caller will jump to this line. return line_found; } else { // No result... // ... we want the client to show the initial line. selection_->clear(); return incrementalSearchStatus_.position().line(); } } qint64 QuickFind::incrementallySearchBackward() { LOG( logDEBUG ) << "QuickFind::incrementallySearchBackward"; // Position where we start the search from FilePosition start_position = selection_->getPreviousPosition(); if ( incrementalSearchStatus_.direction() == Backward ) { // An incremental search is active, we restart the search // from the initial point LOG( logDEBUG ) << "Restart search from initial point"; start_position = incrementalSearchStatus_.position(); } else { // It's a new search so we search from the selection incrementalSearchStatus_ = IncrementalSearchStatus( Backward, start_position, *selection_ ); } qint64 line_found = doSearchBackward( start_position ); if ( line_found >= 0 ) { // We have found a result... // ... the caller will jump to this line. return line_found; } else { // No result... // ... we want the client to show the initial line. selection_->clear(); return incrementalSearchStatus_.position().line(); } } qint64 QuickFind::searchForward() { incrementalSearchStatus_ = IncrementalSearchStatus(); // Position where we start the search from FilePosition start_position = selection_->getNextPosition(); return doSearchForward( start_position ); } qint64 QuickFind::searchBackward() { incrementalSearchStatus_ = IncrementalSearchStatus(); // Position where we start the search from FilePosition start_position = selection_->getPreviousPosition(); return doSearchBackward( start_position ); } // Internal implementation of forward search, // returns the line where the pattern is found or -1 if not found. // Parameters are the position the search shall start qint64 QuickFind::doSearchForward( const FilePosition &start_position ) { bool found = false; int found_start_col; int found_end_col; if ( ! quickFindPattern_->isActive() ) return -1; // Optimisation: if we are already after the last match, // we don't do any search at all. if ( lastMatch_.isLater( start_position ) ) { // Send a notification emit notify( QFNotificationReachedEndOfFile() ); return -1; } qint64 line = start_position.line(); LOG( logDEBUG ) << "Start searching at line " << line; // We look at the rest of the first line if ( quickFindPattern_->isLineMatching( logData_->getExpandedLineString( line ), start_position.column() ) ) { quickFindPattern_->getLastMatch( &found_start_col, &found_end_col ); found = true; } else { searchingNotifier_.reset(); // And then the rest of the file qint64 nb_lines = logData_->getNbLine(); line++; while ( line < nb_lines ) { if ( quickFindPattern_->isLineMatching( logData_->getExpandedLineString( line ) ) ) { quickFindPattern_->getLastMatch( &found_start_col, &found_end_col ); found = true; break; } line++; // See if we need to notify of the ongoing search searchingNotifier_.ping( line, nb_lines ); } } if ( found ) { selection_->selectPortion( line, found_start_col, found_end_col ); // Clear any notification emit clearNotification(); return line; } else { // Update the position of the last match FilePosition last_match_position = selection_->getPreviousPosition(); lastMatch_.set( last_match_position ); // Send a notification emit notify( QFNotificationReachedEndOfFile() ); return -1; } } // Internal implementation of backward search, // returns the line where the pattern is found or -1 if not found. // Parameters are the position the search shall start qint64 QuickFind::doSearchBackward( const FilePosition &start_position ) { bool found = false; int start_col; int end_col; if ( ! quickFindPattern_->isActive() ) return -1; // Optimisation: if we are already before the first match, // we don't do any search at all. if ( firstMatch_.isSooner( start_position ) ) { // Send a notification emit notify( QFNotificationReachedBegininningOfFile() ); return -1; } qint64 line = start_position.line(); LOG( logDEBUG ) << "Start searching at line " << line; // We look at the beginning of the first line if ( ( start_position.column() > 0 ) && ( quickFindPattern_->isLineMatchingBackward( logData_->getExpandedLineString( line ), start_position.column() ) ) ) { quickFindPattern_->getLastMatch( &start_col, &end_col ); found = true; } else { searchingNotifier_.reset(); // And then the rest of the file qint64 nb_lines = logData_->getNbLine(); line--; while ( line >= 0 ) { if ( quickFindPattern_->isLineMatchingBackward( logData_->getExpandedLineString( line ) ) ) { quickFindPattern_->getLastMatch( &start_col, &end_col ); found = true; break; } line--; // See if we need to notify of the ongoing search searchingNotifier_.ping( -line, nb_lines ); } } if ( found ) { selection_->selectPortion( line, start_col, end_col ); // Clear any notification emit clearNotification(); return line; } else { // Update the position of the first match FilePosition first_match_position = selection_->getNextPosition(); firstMatch_.set( first_match_position ); // Send a notification LOG( logDEBUG ) << "Send notification."; emit notify( QFNotificationReachedBegininningOfFile() ); return -1; } } void QuickFind::resetLimits() { lastMatch_.reset(); firstMatch_.reset(); } glogg-0.9.2/quickfind.h000066400000000000000000000142661222324246300147600ustar00rootroot00000000000000/* * Copyright (C) 2010, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef QUICKFIND_H #define QUICKFIND_H #include #include #include #include "utils.h" #include "qfnotifications.h" #include "selection.h" class QuickFindPattern; class AbstractLogData; class Portion; // Handle "long processing" notifications to the UI. // reset() shall be called at the beginning of the search // and then ping() should be called periodically during the processing. // The notify() signal should be forwarded to the UI. class SearchingNotifier : public QObject { Q_OBJECT public: SearchingNotifier() {}; // Reset internal timers at the beiginning of the processing void reset(); // Shall be called frequently during processing, send the notification // and call the event loop when appropriate. // Pass the current line number and total number of line so that // a progress percentage is calculated and displayed. // (line shall be negative if ging in reverse) inline void ping( qint64 line, qint64 nb_lines ) { if ( startTime_.msecsTo( QTime::currentTime() ) > 1000 ) sendNotification( line, nb_lines ); } signals: // Sent when the UI shall display a message to the user. void notify( const QFNotification& message ); private: void sendNotification( qint64 current_line, qint64 nb_lines ); QTime startTime_; int dotToDisplay_; }; // Represents a search made with Quick Find (without its results) // it keeps a pointer to a set of data and to a QuickFindPattern which // are used for the searches. (the caller retains ownership of both). class QuickFind : public QObject { Q_OBJECT public: // Construct a search QuickFind( const AbstractLogData* const logData, Selection* selection, const QuickFindPattern* const quickFindPattern ); // Set the starting point that will be used by the next search void setSearchStartPoint( QPoint startPoint ); // Used for incremental searches // Return the first occurence of the passed pattern from the starting // point. These searches don't use the QFP and don't change the // starting point. // TODO Update comment qint64 incrementallySearchForward(); qint64 incrementallySearchBackward(); // Stop the currently ongoing incremental search, leave the selection // where it is if a match has been found, restore the old one // if not. Also throw away the start point associated with // the search. void incrementalSearchStop(); // Throw away the current search and restore the initial // position/selection void incrementalSearchAbort(); // Used for 'repeated' (n/N) QF searches using the current direction // Return the line of the first occurence of the QFP and // update the selection. It returns -1 if nothing is found. /* int searchNext(); int searchPrevious(); */ // Idem but ignore the direction and always search in the // specified direction qint64 searchForward(); qint64 searchBackward(); // Make the object forget the 'no more match' flag. void resetLimits(); signals: // Sent when the UI shall display a message to the user. void notify( const QFNotification& message ); // Sent when the UI shall clear the notification. void clearNotification(); private: enum QFDirection { None, Forward, Backward, }; class LastMatchPosition { public: LastMatchPosition() : line_( -1 ), column_( -1 ) {} void set( int line, int column ); void set( const FilePosition& position ); void reset() { line_ = -1; column_ = -1; } // Does the passed position come after the recorded one bool isLater( int line, int column ) const; bool isLater( const FilePosition& position ) const; // Does the passed position come before the recorded one bool isSooner( int line, int column ) const; bool isSooner( const FilePosition& position ) const; private: int line_; int column_; }; class IncrementalSearchStatus { public: /* Constructors */ IncrementalSearchStatus() : ongoing_( None ), position_(), initialSelection_() {} IncrementalSearchStatus( QFDirection direction, const FilePosition& position, const Selection& initial_selection ) : ongoing_( direction ), position_( position ), initialSelection_( initial_selection ) {} bool isOngoing() const { return ( ongoing_ != None ); } QFDirection direction() const { return ongoing_; } FilePosition position() const { return position_; } Selection initialSelection() const { return initialSelection_; } private: QFDirection ongoing_; FilePosition position_; Selection initialSelection_; }; // Pointers to external objects const AbstractLogData* const logData_; Selection* selection_; const QuickFindPattern* const quickFindPattern_; // Owned objects // Position of the last match in the file // (to avoid searching multiple times where there is no result) LastMatchPosition lastMatch_; LastMatchPosition firstMatch_; SearchingNotifier searchingNotifier_; // Incremental search status IncrementalSearchStatus incrementalSearchStatus_; // Private functions qint64 doSearchForward( const FilePosition &start_position ); qint64 doSearchBackward( const FilePosition &start_position ); }; #endif glogg-0.9.2/quickfindmux.cpp000066400000000000000000000123071222324246300160370ustar00rootroot00000000000000/* * Copyright (C) 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #include "log.h" #include "persistentinfo.h" #include "configuration.h" #include "quickfindmux.h" QuickFindMux::QuickFindMux( const QuickFindMuxSelectorInterface* selector ) : QObject(), pattern_() { // The selector object we will use when forwarding search requests selector_ = selector; // Forward the pattern's signal to our listeners connect( &pattern_, SIGNAL( patternUpdated() ), this, SLOT( notifyPatternChanged() ) ); } // // Public member functions // void QuickFindMux::registerSearchable( QObject* searchable ) { // The searchable can change our qf pattern connect( searchable, SIGNAL( changeQuickFind( const QString&, QuickFindMux::QFDirection ) ), this, SLOT( changeQuickFind( const QString&, QuickFindMux::QFDirection ) ) ); // Send us notifications connect( searchable, SIGNAL( notifyQuickFind( const QFNotification& ) ), this, SIGNAL( notify( const QFNotification& ) ) ); // And clear them connect( searchable, SIGNAL( clearQuickFindNotification() ), this, SIGNAL( clearNotification() ) ); // Search can be initiated by the view itself connect( searchable, SIGNAL( searchNext() ), this, SLOT( searchNext() ) ); connect( searchable, SIGNAL( searchPrevious() ), this, SLOT( searchPrevious() ) ); } void QuickFindMux::unregisterSearchable( QObject* searchable ) { disconnect( searchable ); } void QuickFindMux::setDirection( QFDirection direction ) { LOG(logDEBUG) << "QuickFindMux::setDirection: new direction: " << direction; currentDirection_ = direction; } // // Public slots // void QuickFindMux::searchNext() { LOG(logDEBUG) << "QuickFindMux::searchNext"; if ( currentDirection_ == Forward ) searchForward(); else searchBackward(); } void QuickFindMux::searchPrevious() { LOG(logDEBUG) << "QuickFindMux::searchPrevious"; if ( currentDirection_ == Forward ) searchBackward(); else searchForward(); } void QuickFindMux::searchForward() { LOG(logDEBUG) << "QuickFindMux::searchForward"; SearchableWidgetInterface* searchable = getSearchableWidget(); searchable->searchForward(); } void QuickFindMux::searchBackward() { LOG(logDEBUG) << "QuickFindMux::searchBackward"; SearchableWidgetInterface* searchable = getSearchableWidget(); searchable->searchBackward(); } void QuickFindMux::setNewPattern( const QString& new_pattern, bool ignore_case ) { static Configuration& config = Persistent( "settings" ); LOG(logDEBUG) << "QuickFindMux::setNewPattern"; pattern_.changeSearchPattern( new_pattern, ignore_case ); // If we must do an incremental search, we do it now if ( config.isQuickfindIncremental() ) { SearchableWidgetInterface* searchable = getSearchableWidget(); if ( currentDirection_ == Forward ) searchable->incrementallySearchForward(); else searchable->incrementallySearchBackward(); } } void QuickFindMux::confirmPattern( const QString& new_pattern, bool ignore_case ) { static Configuration& config = Persistent( "settings" ); pattern_.changeSearchPattern( new_pattern, ignore_case ); // if non-incremental, we perform the search now if ( ! config.isQuickfindIncremental() ) { searchNext(); } else { SearchableWidgetInterface* searchable = getSearchableWidget(); searchable->incrementalSearchStop(); } } void QuickFindMux::cancelSearch() { static Configuration& config = Persistent( "settings" ); if ( config.isQuickfindIncremental() ) { SearchableWidgetInterface* searchable = getSearchableWidget(); searchable->incrementalSearchAbort(); } } // // Private slots // void QuickFindMux::changeQuickFind( const QString& new_pattern, QFDirection new_direction ) { pattern_.changeSearchPattern( new_pattern ); setDirection( new_direction ); } void QuickFindMux::notifyPatternChanged() { emit patternChanged( pattern_.getPattern() ); } // // Private member functions // // Use the registered 'selector' to determine where to send the search requests. SearchableWidgetInterface* QuickFindMux::getSearchableWidget() const { SearchableWidgetInterface* searchable = NULL; if ( selector_ ) searchable = selector_->getActiveSearchable(); else LOG(logERROR) << "QuickFindMux::getActiveSearchable() no registered selector"; return searchable; } glogg-0.9.2/quickfindmux.h000066400000000000000000000075231222324246300155100ustar00rootroot00000000000000/* * Copyright (C) 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef QUICKFINDMUX_H #define QUICKFINDMUX_H #include #include #include "quickfindpattern.h" // Interface representing a widget searchable in both direction. class SearchableWidgetInterface { public: virtual void searchForward() = 0; virtual void searchBackward() = 0; virtual void incrementallySearchForward() = 0; virtual void incrementallySearchBackward() = 0; virtual void incrementalSearchStop() = 0; virtual void incrementalSearchAbort() = 0; }; // Interface representing the selector. It will be called and asked // who the search have to be forwarded to. class QuickFindMuxSelectorInterface { public: virtual SearchableWidgetInterface* getActiveSearchable() const = 0; }; class QFNotification; // Represents a multiplexer (unique application wise) dispatching the // Quick Find search from the UI to the relevant view. // It is also its responsability to determine if an incremental search // must be performed and to react accordingly. // It owns the QuickFindPattern. class QuickFindMux : public QObject { Q_OBJECT public: enum QFDirection { Forward, Backward, }; // Construct the multiplexer, the selector class will be called // and ask who the search have to be forwarded to. QuickFindMux( const QuickFindMuxSelectorInterface* selector ); // Register/unregister this searchable widget with the mux for messages // to be forwarded back to the client. // The client should call this for each possible searchable widget. void registerSearchable( QObject* searchable ); void unregisterSearchable( QObject* searchable ); // Set the direction that will be used by the search when searching // forward. void setDirection( QFDirection direction ); // Get a pointer to the pattern QuickFindPattern* getPattern() { return &pattern_; } signals: void patternChanged( const QString& ); void notify( const QFNotification& ); void clearNotification(); public slots: // Signal the current pattern must be altered (will start an incremental // search if the options are configured in such a way). void setNewPattern( const QString& new_pattern, bool ignore_case ); // Signal the current pattern must be altered and is confirmed // (will stop an incremental search if needed) void confirmPattern( const QString& new_pattern, bool ignore_case ); // Signal the user cancelled the search // (used for incremental only) void cancelSearch(); // Starts a search in the specified direction void searchNext(); void searchPrevious(); // Idem but ignore the direction and always search in the // specified direction void searchForward(); void searchBackward(); private slots: void changeQuickFind( const QString& new_pattern, QuickFindMux::QFDirection new_direction ); void notifyPatternChanged(); private: const QuickFindMuxSelectorInterface* selector_; // The (application wide) quick find pattern QuickFindPattern pattern_; QFDirection currentDirection_; SearchableWidgetInterface* getSearchableWidget() const; }; #endif glogg-0.9.2/quickfindpattern.cpp000066400000000000000000000065141222324246300167060ustar00rootroot00000000000000/* * Copyright (C) 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // This file implements QuickFindPattern. // This class implements part of the Quick Find mechanism, it only stores the // current search pattern, once it has been confirmed (return pressed), // it can be asked to return the matches in a specific string. #include "quickfindpattern.h" #include "persistentinfo.h" #include "configuration.h" QuickFindPattern::QuickFindPattern() : QObject(), regexp_() { active_ = false; } void QuickFindPattern::changeSearchPattern( const QString& pattern ) { // Determine the type of regexp depending on the config QRegExp::PatternSyntax syntax; switch ( Persistent( "settings" ).quickfindRegexpType() ) { case Wildcard: syntax = QRegExp::Wildcard; break; case FixedString: syntax = QRegExp::FixedString; break; default: syntax = QRegExp::RegExp2; break; } regexp_.setPattern( pattern ); regexp_.setPatternSyntax( syntax ); if ( regexp_.isValid() && ( ! regexp_.isEmpty() ) ) active_ = true; else active_ = false; emit patternUpdated(); } void QuickFindPattern::changeSearchPattern( const QString& pattern, bool ignoreCase ) { regexp_.setCaseSensitivity( ignoreCase ? Qt::CaseInsensitive : Qt::CaseSensitive ); changeSearchPattern( pattern ); } bool QuickFindPattern::matchLine( const QString& line, QList& matches ) const { matches.clear(); if ( active_ ) { int pos = 0; while ( ( pos = regexp_.indexIn( line, pos ) ) != -1 ) { int length = regexp_.matchedLength(); matches << QuickFindMatch( pos, length ); pos += length; } } return ( matches.count() > 0 ); } bool QuickFindPattern::isLineMatching( const QString& line, int column ) const { int pos = 0; if ( ! active_ ) return false; if ( ( pos = regexp_.indexIn( line, column ) ) != -1 ) { lastMatchStart_ = pos; lastMatchEnd_ = pos + regexp_.matchedLength() - 1; return true; } else return false; } bool QuickFindPattern::isLineMatchingBackward( const QString& line, int column ) const { int pos = 0; if ( ! active_ ) return false; if ( ( pos = regexp_.lastIndexIn( line, column ) ) != -1 ) { lastMatchStart_ = pos; lastMatchEnd_ = pos + regexp_.matchedLength() - 1; return true; } else return false; } void QuickFindPattern::getLastMatch( int* start_col, int* end_col ) const { *start_col = lastMatchStart_; *end_col = lastMatchEnd_; } glogg-0.9.2/quickfindpattern.h000066400000000000000000000055771222324246300163630ustar00rootroot00000000000000/* * Copyright (C) 2010, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef QUICKFINDPATTERN_H #define QUICKFINDPATTERN_H #include #include #include #include // Represents a match result for QuickFind class QuickFindMatch { public: // Construct a match (must be initialised) QuickFindMatch( int start_column, int length ) { startColumn_ = start_column; length_ = length; } // Accessor functions int startColumn() const { return startColumn_; } int length() const { return length_; } private: int startColumn_; int length_; }; // Represents a search pattern for QuickFind (without its results) class QuickFindPattern : public QObject { Q_OBJECT public: // Construct an empty search QuickFindPattern(); // Set the search to a new pattern, using the current // case status void changeSearchPattern( const QString& pattern ); // Set the search to a new pattern, as well as the case status void changeSearchPattern( const QString& pattern, bool ignoreCase ); // Returns whether the search is active (i.e. valid and non empty regexp) bool isActive() const { return active_; } // Return the text of the regex QString getPattern() const { return regexp_.pattern(); } // Returns whether the passed line match the quick find search. // If so, it populate the passed list with the list of matches // within this particular line. bool matchLine( const QString& line, QList& matches ) const; // Returns whether there is a match in the passed line, starting at // the passed column. // Results are stored internally. bool isLineMatching( const QString& line, int column = 0 ) const; // Same as isLineMatching but search backward bool isLineMatchingBackward( const QString& line, int column = -1 ) const; // Must be called when isLineMatching returns 'true', returns // the position of the first match found. void getLastMatch( int* start_col, int* end_col ) const; signals: // Sent when the pattern is changed void patternUpdated(); private: bool active_; QRegExp regexp_; mutable int lastMatchStart_; mutable int lastMatchEnd_; }; #endif glogg-0.9.2/quickfindwidget.cpp000066400000000000000000000137321222324246300165140ustar00rootroot00000000000000/* * Copyright (C) 2010, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #include "log.h" #include #include #include #include #include #include "configuration.h" #include "qfnotifications.h" #include "quickfindwidget.h" const int QuickFindWidget::NOTIFICATION_TIMEOUT = 5000; const QString QFNotification::REACHED_EOF = "Reached end of file, no occurence found."; const QString QFNotification::REACHED_BOF = "Reached beginning of file, no occurence found."; QuickFindWidget::QuickFindWidget( QWidget* parent ) : QWidget( parent ) { // ui_.setupUi( this ); // setFocusProxy(ui_.findEdit); // setProperty("topBorder", true); QHBoxLayout *layout = new QHBoxLayout( this ); layout->setMargin( 0 ); layout->setSpacing( 6 ); closeButton_ = setupToolButton( QLatin1String(""), QLatin1String( ":/images/darkclosebutton.png" ) ); layout->addWidget( closeButton_ ); editQuickFind_ = new QLineEdit( this ); // FIXME: set MinimumSize might be to constraining editQuickFind_->setMinimumSize( QSize( 150, 0 ) ); layout->addWidget( editQuickFind_ ); ignoreCaseCheck_ = new QCheckBox( "Ignore &case" ); layout->addWidget( ignoreCaseCheck_ ); previousButton_ = setupToolButton( QLatin1String("Previous"), QLatin1String( ":/images/arrowup.png" ) ); layout->addWidget( previousButton_ ); nextButton_ = setupToolButton( QLatin1String("Next"), QLatin1String( ":/images/arrowdown.png" ) ); layout->addWidget( nextButton_ ); notificationText_ = new QLabel( "" ); // FIXME: set MinimumSize might be too constraining int width = QFNotification::maxWidth( notificationText_ ); notificationText_->setMinimumSize( width, 0 ); layout->addWidget( notificationText_ ); setMinimumWidth( minimumSizeHint().width() ); // Behaviour connect( closeButton_, SIGNAL( clicked() ), SLOT( closeHandler() ) ); connect( editQuickFind_, SIGNAL( textEdited( QString ) ), this, SLOT( textChanged() ) ); connect( ignoreCaseCheck_, SIGNAL( stateChanged( int ) ), this, SLOT( textChanged() ) ); /* connect( editQuickFind_. SIGNAL( textChanged( QString ) ), this, SLOT( updateButtons() ) ); */ connect( editQuickFind_, SIGNAL( returnPressed() ), this, SLOT( returnHandler() ) ); connect( previousButton_, SIGNAL( clicked() ), this, SLOT( doSearchBackward() ) ); connect( nextButton_, SIGNAL( clicked() ), this, SLOT( doSearchForward() ) ); // Notification timer: notificationTimer_ = new QTimer( this ); notificationTimer_->setSingleShot( true ); connect( notificationTimer_, SIGNAL( timeout() ), this, SLOT( notificationTimeout() ) ); } void QuickFindWidget::userActivate() { userRequested_ = true; QWidget::show(); editQuickFind_->setFocus( Qt::ShortcutFocusReason ); } // // SLOTS // void QuickFindWidget::changeDisplayedPattern( const QString& newPattern ) { editQuickFind_->setText( newPattern ); } void QuickFindWidget::notify( const QFNotification& message ) { LOG(logDEBUG) << "QuickFindWidget::notify()"; notificationText_->setText( message.message() ); QWidget::show(); notificationTimer_->start( NOTIFICATION_TIMEOUT ); } void QuickFindWidget::clearNotification() { LOG(logDEBUG) << "QuickFindWidget::clearNotification()"; notificationText_->setText( "" ); } // User clicks forward arrow void QuickFindWidget::doSearchForward() { LOG(logDEBUG) << "QuickFindWidget::doSearchForward()"; // The user has clicked on a button, so we assume she wants // the widget to stay visible. userRequested_ = true; emit patternConfirmed( editQuickFind_->text(), isIgnoreCase() ); emit searchForward(); } // User clicks backward arrow void QuickFindWidget::doSearchBackward() { LOG(logDEBUG) << "QuickFindWidget::doSearchBackward()"; // The user has clicked on a button, so we assume she wants // the widget to stay visible. userRequested_ = true; emit patternConfirmed( editQuickFind_->text(), isIgnoreCase() ); emit searchBackward(); } // Close and search when the user presses Return void QuickFindWidget::returnHandler() { emit patternConfirmed( editQuickFind_->text(), isIgnoreCase() ); // Close the widget userRequested_ = false; this->hide(); emit close(); } // Close and reset flag when the user clicks 'close' void QuickFindWidget::closeHandler() { userRequested_ = false; this->hide(); emit close(); emit cancelSearch(); } void QuickFindWidget::notificationTimeout() { // We close the widget if the user hasn't explicitely requested it. if ( userRequested_ == false ) this->hide(); } void QuickFindWidget::textChanged() { emit patternUpdated( editQuickFind_->text(), isIgnoreCase() ); } // // Private functions // QToolButton* QuickFindWidget::setupToolButton( const QString &text, const QString &icon) { QToolButton *toolButton = new QToolButton(this); toolButton->setText(text); toolButton->setAutoRaise(true); toolButton->setIcon(QIcon(icon)); toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); return toolButton; } bool QuickFindWidget::isIgnoreCase() const { return ( ignoreCaseCheck_->checkState() == Qt::Checked ); } glogg-0.9.2/quickfindwidget.h000066400000000000000000000052621222324246300161600ustar00rootroot00000000000000/* * Copyright (C) 2010, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef QUICKFINDWIDGET_H #define QUICKFINDWIDGET_H #include #include class QHBoxLayout; class QLineEdit; class QToolButton; class QLabel; class QCheckBox; class QFNotification; enum QFDirection { Forward, Backward, }; class QuickFindWidget : public QWidget { Q_OBJECT public: QuickFindWidget( QWidget* parent = 0 ); // Show the widget with the given direction // when requested by the user (the widget won't timeout) void userActivate(); public slots: // Instructs the widget to change the pattern displayed void changeDisplayedPattern( const QString& newPattern ); // Show the widget for a notification (will timeout) void notify( const QFNotification& message ); // Clear the notification void clearNotification(); private slots: void doSearchForward(); void doSearchBackward(); void returnHandler(); void closeHandler(); void notificationTimeout(); void textChanged(); signals: // Sent when Return is pressed to confirm the pattern // (pattern and ignor_case flag) void patternConfirmed( const QString&, bool ); // Sent every time the pattern is modified // (pattern and ignor_case flag) void patternUpdated( const QString&, bool ); void close(); // Emitted when the user closes the window void cancelSearch(); void searchForward(); void searchBackward(); void searchNext(); private: const static int NOTIFICATION_TIMEOUT; QHBoxLayout* layout_; QToolButton* closeButton_; QToolButton* nextButton_; QToolButton* previousButton_; QLineEdit* editQuickFind_; QCheckBox* ignoreCaseCheck_; QLabel* notificationText_; QToolButton* setupToolButton(const QString &text, const QString &icon); bool isIgnoreCase() const; QTimer* notificationTimer_; QFDirection direction_; // Whether the user explicitely wants us on the screen bool userRequested_; }; #endif glogg-0.9.2/recentfiles.cpp000066400000000000000000000056261222324246300156410ustar00rootroot00000000000000/* * Copyright (C) 2011 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // This file implements class RecentFiles #include #include #include "log.h" #include "recentfiles.h" const int RecentFiles::RECENTFILES_VERSION = 1; const int RecentFiles::MAX_NUMBER_OF_FILES = 10; RecentFiles::RecentFiles() : recentFiles_() { } void RecentFiles::addRecent( const QString& text ) { // First prune non existent files QMutableStringListIterator i(recentFiles_); while ( i.hasNext() ) { if ( !QFile::exists(i.next()) ) i.remove(); } // Remove any copy of the about to be added filename recentFiles_.removeAll( text ); // Add at the front recentFiles_.push_front( text ); // Trim the list if it's too long while ( recentFiles_.size() > MAX_NUMBER_OF_FILES ) recentFiles_.pop_back(); } QStringList RecentFiles::recentFiles() const { return recentFiles_; } // // Persistable virtual functions implementation // void RecentFiles::saveToStorage( QSettings& settings ) const { LOG(logDEBUG) << "RecentFiles::saveToStorage"; settings.beginGroup( "RecentFiles" ); settings.setValue( "version", RECENTFILES_VERSION ); settings.beginWriteArray( "filesHistory" ); for (int i = 0; i < recentFiles_.size(); ++i) { settings.setArrayIndex( i ); settings.setValue( "name", recentFiles_.at( i ) ); } settings.endArray(); settings.endGroup(); } void RecentFiles::retrieveFromStorage( QSettings& settings ) { LOG(logDEBUG) << "RecentFiles::retrieveFromStorage"; recentFiles_.clear(); if ( settings.contains( "RecentFiles/version" ) ) { // Unserialise the "new style" stored history settings.beginGroup( "RecentFiles" ); if ( settings.value( "version" ) == RECENTFILES_VERSION ) { int size = settings.beginReadArray( "filesHistory" ); for (int i = 0; i < size; ++i) { settings.setArrayIndex(i); QString search = settings.value( "name" ).toString(); recentFiles_.append( search ); } settings.endArray(); } else { LOG(logERROR) << "Unknown version of FilterSet, ignoring it..."; } settings.endGroup(); } } glogg-0.9.2/recentfiles.h000066400000000000000000000030131222324246300152720ustar00rootroot00000000000000/* * Copyright (C) 2011 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef RECENTFILES_H #define RECENTFILES_H #include #include #include "persistable.h" // Manage the list of recently opened files class RecentFiles : public Persistable { public: // Creates an empty set of recent files RecentFiles(); // Adds the passed filename to the list of recently used searches void addRecent( const QString& text ); // Returns a list of recent files (latest loaded first) QStringList recentFiles() const; // Reads/writes the current config in the QSettings object passed virtual void saveToStorage( QSettings& settings ) const; virtual void retrieveFromStorage( QSettings& settings ); private: static const int RECENTFILES_VERSION; static const int MAX_NUMBER_OF_FILES; QStringList recentFiles_; }; #endif glogg-0.9.2/release-source.sh000077500000000000000000000007141222324246300161000ustar00rootroot00000000000000#!/bin/bash VERSION=$(git describe | sed -e "s/^v//") echo "echo \"$VERSION\" > .tarball-version" echo 'touch --date="$(git log -n 1 --pretty=format:%ci)" .tarball-version' echo "git archive --format=tar --prefix=glogg-$VERSION/ v$VERSION >glogg-$VERSION.tmp" echo "tar --append -f glogg-$VERSION.tmp --transform s_^_glogg-$VERSION/_ .tarball-version" echo "gzip -n < glogg-$VERSION.tmp > glogg-$VERSION.tar.gz" echo "rm .tarball-version glogg-$VERSION.tmp" glogg-0.9.2/release-win32-x.sh000077500000000000000000000015721222324246300160120ustar00rootroot00000000000000#!/bin/bash # Build glogg for win32 using the cross-compiler QTXDIR=$HOME/qt-x-win32 QTVERSION=4.8.2 BOOSTDIR=$QTXDIR/boost_1_50_0 make clean if [ "$1" == "debug" ]; then echo "Building a debug version" qmake glogg.pro -spec win32-x-g++ -r CONFIG+="debug win32 rtti" BOOST_PATH=$BOOSTDIR elif [ -z "$VERSION" ]; then echo "Building default version" qmake glogg.pro -spec win32-x-g++ -r CONFIG+="release win32 rtti" BOOST_PATH=$BOOSTDIR else echo "Building version $VERSION" qmake glogg.pro -spec win32-x-g++ -r CONFIG+="release win32 rtti" BOOST_PATH=$BOOSTDIR VERSION="$VERSION" fi make -j3 cp $QTXDIR/$QTVERSION/bin/{QtCore4,QtGui4}.dll release/ cp $QTXDIR/$QTVERSION/bin/{QtCored4,QtGuid4}.dll debug/ if [ -z "$VERSION" ]; then VERSION=`git describe`; fi echo Generating installer for glogg-$VERSION wine $QTXDIR/NSIS/makensis -DVERSION=$VERSION glogg.nsi glogg-0.9.2/release-win32.sh000077500000000000000000000013301222324246300155350ustar00rootroot00000000000000#!/bin/bash BOOSTDIR=$HOME/boost_1_43_0 PATH=/cygdrive/c/qt/2010.02.1/qt/bin:/cygdrive/c/qt/2010.02.1/mingw/bin/:$PATH if [ -z "$VERSION" ]; then echo "Building default version" qmake glogg.pro -spec win32-g++ -r CONFIG+=release BOOST_PATH=$BOOSTDIR else echo "Building version $VERSION" qmake glogg.pro -spec win32-g++ -r CONFIG+=release BOOST_PATH=$BOOSTDIR VERSION="$VERSION" fi mingw32-make cp /cygdrive/c/qt/2010.02.1/qt/bin/{QtCore4,QtGui4}.dll release/ cp /cygdrive/c/qt/2010.02.1/mingw/bin/{mingwm10,libgcc_s_dw2-1}.dll release/ if [ -z "$VERSION" ]; then VERSION=`git describe`; fi echo Generating installer for glogg-$VERSION /cygdrive/c/Program\ Files/NSIS/makensis -DVERSION=$VERSION glogg.nsi glogg-0.9.2/runtests.sh000077500000000000000000000004621222324246300150510ustar00rootroot00000000000000#!/bin/sh ./tests/logcrawler_tests $* result=$? ICON_OK=gtk-apply ICON_ERROR=gtk-cancel if [ `which notify-send` ]; then if [ "$result" != "0" ]; then notify-send -i $ICON_ERROR "glogg" "Tests failed!" else notify-send -i $ICON_OK "glogg" "Tests passed!" fi fi exit $result glogg-0.9.2/savedsearches.cpp000066400000000000000000000077271222324246300161620ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2011 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // This file implements class SavedSearch #include #include "log.h" #include "savedsearches.h" const int SavedSearches::SAVEDSEARCHES_VERSION = 1; const int SavedSearches::maxNumberOfRecentSearches = 50; SavedSearches::SavedSearches() : savedSearches_() { qRegisterMetaTypeStreamOperators( "SavedSearches" ); } void SavedSearches::addRecent( const QString& text ) { // We're not interested in blank lines if ( text.isEmpty() ) return; // Remove any copy of the about to be added text savedSearches_.removeAll( text ); // Add at the front savedSearches_.push_front( text ); // Trim the list if it's too long while (savedSearches_.size() > maxNumberOfRecentSearches) savedSearches_.pop_back(); } QStringList SavedSearches::recentSearches() const { return savedSearches_; } // // Operators for serialization // QDataStream& operator<<( QDataStream& out, const SavedSearches& object ) { LOG(logDEBUG) << "<>( QDataStream& in, SavedSearches& object ) { LOG(logDEBUG) << ">>operator from SavedSearches"; in >> object.savedSearches_; return in; } // // Persistable virtual functions implementation // void SavedSearches::saveToStorage( QSettings& settings ) const { LOG(logDEBUG) << "SavedSearches::saveToStorage"; settings.beginGroup( "SavedSearches" ); // Remove everything in case the array is shorter than the previous one settings.remove(""); settings.setValue( "version", SAVEDSEARCHES_VERSION ); settings.beginWriteArray( "searchHistory" ); for (int i = 0; i < savedSearches_.size(); ++i) { settings.setArrayIndex( i ); settings.setValue( "string", savedSearches_.at( i ) ); } settings.endArray(); settings.endGroup(); } void SavedSearches::retrieveFromStorage( QSettings& settings ) { LOG(logDEBUG) << "SavedSearches::retrieveFromStorage"; savedSearches_.clear(); if ( settings.contains( "SavedSearches/version" ) ) { // Unserialise the "new style" stored history settings.beginGroup( "SavedSearches" ); if ( settings.value( "version" ) == SAVEDSEARCHES_VERSION ) { int size = settings.beginReadArray( "searchHistory" ); for (int i = 0; i < size; ++i) { settings.setArrayIndex(i); QString search = settings.value( "string" ).toString(); savedSearches_.append( search ); } settings.endArray(); } else { LOG(logERROR) << "Unknown version of FilterSet, ignoring it..."; } settings.endGroup(); } else { LOG(logWARNING) << "Trying to import legacy (<=0.8.2) saved searches..."; SavedSearches tmp_saved_searches = settings.value( "savedSearches" ).value(); *this = tmp_saved_searches; LOG(logWARNING) << "...imported searches: " << savedSearches_.count() << " elements"; // Remove the old key once migration is done settings.remove( "savedSearches" ); // And replace it with the new one saveToStorage( settings ); settings.sync(); } } glogg-0.9.2/savedsearches.h000066400000000000000000000036031222324246300156140ustar00rootroot00000000000000/* * Copyright (C) 2009, 2011 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef SAVEDSEARCHES_H #define SAVEDSEARCHES_H #include #include #include #include "persistable.h" // Keeps track of the previously used searches and allows the application // to retrieve them. class SavedSearches : public Persistable { public: // Creates an empty set of saved searches SavedSearches(); // Adds the passed search to the list of recently used searches void addRecent( const QString& text ); // Returns a list of recent searches (newer first) QStringList recentSearches() const; // Operators for serialization // (only for migrating pre 0.8.2 settings, will be removed) friend QDataStream& operator<<( QDataStream& out, const SavedSearches& object ); friend QDataStream& operator>>( QDataStream& in, SavedSearches& object ); // Reads/writes the current config in the QSettings object passed void saveToStorage( QSettings& settings ) const; void retrieveFromStorage( QSettings& settings ); private: static const int SAVEDSEARCHES_VERSION; static const int maxNumberOfRecentSearches; QStringList savedSearches_; }; Q_DECLARE_METATYPE(SavedSearches) #endif glogg-0.9.2/selection.cpp000066400000000000000000000130111222324246300153060ustar00rootroot00000000000000/* * Copyright (C) 2010, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ // This file implements Selection. // This class implements the selection handling. No check is made on // the validity of the selection, it must be handled by the caller. // There are three types of selection, only one type might be active // at any time. #include #include "selection.h" Selection::Selection() { selectedLine_ = -1; selectedPartial_.line = -1; selectedPartial_.startColumn = 0; selectedPartial_.endColumn = 0; selectedRange_.startLine = -1; selectedRange_.endLine = 0; } void Selection::selectPortion( int line, int start_column, int end_column ) { // First unselect any whole line or range selectedLine_ = -1; selectedRange_.startLine = -1; selectedPartial_.line = line; selectedPartial_.startColumn = qMin ( start_column, end_column ); selectedPartial_.endColumn = qMax ( start_column, end_column ); } void Selection::selectRange( int start_line, int end_line ) { // First unselect any whole line and portion selectedLine_ = -1; selectedPartial_.line = -1; selectedRange_.startLine = qMin ( start_line, end_line ); selectedRange_.endLine = qMax ( start_line, end_line ); selectedRange_.firstLine = start_line; } void Selection::selectRangeFromPrevious( int line ) { int previous_line; if ( selectedLine_ >= 0 ) previous_line = selectedLine_; else if ( selectedRange_.startLine >= 0 ) previous_line = selectedRange_.firstLine; else if ( selectedPartial_.line >= 0 ) previous_line = selectedPartial_.line; else previous_line = 0; selectRange( previous_line, line ); } void Selection::crop( int last_line ) { if ( selectedLine_ > last_line ) selectedLine_ = -1; if ( selectedPartial_.line > last_line ) selectedPartial_.line = -1; if ( selectedRange_.endLine > last_line ) selectedRange_.endLine = last_line; if ( selectedRange_.startLine > last_line ) selectedRange_.startLine = last_line; }; bool Selection::getPortionForLine( int line, int* start_column, int* end_column ) const { if ( selectedPartial_.line == line ) { *start_column = selectedPartial_.startColumn; *end_column = selectedPartial_.endColumn; return true; } else { return false; } } bool Selection::isLineSelected( int line ) const { if ( line == selectedLine_ ) return true; else if ( selectedRange_.startLine >= 0 ) return ( ( line >= selectedRange_.startLine ) && ( line <= selectedRange_.endLine ) ); else return false; } qint64 Selection::selectedLine() const { return selectedLine_; } QList Selection::getLines() const { QList selection; if ( selectedLine_ >= 0 ) selection.append( selectedLine_ ); else if ( selectedPartial_.line >= 0 ) selection.append( selectedPartial_.line ); else if ( selectedRange_.startLine >= 0 ) for ( int i = selectedRange_.startLine; i <= selectedRange_.endLine; i++ ) selection.append( i ); return selection; } // The tab behaviour is a bit odd at the moment, full lines are not expanded // but partials (part of line) are, they probably should not ideally. QString Selection::getSelectedText( const AbstractLogData* logData ) const { QString text; if ( selectedLine_ >= 0 ) { text = logData->getLineString( selectedLine_ ); } else if ( selectedPartial_.line >= 0 ) { text = logData->getExpandedLineString( selectedPartial_.line ). mid( selectedPartial_.startColumn, ( selectedPartial_.endColumn - selectedPartial_.startColumn ) + 1 ); } else if ( selectedRange_.startLine >= 0 ) { QStringList list = logData->getLines( selectedRange_.startLine, selectedRange_.endLine - selectedRange_.startLine + 1 ); text = list.join( "\n" ); } return text; } FilePosition Selection::getNextPosition() const { qint64 line = 0; int column = 0; if ( selectedLine_ >= 0 ) { line = selectedLine_ + 1; } else if ( selectedRange_.startLine >= 0 ) { line = selectedRange_.endLine + 1; } else if ( selectedPartial_.line >= 0 ) { line = selectedPartial_.line; column = selectedPartial_.endColumn + 1; } return FilePosition( line, column ); } FilePosition Selection::getPreviousPosition() const { qint64 line = 0; int column = 0; if ( selectedLine_ >= 0 ) { line = selectedLine_; } else if ( selectedRange_.startLine >= 0 ) { line = selectedRange_.startLine; } else if ( selectedPartial_.line >= 0 ) { line = selectedPartial_.line; column = qMax( selectedPartial_.startColumn - 1, 0 ); } return FilePosition( line, column ); } glogg-0.9.2/selection.h000066400000000000000000000101241222324246300147550ustar00rootroot00000000000000/* * Copyright (C) 2010, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef SELECTION_H #define SELECTION_H #include "utils.h" #include "abstractlogdata.h" class Portion { public: Portion() { line_ = -1; } Portion( int line, int start_column, int end_column ) { line_ = line; startColumn_ = start_column; endColumn_ = end_column; } int line() const { return line_; } int startColumn() const { return startColumn_; } int endColumn() const { return endColumn_; } bool isValid() const { return ( line_ != -1 ); } private: int line_; int startColumn_; int endColumn_; }; // Represents a selection in an AbstractLogView class Selection { public: // Construct an empty selection Selection(); // Clear the selection void clear() { selectedPartial_.line = -1; selectedLine_ = -1; }; // Select one line void selectLine( int line ) { selectedPartial_.line = -1; selectedRange_.startLine = -1; selectedLine_ = line; }; // Select a portion of line (both start and end included) void selectPortion( int line, int start_column, int end_column ); void selectPortion( Portion selection ) { selectPortion( selection.line(), selection.startColumn(), selection.endColumn() ); } // Select a range of lines (both start and end included) void selectRange( int start_line, int end_line ); // Select a range from the previously selected line or beginning // of range (shift+click behaviour) void selectRangeFromPrevious( int line ); // Crop selection so that in fit in the range ending with the line passed. void crop( int last_line ); // Returns whether the selection is empty bool isEmpty() const { return ( selectedPartial_.line == -1 ) && ( selectedLine_ == -1 ); } // Returns whether the selection is a single line bool isSingleLine() const { return ( selectedLine_ != -1 ); } // Returns whether the selection is a portion of line bool isPortion() const { return ( selectedPartial_.line != -1 ); } // Returns whether a portion is selected or not on the passed line. // If so, returns the portion position. bool getPortionForLine( int line, int* start_column, int* end_column ) const; // Get a list of selected line(s), in order. QList getLines() const; // Returns wether the line passed is selected (entirely). bool isLineSelected( int line ) const; // Returns the line selected or -1 if not a single line selection qint64 selectedLine() const; // Returns the text selected from the passed AbstractLogData QString getSelectedText( const AbstractLogData* logData ) const; // Return the position immediately after the current selection // (used for searches). // This is the next character or the start of the next line. FilePosition getNextPosition() const; // Idem from the position immediately before selection. FilePosition getPreviousPosition() const; private: // Line number currently selected, or -1 if none selected int selectedLine_; struct SelectedPartial { int line; int startColumn; int endColumn; }; struct SelectedRange { // The limits of the range, sorted int startLine; int endLine; // The line selected first, used for shift+click int firstLine; }; struct SelectedPartial selectedPartial_; struct SelectedRange selectedRange_; }; #endif glogg-0.9.2/sessioninfo.cpp000066400000000000000000000025551222324246300156730ustar00rootroot00000000000000/* * Copyright (C) 2011 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #include "sessioninfo.h" #include #include "log.h" void SessionInfo::retrieveFromStorage( QSettings& settings ) { LOG(logDEBUG) << "SessionInfo::retrieveFromStorage"; geometry_ = settings.value("geometry").toByteArray(); crawlerState_ = settings.value("crawlerWidget").toByteArray(); currentFile_ = settings.value("currentFile").toString(); } void SessionInfo::saveToStorage( QSettings& settings ) const { LOG(logDEBUG) << "SessionInfo::saveToStorage"; settings.setValue( "geometry", geometry_ ); settings.setValue( "crawlerWidget", crawlerState_ ); settings.setValue( "currentFile", currentFile_ ); } glogg-0.9.2/sessioninfo.h000066400000000000000000000035441222324246300153370ustar00rootroot00000000000000/* * Copyright (C) 2011 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef SESSIONINFO_H #define SESSIONINFO_H #include #include #include "persistable.h" // Simple component class containing information related to the session // to be persisted and reloaded upon start class SessionInfo : public Persistable { public: SessionInfo() { } // Geometry of the main window QByteArray geometry() const { return geometry_; } void setGeometry( const QByteArray& geometry ) { geometry_ = geometry; } // Geometry of the CrawlerWidget QByteArray crawlerState() const { return crawlerState_; } void setCrawlerState( const QByteArray& geometry ) { crawlerState_ = geometry; } // Name of the current (last loaded) file QString currentFile() const { return currentFile_; } void setCurrentFile( const QString& filename ) { currentFile_ = filename; } // Reads/writes the current config in the QSettings object passed virtual void saveToStorage( QSettings& settings ) const; virtual void retrieveFromStorage( QSettings& settings ); private: QByteArray geometry_; QByteArray crawlerState_; QString currentFile_; }; #endif glogg-0.9.2/tests/000077500000000000000000000000001222324246300137635ustar00rootroot00000000000000glogg-0.9.2/tests/coverage.sh000077500000000000000000000005421222324246300161160ustar00rootroot00000000000000#!/bin/bash FILES="logdata.cpp logfiltereddata.cpp logdataworkerthread.cpp logfiltereddataworkerthread.cpp" for i in $FILES; do gcov -b $i | perl -e "while(<>) { if (/^File '.*$i'/) { \$print = 1; } if ( \$print ) { if (/:creating '/) { \$print = 0; print \"\n\" } else { print; } } }" done mkdir coverage mv *.gcov coverage/ rm *.{gcda,gcno} glogg-0.9.2/tests/main.cpp000066400000000000000000000004761222324246300154220ustar00rootroot00000000000000#include #include "testlogdata.h" #include "testlogfiltereddata.h" int main(int argc, char** argv) { QApplication app(argc, argv); int retval(0); retval += QTest::qExec(&TestLogData(), argc, argv); retval += QTest::qExec(&TestLogFilteredData(), argc, argv); return (retval ? 1 : 0); } glogg-0.9.2/tests/test-results.txt000066400000000000000000000044301222324246300172030ustar00rootroot00000000000000 page 99999 page 9999 LogData size 9999 Legacy 1305.5 11.009 1,048,576B 16,777,216B QListString 0.055 0.0027 3,777,200B 18,644,700B Memory test: everything commented except RandomPage Memory using top: 99999 (8.5Mib file): legacy 44m,20m qliststr 33m,11m 4999999 (428MiB file): legacy 464m,442m qliststr 1090m,1.0g 'Legacy' implementation: ********* Start testing of TestLogData ********* Config: Using QTest library 4.5.0, Qt 4.5.0 PASS : TestLogData::initTestCase() - 14:12:30.621 DEBUG: Found 99999 lines. - 14:12:30.639 DEBUG: Found 99999 lines. - 14:12:30.707 DEBUG: Found 99999 lines. - 14:12:30.717 DEBUG: Found 99999 lines. - 14:12:30.728 DEBUG: Found 99999 lines. RESULT : TestLogData::simpleLoad(): 10 msec per iteration (total: 21, iterations: 2) PASS : TestLogData::simpleLoad() - 14:12:30.780 DEBUG: Found 99999 lines. RESULT : TestLogData::sequentialRead(): 650,750 msec per iteration (total: 650750, iterations: 1) PASS : TestLogData::sequentialRead() - 14:23:21.586 DEBUG: Found 99999 lines. RESULT : TestLogData::randomPageRead(): 1,305,466 msec per iteration (total: 1305466, iterations: 1) PASS : TestLogData::randomPageRead() PASS : TestLogData::cleanupTestCase() Totals: 5 passed, 0 failed, 0 skipped ********* Finished testing of TestLogData ********* With 9999 lines: RESULT : TestLogData::simpleLoad(): 1.3 msec per iteration (total: 21, iterations: 16) PASS : TestLogData::simpleLoad() - 14:53:26.232 DEBUG: Found 9999 lines. RESULT : TestLogData::sequentialRead(): 6,475 msec per iteration (total: 6475, iterations: 1) PASS : TestLogData::sequentialRead() - 14:53:32.713 DEBUG: Found 9999 lines. RESULT : TestLogData::randomPageRead(): 11,009 msec per iteration (total: 11009, iterations: 1) PASS : TestLogData::randomPageRead() PASS : TestLogData::cleanupTestCase() Totals: 5 passed, 0 failed, 0 skipped Memory: 1,048,576B Tab expansion fix: before: TestLogData::sequentialRead(): 58,011 msec TestLogData::randomPageRead(): 11,376 msec after step 1: TestLogData::sequentialRead(): 55,662 msec TestLogData::sequentialReadExpanded(): 91,141 msec TestLogData::randomPageRead(): 10,592 msec TestLogData::randomPageReadExpanded(): 183,567 msec glogg-0.9.2/tests/testlogdata.cpp000066400000000000000000000271601222324246300170100ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #include #include #include #include "testlogdata.h" #include "logdata.h" #if QT_VERSION < 0x040500 #define QBENCHMARK #endif #if !defined( TMPDIR ) #define TMPDIR "/tmp" #endif static const qint64 VBL_NB_LINES = 4999999LL; static const int VBL_LINE_PER_PAGE = 70; static const char* vbl_format="LOGDATA is a part of glogg, we are going to test it thoroughly, this is line\t\t%07d\n"; static const int VBL_LINE_LENGTH = (76+2+7) ; // Without the final '\n' ! static const int VBL_VISIBLE_LINE_LENGTH = (76+8+4+7); // Without the final '\n' ! static const qint64 SL_NB_LINES = 5000LL; static const int SL_LINE_PER_PAGE = 70; static const char* sl_format="LOGDATA is a part of glogg, we are going to test it thoroughly, this is line %06d\n"; static const int SL_LINE_LENGTH = 83; // Without the final '\n' ! static const char* partial_line_begin = "123... beginning of line."; static const char* partial_line_end = " end of line 123.\n"; void TestLogData::initTestCase() { QVERIFY( generateDataFiles() ); } void TestLogData::simpleLoad() { LogData logData; QSignalSpy progressSpy( &logData, SIGNAL( loadingProgressed( int ) ) ); // Register for notification file is loaded connect( &logData, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); QBENCHMARK { logData.attachFile( TMPDIR "/verybiglog.txt" ); // Wait for the loading to be done { QApplication::exec(); } } QCOMPARE( (qint64) progressSpy.count(), logData.getFileSize() / (5LL*1024*1024) + 2 ); // Blocks of 5 MiB + 1 for the start notification (0%) QCOMPARE( logData.getNbLine(), VBL_NB_LINES ); QCOMPARE( logData.getMaxLength(), VBL_VISIBLE_LINE_LENGTH ); QCOMPARE( logData.getLineLength( 123 ), VBL_VISIBLE_LINE_LENGTH ); QCOMPARE( logData.getFileSize(), VBL_NB_LINES * (VBL_LINE_LENGTH+1LL) ); // Disconnect all signals disconnect( &logData, 0 ); } void TestLogData::multipleLoad() { LogData logData; QSignalSpy finishedSpy( &logData, SIGNAL( loadingFinished( bool ) ) ); // Register for notification file is loaded connect( &logData, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); // Start loading the VBL logData.attachFile( TMPDIR "/verybiglog.txt" ); // Immediately interrupt the loading logData.interruptLoading(); // and wait for the signal QApplication::exec(); // Check we have an empty file QCOMPARE( finishedSpy.count(), 1 ); // TODO: check loadingFinished arg == false QCOMPARE( logData.getNbLine(), 0LL ); QCOMPARE( logData.getMaxLength(), 0 ); QCOMPARE( logData.getFileSize(), 0LL ); // Restart the VBL logData.attachFile( TMPDIR "/verybiglog.txt" ); // Ensure the counting has started { QMutex mutex; QWaitCondition sleep; // sleep.wait( &mutex, 10 ); } // Load the SL (should block until VBL is fully indexed) logData.attachFile( TMPDIR "/smalllog.txt" ); // and wait for the 2 signals (one for each file) QApplication::exec(); QApplication::exec(); // Check we have the small log loaded QCOMPARE( finishedSpy.count(), 3 ); QCOMPARE( logData.getNbLine(), SL_NB_LINES ); QCOMPARE( logData.getMaxLength(), SL_LINE_LENGTH ); QCOMPARE( logData.getFileSize(), SL_NB_LINES * (SL_LINE_LENGTH+1LL) ); // Restart the VBL again logData.attachFile( TMPDIR "/verybiglog.txt" ); // Immediately interrupt the loading logData.interruptLoading(); // and wait for the signal QApplication::exec(); // Check the small log has been restored QCOMPARE( finishedSpy.count(), 4 ); QCOMPARE( logData.getNbLine(), SL_NB_LINES ); QCOMPARE( logData.getMaxLength(), SL_LINE_LENGTH ); QCOMPARE( logData.getFileSize(), SL_NB_LINES * (SL_LINE_LENGTH+1LL) ); // Disconnect all signals disconnect( &logData, 0 ); } void TestLogData::changingFile() { char newLine[90]; LogData logData; QSignalSpy finishedSpy( &logData, SIGNAL( loadingFinished( bool ) ) ); QSignalSpy progressSpy( &logData, SIGNAL( loadingProgressed( int ) ) ); QSignalSpy changedSpy( &logData, SIGNAL( fileChanged( LogData::MonitoredFileStatus ) ) ); // Register for notification file is loaded connect( &logData, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); // Generate a small file QFile file( TMPDIR "/changingfile.txt" ); if ( file.open( QIODevice::WriteOnly ) ) { for (int i = 0; i < 200; i++) { snprintf(newLine, 89, sl_format, i); file.write( newLine, qstrlen(newLine) ); } } file.close(); // Start loading it logData.attachFile( TMPDIR "/changingfile.txt" ); // and wait for the signal QApplication::exec(); // Check we have the small file QCOMPARE( finishedSpy.count(), 1 ); QCOMPARE( logData.getNbLine(), 200LL ); QCOMPARE( logData.getMaxLength(), SL_LINE_LENGTH ); QCOMPARE( logData.getFileSize(), 200 * (SL_LINE_LENGTH+1LL) ); // Add some data to it if ( file.open( QIODevice::Append ) ) { for (int i = 0; i < 200; i++) { snprintf(newLine, 89, sl_format, i); file.write( newLine, qstrlen(newLine) ); } // To test the edge case when the final line is not complete file.write( partial_line_begin, qstrlen( partial_line_begin ) ); } file.close(); // and wait for the signals QApplication::exec(); // Check we have a bigger file QCOMPARE( changedSpy.count(), 1 ); QCOMPARE( finishedSpy.count(), 2 ); QCOMPARE( logData.getNbLine(), 401LL ); QCOMPARE( logData.getMaxLength(), SL_LINE_LENGTH ); QCOMPARE( logData.getFileSize(), (qint64) (400 * (SL_LINE_LENGTH+1LL) + strlen( partial_line_begin ) ) ); // Add a couple more lines, including the end of the unfinished one. if ( file.open( QIODevice::Append ) ) { file.write( partial_line_end, qstrlen( partial_line_end ) ); for (int i = 0; i < 20; i++) { snprintf(newLine, 89, sl_format, i); file.write( newLine, qstrlen(newLine) ); } } file.close(); // and wait for the signals QApplication::exec(); // Check we have a bigger file QCOMPARE( changedSpy.count(), 2 ); QCOMPARE( finishedSpy.count(), 3 ); QCOMPARE( logData.getNbLine(), 421LL ); QCOMPARE( logData.getMaxLength(), SL_LINE_LENGTH ); QCOMPARE( logData.getFileSize(), (qint64) ( 420 * (SL_LINE_LENGTH+1LL) + strlen( partial_line_begin ) + strlen( partial_line_end ) ) ); // Truncate the file QVERIFY( file.open( QIODevice::WriteOnly ) ); file.close(); // and wait for the signals QApplication::exec(); // Check we have an empty file QCOMPARE( changedSpy.count(), 3 ); QCOMPARE( finishedSpy.count(), 4 ); QCOMPARE( logData.getNbLine(), 0LL ); QCOMPARE( logData.getMaxLength(), 0 ); QCOMPARE( logData.getFileSize(), 0LL ); } void TestLogData::sequentialRead() { LogData logData; // Register for notification file is loaded connect( &logData, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); logData.attachFile( TMPDIR "/verybiglog.txt" ); // Wait for the loading to be done { QApplication::exec(); } // Read all lines sequentially QString s; QBENCHMARK { for (int i = 0; i < VBL_NB_LINES; i++) { s = logData.getLineString(i); } } QCOMPARE( s.length(), VBL_LINE_LENGTH ); } void TestLogData::sequentialReadExpanded() { LogData logData; // Register for notification file is loaded connect( &logData, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); logData.attachFile( TMPDIR "/verybiglog.txt" ); // Wait for the loading to be done { QApplication::exec(); } // Read all expanded lines sequentially QString s; QBENCHMARK { for (int i = 0; i < VBL_NB_LINES; i++) { s = logData.getExpandedLineString(i); } } QCOMPARE( s.length(), VBL_VISIBLE_LINE_LENGTH ); } void TestLogData::randomPageRead() { LogData logData; // Register for notification file is loaded connect( &logData, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); logData.attachFile( TMPDIR "/verybiglog.txt" ); // Wait for the loading to be done { QApplication::exec(); } QWARN("Starting random page read test"); // Read page by page from the beginning and the end, using the QStringList // function QStringList list; QBENCHMARK { for (int page = 0; page < (VBL_NB_LINES/VBL_LINE_PER_PAGE)-1; page++) { list = logData.getLines( page*VBL_LINE_PER_PAGE, VBL_LINE_PER_PAGE ); QCOMPARE(list.count(), VBL_LINE_PER_PAGE); int page_from_end = (VBL_NB_LINES/VBL_LINE_PER_PAGE) - page - 1; list = logData.getLines( page_from_end*VBL_LINE_PER_PAGE, VBL_LINE_PER_PAGE ); QCOMPARE(list.count(), VBL_LINE_PER_PAGE); } } } void TestLogData::randomPageReadExpanded() { LogData logData; // Register for notification file is loaded connect( &logData, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); logData.attachFile( TMPDIR "/verybiglog.txt" ); // Wait for the loading to be done { QApplication::exec(); } QWARN("Starting random page read test (expanded lines)"); // Read page by page from the beginning and the end, using the QStringList // function QStringList list; QBENCHMARK { for (int page = 0; page < (VBL_NB_LINES/VBL_LINE_PER_PAGE)-1; page++) { list = logData.getExpandedLines( page*VBL_LINE_PER_PAGE, VBL_LINE_PER_PAGE ); QCOMPARE(list.count(), VBL_LINE_PER_PAGE); int page_from_end = (VBL_NB_LINES/VBL_LINE_PER_PAGE) - page - 1; list = logData.getExpandedLines( page_from_end*VBL_LINE_PER_PAGE, VBL_LINE_PER_PAGE ); QCOMPARE(list.count(), VBL_LINE_PER_PAGE); } } } // // Private functions // void TestLogData::loadingFinished() { QApplication::quit(); } bool TestLogData::generateDataFiles() { char newLine[90]; QFile file( TMPDIR "/verybiglog.txt" ); if ( file.open( QIODevice::WriteOnly ) ) { for (int i = 0; i < VBL_NB_LINES; i++) { snprintf(newLine, 89, vbl_format, i); file.write( newLine, qstrlen(newLine) ); } } else { return false; } file.close(); file.setFileName( TMPDIR "/smalllog.txt" ); if ( file.open( QIODevice::WriteOnly ) ) { for (int i = 0; i < SL_NB_LINES; i++) { snprintf(newLine, 89, sl_format, i); file.write( newLine, qstrlen(newLine) ); } } else { return false; } file.close(); return true; } glogg-0.9.2/tests/testlogdata.h000066400000000000000000000007601222324246300164520ustar00rootroot00000000000000#include #include #include class TestLogData: public QObject { Q_OBJECT private slots: void initTestCase(); void simpleLoad(); void multipleLoad(); void changingFile(); void sequentialRead(); void sequentialReadExpanded(); void randomPageRead(); void randomPageReadExpanded(); public slots: void loadingFinished(); private: bool generateDataFiles(); }; glogg-0.9.2/tests/testlogfiltereddata.cpp000066400000000000000000000511671222324246300205330ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010, 2011 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #include #include #include #include "testlogfiltereddata.h" #include "logdata.h" #include "logfiltereddata.h" #if QT_VERSION < 0x040500 #define QBENCHMARK #endif #if !defined( TMPDIR ) #define TMPDIR "/tmp" #endif static const qint64 ML_NB_LINES = 15000LL; static const char* ml_format="LOGDATA is a part of glogg, we are going to test it thoroughly, this is line\t\t%06d\n"; static const int ML_VISIBLE_LINE_LENGTH = (76+8+4+6); // Without the final '\n' ! static const qint64 SL_NB_LINES = 2000LL; static const char* sl_format="LOGDATA is a part of glogg, we are going to test it thoroughly, this is line %06d\n"; static const char* partial_line_begin = "123... beginning of line."; static const char* partial_line_end = " end of line 123.\n"; static const char* partial_nonmatching_line_begin = "Beginning of line."; TestLogFilteredData::TestLogFilteredData() : QObject(), loadingFinishedMutex_(), searchProgressedMutex_(), loadingFinishedCondition_(), searchProgressedCondition_() { loadingFinished_received_ = false; loadingFinished_read_ = false; searchProgressed_received_ = false; searchProgressed_read_ = false; } void TestLogFilteredData::initTestCase() { QVERIFY( generateDataFiles() ); } void TestLogFilteredData::simpleSearch() { logData_ = new LogData(); // Register for notification file is loaded connect( logData_, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); filteredData_ = logData_->getNewFilteredData(); connect( filteredData_, SIGNAL( searchProgressed( int, int ) ), this, SLOT( searchProgressed( int, int ) ) ); QFuture future = QtConcurrent::run(this, &TestLogFilteredData::simpleSearchTest); QApplication::exec(); disconnect( filteredData_, 0 ); disconnect( logData_, 0 ); delete filteredData_; delete logData_; } void TestLogFilteredData::simpleSearchTest() { // First load the tests file logData_->attachFile( TMPDIR "/mediumlog.txt" ); // Wait for the loading to be done waitLoadingFinished(); QCOMPARE( logData_->getNbLine(), ML_NB_LINES ); signalLoadingFinishedRead(); // Now perform a simple search qint64 matches[] = { 0, 15, 20, 135 }; QBENCHMARK { // Start the search filteredData_->runSearch( QRegExp( "123" ) ); // And check we receive data in 4 chunks (the first being empty) for ( int i = 0; i < 4; i++ ) { std::pair progress = waitSearchProgressed(); // FIXME: The test for this is unfortunately not reliable // (race conditions) // QCOMPARE( (qint64) progress.first, matches[i] ); signalSearchProgressedRead(); } } QCOMPARE( filteredData_->getNbLine(), matches[3] ); // Check the search QCOMPARE( filteredData_->isLineInMatchingList( 123 ), true ); QCOMPARE( filteredData_->isLineInMatchingList( 124 ), false ); QCOMPARE( filteredData_->getMaxLength(), ML_VISIBLE_LINE_LENGTH ); QCOMPARE( filteredData_->getLineLength( 12 ), ML_VISIBLE_LINE_LENGTH ); QCOMPARE( filteredData_->getNbLine(), 135LL ); // Line beyond limit QCOMPARE( filteredData_->isLineInMatchingList( 60000 ), false ); QCOMPARE( filteredData_->getMatchingLineNumber( 0 ), 123LL ); // Now let's try interrupting a search filteredData_->runSearch( QRegExp( "123" ) ); // ... wait for two chunks. waitSearchProgressed(); signalSearchProgressedRead(); // and interrupt! filteredData_->interruptSearch(); { std::pair progress; do { progress = waitSearchProgressed(); signalSearchProgressedRead(); } while ( progress.second < 100 ); // (because there is no guarantee when the search is // interrupted, we are not sure how many chunk of result // we will get.) } QApplication::quit(); } void TestLogFilteredData::multipleSearch() { logData_ = new LogData(); // Register for notification file is loaded connect( logData_, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); filteredData_ = logData_->getNewFilteredData(); connect( filteredData_, SIGNAL( searchProgressed( int, int ) ), this, SLOT( searchProgressed( int, int ) ) ); QFuture future = QtConcurrent::run(this, &TestLogFilteredData::multipleSearchTest); QApplication::exec(); disconnect( filteredData_, 0 ); disconnect( logData_, 0 ); delete filteredData_; delete logData_; } void TestLogFilteredData::multipleSearchTest() { // First load the tests file logData_->attachFile( TMPDIR "/smalllog.txt" ); // Wait for the loading to be done waitLoadingFinished(); QCOMPARE( logData_->getNbLine(), SL_NB_LINES ); signalLoadingFinishedRead(); // Performs two searches in a row // Start the search, and immediately another one // (the second call should block until the first search is done) filteredData_->runSearch( QRegExp( "1234" ) ); filteredData_->runSearch( QRegExp( "123" ) ); for ( int i = 0; i < 3; i++ ) { waitSearchProgressed(); signalSearchProgressedRead(); } // We should have the result for the 2nd search after the last chunk waitSearchProgressed(); QCOMPARE( filteredData_->getNbLine(), 12LL ); signalSearchProgressedRead(); // Now a tricky one: we run a search and immediately attach a new file /* FIXME: sometimes we receive loadingFinished before searchProgressed * -> deadlock in the test. filteredData_->runSearch( QRegExp( "123" ) ); waitSearchProgressed(); signalSearchProgressedRead(); logData_->attachFile( TMPDIR "/mediumlog.txt" ); // We don't expect meaningful results but it should not crash! for ( int i = 0; i < 1; i++ ) { waitSearchProgressed(); signalSearchProgressedRead(); } */ sleep(10); QApplication::quit(); } void TestLogFilteredData::updateSearch() { logData_ = new LogData(); // Register for notification file is loaded connect( logData_, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); filteredData_ = logData_->getNewFilteredData(); connect( filteredData_, SIGNAL( searchProgressed( int, int ) ), this, SLOT( searchProgressed( int, int ) ) ); QFuture future = QtConcurrent::run(this, &TestLogFilteredData::updateSearchTest); QApplication::exec(); disconnect( filteredData_, 0 ); disconnect( logData_, 0 ); delete filteredData_; delete logData_; } void TestLogFilteredData::updateSearchTest() { // First load the tests file logData_->attachFile( TMPDIR "/smalllog.txt" ); // Wait for the loading to be done waitLoadingFinished(); QCOMPARE( logData_->getNbLine(), SL_NB_LINES ); signalLoadingFinishedRead(); // Perform a first search filteredData_->runSearch( QRegExp( "123" ) ); for ( int i = 0; i < 2; i++ ) { waitSearchProgressed(); signalSearchProgressedRead(); } // Check the result QCOMPARE( filteredData_->getNbLine(), 12LL ); sleep(1); QWARN("Starting stage 2"); // Add some data to the file char newLine[90]; QFile file( TMPDIR "/smalllog.txt" ); if ( file.open( QIODevice::Append ) ) { for (int i = 0; i < 3000; i++) { snprintf(newLine, 89, sl_format, i); file.write( newLine, qstrlen(newLine) ); } // To test the edge case when the final line is not complete and matching file.write( partial_line_begin, qstrlen( partial_line_begin ) ); } file.close(); // Let the system do the update (there might be several ones) do { waitLoadingFinished(); signalLoadingFinishedRead(); } while ( logData_->getNbLine() < 5001LL ); sleep(1); // Start an update search filteredData_->updateSearch(); for ( int i = 0; i < 2; i++ ) { waitSearchProgressed(); signalSearchProgressedRead(); } // Check the result QCOMPARE( logData_->getNbLine(), 5001LL ); QCOMPARE( filteredData_->getNbLine(), 26LL ); QWARN("Starting stage 3"); // Add a couple more lines, including the end of the unfinished one. if ( file.open( QIODevice::Append ) ) { file.write( partial_line_end, qstrlen( partial_line_end ) ); for (int i = 0; i < 20; i++) { snprintf(newLine, 89, sl_format, i); file.write( newLine, qstrlen(newLine) ); } // To test the edge case when the final line is not complete and not matching file.write( partial_nonmatching_line_begin, qstrlen( partial_nonmatching_line_begin ) ); } file.close(); // Let the system do the update waitLoadingFinished(); signalLoadingFinishedRead(); // Start an update search filteredData_->updateSearch(); for ( int i = 0; i < 2; i++ ) { waitSearchProgressed(); signalSearchProgressedRead(); } // Check the result QCOMPARE( logData_->getNbLine(), 5022LL ); QCOMPARE( filteredData_->getNbLine(), 26LL ); QWARN("Starting stage 4"); // Now test the case where a match is found at the end of an updated line. if ( file.open( QIODevice::Append ) ) { file.write( partial_line_end, qstrlen( partial_line_end ) ); for (int i = 0; i < 20; i++) { snprintf(newLine, 89, sl_format, i); file.write( newLine, qstrlen(newLine) ); } } file.close(); // Let the system do the update waitLoadingFinished(); signalLoadingFinishedRead(); // Start an update search filteredData_->updateSearch(); for ( int i = 0; i < 2; i++ ) { waitSearchProgressed(); signalSearchProgressedRead(); } // Check the result QCOMPARE( logData_->getNbLine(), 5042LL ); QCOMPARE( filteredData_->getNbLine(), 27LL ); QApplication::quit(); } void TestLogFilteredData::marks() { logData_ = new LogData(); // Register for notification file is loaded connect( logData_, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); filteredData_ = logData_->getNewFilteredData(); connect( filteredData_, SIGNAL( searchProgressed( int, int ) ), this, SLOT( searchProgressed( int, int ) ) ); QFuture future = QtConcurrent::run(this, &TestLogFilteredData::marksTest); QApplication::exec(); disconnect( filteredData_, 0 ); disconnect( logData_, 0 ); delete filteredData_; delete logData_; } void TestLogFilteredData::marksTest() { // First load the tests file logData_->attachFile( TMPDIR "/smalllog.txt" ); // Wait for the loading to be done waitLoadingFinished(); QCOMPARE( logData_->getNbLine(), SL_NB_LINES ); signalLoadingFinishedRead(); // First check no line is marked for ( int i = 0; i < SL_NB_LINES; i++ ) QVERIFY( filteredData_->isLineMarked( i ) == false ); // Try to create some "out of limit" marks filteredData_->addMark( -10 ); filteredData_->addMark( SL_NB_LINES + 25 ); // Check no line is marked still for ( int i = 0; i < SL_NB_LINES; i++ ) QVERIFY( filteredData_->isLineMarked( i ) == false ); // Create a couple of unnamed marks filteredData_->addMark( 10 ); filteredData_->addMark( 44 ); // This one will also be a match filteredData_->addMark( 25 ); // Check they are marked QVERIFY( filteredData_->isLineMarked( 10 ) ); QVERIFY( filteredData_->isLineMarked( 25 ) ); QVERIFY( filteredData_->isLineMarked( 44 ) ); // But others are not QVERIFY( filteredData_->isLineMarked( 15 ) == false ); QVERIFY( filteredData_->isLineMarked( 20 ) == false ); QCOMPARE( filteredData_->getNbLine(), 3LL ); // Performs a search QSignalSpy progressSpy( filteredData_, SIGNAL( searchProgressed( int, int ) ) ); filteredData_->runSearch( QRegExp( "0000.4" ) ); for ( int i = 0; i < 1; i++ ) { waitSearchProgressed(); signalSearchProgressedRead(); } // We should have the result of the search and the marks waitSearchProgressed(); QCOMPARE( filteredData_->getNbLine(), 12LL ); signalSearchProgressedRead(); QString startline = "LOGDATA is a part of glogg, we are going to test it thoroughly, this is line "; QCOMPARE( filteredData_->getLineString(0), startline + "000004" ); QCOMPARE( filteredData_->getLineString(1), startline + "000010" ); QCOMPARE( filteredData_->getLineString(2), startline + "000014" ); QCOMPARE( filteredData_->getLineString(3), startline + "000024" ); QCOMPARE( filteredData_->getLineString(4), startline + "000025" ); QCOMPARE( filteredData_->getLineString(5), startline + "000034" ); QCOMPARE( filteredData_->getLineString(6), startline + "000044" ); QCOMPARE( filteredData_->getLineString(7), startline + "000054" ); QCOMPARE( filteredData_->getLineString(8), startline + "000064" ); QCOMPARE( filteredData_->getLineString(9), startline + "000074" ); QCOMPARE( filteredData_->getLineString(10), startline + "000084" ); QCOMPARE( filteredData_->getLineString(11), startline + "000094" ); filteredData_->setVisibility( LogFilteredData::MatchesOnly ); QCOMPARE( filteredData_->getNbLine(), 10LL ); QCOMPARE( filteredData_->getLineString(0), startline + "000004" ); QCOMPARE( filteredData_->getLineString(1), startline + "000014" ); QCOMPARE( filteredData_->getLineString(2), startline + "000024" ); QCOMPARE( filteredData_->getLineString(3), startline + "000034" ); QCOMPARE( filteredData_->getLineString(4), startline + "000044" ); QCOMPARE( filteredData_->getLineString(5), startline + "000054" ); QCOMPARE( filteredData_->getLineString(6), startline + "000064" ); QCOMPARE( filteredData_->getLineString(7), startline + "000074" ); QCOMPARE( filteredData_->getLineString(8), startline + "000084" ); QCOMPARE( filteredData_->getLineString(9), startline + "000094" ); filteredData_->setVisibility( LogFilteredData::MarksOnly ); QCOMPARE( filteredData_->getNbLine(), 3LL ); QCOMPARE( filteredData_->getLineString(0), startline + "000010" ); QCOMPARE( filteredData_->getLineString(1), startline + "000025" ); QCOMPARE( filteredData_->getLineString(2), startline + "000044" ); // Another test with marks only filteredData_->clearSearch(); filteredData_->clearMarks(); filteredData_->setVisibility( LogFilteredData::MarksOnly ); filteredData_->addMark(18); filteredData_->addMark(19); filteredData_->addMark(20); QCOMPARE( filteredData_->getMatchingLineNumber(0), 18LL ); QCOMPARE( filteredData_->getMatchingLineNumber(1), 19LL ); QCOMPARE( filteredData_->getMatchingLineNumber(2), 20LL ); QApplication::quit(); } void TestLogFilteredData::lineLength() { logData_ = new LogData(); // Register for notification file is loaded connect( logData_, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); filteredData_ = logData_->getNewFilteredData(); connect( filteredData_, SIGNAL( searchProgressed( int, int ) ), this, SLOT( searchProgressed( int, int ) ) ); QFuture future = QtConcurrent::run(this, &TestLogFilteredData::lineLengthTest); QApplication::exec(); disconnect( filteredData_, 0 ); disconnect( logData_, 0 ); delete filteredData_; delete logData_; } void TestLogFilteredData::lineLengthTest() { // Line length tests logData_->attachFile( TMPDIR "/length_test.txt" ); // Wait for the loading to be done waitLoadingFinished(); QCOMPARE( logData_->getNbLine(), 4LL ); signalLoadingFinishedRead(); // Performs a search (the two middle lines matche) filteredData_->setVisibility( LogFilteredData::MatchesOnly ); filteredData_->runSearch( QRegExp( "longer" ) ); std::pair progress; do { progress = waitSearchProgressed(); signalSearchProgressedRead(); QWARN("progress"); } while ( progress.second < 100 ); filteredData_->addMark( 3 ); QCOMPARE( filteredData_->getNbLine(), 2LL ); QCOMPARE( filteredData_->getMaxLength(), 40 ); filteredData_->setVisibility( LogFilteredData::MarksAndMatches ); QCOMPARE( filteredData_->getNbLine(), 3LL ); QCOMPARE( filteredData_->getMaxLength(), 103 ); filteredData_->setVisibility( LogFilteredData::MarksOnly ); QCOMPARE( filteredData_->getNbLine(), 1LL ); QCOMPARE( filteredData_->getMaxLength(), 103 ); filteredData_->addMark( 0 ); QCOMPARE( filteredData_->getNbLine(), 2LL ); QCOMPARE( filteredData_->getMaxLength(), 103 ); filteredData_->deleteMark( 3 ); QCOMPARE( filteredData_->getNbLine(), 1LL ); QCOMPARE( filteredData_->getMaxLength(), 27 ); filteredData_->setVisibility( LogFilteredData::MarksAndMatches ); QCOMPARE( filteredData_->getMaxLength(), 40 ); QApplication::quit(); } // // Private functions // void TestLogFilteredData::loadingFinished() { QMutexLocker locker( &loadingFinishedMutex_ ); QWARN("loadingFinished"); loadingFinished_received_ = true; loadingFinished_read_ = false; loadingFinishedCondition_.wakeOne(); // Wait for the test thread to read the signal while ( ! loadingFinished_read_ ) loadingFinishedCondition_.wait( locker.mutex() ); } void TestLogFilteredData::searchProgressed( int nbMatches, int completion ) { QMutexLocker locker( &searchProgressedMutex_ ); QWARN("searchProgressed"); searchProgressed_received_ = true; searchProgressed_read_ = false; searchLastMatches_ = nbMatches; searchLastProgress_ = completion; searchProgressedCondition_.wakeOne(); // Wait for the test thread to read the signal while ( ! searchProgressed_read_ ) searchProgressedCondition_.wait( locker.mutex() ); } std::pair TestLogFilteredData::waitSearchProgressed() { QMutexLocker locker( &searchProgressedMutex_ ); while ( ! searchProgressed_received_ ) searchProgressedCondition_.wait( locker.mutex() ); QWARN("searchProgressed Received"); return std::pair(searchLastMatches_, searchLastProgress_); } void TestLogFilteredData::waitLoadingFinished() { QMutexLocker locker( &loadingFinishedMutex_ ); while ( ! loadingFinished_received_ ) loadingFinishedCondition_.wait( locker.mutex() ); QWARN("loadingFinished Received"); } void TestLogFilteredData::signalSearchProgressedRead() { QMutexLocker locker( &searchProgressedMutex_ ); searchProgressed_received_ = false; searchProgressed_read_ = true; searchProgressedCondition_.wakeOne(); } void TestLogFilteredData::signalLoadingFinishedRead() { QMutexLocker locker( &loadingFinishedMutex_ ); loadingFinished_received_ = false; loadingFinished_read_ = true; loadingFinishedCondition_.wakeOne(); } bool TestLogFilteredData::generateDataFiles() { char newLine[90]; QFile file( TMPDIR "/mediumlog.txt" ); if ( file.open( QIODevice::WriteOnly ) ) { for (int i = 0; i < ML_NB_LINES; i++) { snprintf(newLine, 89, ml_format, i); file.write( newLine, qstrlen(newLine) ); } } else { return false; } file.close(); file.setFileName( TMPDIR "/smalllog.txt" ); if ( file.open( QIODevice::WriteOnly ) ) { for (int i = 0; i < SL_NB_LINES; i++) { snprintf(newLine, 89, sl_format, i); file.write( newLine, qstrlen(newLine) ); } } else { return false; } file.close(); file.setFileName( TMPDIR "/length_test.txt" ); if ( file.open( QIODevice::WriteOnly ) ) { file.write( "This line is 27 characters.\n" ); file.write( "This line is longer: 36 characters.\n" ); file.write( "This line is even longer: 40 characters.\n" ); file.write( "This line is very long, it's actually hard to count but it is\ probably something around 103 characters.\n" ); } file.close(); return true; } glogg-0.9.2/tests/testlogfiltereddata.h000066400000000000000000000026311222324246300201700ustar00rootroot00000000000000#include #include #include class LogData; class LogFilteredData; class TestLogFilteredData: public QObject { Q_OBJECT public: TestLogFilteredData(); private slots: void initTestCase(); void simpleSearch(); void multipleSearch(); void marks(); void lineLength(); void updateSearch(); public slots: void loadingFinished(); void searchProgressed( int completion, int nbMatches ); private: bool generateDataFiles(); void simpleSearchTest(); void multipleSearchTest(); void updateSearchTest(); void marksTest(); void lineLengthTest(); std::pair waitSearchProgressed(); void waitLoadingFinished(); void signalSearchProgressedRead(); void signalLoadingFinishedRead(); LogData* logData_; LogFilteredData* filteredData_; // Synchronisation variables (protected by the two mutexes) bool loadingFinished_received_; bool loadingFinished_read_; bool searchProgressed_received_; bool searchProgressed_read_; int searchLastMatches_; int searchLastProgress_; QMutex loadingFinishedMutex_; QMutex searchProgressedMutex_; QWaitCondition loadingFinishedCondition_; QWaitCondition searchProgressedCondition_; }; glogg-0.9.2/tests/tests.pro000066400000000000000000000015201222324246300156450ustar00rootroot00000000000000TEMPLATE = app DEPENDPATH += ./ ../ INCLUDEPATH += ./ ../ DESTDIR = ./ CONFIG += qtestlib debug QMAKE_CXXFLAGS += -O2 -fpermissive isEmpty(TMPDIR) { DEFINES = TMPDIR=\\\"/tmp\\\" } else { DEFINES = TMPDIR=\\\"$${TMPDIR}\\\" } mac { CONFIG -= app_bundle } TARGET = logcrawler_tests HEADERS += testlogdata.h testlogfiltereddata.h logdata.h logfiltereddata.h logdataworkerthread.h\ abstractlogdata.h logfiltereddataworkerthread.h filewatcher.h marks.h SOURCES += testlogdata.cpp testlogfiltereddata.cpp abstractlogdata.cpp logdata.cpp main.cpp\ logfiltereddata.cpp logdataworkerthread.cpp logfiltereddataworkerthread.cpp filewatcher.cpp\ marks.cpp coverage:QMAKE_CXXFLAGS += -g -fprofile-arcs -ftest-coverage -O0 coverage:QMAKE_LFLAGS += -fprofile-arcs -ftest-coverage prof:QMAKE_CXXFLAGS += -pg prof:QMAKE_LFLAGS += -pg glogg-0.9.2/tools/000077500000000000000000000000001222324246300137615ustar00rootroot00000000000000glogg-0.9.2/tools/genlogs.sh000077500000000000000000000003751222324246300157630ustar00rootroot00000000000000#!/bin/bash # Generate a set of test logs # 5 million lines - 1, 429Mb perl -e 'for ($i = 0; $i < 4999999; $i++) { printf "LOGDATA is a part of LogCrawler, we are going to test it thoroughly, this is line %06d\n", $i; }'\ >/tmp/verybiglog.txt glogg-0.9.2/tools/perfmeter.pl000077500000000000000000000017441222324246300163200ustar00rootroot00000000000000#!/usr/bin/perl # Take a debug log from logcrawler and output some perf statistics # Can be plotted by echo "plot [ ] [0:0.1] 'foo.data'; pause mouse key;" | gnuplot - # Or an histogram: # plot './qvector.data' using ((floor($1/50)+0.5)*50):(1) smooth frequency w histeps, './qvar_default.data' using ((floor($1/50)+0.5)*50):(1) smooth frequency w histeps, './qvar_50000.data' using ((floor($1/50)+0.5)*50):(1) smooth frequency w histeps # Better: plot './0.6.0-3.data' using ((floor($1/0.005)+0.1)*0.005):(0.1) smooth frequency w histeps while (<>) { strip; if (/(\d\d\.\d\d\d) DEBUG4.*paintEvent.*firstLine=(\d+) lastLine=(\d+) /) { if ( ($3 - $2) > 35 ) { $beginning = $1; $first_line = $2; } } elsif (/(\d\d\.\d\d\d) DEBUG4.*End/) { if ($beginning) { $time = $1 - $beginning; # print "$first_line $time\n"; if ($time > 0) { print "$time\n"; } } } } glogg-0.9.2/tools/timer.pl000077500000000000000000000010011222324246300154310ustar00rootroot00000000000000#!/usr/bin/perl use Time::Local; while (<>) { chomp; print "$_\n"; if (/^- (\d*):(\d*):(\d*)\.(\d*) /) { $time = timelocal($3, $2, $1, (localtime)[3,4,5]) + ($4/1000.0); # print "$time\n"; if (/DEBUG: FullIndexOperation: Starting the count\.\.\./) { $startcount = $time; print "$startcount\n"; } elsif (/DEBUG: FullIndexOperation: \.\.\. finished counting\./) { print "Counting: ",$time-$startcount,"\n"; } } } glogg-0.9.2/utils.h000066400000000000000000000056431222324246300141420ustar00rootroot00000000000000/* * Copyright (C) 2011, 2013 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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. * * glogg 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 glogg. If not, see . */ #ifndef UTILS_H #define UTILS_H // Use a bisection method to find the given line number // in a sorted list. // The T type must be a container containing elements that // implement the lineNumber() member. // Returns true if the lineNumber is found, false if not // foundIndex is the index of the found number or the index // of the closest greater element. template bool lookupLineNumber( const T& list, qint64 lineNumber, int* foundIndex ) { int minIndex = 0; int maxIndex = list.size() - 1; // If the list is not empty if ( maxIndex - minIndex >= 0 ) { // First we test the ends if ( list[minIndex].lineNumber() == lineNumber ) { *foundIndex = minIndex; return true; } else if ( list[maxIndex].lineNumber() == lineNumber ) { *foundIndex = maxIndex; return true; } // Then we test the rest while ( (maxIndex - minIndex) > 1 ) { const int tryIndex = (minIndex + maxIndex) / 2; const qint64 currentMatchingNumber = list[tryIndex].lineNumber(); if ( currentMatchingNumber > lineNumber ) maxIndex = tryIndex; else if ( currentMatchingNumber < lineNumber ) minIndex = tryIndex; else if ( currentMatchingNumber == lineNumber ) { *foundIndex = tryIndex; return true; } } // If we haven't found anything... // ... end of the list or before the next if ( lineNumber > list[maxIndex].lineNumber() ) *foundIndex = maxIndex + 1; else if ( lineNumber > list[minIndex].lineNumber() ) *foundIndex = minIndex + 1; else *foundIndex = minIndex; } else { *foundIndex = 0; } return false; } // Represents a position in a file (line, column) class FilePosition { public: FilePosition() { line_ = -1; column_ = -1; } FilePosition( qint64 line, int column ) { line_ = line; column_ = column; } qint64 line() const { return line_; } int column() const { return column_; } private: qint64 line_; int column_; }; #endif