Revision: 2087
http://geany-plugins.svn.sourceforge.net/geany-plugins/?rev=2087&view=rev
Author: techet
Date: 2011-05-24 22:22:30 +0000 (Tue, 24 May 2011)
Log Message:
-----------
Add the GProject plugin - a simple file-based extension of Geany project
Modified Paths:
--------------
trunk/geany-plugins/Makefile.am
trunk/geany-plugins/README
trunk/geany-plugins/configure.ac
trunk/geany-plugins/po/POTFILES.in
Added Paths:
-----------
trunk/geany-plugins/build/gproject.m4
trunk/geany-plugins/gproject/
trunk/geany-plugins/gproject/AUTHORS
trunk/geany-plugins/gproject/COPYING
trunk/geany-plugins/gproject/ChangeLog
trunk/geany-plugins/gproject/Makefile.am
trunk/geany-plugins/gproject/NEWS
trunk/geany-plugins/gproject/README
trunk/geany-plugins/gproject/THANKS
trunk/geany-plugins/gproject/icons/
trunk/geany-plugins/gproject/icons/Makefile.am
trunk/geany-plugins/gproject/icons/gproject-collapse.png
trunk/geany-plugins/gproject/icons/gproject-expand.png
trunk/geany-plugins/gproject/icons/gproject-file.png
trunk/geany-plugins/gproject/icons/gproject-follow.png
trunk/geany-plugins/gproject/icons/gproject-header.png
trunk/geany-plugins/gproject/icons/gproject-refresh.png
trunk/geany-plugins/gproject/icons/gproject-source.png
trunk/geany-plugins/gproject/src/
trunk/geany-plugins/gproject/src/Makefile.am
trunk/geany-plugins/gproject/src/gproject-main.c
trunk/geany-plugins/gproject/src/gproject-menu.c
trunk/geany-plugins/gproject/src/gproject-menu.h
trunk/geany-plugins/gproject/src/gproject-project.c
trunk/geany-plugins/gproject/src/gproject-project.h
trunk/geany-plugins/gproject/src/gproject-sidebar.c
trunk/geany-plugins/gproject/src/gproject-sidebar.h
trunk/geany-plugins/gproject/src/gproject-utils.c
trunk/geany-plugins/gproject/src/gproject-utils.h
trunk/geany-plugins/gproject/wscript_build
trunk/geany-plugins/gproject/wscript_configure
Modified: trunk/geany-plugins/Makefile.am
===================================================================
--- trunk/geany-plugins/Makefile.am 2011-05-24 21:55:01 UTC (rev 2086)
+++ trunk/geany-plugins/Makefile.am 2011-05-24 22:22:30 UTC (rev 2087)
@@ -18,6 +18,7 @@
geanyprj \
geanysendmail \
geanyvc \
+ gproject \
pretty-printer \
shiftcolumn \
spellcheck \
Modified: trunk/geany-plugins/README
===================================================================
--- trunk/geany-plugins/README 2011-05-24 21:55:01 UTC (rev 2086)
+++ trunk/geany-plugins/README 2011-05-24 22:22:30 UTC (rev 2087)
@@ -52,6 +52,7 @@
* geanyprj -- the GeanyPrj plugin
* geanysendmail -- the GeanySendmail plugin
* geanyvc -- the GeanyVC plugin
+* gproject -- the GProject plugin
* gtkspell -- GeanyVC's spell-check support
* pretty_printer -- the pretty-printer plugin
* shiftcolumn -- the ShiftColumn plugin
Added: trunk/geany-plugins/build/gproject.m4
===================================================================
--- trunk/geany-plugins/build/gproject.m4 (rev 0)
+++ trunk/geany-plugins/build/gproject.m4 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,10 @@
+AC_DEFUN([GP_CHECK_GPROJECT],
+[
+ GP_ARG_DISABLE([GProject], [yes])
+ GP_STATUS_PLUGIN_ADD([GProject], [$enable_gproject])
+ AC_CONFIG_FILES([
+ gproject/Makefile
+ gproject/src/Makefile
+ gproject/icons/Makefile
+ ])
+])
Modified: trunk/geany-plugins/configure.ac
===================================================================
--- trunk/geany-plugins/configure.ac 2011-05-24 21:55:01 UTC (rev 2086)
+++ trunk/geany-plugins/configure.ac 2011-05-24 22:22:30 UTC (rev 2087)
@@ -43,6 +43,7 @@
GP_CHECK_GEANYSENDMAIL
GP_CHECK_GEANYVC
GP_CHECK_GEANYPG
+GP_CHECK_GPROJECT
GP_CHECK_PRETTYPRINTER
GP_CHECK_SHIFTCOLUMN
GP_CHECK_SPELLCHECK
Added: trunk/geany-plugins/gproject/AUTHORS
===================================================================
--- trunk/geany-plugins/gproject/AUTHORS (rev 0)
+++ trunk/geany-plugins/gproject/AUTHORS 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1 @@
+Jiří Techet <techet(a)gmail.com>
Added: trunk/geany-plugins/gproject/COPYING
===================================================================
--- trunk/geany-plugins/gproject/COPYING (rev 0)
+++ trunk/geany-plugins/gproject/COPYING 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, 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 Library 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 St, 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.
+
+ <signature of Ty Coon>, 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 Library General
+Public License instead of this License.
Added: trunk/geany-plugins/gproject/ChangeLog
===================================================================
Added: trunk/geany-plugins/gproject/Makefile.am
===================================================================
--- trunk/geany-plugins/gproject/Makefile.am (rev 0)
+++ trunk/geany-plugins/gproject/Makefile.am 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,8 @@
+if ENABLE_GPROJECT
+include $(top_srcdir)/build/vars.auxfiles.mk
+else
+include $(top_srcdir)/build/vars.docs.mk
+endif
+
+SUBDIRS = src icons
+plugin = gproject
Added: trunk/geany-plugins/gproject/NEWS
===================================================================
Added: trunk/geany-plugins/gproject/README
===================================================================
--- trunk/geany-plugins/gproject/README (rev 0)
+++ trunk/geany-plugins/gproject/README 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,199 @@
+========
+GProject
+========
+
+.. contents::
+
+About
+=====
+
+GProject is an extension of Geany's project management displaying a tree of files
+belonging to the project in the sidebar. In addition, it enables quick swapping
+between header and source files, searching project files by name and more. The plugin
+was created with big projects in mind so everything works fast enough even with
+projects consisting of hundreds of thousands of files.
+
+Why was it created?
+-------------------
+
+A project in Geany is completely file-agnostic when it comes to the question of
+what files are stored in the project. In principle the information that Geany stores
+for every project is:
+
+ * project's base directory
+ * build settings
+ * list of open files
+
+This approach is fine for smaller projects where most of the project files
+are stored in a single directory but doesn't work very well with projects consisting
+of many deeply nested directories containing thousands of files. For such projects
+an expandable tree showing the project files is a better alternative making it much
+easier to to navigate between different project directories.
+
+Why are files belonging to a project defined by patterns?
+---------------------------------------------------------
+
+Maintaining a list of project files is rather painful with larger projects consisting
+of thousands files and many developers working on them. Files are created, deleted
+and moved quite frequently and after each change the project has to be updated manually.
+Instead, GProject defines a list of files belonging to the project implicitly using:
+
+ * project's base directory
+ * a list of glob-like patterns
+
+Every file under the base directory matching the patterns is included into the project
+and updating the file list is as simple as pressing the refresh button.
+
+What are the differences between GProject and GeanyPrj?
+-------------------------------------------------------
+
+There is another project-management Geany plugin - GeanyPrj. GProject and GeanyPrj differ
+in several aspects:
+
+ * GProject is an extension of Geany's project so every Geany project is a GProject
+ at the same time and vice versa. On the other hand, GeanyPrj project is a separate
+ project so if you want to set build properties for a GeanyPrj project, you have to
+ set up a second Geany project in parallel.
+ * Because GeanyPrj is a separate project management plugin, it can do some things that
+ GProject cannot - in particular, it can manage several projects in parallel.
+ If you need to switch between several projects, GeanyPrj might be a better option
+ for you. Alternatively, you can open several Geany instances for different projects
+ to work on several projects in parallel using GProject.
+ * GProject displays full tree in the sidebar while GeanyPrj displays only two-level
+ tree (full directory name as a parent and a list of files under the directory).
+ * GProject has configurable patterns while patterns in GeanyPrj are hard-coded
+ * GProject offers header/source swapping
+ * GProject offers finding project files by name
+
+Usage
+=====
+
+Project configuration
+---------------------
+
+Upon project creation, you should define the list of file patterns under the
+Project->Properties Project tab. For instance, for a typical open source C project,
+use patterns "*.c *h *.am *.ac" to see the source files together with
+automake and autoconf files. After closing the dialog, the files matching the patterns
+should appear in the sidebar under the Project tab (unless the GProject plugin
+was loaded after the project - see Known Issues for more details).
+
+Additional settings is available from the GProject tab under the Project properties
+dialog. You can define patterns to distinguish between header and source files for
+C-like languages. This information is used for header/source swapping and also for
+giving different icons to headers and sources in the sidebar (therefore, this settings
+can be also used for other types of languages if you want to give different file
+types different icons).
+
+In addition, you can define patterns for directories that should be ignored when
+searching for files belonging to the project. These will typically be various
+VCS or hidden directories.
+
+Finally, you can specify whether the tag manager should be used to index all the project
+files or not. This settings is turned off by default because the indexing takes too
+long when too many files are present in the project (several thousands or more).
+
+Sidebar
+-------
+
+The sidebar contains a tree of files belonging to the project. Directories can be expanded
+by double-clicking them; the same action is used to open files. When a sidebar item
+is right-clicked, a context menu appears:
+
+ * Expand all - recursively expands all the subdirectories of the given directory
+ * Find in files - opens the Find in files dialog and sets the search directory
+ to the clicked directory
+ * Find file - opens the Find file dialog and sets the search directory to the
+ clicked directory
+
+The following actions can be invoked from the sidebar's toolbar:
+
+ * Reload all - reloads the project file tree. This is useful when files were added or
+ removed from the project.
+ * Expand all - recursively expands all the directories
+ * Collapse all - recursively collapses all the directories
+ * Follow active editor - automatically selects the current file in the sidebar
+ when the user switches to another file. It auto-expands the tree to reveal the selected
+ file.
+
+Project menu entries
+--------------------
+
+GProject adds some extra entries under the Project menu:
+
+ * Find in Project Files - opens the Find in files dialog and sets the search directory
+ to the base directory of the project
+ * Find Project File - opens the Find file dialog and sets the search directory to the
+ base directory of the project
+ * Swap Header/Source - if the current file matches one of the source patterns from
+ the properties, it opens a project file with the same base name (without extension)
+ matching header patterns (and vice versa). If the files are already open, it
+ just switches the document tabs. Nothing happens if no matching file is found.
+
+Each of these entries can be assigned a key binding under Edit->Preferences->Keybindings.
+
+Find file dialog
+----------------
+
+The Find file dialog can be invoked either from the Project menu or from the
+sidebar's context menu. Searches are performed recursively, starting from the
+"Search inside" directory. All files having the searched string as a substring of their
+name are displayed in the Messages window. Clicking the file name opens the file.
+The following search properties are configurable:
+
+ * Case sensitive - specifies whether the searches should be case-sensitive
+ * Search in full path - when not checked, the search is performed in the file
+ name only (excluding path); when checked, the search is performed in the full path
+
+Editor context menu
+-------------------
+
+GProject adds an extra entry into the context menu of the editor:
+
+ * Open Selected File (GProject) - contrary to the Open Selected File entry present
+ in Geany it also searches for the file in project files
+
+Known issues
+============
+
+ * When the plugin is loaded from the Plugin Manager for the first time and a project
+ is already open, no files are displayed in the sidebar. The workaround is to
+ reload the project in this case. The issue is caused by Geany only passing the project
+ configuration file to a plugin when the project is loaded, which isn't the case when
+ the plugin is loaded.
+ * There might be some issues with UTF8 file names - the testing has been very
+ limited.
+ * GProject hasn't been tested under Windows so probably not everything works there.
+ Patches are welcome.
+
+License
+=======
+
+GProject is distributed 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. A copy of this license
+can be found in the file COPYING included with the source code of this
+program.
+
+Downloads
+=========
+
+GProject is part of the combined Geany Plugins release.
+For more information and downloads, please visit
+http://plugins.geany.org/geany-plugins/
+
+Development Code
+================
+
+Get the code from::
+
+ svn checkout http://geany-plugins.svn.sourceforge.net/svnroot/geany-plugins/trunk/geany-…
+
+Ideas, questions, patches and bug reports
+=========================================
+
+If you add something, or fix a bug, please send a patch (in 'diff -u'
+format) to the geany mailing list or to one of the authors listed bellow.
+
+2010-2011 by Jiří Techet
+techet(at)gmail(dot)com
Added: trunk/geany-plugins/gproject/THANKS
===================================================================
--- trunk/geany-plugins/gproject/THANKS (rev 0)
+++ trunk/geany-plugins/gproject/THANKS 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,8 @@
+What's this file about?
+-----------------------
+This file lists all external people that have contributed to this project.
+
+Testers and contributors:
+-------------------------
+Yura Siamashka <yurand2(at)gmail(dot)com> - author of GeanyPrj which served as a base of this plugin
+People on Geany mailing list who contributed with valuable comments and suggestions
Added: trunk/geany-plugins/gproject/icons/Makefile.am
===================================================================
--- trunk/geany-plugins/gproject/icons/Makefile.am (rev 0)
+++ trunk/geany-plugins/gproject/icons/Makefile.am 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,30 @@
+icondir = $(datadir)/icons/hicolor/16x16/apps
+
+ICONS = \
+ gproject-file.png \
+ gproject-header.png \
+ gproject-source.png \
+ gproject-expand.png \
+ gproject-collapse.png \
+ gproject-follow.png \
+ gproject-refresh.png
+
+EXTRA_DIST = $(ICONS)
+
+gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor
+install-data-hook: update-icon-cache
+uninstall-hook: update-icon-cache
+update-icon-cache:
+ @-if test -z "$(DESTDIR)"; then \
+ echo "Updating Gtk icon cache."; \
+ $(gtk_update_icon_cache); \
+ else \
+ echo "*** Icon cache not updated. After (un)install, run this:"; \
+ echo "*** $(gtk_update_icon_cache)"; \
+ fi
+
+if ENABLE_GPROJECT
+icon_DATA = $(ICONS)
+else
+EXTRA_DATA = $(ICONS)
+endif
Added: trunk/geany-plugins/gproject/icons/gproject-collapse.png
===================================================================
(Binary files differ)
Property changes on: trunk/geany-plugins/gproject/icons/gproject-collapse.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: trunk/geany-plugins/gproject/icons/gproject-expand.png
===================================================================
(Binary files differ)
Property changes on: trunk/geany-plugins/gproject/icons/gproject-expand.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: trunk/geany-plugins/gproject/icons/gproject-file.png
===================================================================
(Binary files differ)
Property changes on: trunk/geany-plugins/gproject/icons/gproject-file.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: trunk/geany-plugins/gproject/icons/gproject-follow.png
===================================================================
(Binary files differ)
Property changes on: trunk/geany-plugins/gproject/icons/gproject-follow.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: trunk/geany-plugins/gproject/icons/gproject-header.png
===================================================================
(Binary files differ)
Property changes on: trunk/geany-plugins/gproject/icons/gproject-header.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: trunk/geany-plugins/gproject/icons/gproject-refresh.png
===================================================================
(Binary files differ)
Property changes on: trunk/geany-plugins/gproject/icons/gproject-refresh.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: trunk/geany-plugins/gproject/icons/gproject-source.png
===================================================================
(Binary files differ)
Property changes on: trunk/geany-plugins/gproject/icons/gproject-source.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: trunk/geany-plugins/gproject/src/Makefile.am
===================================================================
--- trunk/geany-plugins/gproject/src/Makefile.am (rev 0)
+++ trunk/geany-plugins/gproject/src/Makefile.am 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,23 @@
+include $(top_srcdir)/build/vars.build.mk
+
+if ENABLE_GPROJECT
+geanyplugins_LTLIBRARIES = gproject.la
+else
+EXTRA_LTLIBRARIES = gproject.la
+endif
+gproject_la_SOURCES = \
+ gproject-main.c \
+ gproject-project.h \
+ gproject-project.c \
+ gproject-sidebar.h \
+ gproject-sidebar.c \
+ gproject-utils.h \
+ gproject-utils.c \
+ gproject-menu.h \
+ gproject-menu.c
+
+gproject_la_CFLAGS = $(AM_CFLAGS)
+gproject_la_LIBADD = $(COMMONLIBS)
+
+include $(top_srcdir)/build/cppcheck.mk
+
Added: trunk/geany-plugins/gproject/src/gproject-main.c
===================================================================
--- trunk/geany-plugins/gproject/src/gproject-main.c (rev 0)
+++ trunk/geany-plugins/gproject/src/gproject-main.c 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2010 Jiri Techet <techet(a)gmail.com>
+ *
+ * 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.
+ */
+
+#include <sys/time.h>
+#include <string.h>
+
+#include <geany/geanyplugin.h>
+
+#include "gproject-project.h"
+#include "gproject-sidebar.h"
+#include "gproject-menu.h"
+
+PLUGIN_VERSION_CHECK(211);
+PLUGIN_SET_INFO(_("GProject"),
+ _("Glob-pattern-based project management plugin for Geany."),
+ VERSION,
+ "Jiri Techet <techet(a)gmail.com>");
+
+GeanyPlugin *geany_plugin;
+GeanyData *geany_data;
+GeanyFunctions *geany_functions;
+
+
+void plugin_init(G_GNUC_UNUSED GeanyData * data);
+void plugin_cleanup(void);
+
+
+static void on_doc_open(G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED GeanyDocument * doc,
+ G_GNUC_UNUSED gpointer user_data)
+{
+ g_return_if_fail(doc != NULL && doc->file_name != NULL);
+
+ //tags of open files managed by geany
+ if (gprj_project_is_in_project(doc->file_name))
+ gprj_project_remove_file_tag(doc->file_name);
+
+ gprj_sidebar_update(FALSE);
+}
+
+
+static void on_doc_activate(G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED GeanyDocument * doc,
+ G_GNUC_UNUSED gpointer user_data)
+{
+ g_return_if_fail(doc != NULL && doc->file_name != NULL);
+
+ gprj_sidebar_update(FALSE);
+}
+
+
+static void on_doc_close(G_GNUC_UNUSED GObject * obj, GeanyDocument * doc,
+ G_GNUC_UNUSED gpointer user_data)
+{
+ g_return_if_fail(doc != NULL && doc->file_name != NULL);
+
+ //tags of open files managed by geany - when the file gets closed, we should take care of it
+ if (gprj_project_is_in_project(doc->file_name))
+ gprj_project_add_file_tag(doc->file_name);
+
+ gprj_sidebar_update(FALSE);
+}
+
+
+static void on_build_start(GObject *obj, gpointer user_data)
+{
+ gint i;
+
+ foreach_document(i)
+ {
+ if (gprj_project_is_in_project(documents[i]->file_name))
+ document_save_file(documents[i], FALSE);
+ }
+}
+
+
+static void on_project_dialog_create(G_GNUC_UNUSED GObject * obj, GtkWidget * notebook,
+ G_GNUC_UNUSED gpointer user_data)
+{
+ gprj_project_add_properties_tab(notebook);
+}
+
+
+static void on_project_dialog_confirmed(G_GNUC_UNUSED GObject * obj, GtkWidget * notebook,
+ G_GNUC_UNUSED gpointer user_data)
+{
+ gprj_project_read_properties_tab();
+ gprj_sidebar_update(TRUE);
+}
+
+
+static void on_project_open(G_GNUC_UNUSED GObject * obj, GKeyFile * config,
+ G_GNUC_UNUSED gpointer user_data)
+{
+ gprj_project_open(config);
+ gprj_sidebar_update(TRUE);
+ gprj_sidebar_activate(TRUE);
+ gprj_menu_activate_menu_items(TRUE);
+}
+
+
+static void on_project_close(G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED gpointer user_data)
+{
+ gprj_project_close();
+ gprj_sidebar_update(TRUE);
+ gprj_sidebar_activate(FALSE);
+ gprj_menu_activate_menu_items(FALSE);
+}
+
+
+static void on_project_save(G_GNUC_UNUSED GObject * obj, GKeyFile * config,
+ G_GNUC_UNUSED gpointer user_data)
+{
+ if (!g_prj)
+ {
+ //happens when the project is created
+ gprj_project_open(config);
+ gprj_sidebar_update(TRUE);
+ gprj_sidebar_activate(TRUE);
+ gprj_menu_activate_menu_items(TRUE);
+ }
+
+ gprj_project_save(config);
+}
+
+
+PluginCallback plugin_callbacks[] = {
+ {"document-open", (GCallback) & on_doc_open, TRUE, NULL},
+ {"document-activate", (GCallback) & on_doc_activate, TRUE, NULL},
+ {"document-close", (GCallback) & on_doc_close, TRUE, NULL},
+ {"build-start", (GCallback) & on_build_start, TRUE, NULL},
+ {"project-dialog-create", (GCallback) & on_project_dialog_create, TRUE, NULL},
+ {"project-dialog-confirmed", (GCallback) & on_project_dialog_confirmed, TRUE, NULL},
+ {"project-open", (GCallback) & on_project_open, TRUE, NULL},
+ {"project-close", (GCallback) & on_project_close, TRUE, NULL},
+ {"project-save", (GCallback) & on_project_save, TRUE, NULL},
+ {NULL, NULL, FALSE, NULL}
+};
+
+
+void plugin_init(G_GNUC_UNUSED GeanyData * data)
+{
+ gprj_menu_init();
+ gprj_sidebar_init();
+}
+
+
+void plugin_cleanup(void)
+{
+ if (geany_data->app->project)
+ {
+ gprj_project_close();
+ gprj_sidebar_update(TRUE);
+ }
+
+ gprj_menu_cleanup();
+ gprj_sidebar_cleanup();
+}
Added: trunk/geany-plugins/gproject/src/gproject-menu.c
===================================================================
--- trunk/geany-plugins/gproject/src/gproject-menu.c (rev 0)
+++ trunk/geany-plugins/gproject/src/gproject-menu.c 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,406 @@
+/*
+ * Copyright 2010 Jiri Techet <techet(a)gmail.com>
+ *
+ * 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.
+ */
+
+#include <sys/time.h>
+#include <gdk/gdkkeysyms.h>
+#include <glib/gstdio.h>
+
+#include <geany/geanyplugin.h>
+
+#include "gproject-menu.h"
+#include "gproject-project.h"
+#include "gproject-utils.h"
+#include "gproject-sidebar.h"
+
+#include <string.h>
+
+extern GeanyData *geany_data;
+extern GeanyFunctions *geany_functions;
+
+
+enum
+{
+ KB_SWAP_HEADER_SOURCE,
+ KB_FIND_IN_PROJECT,
+ KB_FIND_FILE,
+ KB_COUNT
+};
+
+PLUGIN_KEY_GROUP(gproject, KB_COUNT);
+
+
+static GtkWidget *s_fif_item, *s_ff_item, *s_shs_item, *s_sep_item, *s_context_osf_item, *s_context_sep_item;
+
+
+static gboolean try_swap_header_source(gchar *file_name, gboolean is_header, GSList *file_list, GSList *header_patterns, GSList *source_patterns)
+{
+ gchar *name_pattern;
+ gchar *base_name = NULL;
+ GSList *elem;
+ GPatternSpec *pattern;
+ gboolean found = FALSE;
+
+ name_pattern = g_path_get_basename(file_name);
+ setptr(name_pattern, utils_remove_ext_from_filename(name_pattern));
+ setptr(name_pattern, g_strconcat(name_pattern, ".*", NULL));
+ pattern = g_pattern_spec_new(name_pattern);
+ g_free(name_pattern);
+
+ for (elem = file_list; elem != NULL; elem = g_slist_next(elem))
+ {
+ gchar *full_name = elem->data;
+ base_name = g_path_get_basename(full_name);
+
+ if (g_pattern_match_string(pattern, base_name) &&
+ gprj_project_is_in_project(full_name))
+ {
+ if ((is_header && patterns_match(source_patterns, base_name)) ||
+ (!is_header && patterns_match(header_patterns, base_name)))
+ {
+ open_file(full_name);
+ found = TRUE;
+ break;
+ }
+ }
+ }
+
+ g_free(base_name);
+ g_pattern_spec_free(pattern);
+ return found;
+}
+
+
+static void get_project_file_list(char *file_name, G_GNUC_UNUSED gpointer value, GSList **list)
+{
+ *list = g_slist_prepend(*list, file_name);
+}
+
+
+static void on_swap_header_source(G_GNUC_UNUSED GtkMenuItem * menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ GSList *header_patterns, *source_patterns;
+ GeanyDocument *doc;
+ gboolean known_type = TRUE;
+ gboolean is_header;
+ gchar *doc_basename;
+ doc = document_get_current();
+
+ if (!g_prj || !geany_data->app->project || !doc || !doc->file_name)
+ return;
+
+ header_patterns = get_precompiled_patterns(g_prj->header_patterns);
+ source_patterns = get_precompiled_patterns(g_prj->source_patterns);
+
+ doc_basename = g_path_get_basename(doc->file_name);
+
+ if (patterns_match(header_patterns, doc_basename))
+ is_header = TRUE;
+ else if (patterns_match(source_patterns, doc_basename))
+ is_header = FALSE;
+ else
+ known_type = FALSE;
+
+ if (known_type)
+ {
+ gboolean swapped;
+ GSList *elem, *list = NULL;
+ gint i;
+
+ foreach_document(i)
+ {
+ gchar *filename;
+
+ filename = document_index(i)->file_name;
+ if (gprj_project_is_in_project(filename))
+ list = g_slist_prepend(list, filename);
+ }
+ swapped = try_swap_header_source(doc->file_name, is_header, list, header_patterns, source_patterns);
+ g_slist_free(list);
+ list = NULL;
+
+ if (!swapped)
+ {
+ gchar *doc_dir;
+
+ doc_dir = g_path_get_dirname(doc->file_name);
+ setptr(doc_dir, utils_get_locale_from_utf8(doc_dir));
+
+ list = utils_get_file_list(doc_dir, NULL, NULL);
+ for (elem = list; elem != NULL; elem = g_slist_next(elem))
+ {
+ gchar *full_name;
+
+ full_name = g_build_filename(doc_dir, elem->data, NULL);
+ setptr(full_name, utils_get_utf8_from_locale(full_name));
+ setptr(elem->data, full_name);
+ }
+ swapped = try_swap_header_source(doc->file_name, is_header, list, header_patterns, source_patterns);
+ g_slist_foreach(list, (GFunc) g_free, NULL);
+ g_slist_free(list);
+ g_free(doc_dir);
+ list = NULL;
+ }
+
+ if (!swapped)
+ {
+ g_hash_table_foreach(g_prj->file_tag_table, (GHFunc) get_project_file_list, &list);
+ try_swap_header_source(doc->file_name, is_header, list, header_patterns, source_patterns);
+ g_slist_free(list);
+ }
+ }
+
+ g_free(doc_basename);
+
+ g_slist_foreach(header_patterns, (GFunc) g_pattern_spec_free, NULL);
+ g_slist_free(header_patterns);
+ g_slist_foreach(source_patterns, (GFunc) g_pattern_spec_free, NULL);
+ g_slist_free(source_patterns);
+}
+
+
+static void on_find_in_project(G_GNUC_UNUSED GtkMenuItem * menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ if (geany_data->app->project)
+ search_show_find_in_files_dialog(geany_data->app->project->base_path);
+}
+
+
+static void on_find_file(G_GNUC_UNUSED GtkMenuItem * menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ if (geany_data->app->project)
+ gprj_sidebar_find_file_in_active();
+}
+
+
+static void kb_callback(guint key_id)
+{
+ switch (key_id)
+ {
+ case KB_SWAP_HEADER_SOURCE:
+ on_swap_header_source(NULL, NULL);
+ break;
+ case KB_FIND_IN_PROJECT:
+ on_find_in_project(NULL, NULL);
+ break;
+ case KB_FIND_FILE:
+ on_find_file(NULL, NULL);
+ break;
+ }
+}
+
+
+typedef struct
+{
+ gchar *subpath;
+ gchar *found_path;
+} FindData;
+
+
+static void find_name(char *file_name, G_GNUC_UNUSED gpointer value, FindData *data)
+{
+ gchar *pos;
+
+ if (data->found_path)
+ return;
+
+ pos = g_strrstr(file_name, data->subpath);
+ if (pos && (pos - file_name + strlen(data->subpath) == strlen(file_name)))
+ data->found_path = file_name;
+}
+
+
+static void on_open_selected_file(GtkMenuItem *menuitem, gpointer user_data)
+{
+ GeanyDocument *doc = document_get_current();
+ gchar *sel = NULL;
+ gchar *filename = NULL;
+ gchar *path = NULL;
+
+ g_return_if_fail(doc != NULL);
+
+ sel = get_selection();
+
+ if (!sel)
+ return;
+
+ setptr(sel, utils_get_locale_from_utf8(sel));
+
+ if (g_path_is_absolute(sel))
+ {
+ filename = g_strdup(sel);
+ if (!g_file_test(filename, G_FILE_TEST_EXISTS))
+ {
+ g_free(filename);
+ filename = NULL;
+ }
+ }
+
+ if (!filename)
+ {
+ if (doc->file_name)
+ {
+ path = g_path_get_dirname(doc->file_name);
+ setptr(path, utils_get_locale_from_utf8(path));
+ }
+
+ if (!path)
+ path = g_get_current_dir();
+
+ filename = g_build_path(G_DIR_SEPARATOR_S, path, sel, NULL);
+ if (!g_file_test(filename, G_FILE_TEST_EXISTS))
+ {
+ g_free(filename);
+ filename = NULL;
+ }
+
+ g_free(path);
+ }
+
+ if (!filename && geany_data->app->project != NULL)
+ {
+ gchar **pathv;
+ gint i;
+
+ path = g_strdup("");
+ pathv = g_strsplit_set(sel, "/\\", -1);
+ for (i = g_strv_length(pathv) - 1; i >= 0; i--)
+ {
+ if (g_strcmp0(pathv[i], "..") == 0)
+ break;
+ setptr(path, g_build_filename(G_DIR_SEPARATOR_S, pathv[i], path, NULL));
+ }
+ g_strfreev(pathv);
+
+ if (g_strcmp0(path, "") != 0)
+ {
+ FindData data;
+
+ data.subpath = path;
+ data.found_path = NULL;
+ g_hash_table_foreach(g_prj->file_tag_table, (GHFunc)find_name, &data);
+ if (data.found_path)
+ {
+ filename = g_strdup(data.found_path);
+ setptr(filename, utils_get_locale_from_utf8(filename));
+ if (!g_file_test(filename, G_FILE_TEST_EXISTS))
+ {
+ g_free(filename);
+ filename = NULL;
+ }
+ }
+ }
+ g_free(path);
+ }
+
+#ifdef G_OS_UNIX
+ if (!filename)
+ {
+ filename = g_build_path(G_DIR_SEPARATOR_S, "/usr/local/include", sel, NULL);
+ if (!g_file_test(filename, G_FILE_TEST_EXISTS))
+ {
+ g_free(filename);
+ filename = NULL;
+ }
+ }
+
+ if (!filename)
+ {
+ filename = g_build_path(G_DIR_SEPARATOR_S, "/usr/include", sel, NULL);
+ if (!g_file_test(filename, G_FILE_TEST_EXISTS))
+ {
+ g_free(filename);
+ filename = NULL;
+ }
+ }
+#endif
+
+ if (filename)
+ open_file(filename);
+
+ g_free(filename);
+ g_free(sel);
+}
+
+
+void gprj_menu_init()
+{
+ GtkWidget *image;
+
+ s_sep_item = gtk_separator_menu_item_new();
+ gtk_widget_show(s_sep_item);
+ gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_sep_item);
+
+ image = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
+ gtk_widget_show(image);
+ s_fif_item = gtk_image_menu_item_new_with_mnemonic(_("Find in Project Files"));
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(s_fif_item), image);
+ gtk_widget_show(s_fif_item);
+ gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_fif_item);
+ g_signal_connect((gpointer) s_fif_item, "activate", G_CALLBACK(on_find_in_project), NULL);
+ keybindings_set_item(plugin_key_group, KB_FIND_IN_PROJECT, kb_callback,
+ 0, 0, "find_in_project", _("Find in project files"), s_fif_item);
+
+ image = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
+ gtk_widget_show(image);
+ s_ff_item = gtk_image_menu_item_new_with_mnemonic(_("Find Project File"));
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(s_ff_item), image);
+ gtk_widget_show(s_ff_item);
+ gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_ff_item);
+ g_signal_connect((gpointer) s_ff_item, "activate", G_CALLBACK(on_find_file), NULL);
+ keybindings_set_item(plugin_key_group, KB_FIND_FILE, kb_callback,
+ 0, 0, "find_file", _("Find project file"), s_ff_item);
+
+ s_shs_item = gtk_menu_item_new_with_mnemonic(_("Swap Header/Source"));
+ gtk_widget_show(s_shs_item);
+ gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_shs_item);
+ g_signal_connect((gpointer) s_shs_item, "activate", G_CALLBACK(on_swap_header_source), NULL);
+ keybindings_set_item(plugin_key_group, KB_SWAP_HEADER_SOURCE, kb_callback,
+ 0, 0, "swap_header_source", _("Swap header/source"), s_shs_item);
+
+ s_context_sep_item = gtk_separator_menu_item_new();
+ gtk_widget_show(s_context_sep_item);
+ gtk_menu_shell_prepend(GTK_MENU_SHELL(geany->main_widgets->editor_menu), s_context_sep_item);
+
+ s_context_osf_item = gtk_menu_item_new_with_mnemonic(_("Open Selected File (gproject)"));
+ gtk_widget_show(s_context_osf_item);
+ gtk_menu_shell_prepend(GTK_MENU_SHELL(geany->main_widgets->editor_menu), s_context_osf_item);
+ g_signal_connect((gpointer) s_context_osf_item, "activate", G_CALLBACK(on_open_selected_file), NULL);
+
+ gprj_menu_activate_menu_items(FALSE);
+}
+
+
+void gprj_menu_activate_menu_items(gboolean activate)
+{
+ gtk_widget_set_sensitive(s_context_osf_item, activate);
+ gtk_widget_set_sensitive(s_shs_item, activate);
+ gtk_widget_set_sensitive(s_ff_item, activate);
+ gtk_widget_set_sensitive(s_fif_item, activate);
+}
+
+
+void gprj_menu_cleanup()
+{
+ gtk_widget_destroy(s_fif_item);
+ gtk_widget_destroy(s_ff_item);
+ gtk_widget_destroy(s_shs_item);
+ gtk_widget_destroy(s_sep_item);
+
+ gtk_widget_destroy(s_context_osf_item);
+ gtk_widget_destroy(s_context_sep_item);
+}
Added: trunk/geany-plugins/gproject/src/gproject-menu.h
===================================================================
--- trunk/geany-plugins/gproject/src/gproject-menu.h (rev 0)
+++ trunk/geany-plugins/gproject/src/gproject-menu.h 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2010 Jiri Techet <techet(a)gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef __GPROJECT_MENU_H__
+#define __GPROJECT_MENU_H__
+
+void gprj_menu_init(void);
+void gprj_menu_activate_menu_items(gboolean activate);
+void gprj_menu_cleanup(void);
+
+#endif
Added: trunk/geany-plugins/gproject/src/gproject-project.c
===================================================================
--- trunk/geany-plugins/gproject/src/gproject-project.c (rev 0)
+++ trunk/geany-plugins/gproject/src/gproject-project.c 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,406 @@
+/*
+ * Copyright 2010 Jiri Techet <techet(a)gmail.com>
+ *
+ * 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.
+ */
+
+#include <sys/time.h>
+#include <gdk/gdkkeysyms.h>
+#include <glib/gstdio.h>
+
+#include <geany/geanyplugin.h>
+
+#include "gproject-utils.h"
+#include "gproject-project.h"
+
+extern GeanyData *geany_data;
+extern GeanyFunctions *geany_functions;
+
+typedef struct
+{
+ GtkWidget *source_patterns;
+ GtkWidget *header_patterns;
+ GtkWidget *ignored_dirs_patterns;
+ GtkWidget *generate_tags;
+} PropertyDialogElements;
+
+GPrj *g_prj = NULL;
+
+static PropertyDialogElements *e;
+
+
+static void workspace_remove_tag(gchar *filename, TagObject *obj, gpointer foo)
+{
+ if (obj->tag)
+ {
+ tm_workspace_remove_object(obj->tag, TRUE, TRUE);
+ obj->tag = NULL;
+ }
+}
+
+
+static void workspace_add_tag(gchar *filename, TagObject *obj, gpointer foo)
+{
+ TMWorkObject *tm_obj = NULL;
+
+ if (!document_find_by_filename(filename))
+ {
+ gchar *locale_filename;
+
+ locale_filename = utils_get_locale_from_utf8(filename);
+ tm_obj = tm_source_file_new(locale_filename, FALSE, filetypes_detect_from_file(filename)->name);
+ g_free(locale_filename);
+
+ tm_workspace_add_object(tm_obj);
+ tm_source_file_update(tm_obj, TRUE, FALSE, TRUE);
+ }
+
+ if (obj->tag)
+ tm_workspace_remove_object(obj->tag, TRUE, TRUE);
+
+ obj->tag = tm_obj;
+}
+
+
+/* path - absolute path in locale, returned list in locale */
+static GSList *get_file_list(const gchar * path, GSList *patterns, GSList *ignored_dirs_patterns)
+{
+ GSList *list = NULL;
+ GDir *dir;
+
+ dir = g_dir_open(path, 0, NULL);
+ if (!dir)
+ return NULL;
+
+ while (TRUE)
+ {
+ const gchar *name;
+ gchar *filename;
+
+ name = g_dir_read_name(dir);
+ if (!name)
+ break;
+
+ filename = g_build_filename(path, name, NULL);
+
+ if (g_file_test(filename, G_FILE_TEST_IS_DIR))
+ {
+ GSList *lst;
+
+ if (patterns_match(ignored_dirs_patterns, name))
+ {
+ g_free(filename);
+ continue;
+ }
+
+ lst = get_file_list(filename, patterns, ignored_dirs_patterns);
+ if (lst)
+ list = g_slist_concat(list, lst);
+ g_free(filename);
+ }
+ else if (g_file_test(filename, G_FILE_TEST_IS_REGULAR))
+ {
+ if (patterns_match(patterns, name))
+ list = g_slist_prepend(list, filename);
+ else
+ g_free(filename);
+ }
+ }
+
+ g_dir_close(dir);
+
+ return list;
+}
+
+
+void gprj_project_rescan()
+{
+ GSList *pattern_list = NULL;
+ GSList *ignored_dirs_list = NULL;
+ GSList *lst;
+ GSList *elem;
+
+ if (!g_prj)
+ return;
+
+ if (g_prj->generate_tags)
+ g_hash_table_foreach(g_prj->file_tag_table, (GHFunc)workspace_remove_tag, NULL);
+ g_hash_table_destroy(g_prj->file_tag_table);
+ g_prj->file_tag_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+ pattern_list = get_precompiled_patterns(geany_data->app->project->file_patterns);
+
+ ignored_dirs_list = get_precompiled_patterns(g_prj->ignored_dirs_patterns);
+
+ lst = get_file_list(geany_data->app->project->base_path, pattern_list, ignored_dirs_list);
+
+ for (elem = lst; elem != NULL; elem = g_slist_next(elem))
+ {
+ char *path;
+ TagObject *obj;
+
+ obj = g_new0(TagObject, 1);
+ obj->tag = NULL;
+
+ path = tm_get_real_path(elem->data);
+ setptr(path, utils_get_utf8_from_locale(path));
+ g_hash_table_insert(g_prj->file_tag_table, path, obj);
+ }
+
+ if (g_prj->generate_tags)
+ g_hash_table_foreach(g_prj->file_tag_table, (GHFunc)workspace_add_tag, NULL);
+
+ g_slist_foreach(lst, (GFunc) g_free, NULL);
+ g_slist_free(lst);
+
+ g_slist_foreach(pattern_list, (GFunc) g_pattern_spec_free, NULL);
+ g_slist_free(pattern_list);
+
+ g_slist_foreach(ignored_dirs_list, (GFunc) g_pattern_spec_free, NULL);
+ g_slist_free(ignored_dirs_list);
+}
+
+
+static void update_project(
+ gchar **source_patterns,
+ gchar **header_patterns,
+ gchar **ignored_dirs_patterns,
+ gboolean generate_tags)
+{
+ if (g_prj->source_patterns)
+ g_strfreev(g_prj->source_patterns);
+ g_prj->source_patterns = g_strdupv(source_patterns);
+
+ if (g_prj->header_patterns)
+ g_strfreev(g_prj->header_patterns);
+ g_prj->header_patterns = g_strdupv(header_patterns);
+
+ if (g_prj->ignored_dirs_patterns)
+ g_strfreev(g_prj->ignored_dirs_patterns);
+ g_prj->ignored_dirs_patterns = g_strdupv(ignored_dirs_patterns);
+
+ g_prj->generate_tags = generate_tags;
+
+ gprj_project_rescan();
+}
+
+
+void gprj_project_save(GKeyFile * key_file)
+{
+ if (!g_prj)
+ return;
+
+ g_key_file_set_string_list(key_file, "gproject", "source_patterns",
+ (const gchar**) g_prj->source_patterns, g_strv_length(g_prj->source_patterns));
+ g_key_file_set_string_list(key_file, "gproject", "header_patterns",
+ (const gchar**) g_prj->header_patterns, g_strv_length(g_prj->header_patterns));
+ g_key_file_set_string_list(key_file, "gproject", "ignored_dirs_patterns",
+ (const gchar**) g_prj->ignored_dirs_patterns, g_strv_length(g_prj->ignored_dirs_patterns));
+ g_key_file_set_boolean(key_file, "gproject", "generate_tags", g_prj->generate_tags);
+}
+
+
+void gprj_project_open(GKeyFile * key_file)
+{
+ gchar **source_patterns, **header_patterns, **ignored_dirs_patterns;
+ gboolean generate_tags;
+
+ if (g_prj != NULL)
+ gprj_project_close();
+
+ g_prj = (GPrj *) g_new0(GPrj, 1);
+
+ g_prj->source_patterns = NULL;
+ g_prj->header_patterns = NULL;
+ g_prj->ignored_dirs_patterns = NULL;
+ g_prj->generate_tags = FALSE;
+
+ g_prj->file_tag_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+ source_patterns = g_key_file_get_string_list(key_file, "gproject", "source_patterns", NULL, NULL);
+ if (!source_patterns)
+ source_patterns = g_strsplit("*.c *.C *.cpp *.cxx *.c++ *.cc", " ", -1);
+ header_patterns = g_key_file_get_string_list(key_file, "gproject", "header_patterns", NULL, NULL);
+ if (!header_patterns)
+ header_patterns = g_strsplit("*.h *.H *.hpp *.hxx *.h++ *.hh *.m", " ", -1);
+ ignored_dirs_patterns = g_key_file_get_string_list(key_file, "gproject", "ignored_dirs_patterns", NULL, NULL);
+ if (!ignored_dirs_patterns)
+ ignored_dirs_patterns = g_strsplit(".* CVS", " ", -1);
+ generate_tags = utils_get_setting_boolean(key_file, "gproject", "generate_tags", FALSE);
+
+ update_project(
+ source_patterns,
+ header_patterns,
+ ignored_dirs_patterns,
+ generate_tags);
+
+ g_free(source_patterns);
+ g_free(header_patterns);
+ g_free(ignored_dirs_patterns);
+}
+
+
+static gchar **split_patterns(const gchar *str)
+{
+ GString *tmp;
+ gchar **ret;
+ gchar *input;
+
+ input = g_strdup(str);
+
+ g_strstrip(input);
+ tmp = g_string_new(input);
+ g_free(input);
+ do {} while (utils_string_replace_all(tmp, " ", " "));
+ ret = g_strsplit(tmp->str, " ", -1);
+ g_string_free(tmp, TRUE);
+ return ret;
+}
+
+
+void gprj_project_read_properties_tab()
+{
+ gchar **source_patterns, **header_patterns, **ignored_dirs_patterns;
+
+ source_patterns = split_patterns(gtk_entry_get_text(GTK_ENTRY(e->source_patterns)));
+ header_patterns = split_patterns(gtk_entry_get_text(GTK_ENTRY(e->header_patterns)));
+ ignored_dirs_patterns = split_patterns(gtk_entry_get_text(GTK_ENTRY(e->ignored_dirs_patterns)));
+
+ update_project(
+ source_patterns, header_patterns, ignored_dirs_patterns,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(e->generate_tags)));
+
+ g_free(source_patterns);
+ g_free(header_patterns);
+ g_free(ignored_dirs_patterns);
+}
+
+
+void gprj_project_add_properties_tab(GtkWidget *notebook)
+{
+ GtkWidget *vbox, *hbox, *hbox1;
+ GtkWidget *table;
+ GtkWidget *label;
+ gchar *str;
+
+ e = g_new0(PropertyDialogElements, 1);
+
+ vbox = gtk_vbox_new(FALSE, 0);
+
+ table = gtk_table_new(3, 2, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 5);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 10);
+
+ label = gtk_label_new(_("Source patterns:"));
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+ e->source_patterns = gtk_entry_new();
+ ui_table_add_row(GTK_TABLE(table), 0, label, e->source_patterns, NULL);
+ ui_entry_add_clear_icon(GTK_ENTRY(e->source_patterns));
+ ui_widget_set_tooltip_text(e->source_patterns,
+ _("Space separated list of patterns that are used to identify source files."));
+ str = g_strjoinv(" ", g_prj->source_patterns);
+ gtk_entry_set_text(GTK_ENTRY(e->source_patterns), str);
+ g_free(str);
+
+ label = gtk_label_new(_("Header patterns:"));
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+ e->header_patterns = gtk_entry_new();
+ ui_entry_add_clear_icon(GTK_ENTRY(e->header_patterns));
+ ui_table_add_row(GTK_TABLE(table), 1, label, e->header_patterns, NULL);
+ ui_widget_set_tooltip_text(e->header_patterns,
+ _("Space separated list of patterns that are used to identify headers. "
+ "Used mainly for header/source swapping."));
+ str = g_strjoinv(" ", g_prj->header_patterns);
+ gtk_entry_set_text(GTK_ENTRY(e->header_patterns), str);
+ g_free(str);
+
+ label = gtk_label_new(_("Ignored dirs patterns:"));
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+ e->ignored_dirs_patterns = gtk_entry_new();
+ ui_entry_add_clear_icon(GTK_ENTRY(e->ignored_dirs_patterns));
+ ui_table_add_row(GTK_TABLE(table), 2, label, e->ignored_dirs_patterns, NULL);
+ ui_widget_set_tooltip_text(e->ignored_dirs_patterns,
+ _("Space separated list of patterns that are used to identify directories "
+ "that are not scanned for source files."));
+ str = g_strjoinv(" ", g_prj->ignored_dirs_patterns);
+ gtk_entry_set_text(GTK_ENTRY(e->ignored_dirs_patterns), str);
+ g_free(str);
+
+ gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 6);
+
+ e->generate_tags = gtk_check_button_new_with_label(_("Generate tags for all project files"));
+ ui_widget_set_tooltip_text(e->generate_tags,
+ _("Generate tag list for all project files instead of only for the currently opened files. "
+ "Too slow for big projects (>1000 files) and should be disabled in this case."));
+ gtk_box_pack_start(GTK_BOX(vbox), e->generate_tags, FALSE, FALSE, 6);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(e->generate_tags), g_prj->generate_tags);
+
+ hbox1 = gtk_hbox_new(FALSE, 0);
+ label = gtk_label_new(_("Note: set the patterns of files belonging to the project under the Project tab."));
+ gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox1, FALSE, FALSE, 6);
+
+ label = gtk_label_new(_("GProject"));
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 6);
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, label);
+}
+
+
+void gprj_project_close()
+{
+ g_return_if_fail(g_prj);
+
+ if (g_prj->generate_tags)
+ g_hash_table_foreach(g_prj->file_tag_table, (GHFunc)workspace_remove_tag, NULL);
+
+ g_free(g_prj->source_patterns);
+ g_free(g_prj->header_patterns);
+ g_free(g_prj->ignored_dirs_patterns);
+
+ g_hash_table_destroy(g_prj->file_tag_table);
+
+ g_free(g_prj);
+ g_prj = NULL;
+}
+
+
+void gprj_project_add_file_tag(gchar *filename)
+{
+ TagObject *obj;
+
+ obj = g_hash_table_lookup(g_prj->file_tag_table, filename);
+ if (obj)
+ workspace_add_tag(filename, obj, NULL);
+}
+
+
+void gprj_project_remove_file_tag(gchar *filename)
+{
+ TagObject *obj;
+
+ obj = g_hash_table_lookup(g_prj->file_tag_table, filename);
+ if (obj)
+ workspace_remove_tag(filename, obj, NULL);
+}
+
+
+gboolean gprj_project_is_in_project(const gchar * filename)
+{
+ return filename && g_prj && geany_data->app->project &&
+ g_hash_table_lookup(g_prj->file_tag_table, filename) != NULL;
+}
Added: trunk/geany-plugins/gproject/src/gproject-project.h
===================================================================
--- trunk/geany-plugins/gproject/src/gproject-project.h (rev 0)
+++ trunk/geany-plugins/gproject/src/gproject-project.h 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2010 Jiri Techet <techet(a)gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef __GPROJECT_PROJECT_H__
+#define __GPROJECT_PROJECT_H__
+
+
+typedef struct
+{
+ TMWorkObject *tag;
+} TagObject;
+
+
+typedef struct
+{
+ gchar **source_patterns;
+ gchar **header_patterns;
+ gchar **ignored_dirs_patterns;
+ gboolean generate_tags;
+
+ GHashTable *file_tag_table;
+} GPrj;
+
+extern GPrj *g_prj;
+
+void gprj_project_open(GKeyFile * key_file);
+
+void gprj_project_add_properties_tab(GtkWidget *notebook);
+
+void gprj_project_close(void);
+
+void gprj_project_save(GKeyFile * key_file);
+void gprj_project_read_properties_tab(void);
+void gprj_project_rescan(void);
+
+
+void gprj_project_add_file_tag(gchar *filename);
+void gprj_project_remove_file_tag(gchar *filename);
+
+gboolean gprj_project_is_in_project(const gchar * filename);
+
+
+#endif
Added: trunk/geany-plugins/gproject/src/gproject-sidebar.c
===================================================================
--- trunk/geany-plugins/gproject/src/gproject-sidebar.c (rev 0)
+++ trunk/geany-plugins/gproject/src/gproject-sidebar.c 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,841 @@
+/*
+ * Copyright 2010 Jiri Techet <techet(a)gmail.com>
+ *
+ * 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.
+ */
+
+#include <sys/time.h>
+#include <gdk/gdkkeysyms.h>
+#include <string.h>
+
+#include <geany/geanyplugin.h>
+
+#include "gproject-utils.h"
+#include "gproject-project.h"
+#include "gproject-sidebar.h"
+
+extern GeanyData *geany_data;
+extern GeanyFunctions *geany_functions;
+
+enum
+{
+ FILEVIEW_COLUMN_ICON,
+ FILEVIEW_COLUMN_NAME,
+ FILEVIEW_N_COLUMNS,
+};
+
+
+static GtkWidget *s_file_view_vbox = NULL;
+static GtkWidget *s_file_view = NULL;
+static GtkTreeStore *s_file_store = NULL;
+static gboolean s_follow_editor = FALSE;
+
+static struct
+{
+ GtkWidget *widget;
+
+ GtkWidget *dir_label;
+ GtkWidget *combo;
+ GtkWidget *case_sensitive;
+ GtkWidget *full_path;
+} s_fif_dialog = {NULL, NULL, NULL, NULL, NULL};
+
+
+static struct
+{
+ GtkWidget *widget;
+
+ GtkWidget *find_in_directory;
+ GtkWidget *find_file;
+ GtkWidget *expand;
+} s_popup_menu;
+
+
+static gint show_dialog_find_file(gchar *path, gchar **pattern, gboolean *case_sensitive, gboolean *full_path)
+{
+ gint res;
+ GtkWidget *entry;
+ gchar *selection;
+ GtkSizeGroup *size_group;
+
+ if (!s_fif_dialog.widget)
+ {
+ GtkWidget *label, *vbox, *ebox;
+
+ s_fif_dialog.widget = gtk_dialog_new_with_buttons(
+ _("Find File"), GTK_WINDOW(geany->main_widgets->window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
+ gtk_dialog_add_button(GTK_DIALOG(s_fif_dialog.widget), "gtk-find", GTK_RESPONSE_ACCEPT);
+ gtk_dialog_set_default_response(GTK_DIALOG(s_fif_dialog.widget), GTK_RESPONSE_ACCEPT);
+
+ vbox = ui_dialog_vbox_new(GTK_DIALOG(s_fif_dialog.widget));
+ gtk_box_set_spacing(GTK_BOX(vbox), 6);
+
+ size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
+
+ label = gtk_label_new(_("Search for:"));
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+ gtk_size_group_add_widget(size_group, label);
+ s_fif_dialog.combo = gtk_combo_box_entry_new_text();
+ entry = gtk_bin_get_child(GTK_BIN(s_fif_dialog.combo));
+ gtk_entry_set_width_chars(GTK_ENTRY(entry), 40);
+ gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
+ ui_entry_add_clear_icon(GTK_ENTRY(entry));
+ gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
+
+ ebox = gtk_hbox_new(FALSE, 6);
+ gtk_box_pack_start(GTK_BOX(ebox), label, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(ebox), s_fif_dialog.combo, TRUE, TRUE, 0);
+
+ gtk_box_pack_start(GTK_BOX(vbox), ebox, TRUE, FALSE, 0);
+
+ label = gtk_label_new(_("Search inside:"));
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+ gtk_size_group_add_widget(size_group, label);
+ s_fif_dialog.dir_label = gtk_label_new("");
+ gtk_misc_set_alignment(GTK_MISC(s_fif_dialog.dir_label), 0, 0.5);
+
+ ebox = gtk_hbox_new(FALSE, 6);
+ gtk_box_pack_start(GTK_BOX(ebox), label, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(ebox), s_fif_dialog.dir_label, TRUE, TRUE, 0);
+
+ gtk_box_pack_start(GTK_BOX(vbox), ebox, TRUE, FALSE, 0);
+
+ s_fif_dialog.case_sensitive = gtk_check_button_new_with_mnemonic(_("C_ase sensitive"));
+ gtk_button_set_focus_on_click(GTK_BUTTON(s_fif_dialog.case_sensitive), FALSE);
+
+ s_fif_dialog.full_path = gtk_check_button_new_with_mnemonic(_("Search in full path"));
+ gtk_button_set_focus_on_click(GTK_BUTTON(s_fif_dialog.full_path), FALSE);
+
+ gtk_box_pack_start(GTK_BOX(vbox), s_fif_dialog.case_sensitive, TRUE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), s_fif_dialog.full_path, TRUE, FALSE, 0);
+ gtk_widget_show_all(vbox);
+ }
+
+ gtk_label_set_text(GTK_LABEL(s_fif_dialog.dir_label), path);
+ entry = gtk_bin_get_child(GTK_BIN(s_fif_dialog.combo));
+ selection = get_selection();
+ if (selection)
+ gtk_entry_set_text(GTK_ENTRY(entry), selection);
+ g_free(selection);
+ gtk_widget_grab_focus(entry);
+
+ res = gtk_dialog_run(GTK_DIALOG(s_fif_dialog.widget));
+
+ if (res == GTK_RESPONSE_ACCEPT)
+ {
+ const gchar *str;
+
+ str = gtk_entry_get_text(GTK_ENTRY(entry));
+ *pattern = g_strconcat("*", str, "*", NULL);
+ *case_sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s_fif_dialog.case_sensitive));
+ *full_path = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s_fif_dialog.full_path));
+ ui_combo_box_add_to_history(GTK_COMBO_BOX_ENTRY(s_fif_dialog.combo), str, 0);
+ }
+
+ gtk_widget_hide(s_fif_dialog.widget);
+
+ return res;
+}
+
+
+static gchar *build_path(GtkTreeIter *iter)
+{
+ GtkTreeIter node;
+ GtkTreeIter parent;
+ gchar *path = NULL;
+ GtkTreeModel *model;
+ gchar *name;
+
+ if (!iter)
+ return g_strdup(geany_data->app->project->base_path);
+
+ node = *iter;
+ model = GTK_TREE_MODEL(s_file_store);
+
+ while (gtk_tree_model_iter_parent(model, &parent, &node))
+ {
+ gtk_tree_model_get(model, &node, FILEVIEW_COLUMN_NAME, &name, -1);
+ if (path == NULL)
+ path = g_strdup(name);
+ else
+ setptr(path, g_build_filename(name, path, NULL));
+
+ node = parent;
+ }
+
+ gtk_tree_model_get(model, &node, FILEVIEW_COLUMN_NAME, &name, -1);
+ setptr(path, g_build_filename(name, path, NULL));
+
+ setptr(path, g_build_filename(geany_data->app->project->base_path, path, NULL));
+
+ return path;
+}
+
+
+static void on_expand_all(G_GNUC_UNUSED GtkMenuItem * menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(s_file_view));
+}
+
+
+static void on_collapse_all(G_GNUC_UNUSED GtkMenuItem * menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ gtk_tree_view_collapse_all(GTK_TREE_VIEW(s_file_view));
+}
+
+
+static void on_follow_active(GtkToggleToolButton *button, G_GNUC_UNUSED gpointer user_data)
+{
+ s_follow_editor = gtk_toggle_tool_button_get_active(button);
+ gprj_sidebar_update(FALSE);
+}
+
+
+static void find_file_recursive(GtkTreeIter *iter, gboolean case_sensitive, gboolean full_path, GPatternSpec *pattern)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL(s_file_store);
+ GtkTreeIter child;
+ gboolean iterate;
+
+ iterate = gtk_tree_model_iter_children(model, &child, iter);
+ if (iterate)
+ {
+ while (iterate)
+ {
+ find_file_recursive(&child, case_sensitive, full_path, pattern);
+ iterate = gtk_tree_model_iter_next(model, &child);
+ }
+ }
+ else
+ {
+ gchar *name;
+
+ if (iter == NULL)
+ return;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FILEVIEW_COLUMN_NAME, &name, -1);
+
+ if (full_path)
+ {
+ gchar *path;
+
+ path = build_path(iter);
+ name = get_file_relative_path(geany_data->app->project->base_path, path);
+ g_free(path);
+ }
+ else
+ name = g_strdup(name);
+
+ if (!case_sensitive)
+ setptr(name, g_utf8_strdown(name, -1));
+
+ if (g_pattern_match_string(pattern, name))
+ {
+ gchar *path, *rel_path;
+
+ path = build_path(iter);
+ rel_path = get_file_relative_path(geany_data->app->project->base_path, path);
+ msgwin_msg_add(COLOR_BLACK, -1, NULL, "./%s", rel_path ? rel_path : path);
+ g_free(path);
+ g_free(rel_path);
+ }
+
+ g_free(name);
+ }
+}
+
+
+static void find_file(GtkTreeIter *iter)
+{
+ gchar *pattern_str = NULL;
+ gboolean case_sensitive, full_path;
+ gchar *path;
+
+ path = build_path(iter);
+
+ if (show_dialog_find_file(path, &pattern_str, &case_sensitive, &full_path) == GTK_RESPONSE_ACCEPT)
+ {
+ GPatternSpec *pattern;
+
+ if (!case_sensitive)
+ setptr(pattern_str, g_utf8_strdown(pattern_str, -1));
+
+ pattern = g_pattern_spec_new(pattern_str);
+
+ msgwin_clear_tab(MSG_MESSAGE);
+ msgwin_set_messages_dir(geany_data->app->project->base_path);
+ find_file_recursive(iter, case_sensitive, full_path, pattern);
+ msgwin_switch_tab(MSG_MESSAGE, TRUE);
+ }
+
+ g_free(pattern_str);
+ g_free(path);
+}
+
+
+static void on_find_file(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ GtkTreeSelection *treesel;
+ GtkTreeModel *model;
+ GtkTreeIter iter, parent;
+
+ treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(s_file_view));
+ if (!gtk_tree_selection_get_selected(treesel, &model, &iter))
+ return;
+
+ if (!gtk_tree_model_iter_has_child(model, &iter))
+ {
+ if (gtk_tree_model_iter_parent(model, &parent, &iter))
+ find_file(&parent);
+ else
+ find_file(NULL);
+ }
+ else
+ find_file(&iter);
+}
+
+
+static void on_reload_project(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ gprj_project_rescan();
+ gprj_sidebar_update(TRUE);
+}
+
+
+static void on_open_clicked()
+{
+ GtkTreeSelection *treesel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(s_file_view));
+
+ if (gtk_tree_selection_get_selected(treesel, &model, &iter))
+ {
+ if (gtk_tree_model_iter_has_child(model, &iter))
+ {
+ GtkTreeView *tree_view;
+ GtkTreePath *tree_path;
+
+ tree_view = GTK_TREE_VIEW(s_file_view);
+ tree_path = gtk_tree_model_get_path (model, &iter);
+
+ if (gtk_tree_view_row_expanded(tree_view, tree_path))
+ gtk_tree_view_collapse_row(tree_view, tree_path);
+ else
+ gtk_tree_view_expand_row(tree_view, tree_path, FALSE);
+ }
+ else
+ {
+ gchar *name;
+ gchar *icon;
+
+ gtk_tree_model_get(model, &iter, FILEVIEW_COLUMN_ICON, &icon, -1);
+
+ if (!icon)
+ {
+ // help string doesn't have icon
+ return;
+ }
+
+ name = build_path(&iter);
+ open_file(name);
+ g_free(name);
+ }
+ }
+}
+
+
+static gboolean on_button_press(G_GNUC_UNUSED GtkWidget * widget, GdkEventButton * event,
+ G_GNUC_UNUSED gpointer user_data)
+{
+ if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
+ on_open_clicked();
+
+ return FALSE;
+}
+
+
+static gboolean on_key_press(G_GNUC_UNUSED GtkWidget * widget, GdkEventKey * event, G_GNUC_UNUSED gpointer data)
+{
+ if (event->keyval == GDK_Return
+ || event->keyval == GDK_ISO_Enter
+ || event->keyval == GDK_KP_Enter
+ || event->keyval == GDK_space)
+ on_open_clicked();
+ return FALSE;
+}
+
+
+static void expand_all(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ GtkTreeSelection *treesel;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(s_file_view));
+
+ if (gtk_tree_selection_get_selected(treesel, &model, &iter))
+ gtk_tree_view_expand_row(GTK_TREE_VIEW(s_file_view), gtk_tree_model_get_path (model, &iter), TRUE);
+}
+
+
+static void on_find_in_files(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ GtkTreeSelection *treesel;
+ GtkTreeIter iter, parent;
+ GtkTreeModel *model;
+ gchar *path;
+
+ treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(s_file_view));
+
+ if (!gtk_tree_selection_get_selected(treesel, &model, &iter))
+ return;
+
+ if (!gtk_tree_model_iter_has_child(model, &iter))
+ {
+ if (gtk_tree_model_iter_parent(model, &parent, &iter))
+ path = build_path(&parent);
+ else
+ path = build_path(NULL);
+ }
+ else
+ path = build_path(&iter);
+
+ search_show_find_in_files_dialog(path);
+ g_free(path);
+}
+
+
+static void build_file_list(gpointer name, G_GNUC_UNUSED gpointer value, GSList **lst)
+{
+ gchar *item;
+
+ item = get_file_relative_path(geany_data->app->project->base_path, name);
+ *lst = g_slist_prepend(*lst, item);
+}
+
+
+static void create_branch(gint level, GSList *leaf_list, GtkTreeIter *parent,
+ GSList *header_patterns, GSList *source_patterns)
+{
+ GSList *dir_list = NULL;
+ GSList *file_list = NULL;
+ GSList *elem;
+
+ for (elem = leaf_list; elem != NULL; elem = g_slist_next(elem))
+ {
+ gchar **path_arr = elem->data;
+
+ if (path_arr[level+1] != NULL)
+ dir_list = g_slist_prepend(dir_list, path_arr);
+ else
+ file_list = g_slist_prepend(file_list, path_arr);
+ }
+
+ if (dir_list)
+ {
+ GSList *tmp_list = NULL;
+ GtkTreeIter iter;
+ gchar **path_arr = dir_list->data;
+ gchar *last_dir_name;
+
+ last_dir_name = path_arr[level];
+
+ for (elem = dir_list; elem != NULL; elem = g_slist_next(elem))
+ {
+ gboolean dir_changed;
+
+ path_arr = (gchar **) elem->data;
+ dir_changed = g_strcmp0(last_dir_name, path_arr[level]) != 0;
+
+ if (dir_changed)
+ {
+ gtk_tree_store_append(s_file_store, &iter, parent);
+ gtk_tree_store_set(s_file_store, &iter,
+ FILEVIEW_COLUMN_ICON, "gtk-directory",
+ FILEVIEW_COLUMN_NAME, last_dir_name, -1);
+
+ create_branch(level+1, tmp_list, &iter, header_patterns, source_patterns);
+
+ g_slist_free(tmp_list);
+ tmp_list = NULL;
+ last_dir_name = path_arr[level];
+ }
+
+ tmp_list = g_slist_prepend(tmp_list, path_arr);
+ }
+
+ gtk_tree_store_append(s_file_store, &iter, parent);
+ gtk_tree_store_set(s_file_store, &iter,
+ FILEVIEW_COLUMN_ICON, "gtk-directory",
+ FILEVIEW_COLUMN_NAME, last_dir_name, -1);
+
+ create_branch(level+1, tmp_list, &iter, header_patterns, source_patterns);
+
+ g_slist_free(tmp_list);
+ g_slist_free(dir_list);
+ }
+
+ for (elem = file_list; elem != NULL; elem = g_slist_next(elem))
+ {
+ GtkTreeIter iter;
+ gchar **path_arr = elem->data;
+
+ gtk_tree_store_append(s_file_store, &iter, parent);
+ if (patterns_match(header_patterns, path_arr[level]))
+ {
+ gtk_tree_store_set(s_file_store, &iter,
+ FILEVIEW_COLUMN_ICON, "gproject-header",
+ FILEVIEW_COLUMN_NAME, path_arr[level], -1);
+ }
+ else if (patterns_match(source_patterns, path_arr[level]))
+ {
+ gtk_tree_store_set(s_file_store, &iter,
+ FILEVIEW_COLUMN_ICON, "gproject-source",
+ FILEVIEW_COLUMN_NAME, path_arr[level], -1);
+ }
+ else
+ {
+ gtk_tree_store_set(s_file_store, &iter,
+ FILEVIEW_COLUMN_ICON, "gproject-file",
+ FILEVIEW_COLUMN_NAME, path_arr[level], -1);
+ }
+ }
+
+ g_slist_free(file_list);
+}
+
+
+static void load_project()
+{
+ GSList *lst = NULL;
+ GSList *path_list = NULL;
+ GSList *elem, *header_patterns, *source_patterns;
+ GtkTreeModel *model;
+
+ gtk_tree_store_clear(s_file_store);
+
+ if (!g_prj || !geany_data->app->project)
+ return;
+
+ header_patterns = get_precompiled_patterns(g_prj->header_patterns);
+ source_patterns = get_precompiled_patterns(g_prj->source_patterns);
+
+ g_hash_table_foreach(g_prj->file_tag_table, (GHFunc)build_file_list, &lst);
+ lst = g_slist_sort(lst, (GCompareFunc) strcmp);
+
+ for (elem = lst; elem != NULL; elem = g_slist_next(elem))
+ {
+ gchar **path_split;
+
+ path_split = g_strsplit_set(elem->data, "/\\", 0);
+ path_list = g_slist_prepend(path_list, path_split);
+ }
+
+ model = GTK_TREE_MODEL(s_file_store);
+
+ if (path_list != NULL)
+ create_branch(0, path_list, NULL, header_patterns, source_patterns);
+ else
+ {
+ GtkTreeIter iter;
+
+ gtk_tree_store_append(s_file_store, &iter, NULL);
+ gtk_tree_store_set(s_file_store, &iter,
+ FILEVIEW_COLUMN_NAME, "Set file patterns under Project->Properties", -1);
+ }
+
+ g_slist_foreach(header_patterns, (GFunc) g_pattern_spec_free, NULL);
+ g_slist_free(header_patterns);
+ g_slist_foreach(source_patterns, (GFunc) g_pattern_spec_free, NULL);
+ g_slist_free(source_patterns);
+
+ g_slist_foreach(lst, (GFunc) g_free, NULL);
+ g_slist_free(lst);
+ g_slist_foreach(path_list, (GFunc) g_strfreev, NULL);
+ g_slist_free(path_list);
+}
+
+
+static gboolean find_in_tree(GtkTreeIter *parent, gchar **path_split, gint level, GtkTreeIter *ret)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean iterate;
+
+ model = GTK_TREE_MODEL(s_file_store);
+
+ iterate = gtk_tree_model_iter_children(model, &iter, parent);
+ while (iterate)
+ {
+ gchar *name;
+
+ gtk_tree_model_get(model, &iter, FILEVIEW_COLUMN_NAME, &name, -1);
+
+ if (g_strcmp0(name, path_split[level]) == 0)
+ {
+ GtkTreeIter foo;
+ if (path_split[level+1] == NULL && !gtk_tree_model_iter_children (model, &foo, &iter))
+ {
+ *ret = iter;
+ return TRUE;
+ }
+ else
+ return find_in_tree(&iter, path_split, level + 1, ret);
+ }
+
+ iterate = gtk_tree_model_iter_next(model, &iter);
+ }
+
+ return FALSE;
+}
+
+
+static void follow_editor()
+{
+ GtkTreeIter found_iter;
+ gchar *path;
+ gchar **path_split;
+ GeanyDocument *doc;
+
+ doc = document_get_current();
+
+ if (!doc || !doc->file_name || !geany_data->app->project)
+ return;
+
+ path = get_file_relative_path(geany_data->app->project->base_path, doc->file_name);
+
+ if (!path)
+ return;
+
+ path_split = g_strsplit_set(path, "/\\", 0);
+
+ if (find_in_tree(NULL, path_split, 0, &found_iter))
+ {
+ GtkTreePath *tree_path;
+ GtkTreeModel *model;
+
+ model = GTK_TREE_MODEL(s_file_store);
+ tree_path = gtk_tree_model_get_path (model, &found_iter);
+
+ gtk_tree_view_expand_to_path(GTK_TREE_VIEW(s_file_view), tree_path);
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(s_file_view), tree_path,
+ NULL, FALSE, 0.0, 0.0);
+ gtk_tree_view_set_cursor(GTK_TREE_VIEW(s_file_view), tree_path, NULL, FALSE);
+ }
+}
+
+
+void gprj_sidebar_update(gboolean reload)
+{
+ if (reload)
+ load_project();
+ if (s_follow_editor)
+ follow_editor();
+}
+
+
+void gprj_sidebar_find_file_in_active()
+{
+ find_file(NULL);
+}
+
+
+static gboolean on_button_release(G_GNUC_UNUSED GtkWidget * widget, GdkEventButton * event,
+ G_GNUC_UNUSED gpointer user_data)
+{
+// GeanyDocument *doc;
+
+ if (event->button == 3)
+ {
+ GtkTreeSelection *treesel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(s_file_view));
+
+ if (!gtk_tree_selection_get_selected(treesel, &model, &iter))
+ return FALSE;
+
+
+ gtk_widget_set_sensitive(s_popup_menu.expand, gtk_tree_model_iter_has_child(model, &iter));
+
+ gtk_menu_popup(GTK_MENU(s_popup_menu.widget), NULL, NULL, NULL, NULL,
+ event->button, event->time);
+ }
+
+// doc = document_get_current();
+// if (doc)
+// gtk_widget_grab_focus(GTK_WIDGET(doc->editor->sci));
+
+ return FALSE;
+}
+
+
+void gprj_sidebar_init()
+{
+ GtkWidget *scrollwin, *toolbar, *item, *image;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *sel;
+ PangoFontDescription *pfd;
+
+ s_file_view_vbox = gtk_vbox_new(FALSE, 0);
+
+ // *** toolbar ***
+
+ toolbar = gtk_toolbar_new();
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(toolbar), GTK_ICON_SIZE_MENU);
+ gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
+
+ item = GTK_WIDGET(gtk_tool_button_new(NULL, NULL));
+ gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON(item), "gproject-refresh");
+ ui_widget_set_tooltip_text(item, _("Reload all"));
+ g_signal_connect(item, "clicked", G_CALLBACK(on_reload_project), NULL);
+ gtk_container_add(GTK_CONTAINER(toolbar), item);
+
+ item = GTK_WIDGET(gtk_separator_tool_item_new());
+ gtk_container_add(GTK_CONTAINER(toolbar), item);
+
+ item = GTK_WIDGET(gtk_tool_button_new(NULL, NULL));
+ gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON(item), "gproject-expand");
+ ui_widget_set_tooltip_text(item, _("Expand all"));
+ g_signal_connect(item, "clicked", G_CALLBACK(on_expand_all), NULL);
+ gtk_container_add(GTK_CONTAINER(toolbar), item);
+
+ item = GTK_WIDGET(gtk_tool_button_new(NULL, NULL));
+ gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON(item), "gproject-collapse");
+ ui_widget_set_tooltip_text(item, _("Collapse all"));
+ g_signal_connect(item, "clicked", G_CALLBACK(on_collapse_all), NULL);
+ gtk_container_add(GTK_CONTAINER(toolbar), item);
+
+ item = GTK_WIDGET(gtk_separator_tool_item_new());
+ gtk_container_add(GTK_CONTAINER(toolbar), item);
+
+ item = GTK_WIDGET(gtk_toggle_tool_button_new());
+ gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON(item), "gproject-follow");
+ ui_widget_set_tooltip_text(item, _("Follow active editor"));
+ g_signal_connect(item, "clicked", G_CALLBACK(on_follow_active), NULL);
+ gtk_container_add(GTK_CONTAINER(toolbar), item);
+
+ gtk_box_pack_start(GTK_BOX(s_file_view_vbox), toolbar, FALSE, FALSE, 0);
+
+ // *** tree view ***
+
+ s_file_view = gtk_tree_view_new();
+
+ s_file_store = gtk_tree_store_new(FILEVIEW_N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(s_file_view), GTK_TREE_MODEL(s_file_store));
+
+ renderer = gtk_cell_renderer_pixbuf_new();
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer, "icon-name", FILEVIEW_COLUMN_ICON, NULL);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes(column, renderer, "text", FILEVIEW_COLUMN_NAME, NULL);
+
+ gtk_tree_view_append_column(GTK_TREE_VIEW(s_file_view), column);
+
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(s_file_view), FALSE);
+ gtk_tree_view_set_enable_search(GTK_TREE_VIEW(s_file_view), TRUE);
+ gtk_tree_view_set_search_column(GTK_TREE_VIEW(s_file_view), FILEVIEW_COLUMN_NAME);
+
+ pfd = pango_font_description_from_string(geany_data->interface_prefs->tagbar_font);
+ gtk_widget_modify_font(s_file_view, pfd);
+ pango_font_description_free(pfd);
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(s_file_view));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+
+ g_signal_connect(G_OBJECT(s_file_view), "button-release-event",
+ G_CALLBACK(on_button_release), NULL);
+ g_signal_connect(G_OBJECT(s_file_view), "button-press-event",
+ G_CALLBACK(on_button_press), NULL);
+ g_signal_connect(G_OBJECT(s_file_view), "key-press-event",
+ G_CALLBACK(on_key_press), NULL);
+
+ // *** popup menu ***
+
+ s_popup_menu.widget = gtk_menu_new();
+
+ image = gtk_image_new_from_icon_name("gproject-expand", GTK_ICON_SIZE_MENU);
+ gtk_widget_show(image);
+ item = gtk_image_menu_item_new_with_mnemonic(_("Expand All"));
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect((gpointer) item, "activate", G_CALLBACK(expand_all), NULL);
+ s_popup_menu.expand = item;
+
+ image = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
+ gtk_widget_show(image);
+ item = gtk_image_menu_item_new_with_mnemonic(_("Find in Files"));
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_find_in_files), NULL);
+ s_popup_menu.find_in_directory = item;
+
+ image = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
+ gtk_widget_show(image);
+ item = gtk_image_menu_item_new_with_mnemonic(_("Find File"));
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_find_file), NULL);
+ s_popup_menu.find_file = item;
+
+ item = gtk_separator_menu_item_new();
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+
+ item = gtk_image_menu_item_new_with_mnemonic(_("H_ide Sidebar"));
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
+ gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU));
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect_swapped((gpointer) item, "activate",
+ G_CALLBACK(keybindings_send_command),
+ GINT_TO_POINTER(GEANY_KEYS_VIEW_SIDEBAR));
+
+ // *** the rest ***
+
+ scrollwin = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(scrollwin), s_file_view);
+ gtk_container_add(GTK_CONTAINER(s_file_view_vbox), scrollwin);
+
+ gtk_widget_show_all(s_file_view_vbox);
+ gtk_notebook_append_page(GTK_NOTEBOOK(geany->main_widgets->sidebar_notebook),
+ s_file_view_vbox, gtk_label_new(_("Project")));
+}
+
+
+void gprj_sidebar_activate(gboolean activate)
+{
+ gtk_widget_set_sensitive(s_file_view_vbox, activate);
+}
+
+
+void gprj_sidebar_cleanup()
+{
+ gtk_widget_destroy(s_file_view_vbox);
+}
Added: trunk/geany-plugins/gproject/src/gproject-sidebar.h
===================================================================
--- trunk/geany-plugins/gproject/src/gproject-sidebar.h (rev 0)
+++ trunk/geany-plugins/gproject/src/gproject-sidebar.h 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2010 Jiri Techet <techet(a)gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef __GPROJECT_SIDEBAR_H__
+#define __GPROJECT_SIDEBAR_H__
+
+
+void gprj_sidebar_init(void);
+void gprj_sidebar_cleanup(void);
+void gprj_sidebar_activate(gboolean activate);
+
+void gprj_sidebar_find_file_in_active(void);
+
+void gprj_sidebar_update(gboolean reload);
+
+
+
+#endif
Added: trunk/geany-plugins/gproject/src/gproject-utils.c
===================================================================
--- trunk/geany-plugins/gproject/src/gproject-utils.c (rev 0)
+++ trunk/geany-plugins/gproject/src/gproject-utils.c 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2010 Jiri Techet <techet(a)gmail.com>
+ *
+ * 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.
+ */
+
+#include <string.h>
+#include <glib.h>
+
+#include <geany/geanyplugin.h>
+
+#include "gproject-utils.h"
+
+extern GeanyData *geany_data;
+extern GeanyFunctions *geany_functions;
+
+static gchar *relpath(const gchar *origin_dir, const gchar *dest_dir)
+{
+ gchar *origin, *dest;
+ gchar **originv, **destv;
+ gchar *ret = NULL;
+ guint i, j;
+
+ origin = tm_get_real_path(origin_dir);
+ dest = tm_get_real_path(dest_dir);
+
+ if (!NZV(origin) || !NZV(dest) || origin[0] != dest[0])
+ {
+ g_free(origin);
+ g_free(dest);
+ return NULL;
+ }
+
+ originv = g_strsplit_set(g_path_skip_root(origin), "/\\", -1);
+ destv = g_strsplit_set(g_path_skip_root(dest), "/\\", -1);
+
+ for (i = 0; originv[i] != NULL && destv[i] != NULL; i++)
+ if (g_strcmp0(originv[i], destv[i]) != 0)
+ break;
+
+ ret = g_strdup("");
+
+ for (j = i; originv[j] != NULL; j++)
+ setptr(ret, g_build_filename(ret, "..", NULL));
+
+ for (j = i; destv[j] != NULL; j++)
+ setptr(ret, g_build_filename(ret, destv[j], NULL));
+
+ if (strlen(ret) == 0)
+ setptr(ret, g_strdup("./"));
+
+ g_free(origin);
+ g_free(dest);
+ g_strfreev(originv);
+ g_strfreev(destv);
+
+ return ret;
+}
+
+
+gchar *get_file_relative_path(const gchar *origin_dir, const gchar *dest_file)
+{
+ gchar *dest_dir, *ret;
+
+ dest_dir = g_path_get_dirname(dest_file);
+ ret = relpath(origin_dir, dest_dir);
+ if (ret)
+ {
+ gchar *dest_basename;
+
+ dest_basename = g_path_get_basename(dest_file);
+
+ if (g_strcmp0(ret, "./") != 0)
+ {
+ setptr(ret, g_build_filename(ret, dest_basename, NULL));
+ }
+ else
+ {
+ setptr(ret, g_strdup(dest_basename));
+ }
+
+ g_free(dest_basename);
+ }
+
+ g_free(dest_dir);
+ return ret;
+}
+
+
+GSList *get_precompiled_patterns(gchar **patterns)
+{
+ guint i;
+ GSList *pattern_list = NULL;
+
+ if (!patterns)
+ return NULL;
+
+ for (i = 0; patterns[i] != NULL; i++)
+ {
+ GPatternSpec *pattern_spec = g_pattern_spec_new(patterns[i]);
+ pattern_list = g_slist_prepend(pattern_list, pattern_spec);
+ }
+ return pattern_list;
+}
+
+
+gboolean patterns_match(GSList *patterns, const gchar *str)
+{
+ GSList *elem;
+ for (elem = patterns; elem != NULL; elem = g_slist_next(elem))
+ {
+ GPatternSpec *pattern = elem->data;
+ if (g_pattern_match_string(pattern, str))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+void open_file(gchar *utf8_name)
+{
+ gchar *name;
+ GeanyDocument *doc;
+
+ name = utils_get_locale_from_utf8(utf8_name);
+ doc = document_find_by_filename(utf8_name);
+
+ if (!doc)
+ doc = document_open_file(name, FALSE, NULL, NULL);
+ else
+ {
+ GtkNotebook *notebook;
+ gint page_num;
+
+ notebook = GTK_NOTEBOOK(geany->main_widgets->notebook);
+ page_num = gtk_notebook_page_num(notebook, GTK_WIDGET(doc->editor->sci));
+
+ gtk_notebook_set_current_page(notebook, page_num);
+ }
+
+// if (doc)
+// gtk_widget_grab_focus(GTK_WIDGET(doc->editor->sci));
+
+ g_free(name);
+}
+
+
+gchar *get_selection(void)
+{
+ GeanyDocument *doc = document_get_current();
+ const gchar *wc;
+
+#ifdef G_OS_WIN32
+ wc = GEANY_WORDCHARS "./-" "\\";
+#else
+ wc = GEANY_WORDCHARS "./-";
+#endif
+
+ if (!doc)
+ return NULL;
+
+ if (sci_has_selection(doc->editor->sci))
+ return sci_get_selection_contents(doc->editor->sci);
+ else
+ return editor_get_word_at_pos(doc->editor, -1, wc);
+}
Added: trunk/geany-plugins/gproject/src/gproject-utils.h
===================================================================
--- trunk/geany-plugins/gproject/src/gproject-utils.h (rev 0)
+++ trunk/geany-plugins/gproject/src/gproject-utils.h 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2010 Jiri Techet <techet(a)gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef __GPROJECT_UTILS_H__
+#define __GPROJECT_UTILS_H__
+
+gchar *get_file_relative_path(const gchar *origin_dir, const gchar *dest_file);
+
+gboolean patterns_match(GSList *patterns, const gchar *str);
+GSList *get_precompiled_patterns(gchar **patterns);
+
+void open_file(gchar *utf8_name);
+gchar *get_selection(void);
+
+#endif
Added: trunk/geany-plugins/gproject/wscript_build
===================================================================
--- trunk/geany-plugins/gproject/wscript_build (rev 0)
+++ trunk/geany-plugins/gproject/wscript_build 2011-05-24 22:22:30 UTC (rev 2087)
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+#
+# WAF build script for geany-plugins - GProject
+#
+# Copyright 2010 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
+#
+# 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.
+#
+# $Id$
+
+from build.wafutils import build_plugin
+
+
+name = 'GProject'
+includes = ['gproject/src']
+
+build_plugin(bld, name, includes=includes)
Added: trunk/geany-plugins/gproject/wscript_configure
===================================================================
Modified: trunk/geany-plugins/po/POTFILES.in
===================================================================
--- trunk/geany-plugins/po/POTFILES.in 2011-05-24 21:55:01 UTC (rev 2086)
+++ trunk/geany-plugins/po/POTFILES.in 2011-05-24 22:22:30 UTC (rev 2087)
@@ -153,6 +153,12 @@
geanyvc/src/vc_svn.c
geanyvc/src/utils.c
+# GProject
+gproject/src/gproject-main.c
+gproject/src/gproject-menu.c
+gproject/src/gproject-project.c
+gproject/src/gproject-sidebar.c
+
# Pretty-printer
pretty-printer/src/PluginEntry.c
pretty-printer/src/PrettyPrinter.c
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 2084
http://geany-plugins.svn.sourceforge.net/geany-plugins/?rev=2084&view=rev
Author: codebrainz
Date: 2011-05-24 15:37:38 +0000 (Tue, 24 May 2011)
Log Message:
-----------
Add header file to POTFILES.in (oops).
Modified Paths:
--------------
trunk/geany-plugins/po/POTFILES.in
Modified: trunk/geany-plugins/po/POTFILES.in
===================================================================
--- trunk/geany-plugins/po/POTFILES.in 2011-05-24 14:47:52 UTC (rev 2083)
+++ trunk/geany-plugins/po/POTFILES.in 2011-05-24 15:37:38 UTC (rev 2084)
@@ -37,6 +37,7 @@
debugger/src/watch_model.c
# devhelp
+devhelp/src/dhp.h
devhelp/src/dhp-object.c
devhelp/src/dhp-plugin.c
devhelp/src/dhp-codesearch.c
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.