SF.net SVN: geany-plugins:[2087] trunk/geany-plugins

techet at users.sourceforge.net techet at xxxxx
Tue May 24 22:22:31 UTC 2011


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 at 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-plugins
+
+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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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.



More information about the Plugins-Commits mailing list