pax_global_header00006660000000000000000000000064133343477260014526gustar00rootroot0000000000000052 comment=e461058dcf98b6850e62f6e256a894a6d9912a00 vizigrep-1.4/000077500000000000000000000000001333434772600132315ustar00rootroot00000000000000vizigrep-1.4/.gitignore000066400000000000000000000001101333434772600152110ustar00rootroot00000000000000*.py[cod] *~ install/ubuntu-installer/deb install/ubuntu-installer/*.gz vizigrep-1.4/.version000066400000000000000000000000041333434772600147110ustar00rootroot000000000000001.4 vizigrep-1.4/README000066400000000000000000000050651333434772600141170ustar00rootroot00000000000000Vizigrep ======== Graphical file contents search tool using regular expressions. Copyright: Jason J. Herne (hernejj@gmail.com) Vizigrep is a graphical user interface for performing fast and powerful searches inside a group of files. Simply tell Vizigrep which folder you want to search and what you want to search for and it will quickly find all occurrences of your search string within the files and folders you have selected. The search results are annotated to show you the lines containing your search term and color coding is used to help you quickly lock your eyes on to what you are searching for. If simple search strings are not powerful enough Vizigrep also understands regular expressions. Starting Vizigrep ------------------- If you installed Vizigrep you should be able to simply execute the 'vizigrep' command or start vizigrep from your desktop's application menu. If you have cloned the Git repository and would like to run vizigrep from the cloned copy simply run 'python run.py'. ./setup.py install is not meant to be run directly. You can do it, but the icon and .desktop files are not properly installed. It is best to either run from the cloned copy or generate and install the deb package. See install/ubuntu-installer/readme.txt for details. Vizigrep accepts an optional command line argument that, if specified, will be used to populate the search path field. Navigation ------------ Vizigrep is very intuitively navigated using only the mouse. Simply choose a folder to search, type your search string and then click the Search button. Once the results are displayed you can click on the name of a file to open it in your chosen text editor. At your option you may also carry out searches using the keyboard. Simply use the tab key to move the input focus (the keyboard cursor) between the three main controls: "folder entry", "search string entry", and the "search results". If you've used the tab key to focus the search results you will see a keyboard cursor appear within the results. Hitting enter while the cursor is over one of the filenames will open that file in your chosen text editor. You may also hold down the Shift key and use the arrow keys to select and copy a portion of the results. A new search Tab can be created via keyboard shortcut Ctrl+T. A search Tab can be closed via keyboard shortcut Ctrl+W. License ------------- This application is released under the GNU General Public License v2. A full copy of the license can be found in copyright.txt or here: http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt Thank you for using free software! vizigrep-1.4/changelog000066400000000000000000000016441333434772600151100ustar00rootroot000000000000001.4 * Added command line argument to set search path on startup * Gtk: Minor Gtk style and api fixes * deb: Added missing dependency on gir1.2-gtksource-3.0 1.3 * Allow simultaneous searches in separate tabs * Auto-navigate to new tab upon creation * Moved Spinner to within tab titles * Bugfix: Improve handling of some special chars * Bugfix: Unreverse case sensitivty matching * Bugfix: Status bar now shows correct stats when switching tabs 1.2 * Add tabbed interface for displaying multiple search results * bugfix: Properly handle parenthesis when matching and displaying 1.1 * Colorize background for alternating output rows * Improved handling of special chars in regex and search path strings * Folder dialog remembers last chosen path & defaults to user's home * Bugfix: Display error messages directly from grep stderr * Better unit test coverage 1.0 * Initial release (Closes: #702990) vizigrep-1.4/check-code.sh000077500000000000000000000004371333434772600155610ustar00rootroot00000000000000#!/usr/bin/env bash echo "[pep8]" pep8 --ignore W191,W293,E125,E201,E202,E221,E241,E261,E272,E302,E401,E501,E701 --exclude="./ut/test-data,./build" ./ echo '[pyflakes]' pyflakes vizigrep/*.py vizigrep/guiapp/*.py ut/*.py echo '[desktop-file-validate]' desktop-file-validate *.desktop vizigrep-1.4/copyright.txt000066400000000000000000000432541333434772600160120ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) 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 this service 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 make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. 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. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the 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 a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE 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. 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 convey 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision 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, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This 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. vizigrep-1.4/install/000077500000000000000000000000001333434772600146775ustar00rootroot00000000000000vizigrep-1.4/install/release-prep.txt000066400000000000000000000023061333434772600200250ustar00rootroot00000000000000Things to do to make a release -------------------------------- 1. Update setup.py version field. 2. Update man page (line 1) with current month, year and version number 3. Update .version with correction version number 4. Update debian/changelog 4a. Create new section in changelog for this version cd install/ubuntu-installer/ dch --check-dirname-level 0 --newversion 1.2-1 4b. Change "UNRELEASED; urgency=medium" to "unstable; urgency=low" 4c. Fill in changelog with needed bullet points. 5. Test release: 5a. cd install/ubuntu-installer/ ./make-test-tgz.sh 5b. Create test deb. cd install/ubuntu-installer/ ./make-deb.sh 5c. Install and test install/ubuntu-installer/deb/vizigrep_____.deb 6. Commit the above changes, tag release and push to Github. git commit -a -m'Release v1.2' git tag v1.2 git push git push origin --tags 7. Make deb install data cd install/ubuntu-installer/ rm v1.2.tar.gz ./make-deb.sh Update a release tag ----------------------- If you messed up and need to delete and update a release tag: git tag -d v1.2 git push origin :refs/tags/v1.2 Now just re-add the tag when you are ready. vizigrep-1.4/install/ubuntu-installer/000077500000000000000000000000001333434772600202145ustar00rootroot00000000000000vizigrep-1.4/install/ubuntu-installer/check-deb.sh000077500000000000000000000001621333434772600223570ustar00rootroot00000000000000#!/usr/bin/env bash cd deb echo '[lintian]' lintian *.changes echo '[lintian4py]' lintian4py *.changes cd .. vizigrep-1.4/install/ubuntu-installer/debian/000077500000000000000000000000001333434772600214365ustar00rootroot00000000000000vizigrep-1.4/install/ubuntu-installer/debian/changelog000066400000000000000000000006501333434772600233110ustar00rootroot00000000000000vizigrep (1.4-1) unstable; urgency=low * Added command line argument to set search path on startup * Gtk: Minor Gtk style and api fixes * deb: Added missing dependency on gir1.2-gtksource-3.0 -- Jason J. Herne Wed, 20 Jul 2016 19:11:42 -0400 vizigrep (1.3-1) unstable; urgency=low * Initial release (Closes: #702990) -- Jason J. Herne Fri, 24 Jun 2016 15:36:27 -0400 vizigrep-1.4/install/ubuntu-installer/debian/compat000066400000000000000000000000021333434772600226340ustar00rootroot000000000000009 vizigrep-1.4/install/ubuntu-installer/debian/control000066400000000000000000000020701333434772600230400ustar00rootroot00000000000000Source: vizigrep Section: utils Priority: optional Maintainer: Jason J. Herne Build-Depends: debhelper (>= 9), python-all, dh-python, python-setuptools X-Python-Version: >= 2.6 Standards-Version: 3.9.8 Homepage: https://github.com/hernejj/vizigrep Package: vizigrep Architecture: all Depends: ${misc:Depends}, ${python:Depends}, python-pkg-resources, gir1.2-gtk-3.0, gir1.2-gtksource-3.0, python-gi Description: graphical file contents search tool using regular expressions Vizigrep is a graphical user interface for performing fast and powerful searches inside a group of files. Simply tell Vizigrep which folder you want to search and what you want to search for and it will quickly find all occurrences of your search string within the files and folders you have selected. The search results are annotated to show you the lines containing your search term and color coding is used to help you quickly lock your eyes on to what you are searching for. If simple search strings are not powerful enough Vizigrep also understands regular expressions. vizigrep-1.4/install/ubuntu-installer/debian/copyright000066400000000000000000000020531333434772600233710ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Vizigrep Source: http://github.com/hernejj/vizigrep Files: * Copyright: Copyright 2016 Jason J. Herne License: GPL-2+ 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 2 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 package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA . On Debian systems, the full text of the GNU General Public License version 2 can be found in the file `/usr/share/common-licenses/GPL-2'. vizigrep-1.4/install/ubuntu-installer/debian/docs000066400000000000000000000000071333434772600223060ustar00rootroot00000000000000README vizigrep-1.4/install/ubuntu-installer/debian/manpages000066400000000000000000000000151333434772600231500ustar00rootroot00000000000000vizigrep.man vizigrep-1.4/install/ubuntu-installer/debian/rules000077500000000000000000000002371333434772600225200ustar00rootroot00000000000000#!/usr/bin/make -f # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 export PYBUILD_NAME=vizigrep %: dh $@ --with python2 --buildsystem=pybuild vizigrep-1.4/install/ubuntu-installer/debian/source/000077500000000000000000000000001333434772600227365ustar00rootroot00000000000000vizigrep-1.4/install/ubuntu-installer/debian/source/format000066400000000000000000000000141333434772600241440ustar00rootroot000000000000003.0 (quilt) vizigrep-1.4/install/ubuntu-installer/debian/watch000066400000000000000000000002241333434772600224650ustar00rootroot00000000000000version=3 opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/vizigrep-$1\.tar\.gz/ \ https://github.com/hernejj/vizigrep/releases .*/v?(\d\S*)\.tar\.gz vizigrep-1.4/install/ubuntu-installer/make-deb.sh000077500000000000000000000022531333434772600222220ustar00rootroot00000000000000#!/usr/bin/env bash SRC_DIR="../../" PROGRAM_NAME="vizigrep" VERSION=`cat $SRC_DIR\.version` PKG_TREE_NAME=$PROGRAM_NAME-$VERSION GITHUB_TAR_NAME=v"$VERSION".tar.gz ORIG_TAR_NAME=$PROGRAM_NAME'_'$VERSION.orig.tar.gz # Remove old package tree & DEB files rm -rf $PKG_TREE_NAME rm -rf ./deb #Fetch '.orig' original source package if we don't already have it if [ ! -f $GITHUB_TAR_NAME ]; then wget https://github.com/hernejj/vizigrep/archive/$GITHUB_TAR_NAME fi if [ ! -f $GITHUB_TAR_NAME ]; then echo "Could not download $GITHUB_TAR_NAME from Github" exit $? fi # Deb build process expects to find an original .tar.gz with a very specific name cp -a $GITHUB_TAR_NAME $ORIG_TAR_NAME # Unpack orig source package - this creates a folder named $PKG_TREE_NAME tar -zxf $GITHUB_TAR_NAME # Copy debian build specific files to expected location cp -a $PKG_TREE_NAME/install/ubuntu-installer/debian $PKG_TREE_NAME/ # Build! cd $PKG_TREE_NAME/ pwd debuild cd .. # Place all files neeed for a debian release into ./deb mkdir -p deb mv *.deb *.dsc *.build *.changes deb/ mv $ORIG_TAR_NAME deb/ mv $PROGRAM_NAME'_'$VERSION'-1.debian.tar.xz' deb/ # Cleanup rm -rf $PKG_TREE_NAME vizigrep-1.4/install/ubuntu-installer/make-test-tgz.sh000077500000000000000000000013311333434772600232450ustar00rootroot00000000000000#!/usr/bin/env bash SRC_DIR="../../" PROGRAM_NAME="vizigrep" VERSION=`cat $SRC_DIR\.version` PKG_TREE_NAME=$PROGRAM_NAME-$VERSION GITHUB_TAR_NAME=v"$VERSION".tar.gz # Remove old package tree & tgz rm -rf /tmp/$PKG_TREE_NAME rm -f $GITHUB_TAR_NAME # Make & populate folder containing fake release cp -a $SRC_DIR. /tmp/$PKG_TREE_NAME # Yes, the . is important. It causes hidden files to be copied. # Remove ./deb folder from PKG_TREE if it exists - stops useless files from getting rolled up into test tgz rm -rf /tmp/$PKG_TREE_NAME/install/ubuntu-installer/deb # Tar.gz it! cd /tmp tar -zcf $GITHUB_TAR_NAME $PKG_TREE_NAME # Move tgz to proper location cd - mv /tmp/$GITHUB_TAR_NAME ./ # Cleanup rm -rf /tmp/$PKG_TREE_NAME vizigrep-1.4/install/ubuntu-installer/readme.txt000066400000000000000000000016251333434772600222160ustar00rootroot00000000000000make-deb.sh -------------- This script is used to create an installable deb and associated files that are required for uploading the package to a Debian repository. The output files are put into ./deb. make-deb.sh has a few dependencies. Make sure to install them: devscripts build-essential python-all-dev debhelper dh-python lintian lintian4python This script downloads a source package from the project's Github page. By default it will use the package obtained by downloading the tag v$VERSION where $VERSION is the contents of the file vizigrep/.version. This source package is then used to generate the deb. make-test-tgz.sh ------------------- This script is used to generate a source package using the local copy of the code. It is useful for installing and testing the code before you commit it to Git and/or before you set a release tag on Github. Just run make-test-tgz.sh followed by make-deb.sh. vizigrep-1.4/run.py000077500000000000000000000001021333434772600144030ustar00rootroot00000000000000#!/usr/bin/env python import vizigrep.main vizigrep.main.main() vizigrep-1.4/setup.py000077500000000000000000000025021333434772600147450ustar00rootroot00000000000000#!/usr/bin/python import os from setuptools import setup, Command # Customize 'setup.py clean' to remove unwanted build artifacts class CleanCommand(Command): user_options = [ ('all', None, '(--all, required by deb build process)') ] def initialize_options(self): self.all = False def finalize_options(self): pass def run(self): os.system('rm -rf ./build ./dist ./*.egg-info') setup(name='vizigrep', version='1.4', packages=['vizigrep', 'vizigrep.guiapp'], cmdclass={'clean': CleanCommand}, package_data={'vizigrep': ['ui/*']}, # These files go to the right location when we run via debuild. However, # When we run ./setup.py install manually then their install locations are # relative to the module installation folder: # /usr/local/lib/python2.7/dist-packages/vizigrep-*/share/ data_files=[ ('share/applications', ['vizigrep.desktop']), ('share/vizigrep', ['vizigrep.svg']), ('share/doc/vizigrep', ['changelog']), ], entry_points={ 'gui_scripts': [ 'vizigrep = vizigrep.main:main' ] }, zip_safe=False, include_package_data=True, ) # Create MANIFEST.in, as described here: # https://wiki.python.org/moin/Distutils/Tutorial vizigrep-1.4/ut/000077500000000000000000000000001333434772600136615ustar00rootroot00000000000000vizigrep-1.4/ut/__init__.py000066400000000000000000000000001333434772600157600ustar00rootroot00000000000000vizigrep-1.4/ut/test-data/000077500000000000000000000000001333434772600155475ustar00rootroot00000000000000vizigrep-1.4/ut/test-data/File1000066400000000000000000000000341333434772600164270ustar00rootroot00000000000000This is a test file linefoo vizigrep-1.4/ut/test-data/File2000066400000000000000000000000721333434772600164320ustar00rootroot00000000000000Another file! The brown fox jumped over the green fence ? vizigrep-1.4/ut/test-data/File3000066400000000000000000000000021333434772600164240ustar00rootroot00000000000000 vizigrep-1.4/ut/test-data/FolderA/000077500000000000000000000000001333434772600170635ustar00rootroot00000000000000vizigrep-1.4/ut/test-data/FolderA/File1000066400000000000000000000000261333434772600177440ustar00rootroot00000000000000i am a fi\le file too vizigrep-1.4/ut/test-data/FolderA/File2000066400000000000000000000000461333434772600177470ustar00rootroot00000000000000#include "stdio.h" @fnclude vizigrep-1.4/ut/test-data/FolderA/File3.py000066400000000000000000000037711333434772600204070ustar00rootroot00000000000000import subprocess, re, os, traceback from threading import Thread from gi.repository import Gtk, Gdk, GObject from Window import Window from GrepEngine import GrepEngine, GrepResult, GrepResults, NoResultsException, BadPathException, BadRegexException from PreferencesWindow import PreferencesWindow class ViziGrepWindow(Window): gtk_builder_file = 'vizigrep.glade' window_name = 'win_main' def __init__(self, prefs): self.results = [] Window.__init__(self, self.gtk_builder_file, self.window_name) self.prefs = prefs self.ge = GrepEngine() self.ge.exclude_dirs = self.prefs.get('exclude-dirs') self.ge.exclude_files = self.prefs.get('exclude-files') txtbuf = self.txt_results.get_buffer() self.tag_fixed = txtbuf.create_tag('fixed', family='Monospace') self.tag_link = txtbuf.create_tag('link', foreground='Blue') self.tag_red = txtbuf.create_tag('color', foreground='Red') self.tag_green = txtbuf.create_tag('green', foreground='Dark Green') self.gtk_window.connect('delete_event', self.close) self.btn_search.connect('clicked', self.btn_search_clicked) self.txt_results.connect('button-press-event', self.results_clicked) self.lbl_path.connect('activate-link', self.lbl_path_clicked) self.txt_results.connect('motion-notify-event', self.results_mouse_motion) self.txt_results.connect('key-press-event', self.results_keypress) self.lbl_options.connect('activate--link', self.options_clicked) (win_width, win_height) = self.prefs.get('window-size') self.win_main.resize(win_width,win_height) self.cbox_path.forall(self.cbox_disable_togglebutton_focus, None) self.cbox_search.forall(self.cbox_disable_togglebutton_focus, None) self.deactivate_on_search = [self.btn_search, self.lbl_path, self.lbl_options, self.cbox_search, self.cbox_path, self.txt_results] vizigrep-1.4/ut/test-data/Space A/000077500000000000000000000000001333434772600167435ustar00rootroot00000000000000vizigrep-1.4/ut/test-data/Space A/Space File000066400000000000000000000000151333434772600205550ustar00rootroot00000000000000space_marker vizigrep-1.4/ut/test-data/c-source/000077500000000000000000000000001333434772600172675ustar00rootroot00000000000000vizigrep-1.4/ut/test-data/c-source/xpad.c000066400000000000000000001216011333434772600203700ustar00rootroot00000000000000/* * X-Box gamepad driver * * Copyright (c) 2002 Marko Friedemann * 2004 Oliver Schwartz , * Steven Toth , * Franz Lehner , * Ivan Hawkes * 2005 Dominic Cerquetti * 2006 Adam Buchbinder * 2007 Jan Kratochvil * 2010 Christoph Fritz * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * This driver is based on: * - information from http://euc.jp/periphs/xbox-controller.ja.html * - the iForce driver drivers/char/joystick/iforce.c * - the skeleton-driver drivers/usb/usb-skeleton.c * - Xbox 360 information http://www.free60.org/wiki/Gamepad * * Thanks to: * - ITO Takayuki for providing essential xpad information on his website * - Vojtech Pavlik - iforce driver / input subsystem * - Greg Kroah-Hartman - usb-skeleton driver * - XBOX Linux project - extra USB id's * * TODO: * - fine tune axes (especially trigger axes) * - fix "analog" buttons (reported as digital now) * - get rumble working * - need USB IDs for other dance pads * * History: * * 2002-06-27 - 0.0.1 : first version, just said "XBOX HID controller" * * 2002-07-02 - 0.0.2 : basic working version * - all axes and 9 of the 10 buttons work (german InterAct device) * - the black button does not work * * 2002-07-14 - 0.0.3 : rework by Vojtech Pavlik * - indentation fixes * - usb + input init sequence fixes * * 2002-07-16 - 0.0.4 : minor changes, merge with Vojtech's v0.0.3 * - verified the lack of HID and report descriptors * - verified that ALL buttons WORK * - fixed d-pad to axes mapping * * 2002-07-17 - 0.0.5 : simplified d-pad handling * * 2004-10-02 - 0.0.6 : DDR pad support * - borrowed from the XBOX linux kernel * - USB id's for commonly used dance pads are present * - dance pads will map D-PAD to buttons, not axes * - pass the module paramater 'dpad_to_buttons' to force * the D-PAD to map to buttons if your pad is not detected * * Later changes can be tracked in SCM. */ #include #include #include #include #include #include #define DRIVER_AUTHOR "Marko Friedemann " #define DRIVER_DESC "X-Box pad driver" #define XPAD_PKT_LEN 32 /* xbox d-pads should map to buttons, as is required for DDR pads but we map them to axes when possible to simplify things */ #define MAP_DPAD_TO_BUTTONS (1 << 0) #define MAP_TRIGGERS_TO_BUTTONS (1 << 1) #define MAP_STICKS_TO_NULL (1 << 2) #define DANCEPAD_MAP_CONFIG (MAP_DPAD_TO_BUTTONS | \ MAP_TRIGGERS_TO_BUTTONS | MAP_STICKS_TO_NULL) #define XTYPE_XBOX 0 #define XTYPE_XBOX360 1 #define XTYPE_XBOX360W 2 #define XTYPE_XBOXONE 3 #define XTYPE_UNKNOWN 4 static bool dpad_to_buttons; module_param(dpad_to_buttons, bool, S_IRUGO); MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads"); static bool triggers_to_buttons; module_param(triggers_to_buttons, bool, S_IRUGO); MODULE_PARM_DESC(triggers_to_buttons, "Map triggers to buttons rather than axes for unknown pads"); static bool sticks_to_null; module_param(sticks_to_null, bool, S_IRUGO); MODULE_PARM_DESC(sticks_to_null, "Do not map sticks at all for unknown pads"); static const struct xpad_device { u16 idVendor; u16 idProduct; char *name; u8 mapping; u8 xtype; } xpad_device[] = { { 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", 0, XTYPE_XBOX }, { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", 0, XTYPE_XBOX }, { 0x045e, 0x0287, "Microsoft Xbox Controller S", 0, XTYPE_XBOX }, { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", 0, XTYPE_XBOX }, { 0x045e, 0x028e, "Microsoft X-Box 360 pad", 0, XTYPE_XBOX360 }, { 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE }, { 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX }, { 0x044f, 0xb326, "Thrustmaster Gamepad GP XID", 0, XTYPE_XBOX360 }, { 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 }, { 0x046d, 0xc21e, "Logitech Gamepad F510", 0, XTYPE_XBOX360 }, { 0x046d, 0xc21f, "Logitech Gamepad F710", 0, XTYPE_XBOX360 }, { 0x046d, 0xc242, "Logitech Chillstream Controller", 0, XTYPE_XBOX360 }, { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", 0, XTYPE_XBOX }, { 0x046d, 0xca88, "Logitech Compact Controller for Xbox", 0, XTYPE_XBOX }, { 0x05fd, 0x1007, "Mad Catz Controller (unverified)", 0, XTYPE_XBOX }, { 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", 0, XTYPE_XBOX }, { 0x0738, 0x4516, "Mad Catz Control Pad", 0, XTYPE_XBOX }, { 0x0738, 0x4522, "Mad Catz LumiCON", 0, XTYPE_XBOX }, { 0x0738, 0x4526, "Mad Catz Control Pad Pro", 0, XTYPE_XBOX }, { 0x0738, 0x4536, "Mad Catz MicroCON", 0, XTYPE_XBOX }, { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", 0, XTYPE_XBOX }, { 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x0738, 0x4718, "Mad Catz Street Fighter IV FightStick SE", 0, XTYPE_XBOX360 }, { 0x0738, 0x4726, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x0738, 0x4728, "Mad Catz Street Fighter IV FightPad", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x0738, 0x4740, "Mad Catz Beat Pad", 0, XTYPE_XBOX360 }, { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x0738, 0xb726, "Mad Catz Xbox controller - MW2", 0, XTYPE_XBOX360 }, { 0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad", XTYPE_XBOX360 }, { 0x0738, 0xcb02, "Saitek Cyborg Rumble Pad - PC/Xbox 360", 0, XTYPE_XBOX360 }, { 0x0738, 0xcb03, "Saitek P3200 Rumble Pad - PC/Xbox 360", 0, XTYPE_XBOX360 }, { 0x0738, 0xf738, "Super SFIV FightStick TE S", 0, XTYPE_XBOX360 }, { 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX }, { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", DANCEPAD_MAP_CONFIG, XTYPE_XBOX }, { 0x0c12, 0x880a, "Pelican Eclipse PL-2023", 0, XTYPE_XBOX }, { 0x0c12, 0x8810, "Zeroplus Xbox Controller", 0, XTYPE_XBOX }, { 0x0c12, 0x9902, "HAMA VibraX - *FAULTY HARDWARE*", 0, XTYPE_XBOX }, { 0x0d2f, 0x0002, "Andamiro Pump It Up pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x0e4c, 0x1097, "Radica Gamester Controller", 0, XTYPE_XBOX }, { 0x0e4c, 0x2390, "Radica Games Jtech Controller", 0, XTYPE_XBOX }, { 0x0e6f, 0x0003, "Logic3 Freebird wireless Controller", 0, XTYPE_XBOX }, { 0x0e6f, 0x0005, "Eclipse wireless Controller", 0, XTYPE_XBOX }, { 0x0e6f, 0x0006, "Edge wireless Controller", 0, XTYPE_XBOX }, { 0x0e6f, 0x0105, "HSM3 Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, { 0x0e6f, 0x0113, "Afterglow AX.1 Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x0301, "Logic3 Controller", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x0401, "Logic3 Controller", 0, XTYPE_XBOX360 }, { 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", 0, XTYPE_XBOX }, { 0x0e8f, 0x3008, "Generic xbox control (dealextreme)", 0, XTYPE_XBOX }, { 0x0f0d, 0x000a, "Hori Co. DOA4 FightStick", 0, XTYPE_XBOX360 }, { 0x0f0d, 0x000d, "Hori Fighting Stick EX2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX }, { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX }, { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", 0, XTYPE_XBOX }, { 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, { 0x12ab, 0x0301, "PDP AFTERGLOW AX.1", 0, XTYPE_XBOX360 }, { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", 0, XTYPE_XBOX360 }, { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 }, { 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 }, { 0x15e4, 0x3f00, "Power A Mini Pro Elite", 0, XTYPE_XBOX360 }, { 0x15e4, 0x3f0a, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 }, { 0x15e4, 0x3f10, "Batarang Xbox 360 controller", 0, XTYPE_XBOX360 }, { 0x162e, 0xbeef, "Joytech Neo-Se Take2", 0, XTYPE_XBOX360 }, { 0x1689, 0xfd00, "Razer Onza Tournament Edition", 0, XTYPE_XBOX360 }, { 0x1689, 0xfd01, "Razer Onza Classic Edition", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 }, { 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 }, { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, { 0x1bad, 0xf016, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf023, "MLG Pro Circuit Controller (Xbox)", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf028, "Street Fighter IV FightPad", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf038, "Street Fighter IV FightStick TE", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf900, "Harmonix Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf901, "Gamestop Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf903, "Tron Xbox 360 controller", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5000, "Razer Atrox Arcade Stick", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5303, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5500, "Hori XBOX 360 EX 2 with Turbo", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5501, "Hori Real Arcade Pro VX-SA", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5b02, "Thrustmaster, Inc. GPX Controller", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5b03, "Thrustmaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 }, { 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX }, { 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN } }; /* buttons shared with xbox and xbox360 */ static const signed short xpad_common_btn[] = { BTN_A, BTN_B, BTN_X, BTN_Y, /* "analog" buttons */ BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */ -1 /* terminating entry */ }; /* original xbox controllers only */ static const signed short xpad_btn[] = { BTN_C, BTN_Z, /* "analog" buttons */ -1 /* terminating entry */ }; /* used when dpad is mapped to buttons */ static const signed short xpad_btn_pad[] = { BTN_TRIGGER_HAPPY1, BTN_TRIGGER_HAPPY2, /* d-pad left, right */ BTN_TRIGGER_HAPPY3, BTN_TRIGGER_HAPPY4, /* d-pad up, down */ -1 /* terminating entry */ }; /* used when triggers are mapped to buttons */ static const signed short xpad_btn_triggers[] = { BTN_TL2, BTN_TR2, /* triggers left/right */ -1 }; static const signed short xpad360_btn[] = { /* buttons for x360 controller */ BTN_TL, BTN_TR, /* Button LB/RB */ BTN_MODE, /* The big X button */ -1 }; static const signed short xpad_abs[] = { ABS_X, ABS_Y, /* left stick */ ABS_RX, ABS_RY, /* right stick */ -1 /* terminating entry */ }; /* used when dpad is mapped to axes */ static const signed short xpad_abs_pad[] = { ABS_HAT0X, ABS_HAT0Y, /* d-pad axes */ -1 /* terminating entry */ }; /* used when triggers are mapped to axes */ static const signed short xpad_abs_triggers[] = { ABS_Z, ABS_RZ, /* triggers left/right */ -1 }; /* * Xbox 360 has a vendor-specific class, so we cannot match it with only * USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we * match against vendor id as well. Wired Xbox 360 devices have protocol 1, * wireless controllers have protocol 129. */ #define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \ .idVendor = (vend), \ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \ .bInterfaceSubClass = 93, \ .bInterfaceProtocol = (pr) #define XPAD_XBOX360_VENDOR(vend) \ { XPAD_XBOX360_VENDOR_PROTOCOL(vend,1) }, \ { XPAD_XBOX360_VENDOR_PROTOCOL(vend,129) } /* The Xbox One controller uses subclass 71 and protocol 208. */ #define XPAD_XBOXONE_VENDOR_PROTOCOL(vend, pr) \ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \ .idVendor = (vend), \ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \ .bInterfaceSubClass = 71, \ .bInterfaceProtocol = (pr) #define XPAD_XBOXONE_VENDOR(vend) \ { XPAD_XBOXONE_VENDOR_PROTOCOL(vend, 208) } static struct usb_device_id xpad_table[] = { { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */ XPAD_XBOX360_VENDOR(0x044f), /* Thrustmaster X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */ XPAD_XBOXONE_VENDOR(0x045e), /* Microsoft X-Box One controllers */ XPAD_XBOX360_VENDOR(0x046d), /* Logitech X-Box 360 style controllers */ XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */ { USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */ XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x12ab), /* X-Box 360 dance pads */ XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x146b), /* BigBen Interactive Controllers */ XPAD_XBOX360_VENDOR(0x1bad), /* Harminix Rock Band Guitar and Drums */ XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */ XPAD_XBOX360_VENDOR(0x1689), /* Razer Onza */ XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */ XPAD_XBOX360_VENDOR(0x1532), /* Razer Sabertooth */ XPAD_XBOX360_VENDOR(0x15e4), /* Numark X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x162e), /* Joytech X-Box 360 controllers */ { } }; MODULE_DEVICE_TABLE(usb, xpad_table); struct usb_xpad { struct input_dev *dev; /* input device interface */ struct usb_device *udev; /* usb device */ struct usb_interface *intf; /* usb interface */ int pad_present; struct urb *irq_in; /* urb for interrupt in report */ unsigned char *idata; /* input data */ dma_addr_t idata_dma; struct urb *bulk_out; unsigned char *bdata; struct urb *irq_out; /* urb for interrupt out report */ unsigned char *odata; /* output data */ dma_addr_t odata_dma; int odata_busy; spinlock_t pend_lock; unsigned pend_rum; unsigned char rum_data[XPAD_PKT_LEN]; unsigned pend_led; unsigned char led_data[XPAD_PKT_LEN]; #if defined(CONFIG_JOYSTICK_XPAD_LEDS) struct xpad_led *led; #endif char phys[64]; /* physical device path */ int mapping; /* map d-pad to buttons or to axes */ int xtype; /* type of xbox device */ }; /* * xpad_process_packet * * Completes a request by converting the data into events for the * input subsystem. * * The used report descriptor was taken from ITO Takayukis website: * http://euc.jp/periphs/xbox-controller.ja.html */ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) { struct input_dev *dev = xpad->dev; if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { /* left stick */ input_report_abs(dev, ABS_X, (__s16) le16_to_cpup((__le16 *)(data + 12))); input_report_abs(dev, ABS_Y, ~(__s16) le16_to_cpup((__le16 *)(data + 14))); /* right stick */ input_report_abs(dev, ABS_RX, (__s16) le16_to_cpup((__le16 *)(data + 16))); input_report_abs(dev, ABS_RY, ~(__s16) le16_to_cpup((__le16 *)(data + 18))); } /* triggers left/right */ if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { input_report_key(dev, BTN_TL2, data[10]); input_report_key(dev, BTN_TR2, data[11]); } else { input_report_abs(dev, ABS_Z, data[10]); input_report_abs(dev, ABS_RZ, data[11]); } /* digital pad */ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { /* dpad as buttons (left, right, up, down) */ input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04); input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08); input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01); input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02); } else { input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04)); input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01)); } /* start/back buttons and stick press left/right */ input_report_key(dev, BTN_START, data[2] & 0x10); input_report_key(dev, BTN_SELECT, data[2] & 0x20); input_report_key(dev, BTN_THUMBL, data[2] & 0x40); input_report_key(dev, BTN_THUMBR, data[2] & 0x80); /* "analog" buttons A, B, X, Y */ input_report_key(dev, BTN_A, data[4]); input_report_key(dev, BTN_B, data[5]); input_report_key(dev, BTN_X, data[6]); input_report_key(dev, BTN_Y, data[7]); /* "analog" buttons black, white */ input_report_key(dev, BTN_C, data[8]); input_report_key(dev, BTN_Z, data[9]); input_sync(dev); } /* * xpad360_process_packet * * Completes a request by converting the data into events for the * input subsystem. It is version for xbox 360 controller * * The used report descriptor was taken from: * http://www.free60.org/wiki/Gamepad */ static void xpad360_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) { struct input_dev *dev = xpad->dev; /* digital pad */ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { /* dpad as buttons (left, right, up, down) */ input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04); input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08); input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01); input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02); } else { input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04)); input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01)); } /* start/back buttons */ input_report_key(dev, BTN_START, data[2] & 0x10); input_report_key(dev, BTN_SELECT, data[2] & 0x20); /* stick press left/right */ input_report_key(dev, BTN_THUMBL, data[2] & 0x40); input_report_key(dev, BTN_THUMBR, data[2] & 0x80); /* buttons A,B,X,Y,TL,TR and MODE */ input_report_key(dev, BTN_A, data[3] & 0x10); input_report_key(dev, BTN_B, data[3] & 0x20); input_report_key(dev, BTN_X, data[3] & 0x40); input_report_key(dev, BTN_Y, data[3] & 0x80); input_report_key(dev, BTN_TL, data[3] & 0x01); input_report_key(dev, BTN_TR, data[3] & 0x02); input_report_key(dev, BTN_MODE, data[3] & 0x04); if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { /* left stick */ input_report_abs(dev, ABS_X, (__s16) le16_to_cpup((__le16 *)(data + 6))); input_report_abs(dev, ABS_Y, ~(__s16) le16_to_cpup((__le16 *)(data + 8))); /* right stick */ input_report_abs(dev, ABS_RX, (__s16) le16_to_cpup((__le16 *)(data + 10))); input_report_abs(dev, ABS_RY, ~(__s16) le16_to_cpup((__le16 *)(data + 12))); } /* triggers left/right */ if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { input_report_key(dev, BTN_TL2, data[4]); input_report_key(dev, BTN_TR2, data[5]); } else { input_report_abs(dev, ABS_Z, data[4]); input_report_abs(dev, ABS_RZ, data[5]); } input_sync(dev); } /* * xpad360w_process_packet * * Completes a request by converting the data into events for the * input subsystem. It is version for xbox 360 wireless controller. * * Byte.Bit * 00.1 - Status change: The controller or headset has connected/disconnected * Bits 01.7 and 01.6 are valid * 01.7 - Controller present * 01.6 - Headset present * 01.1 - Pad state (Bytes 4+) valid * */ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) { /* Presence change */ if (data[0] & 0x08) { if (data[1] & 0x80) { xpad->pad_present = 1; usb_submit_urb(xpad->bulk_out, GFP_ATOMIC); } else xpad->pad_present = 0; } /* Valid pad data */ if (!(data[1] & 0x1)) return; xpad360_process_packet(xpad, cmd, &data[4]); } /* * xpadone_process_buttons * * Process a button update packet from an Xbox one controller. */ static void xpadone_process_buttons(struct usb_xpad *xpad, struct input_dev *dev, unsigned char *data) { /* menu/view buttons */ input_report_key(dev, BTN_START, data[4] & 0x04); input_report_key(dev, BTN_SELECT, data[4] & 0x08); /* buttons A,B,X,Y */ input_report_key(dev, BTN_A, data[4] & 0x10); input_report_key(dev, BTN_B, data[4] & 0x20); input_report_key(dev, BTN_X, data[4] & 0x40); input_report_key(dev, BTN_Y, data[4] & 0x80); /* digital pad */ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { /* dpad as buttons (left, right, up, down) */ input_report_key(dev, BTN_TRIGGER_HAPPY1, data[5] & 0x04); input_report_key(dev, BTN_TRIGGER_HAPPY2, data[5] & 0x08); input_report_key(dev, BTN_TRIGGER_HAPPY3, data[5] & 0x01); input_report_key(dev, BTN_TRIGGER_HAPPY4, data[5] & 0x02); } else { input_report_abs(dev, ABS_HAT0X, !!(data[5] & 0x08) - !!(data[5] & 0x04)); input_report_abs(dev, ABS_HAT0Y, !!(data[5] & 0x02) - !!(data[5] & 0x01)); } /* TL/TR */ input_report_key(dev, BTN_TL, data[5] & 0x10); input_report_key(dev, BTN_TR, data[5] & 0x20); /* stick press left/right */ input_report_key(dev, BTN_THUMBL, data[5] & 0x40); input_report_key(dev, BTN_THUMBR, data[5] & 0x80); if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { /* left stick */ input_report_abs(dev, ABS_X, (__s16) le16_to_cpup((__le16 *)(data + 10))); input_report_abs(dev, ABS_Y, ~(__s16) le16_to_cpup((__le16 *)(data + 12))); /* right stick */ input_report_abs(dev, ABS_RX, (__s16) le16_to_cpup((__le16 *)(data + 14))); input_report_abs(dev, ABS_RY, ~(__s16) le16_to_cpup((__le16 *)(data + 16))); } /* triggers left/right */ if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { input_report_key(dev, BTN_TL2, (__u16) le16_to_cpup((__le16 *)(data + 6))); input_report_key(dev, BTN_TR2, (__u16) le16_to_cpup((__le16 *)(data + 8))); } else { input_report_abs(dev, ABS_Z, (__u16) le16_to_cpup((__le16 *)(data + 6))); input_report_abs(dev, ABS_RZ, (__u16) le16_to_cpup((__le16 *)(data + 8))); } input_sync(dev); } /* * xpadone_process_packet * * Completes a request by converting the data into events for the * input subsystem. This version is for the Xbox One controller. * * The report format was gleaned from * https://github.com/kylelemons/xbox/blob/master/xbox.go */ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) { struct input_dev *dev = xpad->dev; switch (data[0]) { case 0x20: xpadone_process_buttons(xpad, dev, data); break; case 0x07: /* the xbox button has its own special report */ input_report_key(dev, BTN_MODE, data[4] & 0x01); input_sync(dev); break; } } static void xpad_irq_in(struct urb *urb) { struct usb_xpad *xpad = urb->context; struct device *dev = &xpad->intf->dev; int retval, status; status = urb->status; switch (status) { case 0: /* success */ break; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status); return; default: dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status); goto exit; } switch (xpad->xtype) { case XTYPE_XBOX360: xpad360_process_packet(xpad, 0, xpad->idata); break; case XTYPE_XBOX360W: xpad360w_process_packet(xpad, 0, xpad->idata); break; case XTYPE_XBOXONE: xpadone_process_packet(xpad, 0, xpad->idata); break; default: xpad_process_packet(xpad, 0, xpad->idata); } exit: retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) dev_err(dev, "%s - usb_submit_urb failed with result %d\n", __func__, retval); } static void xpad_bulk_out(struct urb *urb) { struct usb_xpad *xpad = urb->context; struct device *dev = &xpad->intf->dev; switch (urb->status) { case 0: /* success */ break; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, urb->status); break; default: dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, urb->status); } } static void xpad_irq_out(struct urb *urb) { struct usb_xpad *xpad = urb->context; struct device *dev = &xpad->intf->dev; int retval, status; unsigned long flags; status = urb->status; switch (status) { case 0: /* success */ spin_lock_irqsave(&xpad->pend_lock, flags); xpad->irq_out->transfer_buffer_length = 0; if (xpad->pend_rum != 0) { memcpy(xpad->odata, xpad->rum_data, xpad->pend_rum); xpad->irq_out->transfer_buffer_length = xpad->pend_rum; xpad->pend_rum = 0; } else if (xpad->pend_led != 0) { memcpy(xpad->odata, xpad->led_data, xpad->pend_led); xpad->irq_out->transfer_buffer_length = xpad->pend_led; xpad->pend_led = 0; } if (xpad->irq_out->transfer_buffer_length != 0) { spin_unlock_irqrestore(&xpad->pend_lock, flags); if (usb_submit_urb(xpad->irq_out, GFP_ATOMIC) != 0) { spin_lock_irqsave(&xpad->pend_lock, flags); xpad->odata_busy = 0; spin_unlock_irqrestore(&xpad->pend_lock, flags); } } else { xpad->odata_busy = 0; spin_unlock_irqrestore(&xpad->pend_lock, flags); } return; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status); spin_lock_irqsave(&xpad->pend_lock, flags); xpad->odata_busy = 0; spin_unlock_irqrestore(&xpad->pend_lock, flags); return; default: dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status); retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) { dev_err(dev, "%s - usb_submit_urb failed with result %d\n", __func__, retval); spin_lock_irqsave(&xpad->pend_lock, flags); xpad->odata_busy = 0; spin_unlock_irqrestore(&xpad->pend_lock, flags); } return; } } static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) { struct usb_endpoint_descriptor *ep_irq_out; int ep_irq_out_idx; int error; if (xpad->xtype == XTYPE_UNKNOWN) return 0; xpad->odata = usb_alloc_coherent(xpad->udev, XPAD_PKT_LEN, GFP_KERNEL, &xpad->odata_dma); if (!xpad->odata) { error = -ENOMEM; goto fail1; } xpad->odata_busy = 0; spin_lock_init(&xpad->pend_lock); xpad->pend_rum = 0; xpad->pend_led = 0; xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL); if (!xpad->irq_out) { error = -ENOMEM; goto fail2; } /* Xbox One controller has in/out endpoints swapped. */ ep_irq_out_idx = xpad->xtype == XTYPE_XBOXONE ? 0 : 1; ep_irq_out = &intf->cur_altsetting->endpoint[ep_irq_out_idx].desc; usb_fill_int_urb(xpad->irq_out, xpad->udev, usb_sndintpipe(xpad->udev, ep_irq_out->bEndpointAddress), xpad->odata, XPAD_PKT_LEN, xpad_irq_out, xpad, ep_irq_out->bInterval); xpad->irq_out->transfer_dma = xpad->odata_dma; xpad->irq_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; return 0; fail2: usb_free_coherent(xpad->udev, XPAD_PKT_LEN, xpad->odata, xpad->odata_dma); fail1: return error; } static void xpad_stop_output(struct usb_xpad *xpad) { if (xpad->xtype != XTYPE_UNKNOWN) usb_kill_urb(xpad->irq_out); } static void xpad_deinit_output(struct usb_xpad *xpad) { if (xpad->xtype != XTYPE_UNKNOWN) { usb_free_urb(xpad->irq_out); usb_free_coherent(xpad->udev, XPAD_PKT_LEN, xpad->odata, xpad->odata_dma); } } #ifdef CONFIG_JOYSTICK_XPAD_FF static int xpad_make_rum_data(struct usb_xpad *xpad, __u16 strong, __u16 weak) { switch (xpad->xtype) { case XTYPE_XBOX: xpad->rum_data[0] = 0x00; xpad->rum_data[1] = 0x06; xpad->rum_data[2] = 0x00; xpad->rum_data[3] = strong / 256; /* left actuator */ xpad->rum_data[4] = 0x00; xpad->rum_data[5] = weak / 256; /* right actuator */ xpad->pend_rum = 6; return 0; case XTYPE_XBOX360: xpad->rum_data[0] = 0x00; xpad->rum_data[1] = 0x08; xpad->rum_data[2] = 0x00; xpad->rum_data[3] = strong / 256; /* left actuator? */ xpad->rum_data[4] = weak / 256; /* right actuator? */ xpad->rum_data[5] = 0x00; xpad->rum_data[6] = 0x00; xpad->rum_data[7] = 0x00; xpad->pend_rum = 8; return 0; case XTYPE_XBOX360W: xpad->rum_data[0] = 0x00; xpad->rum_data[1] = 0x01; xpad->rum_data[2] = 0x0F; xpad->rum_data[3] = 0xC0; xpad->rum_data[4] = 0x00; xpad->rum_data[5] = strong / 256; xpad->rum_data[6] = weak / 256; xpad->rum_data[7] = 0x00; xpad->rum_data[8] = 0x00; xpad->rum_data[9] = 0x00; xpad->rum_data[10] = 0x00; xpad->rum_data[11] = 0x00; xpad->pend_rum = 12; return 0; } return -1; } static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) { struct usb_xpad *xpad = input_get_drvdata(dev); if (effect->type == FF_RUMBLE) { unsigned long flags; int mrdrv; spin_lock_irqsave(&xpad->pend_lock, flags); mrdrv = xpad_make_rum_data(xpad, effect->u.rumble.strong_magnitude, effect->u.rumble.weak_magnitude); if (mrdrv == 0 && !xpad->odata_busy) { memcpy(xpad->odata, xpad->rum_data, xpad->pend_rum); xpad->irq_out->transfer_buffer_length = xpad->pend_rum; xpad->pend_rum = 0; xpad->odata_busy = 1; spin_unlock_irqrestore(&xpad->pend_lock, flags); if (usb_submit_urb(xpad->irq_out, GFP_ATOMIC) != 0) { spin_lock_irqsave(&xpad->pend_lock, flags); xpad->odata_busy = 0; spin_unlock_irqrestore(&xpad->pend_lock, flags); } } else { spin_unlock_irqrestore(&xpad->pend_lock, flags); if (mrdrv == 0) { dev_dbg(&xpad->dev->dev, "%s - rumble while urb busy\n", __func__); } } if (mrdrv != 0) { dev_dbg(&xpad->dev->dev, "%s - rumble command sent to unsupported xpad type: %d\n", __func__, xpad->xtype); return -1; } } return 0; } static int xpad_init_ff(struct usb_xpad *xpad) { if (xpad->xtype == XTYPE_UNKNOWN || xpad->xtype == XTYPE_XBOXONE) return 0; input_set_capability(xpad->dev, EV_FF, FF_RUMBLE); return input_ff_create_memless(xpad->dev, NULL, xpad_play_effect); } #else static int xpad_init_ff(struct usb_xpad *xpad) { return 0; } #endif #if defined(CONFIG_JOYSTICK_XPAD_LEDS) #include struct xpad_led { char name[16]; struct led_classdev led_cdev; struct usb_xpad *xpad; }; static void xpad_send_led_command(struct usb_xpad *xpad, int command) { if (command >= 0 && command < 14) { unsigned long flags; spin_lock_irqsave(&xpad->pend_lock, flags); xpad->led_data[0] = 0x01; xpad->led_data[1] = 0x03; xpad->led_data[2] = command; xpad->pend_led = 3; if (!xpad->odata_busy) { memcpy(xpad->odata, xpad->led_data, xpad->pend_led); xpad->irq_out->transfer_buffer_length = xpad->pend_led; xpad->pend_led = 0; xpad->odata_busy = 1; spin_unlock_irqrestore(&xpad->pend_lock, flags); if (usb_submit_urb(xpad->irq_out, GFP_KERNEL) != 0) { spin_lock_irqsave(&xpad->pend_lock, flags); xpad->odata_busy = 0; spin_unlock_irqrestore(&xpad->pend_lock, flags); } } else { spin_unlock_irqrestore(&xpad->pend_lock, flags); dev_dbg(&xpad->dev->dev, "%s - led while urb busy\n", __func__); } } } static void xpad_led_set(struct led_classdev *led_cdev, enum led_brightness value) { struct xpad_led *xpad_led = container_of(led_cdev, struct xpad_led, led_cdev); xpad_send_led_command(xpad_led->xpad, value); } static int xpad_led_probe(struct usb_xpad *xpad) { static atomic_t led_seq = ATOMIC_INIT(-1); unsigned long led_no; struct xpad_led *led; struct led_classdev *led_cdev; int error; if (xpad->xtype != XTYPE_XBOX360) return 0; xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL); if (!led) return -ENOMEM; led_no = atomic_inc_return(&led_seq); snprintf(led->name, sizeof(led->name), "xpad%lu", led_no); led->xpad = xpad; led_cdev = &led->led_cdev; led_cdev->name = led->name; led_cdev->brightness_set = xpad_led_set; error = led_classdev_register(&xpad->udev->dev, led_cdev); if (error) { kfree(led); xpad->led = NULL; return error; } /* * Light up the segment corresponding to controller number */ xpad_send_led_command(xpad, (led_no % 4) + 2); return 0; } static void xpad_led_disconnect(struct usb_xpad *xpad) { struct xpad_led *xpad_led = xpad->led; if (xpad_led) { led_classdev_unregister(&xpad_led->led_cdev); kfree(xpad_led); } } #else static int xpad_led_probe(struct usb_xpad *xpad) { return 0; } static void xpad_led_disconnect(struct usb_xpad *xpad) { } #endif static int xpad_open(struct input_dev *dev) { struct usb_xpad *xpad = input_get_drvdata(dev); /* URB was submitted in probe */ if (xpad->xtype == XTYPE_XBOX360W) return 0; xpad->irq_in->dev = xpad->udev; if (usb_submit_urb(xpad->irq_in, GFP_KERNEL)) return -EIO; if (xpad->xtype == XTYPE_XBOXONE) { /* Xbox one controller needs to be initialized. */ xpad->odata[0] = 0x05; xpad->odata[1] = 0x20; xpad->irq_out->transfer_buffer_length = 2; return usb_submit_urb(xpad->irq_out, GFP_KERNEL); } return 0; } static void xpad_close(struct input_dev *dev) { struct usb_xpad *xpad = input_get_drvdata(dev); if (xpad->xtype != XTYPE_XBOX360W) usb_kill_urb(xpad->irq_in); xpad_stop_output(xpad); } static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) { struct usb_xpad *xpad = input_get_drvdata(input_dev); set_bit(abs, input_dev->absbit); switch (abs) { case ABS_X: case ABS_Y: case ABS_RX: case ABS_RY: /* the two sticks */ input_set_abs_params(input_dev, abs, -32768, 32767, 16, 128); break; case ABS_Z: case ABS_RZ: /* the triggers (if mapped to axes) */ if (xpad->xtype == XTYPE_XBOXONE) input_set_abs_params(input_dev, abs, 0, 1023, 0, 0); else input_set_abs_params(input_dev, abs, 0, 255, 0, 0); break; case ABS_HAT0X: case ABS_HAT0Y: /* the d-pad (only if dpad is mapped to axes */ input_set_abs_params(input_dev, abs, -1, 1, 0, 0); break; } } static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(intf); struct usb_xpad *xpad; struct input_dev *input_dev; struct usb_endpoint_descriptor *ep_irq_in; int ep_irq_in_idx; int i, error; for (i = 0; xpad_device[i].idVendor; i++) { if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) && (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct)) break; } if (xpad_device[i].xtype == XTYPE_XBOXONE && intf->cur_altsetting->desc.bInterfaceNumber != 0) { /* * The Xbox One controller lists three interfaces all with the * same interface class, subclass and protocol. Differentiate by * interface number. */ return -ENODEV; } xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL); input_dev = input_allocate_device(); if (!xpad || !input_dev) { error = -ENOMEM; goto fail1; } xpad->idata = usb_alloc_coherent(udev, XPAD_PKT_LEN, GFP_KERNEL, &xpad->idata_dma); if (!xpad->idata) { error = -ENOMEM; goto fail1; } xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL); if (!xpad->irq_in) { error = -ENOMEM; goto fail2; } xpad->udev = udev; xpad->intf = intf; xpad->mapping = xpad_device[i].mapping; xpad->xtype = xpad_device[i].xtype; if (xpad->xtype == XTYPE_UNKNOWN) { if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) { if (intf->cur_altsetting->desc.bInterfaceProtocol == 129) xpad->xtype = XTYPE_XBOX360W; else xpad->xtype = XTYPE_XBOX360; } else xpad->xtype = XTYPE_XBOX; if (dpad_to_buttons) xpad->mapping |= MAP_DPAD_TO_BUTTONS; if (triggers_to_buttons) xpad->mapping |= MAP_TRIGGERS_TO_BUTTONS; if (sticks_to_null) xpad->mapping |= MAP_STICKS_TO_NULL; } xpad->dev = input_dev; usb_make_path(udev, xpad->phys, sizeof(xpad->phys)); strlcat(xpad->phys, "/input0", sizeof(xpad->phys)); input_dev->name = xpad_device[i].name; input_dev->phys = xpad->phys; usb_to_input_id(udev, &input_dev->id); input_dev->dev.parent = &intf->dev; input_set_drvdata(input_dev, xpad); input_dev->open = xpad_open; input_dev->close = xpad_close; input_dev->evbit[0] = BIT_MASK(EV_KEY); if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { input_dev->evbit[0] |= BIT_MASK(EV_ABS); /* set up axes */ for (i = 0; xpad_abs[i] >= 0; i++) xpad_set_up_abs(input_dev, xpad_abs[i]); } /* set up standard buttons */ for (i = 0; xpad_common_btn[i] >= 0; i++) __set_bit(xpad_common_btn[i], input_dev->keybit); /* set up model-specific ones */ if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W || xpad->xtype == XTYPE_XBOXONE) { for (i = 0; xpad360_btn[i] >= 0; i++) __set_bit(xpad360_btn[i], input_dev->keybit); } else { for (i = 0; xpad_btn[i] >= 0; i++) __set_bit(xpad_btn[i], input_dev->keybit); } if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { for (i = 0; xpad_btn_pad[i] >= 0; i++) __set_bit(xpad_btn_pad[i], input_dev->keybit); } else { for (i = 0; xpad_abs_pad[i] >= 0; i++) xpad_set_up_abs(input_dev, xpad_abs_pad[i]); } if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { for (i = 0; xpad_btn_triggers[i] >= 0; i++) __set_bit(xpad_btn_triggers[i], input_dev->keybit); } else { for (i = 0; xpad_abs_triggers[i] >= 0; i++) xpad_set_up_abs(input_dev, xpad_abs_triggers[i]); } error = xpad_init_output(intf, xpad); if (error) goto fail3; error = xpad_init_ff(xpad); if (error) goto fail4; error = xpad_led_probe(xpad); if (error) goto fail5; /* Xbox One controller has in/out endpoints swapped. */ ep_irq_in_idx = xpad->xtype == XTYPE_XBOXONE ? 1 : 0; ep_irq_in = &intf->cur_altsetting->endpoint[ep_irq_in_idx].desc; usb_fill_int_urb(xpad->irq_in, udev, usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress), xpad->idata, XPAD_PKT_LEN, xpad_irq_in, xpad, ep_irq_in->bInterval); xpad->irq_in->transfer_dma = xpad->idata_dma; xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; error = input_register_device(xpad->dev); if (error) goto fail6; usb_set_intfdata(intf, xpad); if (xpad->xtype == XTYPE_XBOX360W) { /* * Setup the message to set the LEDs on the * controller when it shows up */ xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL); if (!xpad->bulk_out) { error = -ENOMEM; goto fail7; } xpad->bdata = kzalloc(XPAD_PKT_LEN, GFP_KERNEL); if (!xpad->bdata) { error = -ENOMEM; goto fail8; } xpad->bdata[2] = 0x08; switch (intf->cur_altsetting->desc.bInterfaceNumber) { case 0: xpad->bdata[3] = 0x42; break; case 2: xpad->bdata[3] = 0x43; break; case 4: xpad->bdata[3] = 0x44; break; case 6: xpad->bdata[3] = 0x45; } ep_irq_in = &intf->cur_altsetting->endpoint[1].desc; if (usb_endpoint_is_bulk_out(ep_irq_in)) { usb_fill_bulk_urb(xpad->bulk_out, udev, usb_sndbulkpipe(udev, ep_irq_in->bEndpointAddress), xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad); } else { usb_fill_int_urb(xpad->bulk_out, udev, usb_sndintpipe(udev, ep_irq_in->bEndpointAddress), xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad, 0); } /* * Submit the int URB immediately rather than waiting for open * because we get status messages from the device whether * or not any controllers are attached. In fact, it's * exactly the message that a controller has arrived that * we're waiting for. */ xpad->irq_in->dev = xpad->udev; error = usb_submit_urb(xpad->irq_in, GFP_KERNEL); if (error) goto fail9; } return 0; fail9: kfree(xpad->bdata); fail8: usb_free_urb(xpad->bulk_out); fail7: input_unregister_device(input_dev); input_dev = NULL; fail6: xpad_led_disconnect(xpad); fail5: if (input_dev) input_ff_destroy(input_dev); fail4: xpad_deinit_output(xpad); fail3: usb_free_urb(xpad->irq_in); fail2: usb_free_coherent(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); fail1: input_free_device(input_dev); kfree(xpad); return error; } static void xpad_disconnect(struct usb_interface *intf) { struct usb_xpad *xpad = usb_get_intfdata (intf); xpad_led_disconnect(xpad); input_unregister_device(xpad->dev); xpad_deinit_output(xpad); if (xpad->xtype == XTYPE_XBOX360W) { usb_kill_urb(xpad->bulk_out); usb_free_urb(xpad->bulk_out); usb_kill_urb(xpad->irq_in); } usb_free_urb(xpad->irq_in); usb_free_coherent(xpad->udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); kfree(xpad->bdata); kfree(xpad); usb_set_intfdata(intf, NULL); } static struct usb_driver xpad_driver = { .name = "xpad", .probe = xpad_probe, .disconnect = xpad_disconnect, .id_table = xpad_table, }; module_usb_driver(xpad_driver); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); vizigrep-1.4/ut/test.py000077500000000000000000000007311333434772600152160ustar00rootroot00000000000000#!/usr/bin/python import unittest import ut_GrepEngine, ut_Regex ########## # Create and return a test suite for this set of tests. ########## def suite(): suite = unittest.TestSuite() suite.addTest(ut_GrepEngine.suite()) #suite.addTest(ut_GrepEngineRemote.suite()) suite.addTest(ut_Regex.suite()) return suite ########## # MAIN: Execute the tests in this file. ########## if __name__ == '__main__': unittest.TextTestRunner().run(suite()) vizigrep-1.4/ut/ut_GrepEngine.py000077500000000000000000000117251333434772600167770ustar00rootroot00000000000000#!/usr/bin/python import unittest, sys, os sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir, 'vizigrep'))) from GrepEngine import GrepEngine, NoResultsException, GrepException, BadRegexException class testCases(unittest.TestCase): def setUp(self): self.ge = GrepEngine() self.path = 'test-data' def tearDown(self): pass ### Basic Functionality ### def testFindSimple(self): results = self.ge.grep('foo', self.path) self.assertTrue(len(results) == 1) self.checkResult(results[0], "File1", "linefoo", "2") def testNoMatches(self): with self.assertRaises(NoResultsException): self.ge.grep('idonotexistidonotexistidonotexist', self.path) def testIgnoreDirSimple(self): self.ge.exclude_dirs.append('FolderA') results = self.ge.grep('file', self.path) self.ge.exclude_dirs = [] self.assertTrue(len(results) == 2) self.checkResult(results[1], "File1", "This is a test file", "1") self.checkResult(results[0], "File2", "Another file!", "1") def testIgnoreDirWithSpaceInPath(self): self.ge.exclude_dirs.append('Space A') with self.assertRaises(NoResultsException): self.ge.grep('space_marker', self.path) self.ge.exclude_dirs = [] def testGrepGivesErrorMessage(self): with self.assertRaises(GrepException): self.ge.grep('a[]b', self.path) ### Search path ### def testPathWithSpaces(self): path = 'test-data/Space A' results = self.ge.grep('space_marker', path) self.assertTrue(len(results) == 1) self.checkResult(results[0], 'Space File', 'space_marker', '1') ### Broken Regex ### def testStartsWithStar(self): with self.assertRaises(BadRegexException): self.ge.grep('*foo', self.path) def testDoubleStar(self): with self.assertRaises(BadRegexException): self.ge.grep('foo**', self.path) def testEmptyString(self): with self.assertRaises(BadRegexException): self.ge.grep('', self.path) def testAllDots(self): with self.assertRaises(BadRegexException): self.ge.grep('...', self.path) def testDotStar(self): with self.assertRaises(BadRegexException): self.ge.grep('.*', self.path) ### Test Special Chars & Shell Escaping ### def testDoubleQuote(self): results = self.ge.grep('"stdio.h', self.path) self.assertTrue(len(results) == 1) self.checkResult(results[0], 'FolderA/File2', '#include "stdio.h"', '1') def testDoubleQuotes(self): results = self.ge.grep('"stdio.h"', self.path) self.assertTrue(len(results) == 1) self.checkResult(results[0], 'FolderA/File2', '#include "stdio.h"', '1') def testStringStartingWithHash(self): path = os.path.join(self.path, 'FolderA') results = self.ge.grep('#include', path) self.assertTrue(len(results) == 1) self.checkResult(results[0], 'File2', '#include "stdio.h"', '1') def testAngleBrackets(self): results = self.ge.grep('', self.path) self.assertTrue(len(results) == 1) self.checkResult(results[0], 'FolderA/File2', '@fnclude ', '2') def testBackslash(self): results = self.ge.grep('fi\\\\le', self.path) self.assertTrue(len(results) == 1) self.checkResult(results[0], 'FolderA/File1', 'fi\le', '4') def testDash(self): path = os.path.join(self.path, 'FolderA') results = self.ge.grep('-d', path) self.assertTrue(len(results) == 1) self.checkResult(results[0], 'File3.py', " self.ge.exclude_dirs = self.prefs.get('exclude-dirs')", '17') def testDashDash(self): path = os.path.join(self.path, 'FolderA') results = self.ge.grep('--link', path) self.assertTrue(len(results) == 1) self.checkResult(results[0], 'File3.py', " self.lbl_options.connect('activate--link', self.options_clicked)", '32') def testCPointerDereference(self): path = os.path.join(self.path, 'c-source') results = self.ge.grep('urb->status;', path) self.assertTrue(len(results) == 2) self.checkResult(results[0], 'xpad.c', " status = urb->status;", '637') self.checkResult(results[1], 'xpad.c', " status = urb->status;", '706') def checkResult(self, result, fn, line, linenum): self.assertTrue(result.fn == fn) self.assertTrue(result.str == line) self.assertTrue(int(result.linenum) == int(linenum)) ########## # Create and return a test suite for this set of tests. ########## def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(testCases)) return suite ########## # MAIN: Execute the tests in this file. ########## if __name__ == '__main__': unittest.main() vizigrep-1.4/ut/ut_GrepEngineRemote.py000077500000000000000000000123121333434772600201440ustar00rootroot00000000000000#!/usr/bin/python import unittest, sys, os, getpass sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir, 'vizigrep'))) from GrepEngine import GrepEngine, NoResultsException class testCases(unittest.TestCase): def setUp(self): self.ge = GrepEngine() user = getpass.getuser() host = "127.0.0.1" fullpath = os.path.abspath('./test-data') self.path = '%s@%s:%s' % (user, host, fullpath) def tearDown(self): pass ### Basic Functionality ### def testFindSimple(self): results = self.ge.grep('foo', self.path) self.assertTrue(len(results) == 1) self.checkResult(results[0], "File1", "linefoo", "2") def testNoMatches(self): with self.assertRaises(NoResultsException): self.ge.grep('idonotexistidonotexistidonotexist', self.path) def testIgnoreDirSimple(self): self.ge.exclude_dirs.append('FolderA') results = self.ge.grep('file', self.path) self.ge.exclude_dirs = [] self.assertTrue(len(results) == 2) self.checkResult(results[0], "File1", "This is a test file", "1") self.checkResult(results[1], "File2", "Another file!", "1") #def testIgnoreDirWithSpaceInPath(self): # self.ge.exclude_dirs.append('Space A') # with self.assertRaises(NoResultsException): # results = self.ge.grep('space_marker', self.path) # self.ge.exclude_dirs = [] #def testGrepGivesErrorMessage(self): # with self.assertRaises(GrepException): # results = self.ge.grep('a[]b', self.path) ### Search path ### #def testPathWithSpaces(self): # path = 'test-data/Space A' # results = self.ge.grep('space_marker', path) # self.assertTrue(len(results) == 1) # self.checkResult(results[0], 'Space File', 'space_marker', '1') ### Broken Regex ### #def testStartsWithStar(self): # with self.assertRaises(BadRegexException): # results = self.ge.grep('*foo', self.path) #def testDoubleStar(self): # with self.assertRaises(BadRegexException): # results = self.ge.grep('foo**', self.path) #def testEmptyString(self): # with self.assertRaises(BadRegexException): # results = self.ge.grep('', self.path) #def testAllDots(self): # with self.assertRaises(BadRegexException): # results = self.ge.grep('...', self.path) #def testDotStar(self): # with self.assertRaises(BadRegexException): # results = self.ge.grep('.*', self.path) ### Test Special Chars & Shell Escaping ### #def testDoubleQuote(self): # results = self.ge.grep('"stdio.h', self.path) # self.assertTrue(len(results) == 1) # self.checkResult(results[0], 'FolderA/File2', '#include "stdio.h"', '1') #def testDoubleQuotes(self): # results = self.ge.grep('"stdio.h"', self.path) # self.assertTrue(len(results) == 1) # self.checkResult(results[0], 'FolderA/File2', '#include "stdio.h"', '1') #def testStringStartingWithHash(self): # path = os.path.join(self.path, 'FolderA') # results = self.ge.grep('#include', path) # self.assertTrue(len(results) == 1) # self.checkResult(results[0], 'File2', '#include "stdio.h"', '1') #def testAngleBrackets(self): # results = self.ge.grep('', self.path) # self.assertTrue(len(results) == 1) # self.checkResult(results[0], 'FolderA/File2', '@fnclude ', '2') #def testBackslash(self): # results = self.ge.grep('fi\\\\le', self.path) # self.assertTrue(len(results) == 1) # self.checkResult(results[0], 'FolderA/File1', 'fi\le', '4') #def testDash(self): # path = os.path.join(self.path, 'FolderA') # results = self.ge.grep('-d', path) # self.assertTrue(len(results) == 1) # self.checkResult(results[0], 'File3.py', " self.ge.exclude_dirs = self.prefs.get('exclude-dirs')", '17') #def testDashDash(self): # path = os.path.join(self.path, 'FolderA') # results = self.ge.grep('--link', path) # self.assertTrue(len(results) == 1) # self.checkResult(results[0], 'File3.py', " self.lbl_options.connect('activate--link', self.options_clicked)", '32') #def testCPointerDereference(self): # path = os.path.join(self.path, 'c-source') # results = self.ge.grep('urb->status;', path) # self.assertTrue(len(results) == 2) # self.checkResult(results[0], 'xpad.c', " status = urb->status;", '637') # self.checkResult(results[1], 'xpad.c', " status = urb->status;", '706') def checkResult(self, result, fn, line, linenum): self.assertTrue(result.fn == fn) self.assertTrue(result.str == line) self.assertTrue(int(result.linenum) == int(linenum)) ########## # Create and return a test suite for this set of tests. ########## def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(testCases)) return suite ########## # MAIN: Execute the tests in this file. ########## if __name__ == '__main__': unittest.main() vizigrep-1.4/ut/ut_Regex.py000077500000000000000000000036661333434772600160330ustar00rootroot00000000000000#!/usr/bin/python import unittest, re class testCases(unittest.TestCase): def setUp(self): pass def tearDown(self): pass def testMatchSimple(self): self.checkRegex('foo', 'foo', 'foo') def testNoMatch(self): self.checkRegex('foo', 'bar', '') def testMatchDot(self): self.checkRegex('abcdefghijk', 'bc.e', 'bcde') def testMatchDotStar(self): self.checkRegex('abcdefghijk', 'bc.*j', 'bcdefghij') def testMatchGroup(self): self.checkRegex('abcdefghijk', 'bc[wvdg]e', 'bcde') def testMatchGroupStar(self): self.checkRegex('abcdefghijk', 'bc[defg]*h', 'bcdefgh') def testMatchDoubleQuote(self): self.checkRegex('Hello"World"', 'o"W', 'o"W') def testMatchStringStartingWithHash(self): self.checkRegex('#include"', '#inc', '#inc') def testMatchAngleBrackets(self): self.checkRegex('#include"', '', '') def testMatchBackslash(self): self.checkRegex('foo\gbc"', 'o\g', 'o\g') def checkRegex(self, haystack, needle, expected_match): needle = self.escape_regex_str(needle) m = re.search(needle, haystack) if (not m) and (not expected_match): return if not m: self.fail('No match found! haystack=%s, needle=%s, expected_match=%s' % (haystack, needle, expected_match)) matched_text = m.group() self.assertTrue(matched_text == expected_match) def escape_regex_str(self, regex): if '\\' in regex: regex = regex.replace('\\', '\\\\') # Escape \ return regex ########## # Create and return a test suite for this set of tests. ########## def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(testCases)) return suite ########## # MAIN: Execute the tests in this file. ########## if __name__ == '__main__': unittest.main() vizigrep-1.4/vizigrep.desktop000066400000000000000000000004141333434772600164620ustar00rootroot00000000000000[Desktop Entry] Name=vizigrep GenericName=Vizigrep Comment=Search text files Keywords=vizigrep;grep;search; Exec=vizigrep Terminal=false Type=Application StartupNotify=true Icon=/usr/share/vizigrep/vizigrep.svg Categories=GNOME;GTK;Utility; X-GNOME-FullName=Vizigrep vizigrep-1.4/vizigrep.man000066400000000000000000000040201333434772600155610ustar00rootroot00000000000000.TH vizigrep "1" "July 2016" "vizigrep-1.4" "User Commands" .SH NAME vizigrep \- Graphical file contents search tool using regular expressions. .SH SYNOPSIS .B vizigrep [\fISEARCH-PATH\fR] .SH DESCRIPTION Vizigrep is a graphical user interface for performing fast and powerful searches inside a group of files. Simply tell Vizigrep which folder you want to search and what you want to search for and it will quickly find all occurrences of your search string within the files and folders you have selected. The search results are annotated to show you the lines containing your search term and color coding is used to help you quickly lock your eyes on to what you are searching for. If simple search strings are not powerful enough Vizigrep also understands regular expressions. .sp If the optional command line argument SEARCH-PATH is given then it will be used to populate the search path field. .sp .SH Navigation Vizigrep is very intuitively navigated using only the mouse. Simply choose a folder to search, type your search string and then click the Search button. Once the results are displayed you can click on the name of a file to open it in your chosen text editor. .sp At your option you may also carry out searches using the keyboard. Simply use the tab key to move the input focus (the keyboard cursor) between the three main controls: "folder entry", "search string entry", and the "search results". .sp If you've used the tab key to focus the search results you will see a keyboard cursor appear within the results. Hitting enter while the cursor is over one of the filenames will open that file in your chosen text editor. You may also hold down the Shift key and use the arrow keys to select and copy a portion of the results. .sp A new search Tab can be created via keyboard shortcut Ctrl+T. A search Tab can be closed via keyboard shortcut Ctrl+W. .SH COPYRIGHT Copyright \(co 2016 Jason J. Herne License GPLv2: GNU GPL version 2 . .br This is free software: you are free to change and redistribute it. vizigrep-1.4/vizigrep.svg000066400000000000000000002373441333434772600156260ustar00rootroot00000000000000 image/svg+xml import subprocess, osclass NoResultsException(Exception): passclass BadPathException(Exception): passclass GrepEngine: def __init__(self): self.exclude_dirs = [] self.exclude_files = [] def grep(self, string, path, max_matches, case_sensitive): try: if case_sensitive: case_arg = '' else: case_arg = 'i' args = '-Irn%s %s %s' % (case_arg, self.arg_exclude_dirs()) cmd = 'grep %s "%s" %s' % (args, string, path) print case_sensitive, cmd o = subprocess.check_output(cmd, shell=True) o = o.decode('utf-8', 'replace') results = GrepResults() for line in o.splitlines(): (filename, sep, rest) = line.partition(':') (linenum, sep, text) = rest.partition(':') if (not filename) or (not text) or (not linenum): continue if (max_matches > 0) and len(results) == max_matches: break results.append(GrepResult(self.trunc_path(filename, path), text) return results except subprocess.CalledProcessError as e: if (e.returncode == 2): raise BadPathException() elif (e.returncode == 1): raise NoResultsException() else: raise e vizigrep-1.4/vizigrep/000077500000000000000000000000001333434772600150705ustar00rootroot00000000000000vizigrep-1.4/vizigrep/GrepEngine.py000066400000000000000000000127351333434772600174750ustar00rootroot00000000000000import subprocess, tempfile, re import Path class NoResultsException(Exception): pass class GrepException(Exception): def __init__(self, errMsg): self.output = errMsg class BadRegexException(Exception): pass class GrepEngine: def __init__(self): self.exclude_dirs = [] self.exclude_files = [] self.case_sensitive = True self.max_matches = 0 self.cancelled = False self.is_remote = False def remote_params(self, searchPath): m = re.match('(.+)@(.+):(.+)', searchPath) if (m): return (m.group(1), m.group(2), m.group(3)) return (None, None, searchPath) def grep(self, string, searchPath): # Figure out if we're executig a local grep, or remote grep via ssh #(user, host, searchPath) = self.remote_params(searchPath) #self.is_remote = (user != None) self.cancelled = False self.check_regex(string) searchPath = Path.full(searchPath) # Construct args for grep command execution argList = self.build_grep_args(string, searchPath) #if self.is_remote: # argList = self.build_ssh_args(user, host) + argList # Run command stdErrFile = tempfile.TemporaryFile() stdOutFile = tempfile.TemporaryFile() self.grepProc = subprocess.Popen(argList, stdout=stdOutFile, stderr=stdErrFile) self.grepProc.wait() # Handle case where operation was cancelled if self.grepProc.returncode < 0: return # Read data from stdout/stderror stdOutFile.seek(0) output = stdOutFile.read().decode('utf-8', 'replace') stdOutFile.close() stdErrFile.seek(0) errMsg = stdErrFile.read() stdErrFile.close() if self.grepProc.returncode == 1: raise NoResultsException() if self.grepProc.returncode > 1: raise GrepException(errMsg) return self.parse_output(output, searchPath, string, self.is_remote) def build_grep_args(self, string, realPath): argList = ['/bin/grep', '-Irn'] if not self.case_sensitive: argList.append('-i') argList = argList + self.arg_exclude_list() argList.append('--') # End of - or -- style command options argList.append(string) argList.append(realPath) return argList def build_ssh_args(self, user, host): return ['ssh', '-o', 'PasswordAuthentication=no', '-o', 'PubkeyAuthentication=yes', '-o BatchMode=yes', '%s@%s' % (user, host)] def parse_output(self, output, searchPath, searchString, is_remote): results = GrepResults() results.search_path = searchPath results.search_string = searchString results.is_remote = is_remote for line in output.splitlines(): (filename, sep, rest) = line.partition(':') (linenum, sep, text) = rest.partition(':') if (not filename) or (not text) or (not linenum): continue if (self.max_matches > 0) and len(results) == self.max_matches: break # Ignore case where we have malformed data in output try: int(linenum) except: continue results.append(GrepResult(Path.relativeTo(filename, searchPath), text, linenum)) return results def check_regex(self, regex): # Check for invalid regex if regex == '': raise BadRegexException("Search string is empty") if regex.startswith('*'): raise BadRegexException("Search string cannot start with *") if '**' in regex: raise BadRegexException("Search string cannot contain **") if regex.strip().replace('.', '') == '': raise BadRegexException("Search string is way too vague") if regex.strip() == '.*': raise BadRegexException("Search string is way too vague") def arg_exclude_list(self): argList = [] if len(self.exclude_dirs) > 0: for d in self.exclude_dirs: argList.append('--exclude-dir=%s' % d) if len(self.exclude_files) > 0: for d in self.exclude_files: argList.append('--exclude=%s' % d) return argList def cancel(self): self.grepProc.terminate() self.cancelled = True class GrepResult(): def __init__(self, filename, result_string, linenum=None): self.fn = filename self.str = result_string self.linenum = linenum def __str__(self): return '%s:%s %s' % (self.fn, self.linenum, self.str) class GrepResults(list): def max_fnlen(self): maxlen = 0 for result in self: if len(result.fn) > maxlen: maxlen = len(result.fn) return maxlen def max_lnlen(self): maxlen = 0 for result in self: if len(result.linenum) > maxlen: maxlen = len(result.linenum) return maxlen def max_txtlen(self): maxlen = 0 for result in self: if len(result.str) > maxlen: maxlen = len(result.str) return maxlen def unique_fns(self): count = 0 fdict = {} for r in self: if not r.fn in fdict: count += 1 fdict[r.fn] = True return count vizigrep-1.4/vizigrep/Path.py000066400000000000000000000014011333434772600163320ustar00rootroot00000000000000import os def pretty(path): # Remove trailing / path = path.rstrip('/') # Replace user home with ~. if (path.startswith(os.path.expanduser('~'))): path = path.replace(os.path.expanduser('~'), '~') return path def full(path): # Replace ~ if (path.startswith('~')): path = path.replace('~', os.path.expanduser('~')) # Handle relative path if (not path.startswith('/')): path = os.path.join(os.getcwd(), path) return path # If fn start with base, remove base and return a path relative to base. def relativeTo(fn, base): base = base.rstrip('/') if fn.startswith(base + '/'): fn = fn.replace(base + '/', '') return fn vizigrep-1.4/vizigrep/PreferencesWindow.py000066400000000000000000000144041333434772600210760ustar00rootroot00000000000000from gi.repository import Gtk from guiapp.Window import Window import os class PreferencesWindow(Window): gtk_builder_file = os.path.join(os.path.dirname(__file__), 'ui', 'prefs-window.glade') window_name = "win_prefs" def __init__(self, app): Window.__init__(self, app, self.gtk_builder_file, self.window_name) self.prefs = app.prefs self.gtk_window.connect('delete_event', self.close) self.btn_close.connect('clicked', self.close) self.btn_file_add.connect('clicked', self.add_file) self.btn_file_remove.connect('clicked', self.remove_file) self.btn_dir_add.connect('clicked', self.add_dir) self.btn_dir_remove.connect('clicked', self.remove_dir) self.init_files_list() self.init_dirs_list() def activate(self): self.load_editor() self.load_linenum() self.load_files_list() self.load_dirs_list() self.load_match_limit() self.load_alternate_row_color() self.gtk_window.show_all() def close(self, win=None, event=None): self.save_editor() self.save_linenum() self.save_files_list() self.save_dirs_list() self.save_match_limit() self.save_alternate_row_color() self.prefs.write_prefs() self.gtk_window.hide() return True def load_editor(self): default_editor = self.prefs.defaults['editor'] editor = self.prefs.get('editor') self.txt_editor_default.set_text(default_editor) if editor == default_editor: self.rad_editor_default.set_active(True) else: self.rad_editor_custom.set_active(True) self.txt_editor_custom.set_text(editor) def save_editor(self): custom_editor_cmd = self.txt_editor_custom.get_text().strip() if self.rad_editor_default.get_active() or len(custom_editor_cmd) == 0: self.prefs.reset_to_default('editor') else: self.prefs.set('editor', custom_editor_cmd) def load_linenum(self): self.chk_linenum.set_active(self.prefs.get('show-line-numbers')) def save_linenum(self): self.prefs.set('show-line-numbers', self.chk_linenum.get_active()) def load_alternate_row_color(self): self.chk_alternate_row_color.set_active(self.prefs.get('alternate-row-color')) def save_alternate_row_color(self): self.prefs.set('alternate-row-color', self.chk_alternate_row_color.get_active()) def init_files_list(self): column = Gtk.TreeViewColumn("File Name", Gtk.CellRendererText(), text=0) self.tree_files.append_column(column) def add_file(self, btn): text = self.txt_file.get_text().strip() if len(text) == 0: return True if '"' in text or "'" in text: self.app.mbox.error('ERROR: Filename cannot contain quotes') self.txt_file.grab_focus() return True if text in self.prefs.get('exclude-files'): self.app.mbox.error('%s is already in the list.' % text) self.txt_file.grab_focus() return True self.prefs.list_add('exclude-files', text) self.load_files_list() self.txt_file.set_text('') return True def remove_file(self, btn): model, itr = self.tree_files.get_selection().get_selected() if (itr is None): return True text = self.tree_files.get_model().get_value(itr, 0) self.prefs.list_remove('exclude-files', text) self.load_files_list() return True def load_files_list(self): model = Gtk.ListStore(str) self.tree_files.set_model(model) self.prefs.list_sort('exclude-files') for fn in self.prefs.get('exclude-files'): model.append([fn]) def save_files_list(self): lst = [] for row in self.tree_files.get_model(): lst.append(row[0]) self.prefs.set('exclude-files', lst) def init_dirs_list(self): column = Gtk.TreeViewColumn("Folder Name", Gtk.CellRendererText(), text=0) self.tree_dirs.append_column(column) def add_dir(self, btn): text = self.txt_dir.get_text().strip() if len(text) == 0: return True if '"' in text or "'" in text: self.app.mbox.error('Folder name cannot contain quotes') self.txt_dir.grab_focus() return True if text in self.prefs.get('exclude-dirs'): self.app.mbox.error('%s is already in the list.' % text) self.txt_dir.grab_focus() return True self.prefs.list_add('exclude-dirs', text) self.load_dirs_list() self.txt_dir.set_text('') return True def remove_dir(self, btn): model, itr = self.tree_dirs.get_selection().get_selected() if (itr is None): return True text = self.tree_dirs.get_model().get_value(itr, 0) self.prefs.list_remove('exclude-dirs', text) self.load_dirs_list() return True def load_dirs_list(self): model = Gtk.ListStore(str) self.tree_dirs.set_model(model) self.prefs.list_sort('exclude-dirs') for fn in self.prefs.get('exclude-dirs'): model.append([fn]) def save_dirs_list(self): lst = [] for row in self.tree_dirs.get_model(): lst.append(row[0]) self.prefs.set('exclude-dirs', lst) def load_match_limit(self): match_limit = self.prefs.get('match-limit') if match_limit == 0: self.chk_matchlimit.set_active(False) self.txt_matchlimit.set_text('') else: self.chk_matchlimit.set_active(True) self.txt_matchlimit.set_text(str(match_limit)) def save_match_limit(self): match_limit = self.txt_matchlimit.get_text() if not match_limit.isdigit(): return mint = int(match_limit) if self.chk_matchlimit.get_active(): if mint <= 0: mint = self.prefs.defaults['match-limit'] self.prefs.set('match-limit', mint) else: self.prefs.set('match-limit', 0) vizigrep-1.4/vizigrep/ViziGrepWindow.py000066400000000000000000000246371333434772600204050ustar00rootroot00000000000000from gi.repository import Gtk, Gdk from guiapp.Window import Window from PreferencesWindow import PreferencesWindow from VizigrepTab import VizigrepTab import Path import os class ViziGrepWindow(Window): gtk_builder_file = os.path.join(os.path.dirname(__file__), 'ui', 'vizigrep.glade') window_name = "win_main" def __init__(self, app): Window.__init__(self, app, self.gtk_builder_file, self.window_name, True) self.prefs = app.prefs self.gtk_window.connect('delete_event', self.close) self.gtk_window.connect('key-press-event', self.win_keypress) self.notebook.connect('switch-page', self.switched_tab) self.btn_search.connect('clicked', self.btn_search_clicked) self.lbl_path.connect('activate-link', self.lbl_path_clicked) self.lbl_options.connect('activate-link', self.options_clicked) self.lbl_new_tab.connect('activate-link', self.new_tab_clicked) self.lbl_close_tab.connect('activate-link', self.close_tab_clicked) self.cbox_path.forall(self.cbox_disable_togglebutton_focus, None) self.cbox_search.forall(self.cbox_disable_togglebutton_focus, None) self.lbl_path.set_has_tooltip(False) self.lbl_options.set_has_tooltip(False) self.lbl_new_tab.set_has_tooltip(False) self.lbl_close_tab.set_has_tooltip(False) self.initNotebook() self.initNewTab() # GtkComboBoxes have an internal GtkToggleButton widget that accepts focus. This is # quite annoying to a user trying to navigate via keyboard so we disable it's focus. def cbox_disable_togglebutton_focus(self, widget, data): if isinstance(widget, Gtk.ToggleButton): widget.set_can_focus(False) def activate(self, searchPath=None): self.reload_search_box() self.reload_path_box() if searchPath: self.cbox_path.get_child().set_text(Path.pretty(searchPath)) self.chk_case.set_active(self.prefs.get('case-sensitive')) self.cbox_search.get_child().grab_focus() self.gtk_window.show_all() def close(self, win, event): self.deactivate() self.prefs.set('case-sensitive', self.chk_case.get_active()) self.prefs.write_prefs() Gtk.main_quit() # Returns 1 if the 1-key constant is given as input (Gdk.KEY_1, Gdk.KEY_2, etc). # Returns 10 for Gdk.KEY_0. # Returns None if constant given is not recognized as being a Gdk number key constant def __GdkNumKey2Int(self, gdkKeyConst): rv = gdkKeyConst - 48 if (rv < 1) or (rv > 10): return None return rv def win_keypress(self, win, kb_event): # Control is held if kb_event.state & Gdk.ModifierType.CONTROL_MASK: if kb_event.keyval == Gdk.KEY_t: self.new_tab_clicked() return True if kb_event.keyval == Gdk.KEY_w: self.close_tab_clicked() return True # Alt is held if kb_event.state & Gdk.ModifierType.MOD1_MASK: # If a number key was pressed, switch to corresponding tab numberPressed = self.__GdkNumKey2Int(kb_event.keyval) if numberPressed: self.notebook.set_current_page(numberPressed - 1) return True return False def lbl_path_clicked(self, lbl): btns = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK) dialog = Gtk.FileChooserDialog('Choose a folder', self.win_main, Gtk.FileChooserAction.SELECT_FOLDER, btns) startingPath = Path.full(self.prefs.get('last-opened-folder')) dialog.set_current_folder(startingPath) response = dialog.run() if response == Gtk.ResponseType.OK: pathStr = dialog.get_filename() self.prefs.set('last-opened-folder', pathStr) self.cbox_path.get_child().set_text(Path.pretty(pathStr)) dialog.destroy() return True def btn_search_clicked(self, data): tab = self.getActiveTab() if tab.isSearching: self.app.mbox.error('There is already a search happening in this tab.') return self.clear_results() string = self.cbox_search.get_active_text() path = Path.pretty(self.cbox_path.get_active_text()) self.cbox_path.get_child().set_text(path) if not string.strip(): tab.getTextBuffer().set_text("You forgot to provide a search string") self.cbox_search.get_child().grab_focus() return True if not path.strip(): tab.getTextBuffer().set_text("You forgot to provide a search folder") self.cbox_path.get_child().grab_focus() return True self.add_path_history(path) self.add_search_history(string) # Pass parameters to tab's grep engine tab.ge.exclude_dirs = self.prefs.get('exclude-dirs') tab.ge.exclude_files = self.prefs.get('exclude-files') tab.ge.max_matches = self.app.prefs.get('match-limit') tab.ge.case_sensitive = self.chk_case.get_active() # Update Tab label widgets tab.setTitleText(string + " : " + path) tab.setSpinner(True) tab.startSearch(string, path, self.set_result_status) def set_result_status(self, tab): results = tab.results if results: self.lbl_matches.set_text(str(len(results))) self.lbl_files.set_text(str(results.unique_fns())) else: self.lbl_matches.set_text('') self.lbl_files.set_text('') def clear_results(self): self.getActiveTab().getTextBuffer().set_text('') self.getActiveTab().setTitleText('[New tab]') self.lbl_matches.set_text('') self.lbl_files.set_text('') mint = self.prefs.get('match-limit') if (mint == 0): self.lbl_max.set_text("No Limit") else: self.lbl_max.set_text(str(mint)) def add_path_history(self, path): pathlist = self.prefs.get('path-history') pathlist.insert(0, path) newlist = [] for item in pathlist: if item in newlist: continue if len(newlist) >= 10: break newlist.append(item) self.prefs.set('path-history', newlist) self.reload_path_box() def add_search_history(self, string): searchlist = self.prefs.get('search-history') searchlist.insert(0, string) newlist = [] for item in searchlist: if item in newlist: continue if len(newlist) >= 10: break newlist.append(item) self.prefs.set('search-history', newlist) self.reload_search_box() def reload_search_box(self): self.cbox_search.get_model().clear() search_list = self.prefs.get('search-history') if len(search_list) > 0: self.cbox_search.get_child().set_text(search_list[0]) for string in search_list: self.cbox_search.append_text(string) def reload_path_box(self): self.cbox_path.get_model().clear() path_list = self.prefs.get('path-history') if len(path_list) > 0: self.cbox_path.get_child().set_text(path_list[0]) for path in path_list: self.cbox_path.append_text(path) def results_clicked(self, txtview, event_button): if (event_button.button != 1): return False (cx, cy) = txtview.window_to_buffer_coords(Gtk.TextWindowType.WIDGET, event_button.x, event_button.y) itr = txtview.get_iter_at_location(cx, cy) link_activated = self.getActiveTab().activate_result(itr) if not link_activated: txtview.grab_focus() return True def results_keypress(self, txtview, event_key): if Gdk.keyval_name(event_key.keyval) in ["Return", "KP_Enter"]: txtbuf = txtview.get_buffer() itr = txtbuf.get_iter_at_mark( txtbuf.get_insert() ) self.getActiveTab().activate_result(itr) return True def results_mouse_motion(self, txtview, event): if self.getActiveTab().results and self.getActiveTab().results.is_remote: return True (cx, cy) = txtview.window_to_buffer_coords(Gtk.TextWindowType.WIDGET, event.x, event.y) itr = txtview.get_iter_at_location(cx, cy) cursor = Gdk.Cursor(Gdk.CursorType.XTERM) for tag in itr.get_tags(): if tag.get_property('name') == 'link': cursor = Gdk.Cursor(Gdk.CursorType.HAND2) break txtview.get_window(Gtk.TextWindowType.TEXT).set_cursor(cursor) return False def options_clicked(self, lbl): PreferencesWindow(self.app).activate() return True # Prevents attempted activation of link button's URI def new_tab_clicked(self, lbl=None): tabIdx = self.initNewTab() self.notebook.show_all() self.notebook.set_current_page(tabIdx) return True # Prevents attempted activation of link button's URI def close_tab_clicked(self, lbl=None): tab = self.getActiveTab() if tab.isSearching: tab.ge.cancel() self.deleteActiveTab() return True # Prevents attempted activation of link button's URI def switched_tab(self, notebook, junkPagePtr, pageIdx): tab = notebook.get_nth_page(pageIdx) self.set_result_status(tab) def initNotebook(self): # Notebooks contain 3 built-in tabs by default. Remove all of them. while self.notebook.get_n_pages() > 0: self.notebook.remove_page(-1) def initNewTab(self): newTab = VizigrepTab(self.app, self.notebook) # Connect Signal handlers txtview = newTab.getTextView() txtview.connect('button-press-event', self.results_clicked) txtview.connect('motion-notify-event', self.results_mouse_motion) txtview.connect('key-press-event', self.results_keypress) return newTab.getIndex() def deleteActiveTab(self): if self.notebook.get_n_pages() == 1: self.clear_results() else: self.notebook.remove_page(self.notebook.get_current_page()) def getActiveTab(self): tabIdx = self.notebook.get_current_page() return self.notebook.get_nth_page(tabIdx) vizigrep-1.4/vizigrep/VizigrepTab.py000066400000000000000000000204171333434772600176740ustar00rootroot00000000000000from gi.repository import Gtk, GObject import subprocess, re, os, traceback from threading import Thread from GrepEngine import GrepEngine, NoResultsException, GrepException, BadRegexException import Path # This is a scrolledWindow subclass. We add it directly to the notebook in the # main window. We subclass it to add helper/convenience functions. class VizigrepTab(Gtk.ScrolledWindow): def __init__(self, app, notebook): Gtk.ScrolledWindow.__init__(self) self.app = app self.notebook = notebook self.results = None self.isSearching = False newTextView = Gtk.TextView() newTextView.set_editable(False) self.createTags(newTextView.get_buffer()) self.add(newTextView) self.notebook.append_page(self) # Construct tab label and hidden spinner box = Gtk.Box(Gtk.Orientation.HORIZONTAL, spacing=6) self.label = Gtk.Label('[New tab]') self.spinner = Gtk.Spinner() self.notebook.set_tab_label(self, box) box.pack_start(self.label, True, True, 0) box.pack_start(self.spinner, True, True, 0) box.get_children()[0].show() # Always show label self.ge = GrepEngine() def getIndex(self): return self.notebook.page_num(self) def getTextView(self): return self.get_child() def getTextBuffer(self): return self.get_child().get_buffer() def setTitleText(self, text): self.label.set_text(text) def setSpinner(self, active): if active: self.spinner.show() self.spinner.start() else: self.spinner.stop() self.spinner.hide() def createTags(self, txtbuf): txtbuf.create_tag("fixed", family="Monospace") txtbuf.create_tag("link", foreground="Blue") txtbuf.create_tag("red", foreground="Red") txtbuf.create_tag("green", foreground="Dark Green") txtbuf.create_tag("bg1", background="#DDDDDD") def startSearch(self, searchString, path, doneCallback): self.isSearching = True new_thread = Thread(target=self.grep_thread, args=(searchString, path, doneCallback)) new_thread.start() def grep_thread(self, searchString, path, doneCallback): self.results = None ex = None try: self.results = self.ge.grep(searchString, path) except Exception as e: ex = e GObject.idle_add(self.grep_thread_done, ex) GObject.idle_add(doneCallback, self) # Call main window's callback def grep_thread_done(self, exception): txtbuf = self.getTextBuffer() if self.ge.cancelled: txtbuf.set_text("The search was cancelled") elif self.results: try: self.set_results() except Exception as e: print type(e) print traceback.format_exc() elif exception: if isinstance(exception, GrepException): txtbuf.set_text("Grep error: %s" % exception.output) elif isinstance(exception, NoResultsException): txtbuf.set_text("No results found") elif isinstance(exception, BadRegexException): txtbuf.set_text("Search string error: %s" % str(exception)) else: txtbuf.set_text("Unexpected Error: " + str(exception)) print type(exception) print traceback.format_exc() self.setSpinner(False) self.isSearching = False def set_results(self): results = self.results txtbuf = self.getTextBuffer() tag_link = txtbuf.get_tag_table().lookup('link') tag_red = txtbuf.get_tag_table().lookup('red') tag_green = txtbuf.get_tag_table().lookup('green') tag_bg1 = txtbuf.get_tag_table().lookup('bg1') max_fnlen = results.max_fnlen() max_lnlen = results.max_lnlen() max_txtlen = results.max_txtlen() taglist = [] rstr = '' # Figure out max line length max_linelen = max_fnlen + 1 + max_txtlen if (self.app.prefs.get('show-line-numbers')): max_linelen += max_lnlen + 1 string = self.escape_regex_str(results.search_string) lineNum = 1 for r in results: lineStartIdx = len(rstr) # File name taglist.append( (len(rstr), len(r.fn), tag_link) ) rstr += r.fn # Spaces to pad out filename if max_fnlen > len(r.fn): rstr += ' ' * (max_fnlen - len(r.fn)) # : after filename rstr += ':' # Line number and : if (self.app.prefs.get('show-line-numbers')): taglist.append( (len(rstr), len(r.linenum), tag_green) ) rstr += r.linenum if max_lnlen > len(r.linenum): rstr += " " * (max_lnlen - len(r.linenum)) rstr += ':' if self.ge.case_sensitive: m = re.search(string, r.str) else: m = re.search(string, r.str, re.IGNORECASE) # Line contents if(m and len(m.group()) > 0): matched_text = m.group() (prematch, match, postmatch) = r.str.partition(matched_text) rstr += prematch taglist.append( (len(rstr), len(matched_text), tag_red) ) rstr += matched_text rstr += postmatch else: rstr += r.str # Spaces to pad out line contents if max_txtlen > len(r.str): rstr += ' ' * (max_txtlen - len(r.str)) rstr += '\n' # Add text background tag every other line lineLength = len(rstr) - lineStartIdx - 1 if (self.app.prefs.get('alternate-row-color')): if (lineNum % 2 == 1): taglist.append( (lineStartIdx, lineLength, tag_bg1) ) lineNum += 1 txtbuf.set_text(rstr) self.apply_tags(txtbuf, rstr, taglist) def escape_regex_str(self, regex): if '(' in regex: regex = regex.replace('(', '\\(') # Escape ( if ')' in regex: regex = regex.replace(')', '\\)') # Escape ) return regex def apply_tags(self, txtbuf, rstr, taglist): tag_fixed = txtbuf.get_tag_table().lookup('fixed') txtbuf.apply_tag(tag_fixed, txtbuf.get_start_iter(), txtbuf.get_end_iter()) for tagtuple in taglist: (sidx, length, tag) = tagtuple sitr = txtbuf.get_iter_at_offset(sidx) eitr = txtbuf.get_iter_at_offset(sidx + length) txtbuf.apply_tag(tag, sitr, eitr) def activate_result(self, itr): if not self.results or self.results.is_remote: return True if len(self.results) <= itr.get_line(): return True result = self.results[itr.get_line()] for tag in itr.get_tags(): # FIXME: Create a findTagByType function? if tag.get_property('name') == 'link': (itr, itr_end) = self.get_tag_pos(itr, tag) filename = self.getTextBuffer().get_text(itr, itr_end, False) filename = os.path.join(self.results.search_path, filename) filename = Path.full(filename) cmdList = [] for itm in self.app.prefs.get('editor').split(' '): if '$1' in itm: itm = itm.replace('$1', filename) if '$n' in itm: itm = itm.replace('$n', result.linenum) cmdList.append(itm) subprocess.Popen(cmdList) return True return False # Locate start/end of tag at given iterator def get_tag_pos(self, itr, tag): itr_end = itr.copy() # Find start of tag while (not itr.begins_tag(tag)): itr.backward_char() # Find end of tag while (not itr_end.ends_tag(tag)): itr_end.forward_char() return (itr, itr_end) vizigrep-1.4/vizigrep/__init__.py000066400000000000000000000000001333434772600171670ustar00rootroot00000000000000vizigrep-1.4/vizigrep/guiapp/000077500000000000000000000000001333434772600163555ustar00rootroot00000000000000vizigrep-1.4/vizigrep/guiapp/SimpleTreeView.py000066400000000000000000000101631333434772600216340ustar00rootroot00000000000000class SimpleTreeView: def __init__(self, gtkTreeView): self.tv = gtkTreeView self.enableOnSelectList = [] self.disableOnSelectList = [] self.selected_col_id = 0 self.last_selected_item = None self.last_selected_path = None self.last_topmost_path = None self.last_topmost_y = None self.tv.connect("cursor-changed", self.selection_changed) def reload(self): raise Exception("This method must be subclassed!") def enableOnSelect(self, widgetList): self.enableOnSelectList += widgetList self.selection_changed(None) def disableOnSelect(self, widgetList): self.disableOnSelectList += widgetList self.selection_changed(None) def get_selected(self): model, itr = self.tv.get_selection().get_selected() if (itr is None): return None return model.get_value(itr, self.selected_col_id) def get_selected_path(self): model, itr = self.tv.get_selection().get_selected() if (itr is None): return None return model.get_path(itr) def get_topmost_path(self): r = self.tv.get_visible_range() if not r: return None return r[0] def set_topmost_path(self, path): if path: self.tv.scroll_to_cell(path) def get_topmost_y(self): self.app.log.debug('Remember TopMostY as {0}'.format(self.tv.get_visible_rect().y)) return self.tv.get_visible_rect().y def set_topmost_y(self, y): if y: self.app.log.debug('Set TopMostY to {0}'.format(y)) self.tv.scroll_to_point(-1, y) def select_something(self): # Try to select an exact match if self.last_selected_item and self.select_item(self.last_selected_item): self.set_topmost_y(self.last_topmost_y) return # Failing that, try to select the same position/index if self.last_selected_path: self.tv.set_cursor(self.last_selected_path) if self.get_selected_path() == self.last_selected_path: # Did it work? self.set_topmost_y(self.last_topmost_y) return # If all else fails try to select the first item if (len(self.tv.get_model()) > 0): self.tv.set_cursor(0) self.set_topmost_y(self.last_topmost_y) def select_topmost(self): if (len(self.tv.get_model()) > 0): self.tv.set_cursor(0) def search_for_item(self, model, itr, item): while itr: if model.iter_has_child(itr): r = self.search_for_item(model, model.iter_children(itr), item) if r: return r if model.get_value(itr, self.selected_col_id) == item: return itr itr = model.iter_next(itr) return None # Selects the given item in the list. Returns true if the requested item was able to be selected. False otherwise. def select_item(self, item): model = self.tv.get_model() itr = model.get_iter_first() if (itr is None): return False itemItr = self.search_for_item(model, itr, item) if itemItr: self.select_path(model.get_path(itemItr)) return True return False def select_path(self, path): self.tv.set_cursor(path) #tvsel = self.tv.get_selection() #tvsel.select_path(path) def remember_selection(self): self.last_selected_item = self.get_selected() self.last_selected_path = self.get_selected_path() self.last_topmost_y = self.get_topmost_y() def selection_changed(self, tv): if self.get_selected(): enableList = self.enableOnSelectList disableList = self.disableOnSelectList else: enableList = self.disableOnSelectList disableList = self.enableOnSelectList for widget in enableList: widget.set_sensitive(True) for widget in disableList: widget.set_sensitive(False) vizigrep-1.4/vizigrep/guiapp/SuperGtkBuilder.py000066400000000000000000000016531333434772600220070ustar00rootroot00000000000000import gi gi.require_version('GtkSource', '3.0') from gi.repository import Gtk, GObject, GtkSource # GtkBuilder that will populate the given object (representing a Window) with # all of that window's UI elements as found in the GtkBuilder UI file. class SuperGtkBuilder(Gtk.Builder): def __init__(self, window_object, ui_file_path): Gtk.Builder.__init__(self) GObject.type_register(GtkSource.View) # Needed to work with GtkSourceView objects self.add_from_file(ui_file_path) self.populate_window_with_ui_elements(window_object) # Create a data-member in window_object to represent each ui element # found in the GtkBuilder UI file. def populate_window_with_ui_elements(self, window_object): for ui_element in self.get_objects(): if isinstance(ui_element, Gtk.TreeSelection): continue setattr(window_object, Gtk.Buildable.get_name(ui_element), ui_element) vizigrep-1.4/vizigrep/guiapp/Tab.py000066400000000000000000000013301333434772600174320ustar00rootroot00000000000000from gi.repository import Gtk from SuperGtkBuilder import SuperGtkBuilder class Tab: # The Window this object represents. gtk_window = None # GtkBuilder object containing GUI elements. builder = None def __init__(self, gtkBuilderFile, containerName, gtkNotebook, tabName): self.builder = SuperGtkBuilder(self, gtkBuilderFile) self.container = self.builder.get_object(containerName) self.gtkNotebook = gtkNotebook # Remove the container from the window glade forces us to place it in. self.parentWindow = self.builder.get_object("win_main") self.parentWindow.remove(self.container) self.gtkNotebook.append_page(self.container, Gtk.Label(tabName)) vizigrep-1.4/vizigrep/guiapp/Window.py000066400000000000000000000037341333434772600202050ustar00rootroot00000000000000from gi.repository import GObject from SuperGtkBuilder import SuperGtkBuilder class Window: # The Window this object represents. gtk_window = None # GtkBuilder object containing GUI elements. builder = None def __init__(self, app, gtk_builder_file, window_name, rememberSizePos=False): self.app = app self.builder = SuperGtkBuilder(self, gtk_builder_file) self.gtk_window = self.builder.get_object(window_name) self.window_name = window_name self.deactivateCallback = None self.rememberSizePos = rememberSizePos if rememberSizePos: self.loadSizePosition() # Subclasses can override this def activate(self): self.gtk_window.show_all() def deactivate(self): if self.rememberSizePos: self.saveSizePosition() self.gtk_window.hide() if self.deactivateCallback: GObject.idle_add(self.deactivateCallback) # Set function to be called when this window has been deactivated. # Caller can set this to get alerted when the user has finished with this # window. def setDeactivateCallback(self, fn): self.deactivateCallback = fn # save/load uiSizings def saveSizePosition(self): sizePrefName = self.window_name + '-window-size' positionPrefName = self.window_name + '-window-position' self.app.prefs.set(sizePrefName, self.gtk_window.get_size()) self.app.prefs.set(positionPrefName, self.gtk_window.get_position()) def loadSizePosition(self): sizePrefName = self.window_name + '-window-size' positionPrefName = self.window_name + '-window-position' size = self.app.prefs.get(sizePrefName) if size: (width, height) = size self.gtk_window.resize(width, height) pos = self.app.prefs.get(positionPrefName) if pos: (x, y) = pos self.gtk_window.move(x, y) vizigrep-1.4/vizigrep/guiapp/__init__.py000066400000000000000000000000001333434772600204540ustar00rootroot00000000000000vizigrep-1.4/vizigrep/guiapp/guiapp.py000066400000000000000000000033631333434772600202210ustar00rootroot00000000000000import os import multilogger import preferences import mbox class GuiApp: def __init__(self, shortName): self.shortName = shortName self.appHomeDir = os.path.join(os.path.expanduser('~'), '.config', self.shortName) self.__checkHomeDir() # Components self.log = None self.prefs = None self.mbox = mbox.mbox() # Settings self.writePrefsOnClose = True def __checkHomeDir(self): if not os.path.exists(self.appHomeDir): os.mkdir(self.appHomeDir) def checkAppHomeDirPermissions(self): if not os.access(self.appHomeDir, os.W_OK): self.__homeDirError(self.appHomeDir) return path = os.path.join(self.appHomeDir, self.shortName + '.prefs') if not os.access(path, os.W_OK): self.__homeDirError(path) return def __homeDirError(self, path): msg = 'Your user does not have write permission for file %s, this means %s will be unable to save preferences and data' % (path, self.shortName) if self.mbox: self.mbox.error(msg) else: print msg def initLogging(self): self.logFilePath = os.path.join(self.appHomeDir, self.shortName + '.log') self.log = multilogger.MultiLogger() if not self.log.open_logfile(self.logFilePath): self.log = None def initPrefs(self): self.prefsFilePath = os.path.join(self.appHomeDir, self.shortName + '.prefs') self.prefs = preferences.Preferences(self.prefsFilePath) def shutdown(self): if self.log: self.log.close_logfile() if self.prefs and self.writePrefsOnClose: self.prefs.write_prefs() vizigrep-1.4/vizigrep/guiapp/mbox.py000066400000000000000000000144661333434772600177070ustar00rootroot00000000000000#!/usr/bin/python import time from gi.repository import Gtk, GObject # This class provides a clean and easy to use interface for displaying simple # message boxes to the user and getting a button-clicked type response. It can be # used in the main thread (gtk main) or in a 2ndary thread. All message boxes are # modal which means the rest of the GUI is unresponsive while a message box is being # displayed. Also, code execution blocks which waiting for the user's response to # the message box. # # THREADING NOTE: While each mbox function can work in a threaded environment, this # class itself has 1 response variable, therefore, you should not be calling these # functions simultaneously from multiple threads. One message box at a time! # # The recommended method of use is as follows: # # Create a mbox object and store it in your gui's main class, or in any class in # which you intend to use message boxes. When you wish to show a message box just # call one of the mbox functions. Example # # response = mbox.error("Cereal port not found. Breakfast halted.") # # NOTE: The above call is meant to be used in the main thread (gtk.main()). If you # call this function as shown in a 2ndary thread (any thread other than gtk.main()) # your program will deadlock. If you need to create a message box in a 2ndary thread # then you'l need to ensure that you set the threaded property to True. Example: # # response = mbox.error("Cereal port not found. Breakfast halted.", threaded=True) # # All mbox functions return a response. The following are valid responses: # # Gtk.ResponseType.CLOSE - The "little X" was clicked and the window closed :) # Gtk.ResponseType.OK - "OK" button clicked # Gtk.ResponseType.YES - "YES" button clicked # Gtk.ResponseType.NO - "NO" button clicked # # class mbox: # Indicates the user's response to the message box (which button was clicked). response = None log = None # Creates an "error" dialog with a window title "Error" and a single close # button. The given message is displayed to the user. def error(self, message_text, threaded=False): if self.log: self.log.error(message_text) return self.mbox(Gtk.MessageType.QUESTION, message_text, "Error", Gtk.ButtonsType.CLOSE, threaded) # Creates a dialog to ask the user a yes/no question. Yes and No buttons are # presented to the user. def question(self, message_text, threaded=False): return self.mbox(Gtk.MessageType.QUESTION, message_text, "", Gtk.ButtonsType.YES_NO, threaded) ############################################################################ #################################### Internal ############################## ############################################################################ # Create a new message box with the given parameters. msg_type allows you to # change the icon displayed in the dialog. Valid values are as follows: # # Gtk.MessageType.ERROR # Gtk.MessageType.QUESTION # Gtk.MessageType.WARNING # Gtk.MessageType.INFO # # Valid choices for buttons are: # # Gtk.ButtonsType.CLOSE # Gtk.ButtonsType.NONE # Gtk.ButtonsType.OK # Gtk.ButtonsType.CLOSE # Gtk.ButtonsType.CANCEL # Gtk.ButtonsType.YES_NO # Gtk.ButtonsType.OK_CANCEL # def mbox(self, msg_type, message_text, window_title, buttons, threaded): if(threaded): return self.mbox_t(msg_type, message_text, window_title, buttons) else: return self.mbox_nt(msg_type, message_text, window_title, buttons) ############################################################################ # Non-threaded message box interface # # Use in the gtk.main() thread # ############################################################################ def mbox_nt(self, msg_type, message_text, window_title, buttons): # Create message dailog mbox = Gtk.MessageDialog(type=msg_type, buttons=buttons, message_format=message_text) mbox.set_title("Error") mbox.set_modal(True) # Ensure message dialog is centered on screen mbox.set_position(Gtk.WindowPosition.CENTER) # Set window title, if it exists if (window_title is not None): mbox.set_title(window_title) # Display message box and collect response response = mbox.run() # Clean up and return user's response to caller mbox.destroy() return response ############################################################################ # Threaded message box interface # # Use in a thread other than your gtk.main() thread. # ############################################################################ def mbox_t(self, msg_type, message_text, window_title, buttons): # Create message dialog mbox = Gtk.MessageDialog(type=msg_type, buttons=buttons, flags=Gtk.DialogFlags.MODAL, message_format=message_text) # Ensure message dialog is centered on screen mbox.set_position(Gtk.WindowPosition.CENTER) # Set window title, if it exists if (window_title is not None): mbox.set_title(window_title) # Clear any previous response self.response = None # Show mbox #response = mbox.run() # DO NOT USE! Causes hang if run in a thread!! mbox.connect('response', self.mbox_t_response) GObject.idle_add(mbox.show) # Await response, then return it FIXME: Can I do this?? while (self.response is None): time.sleep(0.1) return self.response # The user clicked a button on the mbox. We'll save the response that indicates # which button the user clicked and destroy the mbox. def mbox_t_response(self, mbox, response_id): self.response = response_id # fixme: need lock! mbox.destroy() return False # Creates an "error" dialog that terminates the gtk main loop when it is # closed. # # Note: This method is only intended for printing out exceptions that occur # during initialization. def terminate(self, message_text, threaded=False): response = self.error(message_text, threaded) Gtk.main_quit() return response vizigrep-1.4/vizigrep/guiapp/multilogger.py000066400000000000000000000170631333434772600212700ustar00rootroot00000000000000import os, stat, time, thread, inspect, platform # Multilogger is a multithread-friendly logging service that logs information # to a data source (currently a local file is the only supported data source). # Optionally logged data can also be printed to standard out. # NOTE: It is safe to use multilogger to log data from multiple threads # in the same prorgam. It is NOT SAFE to create two separate multilogger instances # that both log data to the same file (either in the same or different programs)! class MultiLogger: # Log file logfile = None logfile_path = None # Lock protecting logfile. logfile_lock = thread.allocate_lock() # Options affecting behavior of this multilogger. stdout = False # Print logged data to stdout. max_size = 0 # Upon opening the log file, its size in # bytes is compared with this value. If it # is larger, the log file will be reduced # by removing the oldest lines first. A # value of zero indicates that the log file # should be allowed to grow without limit. reduced_size = 0 # This is the maximum size of the reduced # log file. The oldest line is removed # until its size in bytes is less than or # equal to this value. (must be <= max_size # when max_size > 0) def __init__(self, stdout=False, max_size=100000, reduced_size=50000): if ( max_size < 0) or (reduced_size < 0): raise Exception("max_size must be greater than 0") self.stdout = stdout self.max_size = max_size self.reduced_size = reduced_size self.logDebug = False def log_debug(self, isDebugOn): self.logDebug = isDebugOn # Open the log file that this multilogger instance should use to log data. If the # given file exists data will be appended to it. If the given file does not exist # it will be created. If the parent directory of the log file does not exist then # an error is returned. # # Before the log file is opened, it is checked to see if it has grown past # its maximum byte size. If this is the case, the oldest lines in the file # are removed until it is less than or equal to the maximum reduced size # (see the max_size & reduced_size attributes). def open_logfile(self, logfile_path): if ( not self.__validateLogfilePath__(logfile_path) ): return False # Open the log file and reduce it if necessary. try: self.reduce_logfile(logfile_path) self.logfile = open(logfile_path, 'a') except Exception as e: print e return False # Remember path to log file. self.logfile_path = logfile_path return True def __validateLogfilePath__(self, logfile_path): if ( self.__parentDirectoryExists__(logfile_path) ): return False if ( self.__isSpecialFile__(logfile_path) ): return False return True def __parentDirectoryExists__(self, file_path): parent_dir = os.path.dirname(file_path) if (not os.path.exists(parent_dir)): return False def __isSpecialFile__(self, file_path): if ( os.path.exists(file_path) ): mode = os.stat(file_path)[stat.ST_MODE] if (not stat.S_ISREG(mode)): return True return False # Reduce the size of the given log file if it has grown past its maximum # size. The file is reduced by removing as many lines (starting from the # beginning) as it takes for the byte size of the file to be less than or # equal to its maximum reduced size. When the log file does not exist, this # method does nothing. # # In the event that any problems are encountered while reading or writing # the log file, an IOError will be raised. def reduce_logfile(self, path): # Check if the file exists and has grown past its limit. if (self.max_size > 0 and os.path.isfile(path) and os.path.getsize(path) > self.max_size): # Read in the greatest number of lines from the end of the file # that do not put us past the maximum reduced size. with open(path, "rb") as f: # We only want to read in complete lines, so we're going to # start one byte before the first character we want to save. # This allows us to preserve the entire first line if possible # since the previous line will be discarded later. offset = self.reduced_size + 1 f.seek(-offset, os.SEEK_END) f.readline() # Discard the first line content = f.read() # Read in the remaining data # Save the reduced log file. with open(path, "wb") as f: f.write(content) # # Closes the log file. Call this when all threads are done writting to it. # def close_logfile(self): self.logfile_lock.acquire() self.logfile.close() self.logfile = None self.logfile_lock.release() # Writes a given piece of data (meant to be a single line of text) to the log file. # The data will be prepended with date/time information and a component identifier. def write(self, data, level='Info', component=None): # Get component (if it exists) ==> caller's class if component is None: try: component = str(inspect.stack()[1][0].f_locals['self'].__module__) except KeyError: component = '' component = component.replace(' ', '') # Remove spaces # If the log file is not open, just bail. if (not self.logfile): return # Get the date/time this data is being logged. time_str = time.asctime() # Concatenate time, componenet and data log_line = '{0} [{1:8.8}] - {2:18.18} : {3}'.format(time_str, level, component, data) # Get lock self.logfile_lock.acquire() # Write the line to the log self.logfile.write(log_line + '\n') self.logfile.flush() # Write to stdout - Not on Windows, causes "Bad File Descriptor" messages if (self.stdout and (not platform.system() == "Windows") ): print log_line # Release lock self.logfile_lock.release() def debug(self, data): if not self.logDebug: return try: component = str(inspect.stack()[1][0].f_locals['self'].__module__) except KeyError: component = '' component = component.replace(' ', '') # Remove spaces component = component.ljust(18)[0:18] # Ensure string is exactly 18 characters self.write(data, level='Debug', component=component) def error(self, data): if not self.logDebug: return try: component = str(inspect.stack()[1][0].f_locals['self'].__module__) except KeyError: component = '' component = component.replace(' ', '') # Remove spaces component = component.ljust(18)[0:18] # Ensure string is exactly 18 characters self.write(data.replace('\n', ' \\n '), level='Error', component=component) vizigrep-1.4/vizigrep/guiapp/preferences.py000066400000000000000000000041741333434772600212360ustar00rootroot00000000000000import os, pickle class Preferences: def __init__(self, prefsFilePath): #self.appdir = os.path.join(os.path.expanduser('~'), '.local', 'share', appname) self.prefsFilePath = prefsFilePath self.set_defaults() self.read_prefs() # FIXME: Move defaults out to application code def set_defaults(self): self.defaults = {} self.defaults['path-history'] = [] self.defaults['search-history'] = [] self.defaults['last-opened-folder'] = '~' self.defaults['exclude-dirs'] = ['.git', '.svn'] self.defaults['exclude-files'] = ['*~'] self.defaults['show-line-numbers'] = True self.defaults['editor'] = 'gedit $1 +$n' self.defaults['match-limit'] = 200 self.defaults['alternate-row-color'] = True self.defaults['case-sensitive'] = True def read_prefs(self): self.pdict = {} if not os.path.exists(self.prefsFilePath): return try: f = open(self.prefsFilePath, "rb") self.pdict = pickle.load(f) f.close() except Exception as e: raise e def write_prefs(self): try: f = open(self.prefsFilePath, "wb") pickle.dump(self.pdict, f) f.close() except Exception as e: print e def get(self, prefname): if prefname in self.pdict: return self.pdict[prefname] elif prefname in self.defaults: return self.defaults[prefname] else: return None def set(self, prefname, value): self.pdict[prefname] = value def reset_to_default(self, prefname): self.set(prefname, self.defaults[prefname]) def list_add(self, prefname, item): self.pdict[prefname] = self.get(prefname) self.pdict[prefname].append(item) def list_remove(self, prefname, item): self.pdict[prefname] = self.get(prefname) self.pdict[prefname].remove(item) def list_sort(self, prefname): self.pdict[prefname] = self.get(prefname) self.pdict[prefname].sort() vizigrep-1.4/vizigrep/main.py000077500000000000000000000014101333434772600163650ustar00rootroot00000000000000#!/usr/bin/python # Copyright: Jason J. Herne (hernejj@gmail.com) # License: GPL-v2 (http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt) import sys import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GObject from vizigrep.guiapp.guiapp import GuiApp from ViziGrepWindow import ViziGrepWindow def main(args=None): app = GuiApp('vizigrep') app.initPrefs() app.initLogging() app.log.stdout = True app.mbox.log = app.log # FIXME: Silly to have this here argSearchPath = None if (len(sys.argv) > 1): argSearchPath = sys.argv[1] GObject.threads_init() app.checkAppHomeDirPermissions() vgw = ViziGrepWindow(app) vgw.activate(argSearchPath) Gtk.main() if __name__ == "__main__": main() vizigrep-1.4/vizigrep/ui/000077500000000000000000000000001333434772600155055ustar00rootroot00000000000000vizigrep-1.4/vizigrep/ui/prefs-window.glade000066400000000000000000000564671333434772600211510ustar00rootroot00000000000000 False 5 True 450 400 dialog False vertical 2 False end _Close False True True True True False True 1 False True end 0 True False vertical True False Use Default False True True False 0 True True 0 1 1 1 True False True 10 True False 0.019999999552965164 1 1 1 1 Custom False True True False 0 True True rad_editor_default 0 2 1 1 True True 10 True 1 2 1 1 True False 5 0 Editor Command 0 0 2 1 True False 12 5 0 Results 0 3 2 1 Show Line Numbers False True True False 0 True 0 4 2 1 Limit Matches to False True True False 0 True 0 5 1 1 True True 8 8 1 5 1 1 Colorize rows False True True False Use a different background color for alternating rows 0 True 0 6 1 1 False True 0 True False 10 True False True False 12 5 0 Files to ignore 0 0 3 1 True True True True in True True False 0 2 3 1 True True 4 True 0 1 1 1 Add False True True True 4 1 1 1 1 Remove False True True True 2 1 1 1 False True 0 True False True False 12 5 0 Folders to ignore 0 0 3 1 True True True True in True True False 0 2 3 1 True True 4 True 0 1 1 1 Remove False True True True 2 1 1 1 Add False True True True 4 1 1 1 1 False True 1 False True 2 False True 1 btn_close vizigrep-1.4/vizigrep/ui/vizigrep.glade000066400000000000000000000440171333434772600203500ustar00rootroot00000000000000 False Vizigrep 900 400 True False vertical True False _Search False True False True True True True 2 1 1 1 True False True 0 True True True True True True 1 1 1 1 True False True 0 True True True True True True True 1 0 1 1 True False S_earch String: True 0 1 1 1 Folder to Search: False True False True True start none none 0 0 1 1 Options False True False True True none none 2 0 1 1 True False _Case Sensitive False True True False True 0 True False True 0 New tab(ctrl+t) False True True True none none False True 1 Close tab(ctrl+w) False True True True none none False True 2 1 2 1 1 False True 0 True True True True True in True True False 2 True False page 1 False True False page 2 1 True False page 2 1 False True False page 3 2 True False page 3 2 False True True 1 True False True False 4 Matches: False True 0 True False 0 0 end 8 False True 1 True False 10 4 Max Matches: False True 2 True False 0 0 8 False True 3 True False 10 4 Files: False True 4 True False 0 0 8 False True 5 False True 2