diff --git a/Makefile.am b/Makefile.am
index 2d8c87d51..1baff0ebf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -172,6 +172,10 @@ if ENABLE_WEBHELPER
SUBDIRS += webhelper
endif
+if ENABLE_WORKBENCH
+SUBDIRS += workbench
+endif
+
if ENABLE_XMLSNIPPETS
SUBDIRS += xmlsnippets
endif
diff --git a/build/workbench.m4 b/build/workbench.m4
new file mode 100644
index 000000000..2e883a9b1
--- /dev/null
+++ b/build/workbench.m4
@@ -0,0 +1,15 @@
+AC_DEFUN([GP_CHECK_WORKBENCH],
+[
+ GP_ARG_DISABLE([Workbench], [auto])
+ GP_COMMIT_PLUGIN_STATUS([Workbench])
+ AC_CONFIG_FILES([
+ workbench/Makefile
+ workbench/src/Makefile
+ workbench/icons/Makefile
+ workbench/icons/16x16/Makefile
+ workbench/icons/24x24/Makefile
+ workbench/icons/32x32/Makefile
+ workbench/icons/48x48/Makefile
+ workbench/icons/scalable/Makefile
+ ])
+])
diff --git a/configure.ac b/configure.ac
index f03726dcc..48c693250 100644
--- a/configure.ac
+++ b/configure.ac
@@ -71,6 +71,7 @@ GP_CHECK_TREEBROWSER
GP_CHECK_TABLECONVERT
GP_CHECK_UPDATECHECKER
GP_CHECK_WEBHELPER
+GP_CHECK_WORKBENCH
GP_CHECK_XMLSNIPPETS
AC_CONFIG_FILES([
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 05ed01d2a..e281d86b1 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -312,6 +312,17 @@ webhelper/src/gwh-utils.c
webhelper/src/gwh-browser.c
webhelper/src/gwh-plugin.c
+# workbench
+workbench/src/dialogs.c
+workbench/src/menu.c
+workbench/src/plugin_main.c
+workbench/src/popup_menu.c
+workbench/src/sidebar.c
+workbench/src/utils.c
+workbench/src/wb_globals.c
+workbench/src/wb_project.c
+workbench/src/workbench.c
+
# XMLSnippets
xmlsnippets/src/plugin.c
xmlsnippets/src/xmlsnippets.c
diff --git a/po/be.po b/po/be.po
index 5c0a4a503..681d893f2 100644
--- a/po/be.po
+++ b/po/be.po
@@ -3405,7 +3405,7 @@ msgstr ""
msgid "Toggle Insert/Overwrite mode"
msgstr ""
-#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:239
+#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:429
msgid "Tab"
msgstr ""
@@ -5564,7 +5564,7 @@ msgid ""
"installation."
msgstr ""
-#: ../pretty-printer/src/PluginEntry.c:36
+#: ../pretty-printer/src/PluginEntry.c:41
msgid "XML PrettyPrinter"
msgstr ""
@@ -5623,31 +5623,31 @@ msgstr ""
msgid "CDATA"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:205
+#: ../pretty-printer/src/ConfigUI.c:395
msgid "Empty nodes"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:206
+#: ../pretty-printer/src/ConfigUI.c:396
msgid "Concatenation ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:207
+#: ../pretty-printer/src/ConfigUI.c:397
msgid "Spacing ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:208
+#: ../pretty-printer/src/ConfigUI.c:398
msgid "Expansion ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:235
+#: ../pretty-printer/src/ConfigUI.c:425
msgid "Indentation"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:240
+#: ../pretty-printer/src/ConfigUI.c:430
msgid "Space"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:264
+#: ../pretty-printer/src/ConfigUI.c:454
msgid "Line break"
msgstr ""
@@ -5704,70 +5704,70 @@ msgstr ""
msgid "Open Selected File (Project Organizer)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:573
+#: ../projectorganizer/src/prjorg-project.c:578
msgid "Source patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:579
+#: ../projectorganizer/src/prjorg-project.c:584
msgid ""
"Space separated list of patterns that are used to identify source files. "
"Used for header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:585
+#: ../projectorganizer/src/prjorg-project.c:590
msgid "Header patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:591
+#: ../projectorganizer/src/prjorg-project.c:596
msgid ""
"Space separated list of patterns that are used to identify headers. Used for "
"header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:597
+#: ../projectorganizer/src/prjorg-project.c:602
msgid "Ignored file patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:603
+#: ../projectorganizer/src/prjorg-project.c:608
msgid ""
"Space separated list of patterns that are used to identify files that are "
"not displayed in the project tree."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:609
+#: ../projectorganizer/src/prjorg-project.c:614
msgid "Ignored directory patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:615
+#: ../projectorganizer/src/prjorg-project.c:620
msgid ""
"Space separated list of patterns that are used to identify directories that "
"are not scanned for source files."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:621
+#: ../projectorganizer/src/prjorg-project.c:626
#, fuzzy
msgid "Index all project files:"
msgstr "Знайсьці і праэкце"
-#: ../projectorganizer/src/prjorg-project.c:624
+#: ../projectorganizer/src/prjorg-project.c:629
msgid "Auto (index if less than 300 files)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:625
+#: ../projectorganizer/src/prjorg-project.c:630
msgid "Yes"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:626
+#: ../projectorganizer/src/prjorg-project.c:631
msgid "No"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:630
+#: ../projectorganizer/src/prjorg-project.c:635
msgid ""
"Generate symbol list for all project files instead of only for the currently "
"opened files. Might be slow for big projects."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:636
+#: ../projectorganizer/src/prjorg-project.c:641
msgid ""
"Note: the patterns above affect only the sidebar and are not used in the "
"Find in Files\n"
diff --git a/po/ca.po b/po/ca.po
index 3dc78382b..a883dbb67 100644
--- a/po/ca.po
+++ b/po/ca.po
@@ -3344,7 +3344,7 @@ msgstr "Cancel·la la la Selecció"
msgid "Toggle Insert/Overwrite mode"
msgstr "Commuta entre el mode d'Inserció i el mode de Sobreescriptura"
-#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:239
+#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:429
msgid "Tab"
msgstr "Tabulador"
@@ -5517,7 +5517,7 @@ msgid ""
"installation."
msgstr ""
-#: ../pretty-printer/src/PluginEntry.c:36
+#: ../pretty-printer/src/PluginEntry.c:41
msgid "XML PrettyPrinter"
msgstr ""
@@ -5576,31 +5576,31 @@ msgstr ""
msgid "CDATA"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:205
+#: ../pretty-printer/src/ConfigUI.c:395
msgid "Empty nodes"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:206
+#: ../pretty-printer/src/ConfigUI.c:396
msgid "Concatenation ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:207
+#: ../pretty-printer/src/ConfigUI.c:397
msgid "Spacing ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:208
+#: ../pretty-printer/src/ConfigUI.c:398
msgid "Expansion ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:235
+#: ../pretty-printer/src/ConfigUI.c:425
msgid "Indentation"
msgstr "Sagnat"
-#: ../pretty-printer/src/ConfigUI.c:240
+#: ../pretty-printer/src/ConfigUI.c:430
msgid "Space"
msgstr "Espai"
-#: ../pretty-printer/src/ConfigUI.c:264
+#: ../pretty-printer/src/ConfigUI.c:454
msgid "Line break"
msgstr "Salt de línia"
@@ -5655,74 +5655,74 @@ msgstr ""
msgid "Open Selected File (Project Organizer)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:573
+#: ../projectorganizer/src/prjorg-project.c:578
msgid "Source patterns:"
msgstr "Patrons de fitxers de codi font:"
-#: ../projectorganizer/src/prjorg-project.c:579
+#: ../projectorganizer/src/prjorg-project.c:584
msgid ""
"Space separated list of patterns that are used to identify source files. "
"Used for header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:585
+#: ../projectorganizer/src/prjorg-project.c:590
msgid "Header patterns:"
msgstr "Patrons de fitxers de capçalera:"
-#: ../projectorganizer/src/prjorg-project.c:591
+#: ../projectorganizer/src/prjorg-project.c:596
msgid ""
"Space separated list of patterns that are used to identify headers. Used for "
"header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:597
+#: ../projectorganizer/src/prjorg-project.c:602
#, fuzzy
msgid "Ignored file patterns:"
msgstr "Patrons de fitxers de codi font:"
-#: ../projectorganizer/src/prjorg-project.c:603
+#: ../projectorganizer/src/prjorg-project.c:608
msgid ""
"Space separated list of patterns that are used to identify files that are "
"not displayed in the project tree."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:609
+#: ../projectorganizer/src/prjorg-project.c:614
#, fuzzy
msgid "Ignored directory patterns:"
msgstr "Patrons de fitxers de capçalera:"
-#: ../projectorganizer/src/prjorg-project.c:615
+#: ../projectorganizer/src/prjorg-project.c:620
msgid ""
"Space separated list of patterns that are used to identify directories that "
"are not scanned for source files."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:621
+#: ../projectorganizer/src/prjorg-project.c:626
#, fuzzy
msgid "Index all project files:"
msgstr "Cerca en els fitxers del projecte"
-#: ../projectorganizer/src/prjorg-project.c:624
+#: ../projectorganizer/src/prjorg-project.c:629
msgid "Auto (index if less than 300 files)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:625
+#: ../projectorganizer/src/prjorg-project.c:630
#, fuzzy
msgid "Yes"
msgstr "sí"
-#: ../projectorganizer/src/prjorg-project.c:626
+#: ../projectorganizer/src/prjorg-project.c:631
#, fuzzy
msgid "No"
msgstr "No."
-#: ../projectorganizer/src/prjorg-project.c:630
+#: ../projectorganizer/src/prjorg-project.c:635
msgid ""
"Generate symbol list for all project files instead of only for the currently "
"opened files. Might be slow for big projects."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:636
+#: ../projectorganizer/src/prjorg-project.c:641
msgid ""
"Note: the patterns above affect only the sidebar and are not used in the "
"Find in Files\n"
diff --git a/po/da.po b/po/da.po
index 5c7976447..b55a22816 100644
--- a/po/da.po
+++ b/po/da.po
@@ -3361,7 +3361,7 @@ msgstr ""
msgid "Toggle Insert/Overwrite mode"
msgstr ""
-#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:239
+#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:429
msgid "Tab"
msgstr ""
@@ -5490,7 +5490,7 @@ msgid ""
"installation."
msgstr ""
-#: ../pretty-printer/src/PluginEntry.c:36
+#: ../pretty-printer/src/PluginEntry.c:41
msgid "XML PrettyPrinter"
msgstr ""
@@ -5549,31 +5549,31 @@ msgstr ""
msgid "CDATA"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:205
+#: ../pretty-printer/src/ConfigUI.c:395
msgid "Empty nodes"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:206
+#: ../pretty-printer/src/ConfigUI.c:396
msgid "Concatenation ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:207
+#: ../pretty-printer/src/ConfigUI.c:397
msgid "Spacing ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:208
+#: ../pretty-printer/src/ConfigUI.c:398
msgid "Expansion ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:235
+#: ../pretty-printer/src/ConfigUI.c:425
msgid "Indentation"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:240
+#: ../pretty-printer/src/ConfigUI.c:430
msgid "Space"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:264
+#: ../pretty-printer/src/ConfigUI.c:454
msgid "Line break"
msgstr ""
@@ -5623,69 +5623,69 @@ msgstr ""
msgid "Open Selected File (Project Organizer)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:573
+#: ../projectorganizer/src/prjorg-project.c:578
msgid "Source patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:579
+#: ../projectorganizer/src/prjorg-project.c:584
msgid ""
"Space separated list of patterns that are used to identify source files. "
"Used for header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:585
+#: ../projectorganizer/src/prjorg-project.c:590
msgid "Header patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:591
+#: ../projectorganizer/src/prjorg-project.c:596
msgid ""
"Space separated list of patterns that are used to identify headers. Used for "
"header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:597
+#: ../projectorganizer/src/prjorg-project.c:602
msgid "Ignored file patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:603
+#: ../projectorganizer/src/prjorg-project.c:608
msgid ""
"Space separated list of patterns that are used to identify files that are "
"not displayed in the project tree."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:609
+#: ../projectorganizer/src/prjorg-project.c:614
msgid "Ignored directory patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:615
+#: ../projectorganizer/src/prjorg-project.c:620
msgid ""
"Space separated list of patterns that are used to identify directories that "
"are not scanned for source files."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:621
+#: ../projectorganizer/src/prjorg-project.c:626
msgid "Index all project files:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:624
+#: ../projectorganizer/src/prjorg-project.c:629
msgid "Auto (index if less than 300 files)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:625
+#: ../projectorganizer/src/prjorg-project.c:630
msgid "Yes"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:626
+#: ../projectorganizer/src/prjorg-project.c:631
msgid "No"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:630
+#: ../projectorganizer/src/prjorg-project.c:635
msgid ""
"Generate symbol list for all project files instead of only for the currently "
"opened files. Might be slow for big projects."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:636
+#: ../projectorganizer/src/prjorg-project.c:641
msgid ""
"Note: the patterns above affect only the sidebar and are not used in the "
"Find in Files\n"
diff --git a/po/de.po b/po/de.po
index faa20d376..85723dba6 100644
--- a/po/de.po
+++ b/po/de.po
@@ -16,7 +16,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Geany-Plugins 1.30\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-06-24 16:57+0200\n"
+"POT-Creation-Date: 2017-08-20 13:13+0200\n"
"PO-Revision-Date: 2016-08-08 22:38+0200\n"
"Last-Translator: Dominic Hopf \n"
"Language-Team: German \n"
@@ -3828,7 +3828,10 @@ msgstr "_OK"
#: ../geanymacro/src/geanymacro.c:1394 ../geanymacro/src/geanymacro.c:1537
#: ../geanymacro/src/geanymacro.c:1827
-#: ../projectorganizer/src/prjorg-sidebar.c:306
+#: ../projectorganizer/src/prjorg-sidebar.c:306 ../workbench/src/dialogs.c:45
+#: ../workbench/src/dialogs.c:76 ../workbench/src/dialogs.c:115
+#: ../workbench/src/dialogs.c:152 ../workbench/src/dialogs.c:221
+#: ../workbench/src/dialogs.c:350
msgid "_Cancel"
msgstr "_Abbrechen"
@@ -4523,7 +4526,7 @@ msgstr "Projekteigenschaften"
msgid "New Project"
msgstr "Neues Projekt"
-#: ../geanyprj/src/menu.c:110
+#: ../geanyprj/src/menu.c:110 ../workbench/src/dialogs.c:46
msgid "C_reate"
msgstr "_Erstellen"
@@ -5425,7 +5428,8 @@ msgstr "Datei als HTML speichern"
msgid "HTML Files"
msgstr "HTML-Dateien"
-#: ../markdown/src/plugin.c:311
+#: ../markdown/src/plugin.c:311 ../workbench/src/dialogs.c:84
+#: ../workbench/src/dialogs.c:123
msgid "All Files"
msgstr "Alle Dateien"
@@ -6034,7 +6038,7 @@ msgstr ""
"identifizieren. Wird hauptsächlich für das Umschalten zwischen Quellen und "
"Header verwendet."
-#: ../projectorganizer/src/prjorg-project.c:597
+#: ../projectorganizer/src/prjorg-project.c:597 ../workbench/src/dialogs.c:243
msgid "Ignored file patterns:"
msgstr "Muster für zu ignorierende Dateien:"
@@ -6047,7 +6051,7 @@ msgstr ""
"erkennen, die nicht im projektspezifischen Dateibaum angezeigt werden "
"sollen. "
-#: ../projectorganizer/src/prjorg-project.c:609
+#: ../projectorganizer/src/prjorg-project.c:609 ../workbench/src/dialogs.c:254
msgid "Ignored directory patterns:"
msgstr "Muster für zu ignorierende Verzeichnisse:"
@@ -6146,6 +6150,7 @@ msgid "Add external directory"
msgstr "Externes Verzeichnis hinzufügen"
#: ../projectorganizer/src/prjorg-sidebar.c:1374
+#: ../workbench/src/popup_menu.c:435
msgid "Expand all"
msgstr "Alle ausklappen"
@@ -7977,6 +7982,326 @@ msgstr "Browser"
msgid "Windows"
msgstr "Fenster"
+#: ../workbench/src/dialogs.c:43
+#, fuzzy
+msgid "Create new workbench"
+msgstr "Neue Werkbank anlegen"
+
+#: ../workbench/src/dialogs.c:74
+#, fuzzy
+msgid "Open workbench"
+msgstr "Öffne Werkbank"
+
+#. Create new menu item "Open Workbench"
+#: ../workbench/src/dialogs.c:77 ../workbench/src/menu.c:188
+#, fuzzy
+msgid "_Open"
+msgstr "Öffnen"
+
+#: ../workbench/src/dialogs.c:80
+msgid "Workbench files (.geanywb)"
+msgstr "Werkbank Dateien (.geanywb)"
+
+#: ../workbench/src/dialogs.c:113 ../workbench/src/popup_menu.c:373
+#, fuzzy
+msgid "Add project"
+msgstr "Projekt hinzufügen"
+
+#: ../workbench/src/dialogs.c:116 ../workbench/src/dialogs.c:153
+#, fuzzy
+msgid "_Add"
+msgstr "_Beides hinzufügen"
+
+#: ../workbench/src/dialogs.c:119
+#, fuzzy
+msgid "Project files (.geany)"
+msgstr "Projektdateien (.geany)"
+
+#: ../workbench/src/dialogs.c:150 ../workbench/src/popup_menu.c:401
+#, fuzzy
+msgid "Add directory"
+msgstr "Verzeichnis hinzufügen"
+
+#: ../workbench/src/dialogs.c:218 ../workbench/src/popup_menu.c:419
+#, fuzzy
+msgid "Directory settings"
+msgstr "Verzeichnis Einstellungen"
+
+#: ../workbench/src/dialogs.c:222 ../workbench/src/dialogs.c:351
+#, fuzzy
+msgid "_OK"
+msgstr "OK"
+
+#: ../workbench/src/dialogs.c:232
+#, fuzzy
+msgid "File patterns:"
+msgstr "Dateimuster:"
+
+#: ../workbench/src/dialogs.c:238
+#, fuzzy
+msgid ""
+"Space separated list of patterns that are used to identify files that shall "
+"be displayed in the directory tree."
+msgstr ""
+"Leerzeichengetrennte Liste mit Mustern, die genutzt werden, um Dateien zu "
+"erkennen, die im Verzeichnisbaum angezeigt werden sollen."
+
+#: ../workbench/src/dialogs.c:249
+#, fuzzy
+msgid ""
+"Space separated list of patterns that are used to identify files that shall "
+"not be displayed in the directory tree."
+msgstr ""
+"Leerzeichengetrennte Liste mit Mustern, die genutzt werden, um Dateien zu "
+"erkennen, die im Verzeichnisbaum nicht angezeigt werden sollen."
+
+#: ../workbench/src/dialogs.c:260
+#, fuzzy
+msgid ""
+"Space separated list of patterns that are used to identify directories that "
+"shall not be scanned for source files."
+msgstr ""
+"Leerzeichengetrennte Liste mit Mustern, die genutzt werden, um Verzeichnisse "
+"zu erkennen, die nicht nach Quelldateien durchsucht werden sollen. "
+
+#: ../workbench/src/dialogs.c:268
+msgid ""
+"Note: the patterns above affect only the workbench directory and are not "
+"used in the Find in Files\n"
+"dialog."
+msgstr ""
+"Anmerkung: die Muser oben beeinflußen nur das Werkbank-Verzeichnis und "
+"werden nicht in In Dateien suchen verwendet."
+
+#: ../workbench/src/dialogs.c:347
+msgid "Workbench settings"
+msgstr "Werkbank Einstellungen"
+
+#: ../workbench/src/dialogs.c:361
+#, fuzzy
+msgid "Rescan all projects on open:"
+msgstr "Beim Öffnen alle Projekte durchsuchen:"
+
+#: ../workbench/src/dialogs.c:366
+msgid ""
+"If the option is activated (default), then all projects will be re-scanned "
+"on opening of the workbench."
+msgstr ""
+"Wenn die Option aktiviert ist (Standard), dann werden beim Öffnen einer "
+"Werkbank alle Projekte durchsucht."
+
+#: ../workbench/src/menu.c:95
+#, fuzzy, c-format
+msgid "Could not create new workbench file: %s"
+msgstr "Konnte die Werkbank Datei nicht anlegen: %s"
+
+#: ../workbench/src/menu.c:122
+#, fuzzy, c-format
+msgid "Could not open workbench file: %s"
+msgstr "Konnte die Werkbank Datei nicht öffnen: %s"
+
+#: ../workbench/src/menu.c:139
+#, fuzzy, c-format
+msgid "Could not save workbench file: %s"
+msgstr "Konnte die Werkbank Datei nicht speichern: %s"
+
+#. Set metadata
+#: ../workbench/src/menu.c:177 ../workbench/src/plugin_main.c:129
+#: ../workbench/src/sidebar.c:992
+msgid "Workbench"
+msgstr "Werkbank"
+
+#. Create new menu item "New Workbench"
+#: ../workbench/src/menu.c:181
+msgid "_New"
+msgstr ""
+
+#. Create new menu item "Save Workbench"
+#: ../workbench/src/menu.c:195
+#, fuzzy
+msgid "_Save"
+msgstr "Datei speichern"
+
+#. Create new menu item "Workbench Settings"
+#: ../workbench/src/menu.c:202
+#, fuzzy
+msgid "S_ettings"
+msgstr "Einstellungen"
+
+#. Create new menu item "Close Workbench"
+#: ../workbench/src/menu.c:209
+#, fuzzy
+msgid "_Close"
+msgstr "Schließen"
+
+#: ../workbench/src/plugin_main.c:93 ../workbench/src/sidebar.c:564
+msgid ""
+"Create or open a workbench\n"
+"using the workbench menu."
+msgstr ""
+"Erstellen oder Öffen Sie eine Werkbank\n"
+"mit dem Werkbank Menü."
+
+#: ../workbench/src/plugin_main.c:130
+msgid "Manage and customize multiple projects."
+msgstr "Verwalten und anpassen mehrere Projekte."
+
+#: ../workbench/src/popup_menu.c:171
+#, fuzzy, c-format
+msgid "Could not add project file: %s"
+msgstr "Konnte die Projektdatei nicht hinzufügen: %s"
+
+#: ../workbench/src/popup_menu.c:379
+#, fuzzy
+msgid "Save project"
+msgstr "Projekt speichern"
+
+#: ../workbench/src/popup_menu.c:385
+#, fuzzy
+msgid "Remove project"
+msgstr "Projekt entfernen"
+
+#: ../workbench/src/popup_menu.c:391
+#, fuzzy
+msgid "Fold/unfold project"
+msgstr "Projekt falten/ausklappen"
+
+#: ../workbench/src/popup_menu.c:407
+#, fuzzy
+msgid "Remove directory"
+msgstr "Verzeichnis entfernen"
+
+#: ../workbench/src/popup_menu.c:413
+#, fuzzy
+msgid "Rescan directory"
+msgstr "Verzeichnis durchsuchen"
+
+#: ../workbench/src/popup_menu.c:425
+#, fuzzy
+msgid "Fold/unfold directory"
+msgstr "Verzeichnis falten/ausklappen"
+
+#: ../workbench/src/popup_menu.c:441
+#, fuzzy
+msgid "Collapse all"
+msgstr "Alle e_inklappen"
+
+#: ../workbench/src/popup_menu.c:451
+msgid "Add to Workbench Bookmarks"
+msgstr "Zu Werkbank Lesezeichen hinzufügen"
+
+#: ../workbench/src/popup_menu.c:457
+msgid "Add to Project Bookmarks"
+msgstr "Zu Projekt Lesezeichen hinzufügen"
+
+#: ../workbench/src/popup_menu.c:463
+#, fuzzy
+msgid "Remove from Bookmarks"
+msgstr "Von _Lesezeichen entfernen"
+
+#: ../workbench/src/sidebar.c:300
+#, fuzzy
+msgid "No directories"
+msgstr "Keine Verzeichnisse"
+
+#. *** label ***
+#: ../workbench/src/sidebar.c:562 ../workbench/src/sidebar.c:943
+msgid "No workbench opened."
+msgstr "Keine Werkbank geöffnet."
+
+#: ../workbench/src/sidebar.c:574
+#, fuzzy
+msgid "Projects"
+msgstr "Projekte"
+
+#: ../workbench/src/sidebar.c:584
+msgid ""
+"Add a project\n"
+"using the context menu."
+msgstr ""
+"Fügen Sie ein Projekt\n"
+"über das Kontextmenü hinzu."
+
+#: ../workbench/src/sidebar.c:696
+#, c-format
+msgid ""
+"%s\n"
+"Project file not found!"
+msgstr ""
+"%s\n"
+"Projekt Datei nicht gefunden!"
+
+#: ../workbench/src/sidebar.c:710
+msgid ""
+"This project has no directories. Directories can be added to a project using "
+"the context menu."
+msgstr ""
+"Dieses Projekt hat keine Verzeichnisse. Verzeichnisse können über "
+"dasKontextmenü zum Projekt hinzugefügt werden."
+
+#: ../workbench/src/wb_project.c:997
+#, fuzzy, c-format
+msgid "Directory-Name: %s\n"
+msgstr "Verzeichnis-Name: %s\n"
+
+#: ../workbench/src/wb_project.c:998
+#, fuzzy, c-format
+msgid "Base-Directory: %s\n"
+msgstr "Basis-Verzeichnis: %s\n"
+
+#: ../workbench/src/wb_project.c:1000
+#, fuzzy
+msgid "File Patterns:"
+msgstr "Datei Muster:"
+
+#: ../workbench/src/wb_project.c:1012
+#, fuzzy
+msgid "Ignored Dir. Patterns:"
+msgstr "Muster für zu ignorierende Dateien:"
+
+#: ../workbench/src/wb_project.c:1024
+#, fuzzy
+msgid "Ignored File Patterns:"
+msgstr "Muster für zu ignorierende Dateien:"
+
+#: ../workbench/src/wb_project.c:1036
+#, c-format
+msgid "Number of Sub-Folders: %u\n"
+msgstr "Anzahl der Unter-Verzeichnisse: %u\n"
+
+#: ../workbench/src/wb_project.c:1037
+#, fuzzy, c-format
+msgid "Number of Files: %u\n"
+msgstr "Anzahl von Dateien: %u\n"
+
+#: ../workbench/src/wb_project.c:1061
+#, fuzzy, c-format
+msgid "Project: %s\n"
+msgstr "Projekt: %s\n"
+
+#: ../workbench/src/wb_project.c:1062
+#, fuzzy, c-format
+msgid "File: %s\n"
+msgstr "Datei: %s\n"
+
+#: ../workbench/src/wb_project.c:1063
+#, fuzzy, c-format
+msgid "Number of Directories: %u\n"
+msgstr "Anzahl der Verzeichnisse: %u\n"
+
+#: ../workbench/src/wb_project.c:1066
+msgid ""
+"\n"
+"The project contains unsafed changes!\n"
+msgstr ""
+"\n"
+"Das Projekt enthält nicht gesicherte Änderungen!\n"
+
+#: ../workbench/src/workbench.c:710
+#, fuzzy, c-format
+msgid "File %s is not a valid workbench file!"
+msgstr "Die Datei %s ist keine gültige Workbench-Datei!"
+
#: ../xmlsnippets/src/plugin.c:44
msgid "XML Snippets"
msgstr "XML-Schnipsel"
@@ -7985,6 +8310,14 @@ msgstr "XML-Schnipsel"
msgid "Autocompletes XML/HTML tags using snippets."
msgstr "Autovervollständigt XML/HTML-Tags unter Verwendung von Schnipseln."
+#, fuzzy
+#~ msgid "Create"
+#~ msgstr "_Erstellen"
+
+#, fuzzy
+#~ msgid "New"
+#~ msgstr "Neu"
+
#~ msgid "Call documentation viewer on current symbol."
#~ msgstr "Dokumentation für das aktuelle Wort anzeigen."
@@ -8072,9 +8405,6 @@ msgstr "Autovervollständigt XML/HTML-Tags unter Verwendung von Schnipseln."
#~ msgid "Find Project Tag..."
#~ msgstr "Tag im Projekt finden..."
-#~ msgid "Find project tag"
-#~ msgstr "Tag im Projekt finden"
-
#~ msgid "Generate tags for all project files:"
#~ msgstr "Tags für alle Projektdateien erstellen"
diff --git a/po/es.po b/po/es.po
index 029cb9a49..4369bd318 100644
--- a/po/es.po
+++ b/po/es.po
@@ -3502,7 +3502,7 @@ msgstr "Cancelar selección"
msgid "Toggle Insert/Overwrite mode"
msgstr "Intercambiar modo inserción/sobrescritura"
-#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:239
+#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:429
msgid "Tab"
msgstr "Tab"
@@ -5830,7 +5830,7 @@ msgstr ""
"No se pudo encontrar el widget «%s» en la definición de UI, compruebe su "
"instalación."
-#: ../pretty-printer/src/PluginEntry.c:36
+#: ../pretty-printer/src/PluginEntry.c:41
msgid "XML PrettyPrinter"
msgstr "PrettyPrinter XML"
@@ -5896,31 +5896,31 @@ msgstr "Nodos de texto"
msgid "CDATA"
msgstr "CDATA"
-#: ../pretty-printer/src/ConfigUI.c:205
+#: ../pretty-printer/src/ConfigUI.c:395
msgid "Empty nodes"
msgstr "Nodos vacíos"
-#: ../pretty-printer/src/ConfigUI.c:206
+#: ../pretty-printer/src/ConfigUI.c:396
msgid "Concatenation ( to )"
msgstr "Concatenación ( a )"
-#: ../pretty-printer/src/ConfigUI.c:207
+#: ../pretty-printer/src/ConfigUI.c:397
msgid "Spacing ( to )"
msgstr "Espaciado ( a )"
-#: ../pretty-printer/src/ConfigUI.c:208
+#: ../pretty-printer/src/ConfigUI.c:398
msgid "Expansion ( to )"
msgstr "Expansión ( a )"
-#: ../pretty-printer/src/ConfigUI.c:235
+#: ../pretty-printer/src/ConfigUI.c:425
msgid "Indentation"
msgstr "Sangría"
-#: ../pretty-printer/src/ConfigUI.c:240
+#: ../pretty-printer/src/ConfigUI.c:430
msgid "Space"
msgstr "Espacio"
-#: ../pretty-printer/src/ConfigUI.c:264
+#: ../pretty-printer/src/ConfigUI.c:454
msgid "Line break"
msgstr "Cambio de línea"
@@ -5972,11 +5972,11 @@ msgstr "Intercambiar cabecera/fuente"
msgid "Open Selected File (Project Organizer)"
msgstr "Abrir archivo seleccionado (organizador de proyectos)"
-#: ../projectorganizer/src/prjorg-project.c:573
+#: ../projectorganizer/src/prjorg-project.c:578
msgid "Source patterns:"
msgstr "Patrones de código:"
-#: ../projectorganizer/src/prjorg-project.c:579
+#: ../projectorganizer/src/prjorg-project.c:584
msgid ""
"Space separated list of patterns that are used to identify source files. "
"Used for header/source swapping."
@@ -5984,11 +5984,11 @@ msgstr ""
"Lista de patrones separados por espacios utilizados para identificar "
"archivos de código. Usado para el intercambio entre cabeceras y código."
-#: ../projectorganizer/src/prjorg-project.c:585
+#: ../projectorganizer/src/prjorg-project.c:590
msgid "Header patterns:"
msgstr "Patrones de cabecera:"
-#: ../projectorganizer/src/prjorg-project.c:591
+#: ../projectorganizer/src/prjorg-project.c:596
msgid ""
"Space separated list of patterns that are used to identify headers. Used for "
"header/source swapping."
@@ -5996,11 +5996,11 @@ msgstr ""
"Lista de patrones separados por espacios, usados para identificar archivos "
"de cabeceras. Usado para el intercambio entre cabeceras y código."
-#: ../projectorganizer/src/prjorg-project.c:597
+#: ../projectorganizer/src/prjorg-project.c:602
msgid "Ignored file patterns:"
msgstr "Patrones de directorios ignorados:"
-#: ../projectorganizer/src/prjorg-project.c:603
+#: ../projectorganizer/src/prjorg-project.c:608
msgid ""
"Space separated list of patterns that are used to identify files that are "
"not displayed in the project tree."
@@ -6008,11 +6008,11 @@ msgstr ""
"Lista de patrones separados por espacios, usados para identificar archivos "
"que no se mostrarán en el árbol de proyectos."
-#: ../projectorganizer/src/prjorg-project.c:609
+#: ../projectorganizer/src/prjorg-project.c:614
msgid "Ignored directory patterns:"
msgstr "Patrones de directorios ignorados:"
-#: ../projectorganizer/src/prjorg-project.c:615
+#: ../projectorganizer/src/prjorg-project.c:620
msgid ""
"Space separated list of patterns that are used to identify directories that "
"are not scanned for source files."
@@ -6020,23 +6020,23 @@ msgstr ""
"Lista de patrones separados por espacios, usados para identificar "
"directorios en los que no se buscarán archivos de código fuente."
-#: ../projectorganizer/src/prjorg-project.c:621
+#: ../projectorganizer/src/prjorg-project.c:626
msgid "Index all project files:"
msgstr "Indexar todos los archivos del proyecto:"
-#: ../projectorganizer/src/prjorg-project.c:624
+#: ../projectorganizer/src/prjorg-project.c:629
msgid "Auto (index if less than 300 files)"
msgstr "Auto (indexar si hay menos de 300 archivos)"
-#: ../projectorganizer/src/prjorg-project.c:625
+#: ../projectorganizer/src/prjorg-project.c:630
msgid "Yes"
msgstr "Sí"
-#: ../projectorganizer/src/prjorg-project.c:626
+#: ../projectorganizer/src/prjorg-project.c:631
msgid "No"
msgstr "No"
-#: ../projectorganizer/src/prjorg-project.c:630
+#: ../projectorganizer/src/prjorg-project.c:635
msgid ""
"Generate symbol list for all project files instead of only for the currently "
"opened files. Might be slow for big projects."
@@ -6045,7 +6045,7 @@ msgstr ""
"solo para los archivos abiertos actualmente. Podría resultar lento para "
"proyectos grandes."
-#: ../projectorganizer/src/prjorg-project.c:636
+#: ../projectorganizer/src/prjorg-project.c:641
msgid ""
"Note: the patterns above affect only the sidebar and are not used in the "
"Find in Files\n"
@@ -7360,6 +7360,7 @@ msgid "TreeBrowser"
msgstr "Visor de árbol"
#: ../treebrowser/src/treebrowser.c:128
+#, fuzzy
msgid ""
"This plugin adds a tree browser to Geany, allowing the user to browse files "
"using a tree view of the directory being browsed.\n"
diff --git a/po/fr.po b/po/fr.po
index 40c6d890d..78ab8e2c3 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -3487,7 +3487,7 @@ msgstr "Annuler la sélection"
msgid "Toggle Insert/Overwrite mode"
msgstr "Basculer le mode d'insertion/écrasement"
-#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:239
+#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:429
msgid "Tab"
msgstr "Tab"
@@ -5822,7 +5822,7 @@ msgstr ""
"Impossible de trouver le widget « %s » dans la définition d'interface, "
"veuillez vérifier votre installation."
-#: ../pretty-printer/src/PluginEntry.c:36
+#: ../pretty-printer/src/PluginEntry.c:41
msgid "XML PrettyPrinter"
msgstr ""
@@ -5881,31 +5881,31 @@ msgstr "Nœuds texte"
msgid "CDATA"
msgstr "CDATA"
-#: ../pretty-printer/src/ConfigUI.c:205
+#: ../pretty-printer/src/ConfigUI.c:395
msgid "Empty nodes"
msgstr "Nœuds vides"
-#: ../pretty-printer/src/ConfigUI.c:206
+#: ../pretty-printer/src/ConfigUI.c:396
msgid "Concatenation ( to )"
msgstr "Concaténation ( to )"
-#: ../pretty-printer/src/ConfigUI.c:207
+#: ../pretty-printer/src/ConfigUI.c:397
msgid "Spacing ( to )"
msgstr "Espace ( to )"
-#: ../pretty-printer/src/ConfigUI.c:208
+#: ../pretty-printer/src/ConfigUI.c:398
msgid "Expansion ( to )"
msgstr "Expansion ( to )"
-#: ../pretty-printer/src/ConfigUI.c:235
+#: ../pretty-printer/src/ConfigUI.c:425
msgid "Indentation"
msgstr "Indentation"
-#: ../pretty-printer/src/ConfigUI.c:240
+#: ../pretty-printer/src/ConfigUI.c:430
msgid "Space"
msgstr "Espace"
-#: ../pretty-printer/src/ConfigUI.c:264
+#: ../pretty-printer/src/ConfigUI.c:454
msgid "Line break"
msgstr "Retour à la ligne"
@@ -5957,11 +5957,11 @@ msgstr "Basculer à l'en-tête/la source"
msgid "Open Selected File (Project Organizer)"
msgstr "Ouvrir le fichier sélectionné (Organisateur de projet)"
-#: ../projectorganizer/src/prjorg-project.c:573
+#: ../projectorganizer/src/prjorg-project.c:578
msgid "Source patterns:"
msgstr "Motifs de sources :"
-#: ../projectorganizer/src/prjorg-project.c:579
+#: ../projectorganizer/src/prjorg-project.c:584
msgid ""
"Space separated list of patterns that are used to identify source files. "
"Used for header/source swapping."
@@ -5969,11 +5969,11 @@ msgstr ""
"Une liste séparée par des espaces de motifs utilisés pour identifier les "
"fichiers source. Utilisée pour basculer entre l'en-tête et la source."
-#: ../projectorganizer/src/prjorg-project.c:585
+#: ../projectorganizer/src/prjorg-project.c:590
msgid "Header patterns:"
msgstr "Motifs d'en-têtes :"
-#: ../projectorganizer/src/prjorg-project.c:591
+#: ../projectorganizer/src/prjorg-project.c:596
msgid ""
"Space separated list of patterns that are used to identify headers. Used for "
"header/source swapping."
@@ -5981,11 +5981,11 @@ msgstr ""
"Une liste séparée par des espaces de motifs utilisés pour identifier les "
"fichiers d'en-tête. Utilisée pour basculer entre l'en-tête et la source."
-#: ../projectorganizer/src/prjorg-project.c:597
+#: ../projectorganizer/src/prjorg-project.c:602
msgid "Ignored file patterns:"
msgstr "Motifs de dossiers ignorés :"
-#: ../projectorganizer/src/prjorg-project.c:603
+#: ../projectorganizer/src/prjorg-project.c:608
msgid ""
"Space separated list of patterns that are used to identify files that are "
"not displayed in the project tree."
@@ -5993,11 +5993,11 @@ msgstr ""
"Une liste séparée par des espaces de motifs utilisés pour identifier les "
"fichiers à ne pas afficher dans l'arborescence du projet."
-#: ../projectorganizer/src/prjorg-project.c:609
+#: ../projectorganizer/src/prjorg-project.c:614
msgid "Ignored directory patterns:"
msgstr "Motifs de dossiers ignorés :"
-#: ../projectorganizer/src/prjorg-project.c:615
+#: ../projectorganizer/src/prjorg-project.c:620
msgid ""
"Space separated list of patterns that are used to identify directories that "
"are not scanned for source files."
@@ -6005,23 +6005,23 @@ msgstr ""
"Une liste séparée par des espaces de motifs utilisés pour identifier les "
"dossiers à ignorer."
-#: ../projectorganizer/src/prjorg-project.c:621
+#: ../projectorganizer/src/prjorg-project.c:626
msgid "Index all project files:"
msgstr "Indexer tous les fichiers du projet :"
-#: ../projectorganizer/src/prjorg-project.c:624
+#: ../projectorganizer/src/prjorg-project.c:629
msgid "Auto (index if less than 300 files)"
msgstr "Automatique (indexer s'il y a moins de 300 fichiers)"
-#: ../projectorganizer/src/prjorg-project.c:625
+#: ../projectorganizer/src/prjorg-project.c:630
msgid "Yes"
msgstr "Oui"
-#: ../projectorganizer/src/prjorg-project.c:626
+#: ../projectorganizer/src/prjorg-project.c:631
msgid "No"
msgstr "Non"
-#: ../projectorganizer/src/prjorg-project.c:630
+#: ../projectorganizer/src/prjorg-project.c:635
msgid ""
"Generate symbol list for all project files instead of only for the currently "
"opened files. Might be slow for big projects."
@@ -6029,7 +6029,7 @@ msgstr ""
"Génère la liste des symboles pour tous les fichiers du projet et non "
"uniquement ceux ouverts. Peut être lent avec de gros projets."
-#: ../projectorganizer/src/prjorg-project.c:636
+#: ../projectorganizer/src/prjorg-project.c:641
msgid ""
"Note: the patterns above affect only the sidebar and are not used in the "
"Find in Files\n"
diff --git a/po/gl.po b/po/gl.po
index 7301381d0..89f88fc94 100644
--- a/po/gl.po
+++ b/po/gl.po
@@ -3530,7 +3530,7 @@ msgstr "Selección extra"
msgid "Toggle Insert/Overwrite mode"
msgstr ""
-#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:239
+#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:429
msgid "Tab"
msgstr ""
@@ -5770,7 +5770,7 @@ msgid ""
"installation."
msgstr ""
-#: ../pretty-printer/src/PluginEntry.c:36
+#: ../pretty-printer/src/PluginEntry.c:41
msgid "XML PrettyPrinter"
msgstr ""
@@ -5831,33 +5831,33 @@ msgstr ""
msgid "CDATA"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:205
+#: ../pretty-printer/src/ConfigUI.c:395
#, fuzzy
msgid "Empty nodes"
msgstr "(Liña baleira)"
-#: ../pretty-printer/src/ConfigUI.c:206
+#: ../pretty-printer/src/ConfigUI.c:396
msgid "Concatenation ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:207
+#: ../pretty-printer/src/ConfigUI.c:397
msgid "Spacing ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:208
+#: ../pretty-printer/src/ConfigUI.c:398
msgid "Expansion ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:235
+#: ../pretty-printer/src/ConfigUI.c:425
#, fuzzy
msgid "Indentation"
msgstr "Presentación"
-#: ../pretty-printer/src/ConfigUI.c:240
+#: ../pretty-printer/src/ConfigUI.c:430
msgid "Space"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:264
+#: ../pretty-printer/src/ConfigUI.c:454
msgid "Line break"
msgstr ""
@@ -5914,74 +5914,74 @@ msgstr ""
msgid "Open Selected File (Project Organizer)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:573
+#: ../projectorganizer/src/prjorg-project.c:578
msgid "Source patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:579
+#: ../projectorganizer/src/prjorg-project.c:584
msgid ""
"Space separated list of patterns that are used to identify source files. "
"Used for header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:585
+#: ../projectorganizer/src/prjorg-project.c:590
#, fuzzy
msgid "Header patterns:"
msgstr "Extensións de cabeceiras"
-#: ../projectorganizer/src/prjorg-project.c:591
+#: ../projectorganizer/src/prjorg-project.c:596
msgid ""
"Space separated list of patterns that are used to identify headers. Used for "
"header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:597
+#: ../projectorganizer/src/prjorg-project.c:602
#, fuzzy
msgid "Ignored file patterns:"
msgstr "Extensións de cabeceiras"
-#: ../projectorganizer/src/prjorg-project.c:603
+#: ../projectorganizer/src/prjorg-project.c:608
msgid ""
"Space separated list of patterns that are used to identify files that are "
"not displayed in the project tree."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:609
+#: ../projectorganizer/src/prjorg-project.c:614
#, fuzzy
msgid "Ignored directory patterns:"
msgstr "Extensións de cabeceiras"
-#: ../projectorganizer/src/prjorg-project.c:615
+#: ../projectorganizer/src/prjorg-project.c:620
msgid ""
"Space separated list of patterns that are used to identify directories that "
"are not scanned for source files."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:621
+#: ../projectorganizer/src/prjorg-project.c:626
#, fuzzy
msgid "Index all project files:"
msgstr "Procurar en todo o proxecto "
-#: ../projectorganizer/src/prjorg-project.c:624
+#: ../projectorganizer/src/prjorg-project.c:629
msgid "Auto (index if less than 300 files)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:625
+#: ../projectorganizer/src/prjorg-project.c:630
msgid "Yes"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:626
+#: ../projectorganizer/src/prjorg-project.c:631
#, fuzzy
msgid "No"
msgstr "Núm."
-#: ../projectorganizer/src/prjorg-project.c:630
+#: ../projectorganizer/src/prjorg-project.c:635
msgid ""
"Generate symbol list for all project files instead of only for the currently "
"opened files. Might be slow for big projects."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:636
+#: ../projectorganizer/src/prjorg-project.c:641
msgid ""
"Note: the patterns above affect only the sidebar and are not used in the "
"Find in Files\n"
diff --git a/po/it.po b/po/it.po
index 7ea9d7da6..71f1889ab 100644
--- a/po/it.po
+++ b/po/it.po
@@ -3465,7 +3465,7 @@ msgstr "Elimina selezione"
msgid "Toggle Insert/Overwrite mode"
msgstr "Attiva/Disattiva modalità sovrascrivi"
-#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:239
+#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:429
msgid "Tab"
msgstr "Tab"
@@ -5774,7 +5774,7 @@ msgstr ""
"Impossibile trovare il widge t\"%s\" nella definizione delle UI, si prega di "
"controllare l'installazione."
-#: ../pretty-printer/src/PluginEntry.c:36
+#: ../pretty-printer/src/PluginEntry.c:41
msgid "XML PrettyPrinter"
msgstr "XML PrettyPrinter"
@@ -5837,31 +5837,31 @@ msgstr "Nodi di testo"
msgid "CDATA"
msgstr "CDATA"
-#: ../pretty-printer/src/ConfigUI.c:205
+#: ../pretty-printer/src/ConfigUI.c:395
msgid "Empty nodes"
msgstr "Nodi vuoti"
-#: ../pretty-printer/src/ConfigUI.c:206
+#: ../pretty-printer/src/ConfigUI.c:396
msgid "Concatenation ( to )"
msgstr "Concatenazione (da a )"
-#: ../pretty-printer/src/ConfigUI.c:207
+#: ../pretty-printer/src/ConfigUI.c:397
msgid "Spacing ( to )"
msgstr "Spaziatura (da a )"
-#: ../pretty-printer/src/ConfigUI.c:208
+#: ../pretty-printer/src/ConfigUI.c:398
msgid "Expansion ( to )"
msgstr "Espansione (da a )"
-#: ../pretty-printer/src/ConfigUI.c:235
+#: ../pretty-printer/src/ConfigUI.c:425
msgid "Indentation"
msgstr "Indentazione"
-#: ../pretty-printer/src/ConfigUI.c:240
+#: ../pretty-printer/src/ConfigUI.c:430
msgid "Space"
msgstr "Spazio"
-#: ../pretty-printer/src/ConfigUI.c:264
+#: ../pretty-printer/src/ConfigUI.c:454
msgid "Line break"
msgstr "Interruzione di riga"
@@ -5917,11 +5917,11 @@ msgstr ""
msgid "Open Selected File (Project Organizer)"
msgstr "Open file selezionato (gproject)"
-#: ../projectorganizer/src/prjorg-project.c:573
+#: ../projectorganizer/src/prjorg-project.c:578
msgid "Source patterns:"
msgstr "Patterns sorgente:"
-#: ../projectorganizer/src/prjorg-project.c:579
+#: ../projectorganizer/src/prjorg-project.c:584
#, fuzzy
msgid ""
"Space separated list of patterns that are used to identify source files. "
@@ -5930,11 +5930,11 @@ msgstr ""
"Lista separata da spazi dei patterns che saranno usati per identificare i "
"file intestazione. Usato principalmente per scambiare intestazione/sorgente"
-#: ../projectorganizer/src/prjorg-project.c:585
+#: ../projectorganizer/src/prjorg-project.c:590
msgid "Header patterns:"
msgstr "Intestazione patterns:"
-#: ../projectorganizer/src/prjorg-project.c:591
+#: ../projectorganizer/src/prjorg-project.c:596
#, fuzzy
msgid ""
"Space separated list of patterns that are used to identify headers. Used for "
@@ -5943,12 +5943,12 @@ msgstr ""
"Lista separata da spazi dei patterns che saranno usati per identificare i "
"file intestazione. Usato principalmente per scambiare intestazione/sorgente"
-#: ../projectorganizer/src/prjorg-project.c:597
+#: ../projectorganizer/src/prjorg-project.c:602
#, fuzzy
msgid "Ignored file patterns:"
msgstr "Ignora patters directory:"
-#: ../projectorganizer/src/prjorg-project.c:603
+#: ../projectorganizer/src/prjorg-project.c:608
#, fuzzy
msgid ""
"Space separated list of patterns that are used to identify files that are "
@@ -5957,12 +5957,12 @@ msgstr ""
"Lista separata da spazi dei patterns che saranno usati per identificare le "
"directory che non saranno scansione per file sorgenti."
-#: ../projectorganizer/src/prjorg-project.c:609
+#: ../projectorganizer/src/prjorg-project.c:614
#, fuzzy
msgid "Ignored directory patterns:"
msgstr "Ignora patters directory:"
-#: ../projectorganizer/src/prjorg-project.c:615
+#: ../projectorganizer/src/prjorg-project.c:620
msgid ""
"Space separated list of patterns that are used to identify directories that "
"are not scanned for source files."
@@ -5970,26 +5970,26 @@ msgstr ""
"Lista separata da spazi dei patterns che saranno usati per identificare le "
"directory che non saranno scansione per file sorgenti."
-#: ../projectorganizer/src/prjorg-project.c:621
+#: ../projectorganizer/src/prjorg-project.c:626
#, fuzzy
msgid "Index all project files:"
msgstr "Cerca nei file del progetto"
-#: ../projectorganizer/src/prjorg-project.c:624
+#: ../projectorganizer/src/prjorg-project.c:629
msgid "Auto (index if less than 300 files)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:625
+#: ../projectorganizer/src/prjorg-project.c:630
#, fuzzy
msgid "Yes"
msgstr "sì"
-#: ../projectorganizer/src/prjorg-project.c:626
+#: ../projectorganizer/src/prjorg-project.c:631
#, fuzzy
msgid "No"
msgstr "N°"
-#: ../projectorganizer/src/prjorg-project.c:630
+#: ../projectorganizer/src/prjorg-project.c:635
#, fuzzy
msgid ""
"Generate symbol list for all project files instead of only for the currently "
@@ -5999,7 +5999,7 @@ msgstr ""
"correttamente aperti. Troppo lento per grandi progetti (>1000 files) e "
"dovrebbe essere disabilitato in questo caso."
-#: ../projectorganizer/src/prjorg-project.c:636
+#: ../projectorganizer/src/prjorg-project.c:641
msgid ""
"Note: the patterns above affect only the sidebar and are not used in the "
"Find in Files\n"
diff --git a/po/ja.po b/po/ja.po
index 968b3e175..e7481f4ee 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -3430,7 +3430,7 @@ msgstr "選択を解除"
msgid "Toggle Insert/Overwrite mode"
msgstr "挿入/上書きモードを切替"
-#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:239
+#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:429
msgid "Tab"
msgstr "タブ"
@@ -5688,7 +5688,7 @@ msgid ""
"installation."
msgstr ""
-#: ../pretty-printer/src/PluginEntry.c:36
+#: ../pretty-printer/src/PluginEntry.c:41
msgid "XML PrettyPrinter"
msgstr "XML整形"
@@ -5750,31 +5750,31 @@ msgstr "文字ノード"
msgid "CDATA"
msgstr "CDATA"
-#: ../pretty-printer/src/ConfigUI.c:205
+#: ../pretty-printer/src/ConfigUI.c:395
msgid "Empty nodes"
msgstr "空ノード"
-#: ../pretty-printer/src/ConfigUI.c:206
+#: ../pretty-printer/src/ConfigUI.c:396
msgid "Concatenation ( to )"
msgstr "結合 ( を にする)"
-#: ../pretty-printer/src/ConfigUI.c:207
+#: ../pretty-printer/src/ConfigUI.c:397
msgid "Spacing ( to )"
msgstr "空白を開ける ( を にする)"
-#: ../pretty-printer/src/ConfigUI.c:208
+#: ../pretty-printer/src/ConfigUI.c:398
msgid "Expansion ( to )"
msgstr "展開 ( を にする)"
-#: ../pretty-printer/src/ConfigUI.c:235
+#: ../pretty-printer/src/ConfigUI.c:425
msgid "Indentation"
msgstr "インデント"
-#: ../pretty-printer/src/ConfigUI.c:240
+#: ../pretty-printer/src/ConfigUI.c:430
msgid "Space"
msgstr "空白"
-#: ../pretty-printer/src/ConfigUI.c:264
+#: ../pretty-printer/src/ConfigUI.c:454
msgid "Line break"
msgstr "改行"
@@ -5830,11 +5830,11 @@ msgstr "ヘッダ/ソースを交換"
msgid "Open Selected File (Project Organizer)"
msgstr "選択したファイルを開く(gproject)"
-#: ../projectorganizer/src/prjorg-project.c:573
+#: ../projectorganizer/src/prjorg-project.c:578
msgid "Source patterns:"
msgstr "ソースのフィルタ:"
-#: ../projectorganizer/src/prjorg-project.c:579
+#: ../projectorganizer/src/prjorg-project.c:584
#, fuzzy
msgid ""
"Space separated list of patterns that are used to identify source files. "
@@ -5843,11 +5843,11 @@ msgstr ""
"空白で区切られたヘッダファイルとして使用するファイルパターンのリストヘッダ。"
"ソースの交換に使用します。"
-#: ../projectorganizer/src/prjorg-project.c:585
+#: ../projectorganizer/src/prjorg-project.c:590
msgid "Header patterns:"
msgstr "ヘッダのフィルタ:"
-#: ../projectorganizer/src/prjorg-project.c:591
+#: ../projectorganizer/src/prjorg-project.c:596
#, fuzzy
msgid ""
"Space separated list of patterns that are used to identify headers. Used for "
@@ -5856,12 +5856,12 @@ msgstr ""
"空白で区切られたヘッダファイルとして使用するファイルパターンのリストヘッダ。"
"ソースの交換に使用します。"
-#: ../projectorganizer/src/prjorg-project.c:597
+#: ../projectorganizer/src/prjorg-project.c:602
#, fuzzy
msgid "Ignored file patterns:"
msgstr "無視するディレクトリ:"
-#: ../projectorganizer/src/prjorg-project.c:603
+#: ../projectorganizer/src/prjorg-project.c:608
#, fuzzy
msgid ""
"Space separated list of patterns that are used to identify files that are "
@@ -5870,12 +5870,12 @@ msgstr ""
"空白で区切られたソースファイル検索の対象から外すディレクトリのパターンのリス"
"ト"
-#: ../projectorganizer/src/prjorg-project.c:609
+#: ../projectorganizer/src/prjorg-project.c:614
#, fuzzy
msgid "Ignored directory patterns:"
msgstr "無視するディレクトリ:"
-#: ../projectorganizer/src/prjorg-project.c:615
+#: ../projectorganizer/src/prjorg-project.c:620
msgid ""
"Space separated list of patterns that are used to identify directories that "
"are not scanned for source files."
@@ -5883,26 +5883,26 @@ msgstr ""
"空白で区切られたソースファイル検索の対象から外すディレクトリのパターンのリス"
"ト"
-#: ../projectorganizer/src/prjorg-project.c:621
+#: ../projectorganizer/src/prjorg-project.c:626
#, fuzzy
msgid "Index all project files:"
msgstr "プロジェクトファイルを検索"
-#: ../projectorganizer/src/prjorg-project.c:624
+#: ../projectorganizer/src/prjorg-project.c:629
msgid "Auto (index if less than 300 files)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:625
+#: ../projectorganizer/src/prjorg-project.c:630
#, fuzzy
msgid "Yes"
msgstr "はい"
-#: ../projectorganizer/src/prjorg-project.c:626
+#: ../projectorganizer/src/prjorg-project.c:631
#, fuzzy
msgid "No"
msgstr "番号"
-#: ../projectorganizer/src/prjorg-project.c:630
+#: ../projectorganizer/src/prjorg-project.c:635
#, fuzzy
msgid ""
"Generate symbol list for all project files instead of only for the currently "
@@ -5912,7 +5912,7 @@ msgstr ""
"トを生成します。大きなプロジェクト(1000ファイル以上)ではとても遅いので、使用"
"しないでください。"
-#: ../projectorganizer/src/prjorg-project.c:636
+#: ../projectorganizer/src/prjorg-project.c:641
msgid ""
"Note: the patterns above affect only the sidebar and are not used in the "
"Find in Files\n"
diff --git a/po/kk.po b/po/kk.po
index 008df96e2..27a20cbf5 100644
--- a/po/kk.po
+++ b/po/kk.po
@@ -3327,7 +3327,7 @@ msgstr ""
msgid "Toggle Insert/Overwrite mode"
msgstr ""
-#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:239
+#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:429
msgid "Tab"
msgstr "Tab"
@@ -5452,7 +5452,7 @@ msgid ""
"installation."
msgstr ""
-#: ../pretty-printer/src/PluginEntry.c:36
+#: ../pretty-printer/src/PluginEntry.c:41
msgid "XML PrettyPrinter"
msgstr ""
@@ -5511,31 +5511,31 @@ msgstr ""
msgid "CDATA"
msgstr "CDATA"
-#: ../pretty-printer/src/ConfigUI.c:205
+#: ../pretty-printer/src/ConfigUI.c:395
msgid "Empty nodes"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:206
+#: ../pretty-printer/src/ConfigUI.c:396
msgid "Concatenation ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:207
+#: ../pretty-printer/src/ConfigUI.c:397
msgid "Spacing ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:208
+#: ../pretty-printer/src/ConfigUI.c:398
msgid "Expansion ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:235
+#: ../pretty-printer/src/ConfigUI.c:425
msgid "Indentation"
msgstr "Шегіну"
-#: ../pretty-printer/src/ConfigUI.c:240
+#: ../pretty-printer/src/ConfigUI.c:430
msgid "Space"
msgstr "Space"
-#: ../pretty-printer/src/ConfigUI.c:264
+#: ../pretty-printer/src/ConfigUI.c:454
msgid "Line break"
msgstr ""
@@ -5585,69 +5585,69 @@ msgstr ""
msgid "Open Selected File (Project Organizer)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:573
+#: ../projectorganizer/src/prjorg-project.c:578
msgid "Source patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:579
+#: ../projectorganizer/src/prjorg-project.c:584
msgid ""
"Space separated list of patterns that are used to identify source files. "
"Used for header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:585
+#: ../projectorganizer/src/prjorg-project.c:590
msgid "Header patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:591
+#: ../projectorganizer/src/prjorg-project.c:596
msgid ""
"Space separated list of patterns that are used to identify headers. Used for "
"header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:597
+#: ../projectorganizer/src/prjorg-project.c:602
msgid "Ignored file patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:603
+#: ../projectorganizer/src/prjorg-project.c:608
msgid ""
"Space separated list of patterns that are used to identify files that are "
"not displayed in the project tree."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:609
+#: ../projectorganizer/src/prjorg-project.c:614
msgid "Ignored directory patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:615
+#: ../projectorganizer/src/prjorg-project.c:620
msgid ""
"Space separated list of patterns that are used to identify directories that "
"are not scanned for source files."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:621
+#: ../projectorganizer/src/prjorg-project.c:626
msgid "Index all project files:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:624
+#: ../projectorganizer/src/prjorg-project.c:629
msgid "Auto (index if less than 300 files)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:625
+#: ../projectorganizer/src/prjorg-project.c:630
msgid "Yes"
msgstr "Иә"
-#: ../projectorganizer/src/prjorg-project.c:626
+#: ../projectorganizer/src/prjorg-project.c:631
msgid "No"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:630
+#: ../projectorganizer/src/prjorg-project.c:635
msgid ""
"Generate symbol list for all project files instead of only for the currently "
"opened files. Might be slow for big projects."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:636
+#: ../projectorganizer/src/prjorg-project.c:641
msgid ""
"Note: the patterns above affect only the sidebar and are not used in the "
"Find in Files\n"
diff --git a/po/nl.po b/po/nl.po
index 527bbd0b3..d9483c64f 100644
--- a/po/nl.po
+++ b/po/nl.po
@@ -3355,7 +3355,7 @@ msgstr ""
msgid "Toggle Insert/Overwrite mode"
msgstr ""
-#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:239
+#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:429
msgid "Tab"
msgstr ""
@@ -5554,7 +5554,7 @@ msgstr ""
"Kon widget '%s' niet vinden in grafische interface definitie. Controleer de "
"installatie."
-#: ../pretty-printer/src/PluginEntry.c:36
+#: ../pretty-printer/src/PluginEntry.c:41
msgid "XML PrettyPrinter"
msgstr ""
@@ -5613,31 +5613,31 @@ msgstr ""
msgid "CDATA"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:205
+#: ../pretty-printer/src/ConfigUI.c:395
msgid "Empty nodes"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:206
+#: ../pretty-printer/src/ConfigUI.c:396
msgid "Concatenation ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:207
+#: ../pretty-printer/src/ConfigUI.c:397
msgid "Spacing ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:208
+#: ../pretty-printer/src/ConfigUI.c:398
msgid "Expansion ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:235
+#: ../pretty-printer/src/ConfigUI.c:425
msgid "Indentation"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:240
+#: ../pretty-printer/src/ConfigUI.c:430
msgid "Space"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:264
+#: ../pretty-printer/src/ConfigUI.c:454
msgid "Line break"
msgstr ""
@@ -5692,72 +5692,72 @@ msgstr ""
msgid "Open Selected File (Project Organizer)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:573
+#: ../projectorganizer/src/prjorg-project.c:578
msgid "Source patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:579
+#: ../projectorganizer/src/prjorg-project.c:584
msgid ""
"Space separated list of patterns that are used to identify source files. "
"Used for header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:585
+#: ../projectorganizer/src/prjorg-project.c:590
msgid "Header patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:591
+#: ../projectorganizer/src/prjorg-project.c:596
msgid ""
"Space separated list of patterns that are used to identify headers. Used for "
"header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:597
+#: ../projectorganizer/src/prjorg-project.c:602
msgid "Ignored file patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:603
+#: ../projectorganizer/src/prjorg-project.c:608
msgid ""
"Space separated list of patterns that are used to identify files that are "
"not displayed in the project tree."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:609
+#: ../projectorganizer/src/prjorg-project.c:614
msgid "Ignored directory patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:615
+#: ../projectorganizer/src/prjorg-project.c:620
msgid ""
"Space separated list of patterns that are used to identify directories that "
"are not scanned for source files."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:621
+#: ../projectorganizer/src/prjorg-project.c:626
#, fuzzy
msgid "Index all project files:"
msgstr "Zoek in projectbestanden"
-#: ../projectorganizer/src/prjorg-project.c:624
+#: ../projectorganizer/src/prjorg-project.c:629
msgid "Auto (index if less than 300 files)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:625
+#: ../projectorganizer/src/prjorg-project.c:630
#, fuzzy
msgid "Yes"
msgstr "ja"
-#: ../projectorganizer/src/prjorg-project.c:626
+#: ../projectorganizer/src/prjorg-project.c:631
#, fuzzy
msgid "No"
msgstr "Nee."
-#: ../projectorganizer/src/prjorg-project.c:630
+#: ../projectorganizer/src/prjorg-project.c:635
msgid ""
"Generate symbol list for all project files instead of only for the currently "
"opened files. Might be slow for big projects."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:636
+#: ../projectorganizer/src/prjorg-project.c:641
msgid ""
"Note: the patterns above affect only the sidebar and are not used in the "
"Find in Files\n"
diff --git a/po/pt.po b/po/pt.po
index 08221fc74..9aa260fa6 100644
--- a/po/pt.po
+++ b/po/pt.po
@@ -3493,7 +3493,7 @@ msgstr "Cancelar seleção"
msgid "Toggle Insert/Overwrite mode"
msgstr "Alternar modo Inserir/Sobrescrever"
-#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:239
+#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:429
msgid "Tab"
msgstr "Tab"
@@ -5812,7 +5812,7 @@ msgstr ""
"Impossível encontrar o widget \"%s\" na definição do UI, por favor verifique "
"a sua instalação."
-#: ../pretty-printer/src/PluginEntry.c:36
+#: ../pretty-printer/src/PluginEntry.c:41
msgid "XML PrettyPrinter"
msgstr "XML PrettyPrinter"
@@ -5877,31 +5877,31 @@ msgstr "Nós de texto"
msgid "CDATA"
msgstr "CDATA"
-#: ../pretty-printer/src/ConfigUI.c:205
+#: ../pretty-printer/src/ConfigUI.c:395
msgid "Empty nodes"
msgstr "Nós vazios"
-#: ../pretty-printer/src/ConfigUI.c:206
+#: ../pretty-printer/src/ConfigUI.c:396
msgid "Concatenation ( to )"
msgstr "Concatenação ( to )"
-#: ../pretty-printer/src/ConfigUI.c:207
+#: ../pretty-printer/src/ConfigUI.c:397
msgid "Spacing ( to )"
msgstr "Espaçamento ( to )"
-#: ../pretty-printer/src/ConfigUI.c:208
+#: ../pretty-printer/src/ConfigUI.c:398
msgid "Expansion ( to )"
msgstr "Expansão ( to )"
-#: ../pretty-printer/src/ConfigUI.c:235
+#: ../pretty-printer/src/ConfigUI.c:425
msgid "Indentation"
msgstr "Indentação"
-#: ../pretty-printer/src/ConfigUI.c:240
+#: ../pretty-printer/src/ConfigUI.c:430
msgid "Space"
msgstr "Espaço"
-#: ../pretty-printer/src/ConfigUI.c:264
+#: ../pretty-printer/src/ConfigUI.c:454
msgid "Line break"
msgstr "Quebra de linha"
@@ -5953,11 +5953,11 @@ msgstr "Trocar cabeçalho/código"
msgid "Open Selected File (Project Organizer)"
msgstr "Abrir ficheiro selecionado (Organizador de projeto)"
-#: ../projectorganizer/src/prjorg-project.c:573
+#: ../projectorganizer/src/prjorg-project.c:578
msgid "Source patterns:"
msgstr "Padrões de fontes:"
-#: ../projectorganizer/src/prjorg-project.c:579
+#: ../projectorganizer/src/prjorg-project.c:584
msgid ""
"Space separated list of patterns that are used to identify source files. "
"Used for header/source swapping."
@@ -5965,11 +5965,11 @@ msgstr ""
"Lista de padrões separados por espaços, usados para identificar ficheiros "
"fonte. Usado para trocas entre cabeçalho/fonte."
-#: ../projectorganizer/src/prjorg-project.c:585
+#: ../projectorganizer/src/prjorg-project.c:590
msgid "Header patterns:"
msgstr "Padrões de ficheiros de cabeçalhos:"
-#: ../projectorganizer/src/prjorg-project.c:591
+#: ../projectorganizer/src/prjorg-project.c:596
msgid ""
"Space separated list of patterns that are used to identify headers. Used for "
"header/source swapping."
@@ -5977,11 +5977,11 @@ msgstr ""
"Lista de padrões separados por espaços, usados para identificar cabeçalhos. "
"Usado para trocas entre cabeçalho/fonte."
-#: ../projectorganizer/src/prjorg-project.c:597
+#: ../projectorganizer/src/prjorg-project.c:602
msgid "Ignored file patterns:"
msgstr "Padrões de ficheiro a ignorar:"
-#: ../projectorganizer/src/prjorg-project.c:603
+#: ../projectorganizer/src/prjorg-project.c:608
msgid ""
"Space separated list of patterns that are used to identify files that are "
"not displayed in the project tree."
@@ -5989,11 +5989,11 @@ msgstr ""
"Lista de padrões separados por espaços, usados para identificar ficheiros "
"que não são mostrados na árvore do projeto."
-#: ../projectorganizer/src/prjorg-project.c:609
+#: ../projectorganizer/src/prjorg-project.c:614
msgid "Ignored directory patterns:"
msgstr "Padrões de pasta a ignorar:"
-#: ../projectorganizer/src/prjorg-project.c:615
+#: ../projectorganizer/src/prjorg-project.c:620
msgid ""
"Space separated list of patterns that are used to identify directories that "
"are not scanned for source files."
@@ -6001,23 +6001,23 @@ msgstr ""
"Lista separada por espaços, de padrões a ser usados para identificar "
"diretórios onde não se procurarão ficheiros de código."
-#: ../projectorganizer/src/prjorg-project.c:621
+#: ../projectorganizer/src/prjorg-project.c:626
msgid "Index all project files:"
msgstr "Indexar todos os ficheiros de projeto:"
-#: ../projectorganizer/src/prjorg-project.c:624
+#: ../projectorganizer/src/prjorg-project.c:629
msgid "Auto (index if less than 300 files)"
msgstr "Automático (indexar se menos de 300 ficheiros)"
-#: ../projectorganizer/src/prjorg-project.c:625
+#: ../projectorganizer/src/prjorg-project.c:630
msgid "Yes"
msgstr "Sim"
-#: ../projectorganizer/src/prjorg-project.c:626
+#: ../projectorganizer/src/prjorg-project.c:631
msgid "No"
msgstr "Não"
-#: ../projectorganizer/src/prjorg-project.c:630
+#: ../projectorganizer/src/prjorg-project.c:635
msgid ""
"Generate symbol list for all project files instead of only for the currently "
"opened files. Might be slow for big projects."
@@ -6025,7 +6025,7 @@ msgstr ""
"Gerar lista de símbolos para todos os ficheiros do projeto e não só para os "
"atualmente abertos. Pode ser lento para projetos grandes."
-#: ../projectorganizer/src/prjorg-project.c:636
+#: ../projectorganizer/src/prjorg-project.c:641
msgid ""
"Note: the patterns above affect only the sidebar and are not used in the "
"Find in Files\n"
@@ -7337,6 +7337,7 @@ msgid "TreeBrowser"
msgstr "Navegador em árvore"
#: ../treebrowser/src/treebrowser.c:128
+#, fuzzy
msgid ""
"This plugin adds a tree browser to Geany, allowing the user to browse files "
"using a tree view of the directory being browsed.\n"
diff --git a/po/pt_BR.po b/po/pt_BR.po
index e91fff4fe..62b684dde 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -3542,7 +3542,7 @@ msgstr "Seleção Extra"
msgid "Toggle Insert/Overwrite mode"
msgstr ""
-#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:239
+#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:429
msgid "Tab"
msgstr ""
@@ -5775,7 +5775,7 @@ msgid ""
"installation."
msgstr ""
-#: ../pretty-printer/src/PluginEntry.c:36
+#: ../pretty-printer/src/PluginEntry.c:41
msgid "XML PrettyPrinter"
msgstr ""
@@ -5836,33 +5836,33 @@ msgstr ""
msgid "CDATA"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:205
+#: ../pretty-printer/src/ConfigUI.c:395
#, fuzzy
msgid "Empty nodes"
msgstr "(Linha vazia)"
-#: ../pretty-printer/src/ConfigUI.c:206
+#: ../pretty-printer/src/ConfigUI.c:396
msgid "Concatenation ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:207
+#: ../pretty-printer/src/ConfigUI.c:397
msgid "Spacing ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:208
+#: ../pretty-printer/src/ConfigUI.c:398
msgid "Expansion ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:235
+#: ../pretty-printer/src/ConfigUI.c:425
#, fuzzy
msgid "Indentation"
msgstr "Apresentação"
-#: ../pretty-printer/src/ConfigUI.c:240
+#: ../pretty-printer/src/ConfigUI.c:430
msgid "Space"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:264
+#: ../pretty-printer/src/ConfigUI.c:454
msgid "Line break"
msgstr ""
@@ -5919,74 +5919,74 @@ msgstr ""
msgid "Open Selected File (Project Organizer)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:573
+#: ../projectorganizer/src/prjorg-project.c:578
msgid "Source patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:579
+#: ../projectorganizer/src/prjorg-project.c:584
msgid ""
"Space separated list of patterns that are used to identify source files. "
"Used for header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:585
+#: ../projectorganizer/src/prjorg-project.c:590
#, fuzzy
msgid "Header patterns:"
msgstr "Extensões de cabeçalhos"
-#: ../projectorganizer/src/prjorg-project.c:591
+#: ../projectorganizer/src/prjorg-project.c:596
msgid ""
"Space separated list of patterns that are used to identify headers. Used for "
"header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:597
+#: ../projectorganizer/src/prjorg-project.c:602
#, fuzzy
msgid "Ignored file patterns:"
msgstr "Extensões de cabeçalhos"
-#: ../projectorganizer/src/prjorg-project.c:603
+#: ../projectorganizer/src/prjorg-project.c:608
msgid ""
"Space separated list of patterns that are used to identify files that are "
"not displayed in the project tree."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:609
+#: ../projectorganizer/src/prjorg-project.c:614
#, fuzzy
msgid "Ignored directory patterns:"
msgstr "Extensões de cabeçalhos"
-#: ../projectorganizer/src/prjorg-project.c:615
+#: ../projectorganizer/src/prjorg-project.c:620
msgid ""
"Space separated list of patterns that are used to identify directories that "
"are not scanned for source files."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:621
+#: ../projectorganizer/src/prjorg-project.c:626
#, fuzzy
msgid "Index all project files:"
msgstr "Pesquisa em Projeto"
-#: ../projectorganizer/src/prjorg-project.c:624
+#: ../projectorganizer/src/prjorg-project.c:629
msgid "Auto (index if less than 300 files)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:625
+#: ../projectorganizer/src/prjorg-project.c:630
msgid "Yes"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:626
+#: ../projectorganizer/src/prjorg-project.c:631
#, fuzzy
msgid "No"
msgstr "Não."
-#: ../projectorganizer/src/prjorg-project.c:630
+#: ../projectorganizer/src/prjorg-project.c:635
msgid ""
"Generate symbol list for all project files instead of only for the currently "
"opened files. Might be slow for big projects."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:636
+#: ../projectorganizer/src/prjorg-project.c:641
msgid ""
"Note: the patterns above affect only the sidebar and are not used in the "
"Find in Files\n"
diff --git a/po/ru.po b/po/ru.po
index a2df2ba48..643d1d507 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -3462,7 +3462,7 @@ msgstr "Сбросить выделение"
msgid "Toggle Insert/Overwrite mode"
msgstr "Переключить режим вставки/замены"
-#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:239
+#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:429
msgid "Tab"
msgstr "Табуляция"
@@ -5761,7 +5761,7 @@ msgstr ""
"Невозможно найти виджет \"%s\" в файле интерфейса, проверьте правильность "
"установки."
-#: ../pretty-printer/src/PluginEntry.c:36
+#: ../pretty-printer/src/PluginEntry.c:41
msgid "XML PrettyPrinter"
msgstr "Форматирование XML"
@@ -5824,31 +5824,31 @@ msgstr "Текстовые элементы"
msgid "CDATA"
msgstr "Символьные данные"
-#: ../pretty-printer/src/ConfigUI.c:205
+#: ../pretty-printer/src/ConfigUI.c:395
msgid "Empty nodes"
msgstr "Пустые элементы"
-#: ../pretty-printer/src/ConfigUI.c:206
+#: ../pretty-printer/src/ConfigUI.c:396
msgid "Concatenation ( to )"
msgstr "Объединение ( to )"
-#: ../pretty-printer/src/ConfigUI.c:207
+#: ../pretty-printer/src/ConfigUI.c:397
msgid "Spacing ( to )"
msgstr "Пробелы ( to )"
-#: ../pretty-printer/src/ConfigUI.c:208
+#: ../pretty-printer/src/ConfigUI.c:398
msgid "Expansion ( to )"
msgstr "Расширение ( to )"
-#: ../pretty-printer/src/ConfigUI.c:235
+#: ../pretty-printer/src/ConfigUI.c:425
msgid "Indentation"
msgstr "Отступы"
-#: ../pretty-printer/src/ConfigUI.c:240
+#: ../pretty-printer/src/ConfigUI.c:430
msgid "Space"
msgstr "Пробелы"
-#: ../pretty-printer/src/ConfigUI.c:264
+#: ../pretty-printer/src/ConfigUI.c:454
msgid "Line break"
msgstr "Перевод строки"
@@ -5899,11 +5899,11 @@ msgstr "Переключить заголовок/реализацию"
msgid "Open Selected File (Project Organizer)"
msgstr "Открыть выбранный файл (органайзер проектов)"
-#: ../projectorganizer/src/prjorg-project.c:573
+#: ../projectorganizer/src/prjorg-project.c:578
msgid "Source patterns:"
msgstr "Шаблоны файлов с исходным текстом:"
-#: ../projectorganizer/src/prjorg-project.c:579
+#: ../projectorganizer/src/prjorg-project.c:584
msgid ""
"Space separated list of patterns that are used to identify source files. "
"Used for header/source swapping."
@@ -5911,11 +5911,11 @@ msgstr ""
"Разделённый пробелами список шаблонов для определения файлов с исходным "
"текстом. Нужно для переключения исходник/заголовок."
-#: ../projectorganizer/src/prjorg-project.c:585
+#: ../projectorganizer/src/prjorg-project.c:590
msgid "Header patterns:"
msgstr "Шаблоны заголовочных файлов:"
-#: ../projectorganizer/src/prjorg-project.c:591
+#: ../projectorganizer/src/prjorg-project.c:596
msgid ""
"Space separated list of patterns that are used to identify headers. Used for "
"header/source swapping."
@@ -5923,11 +5923,11 @@ msgstr ""
"Разделённый пробелами список шаблонов для определения заголовочных файлов. "
"Нужно для переключения исходник/заголовок."
-#: ../projectorganizer/src/prjorg-project.c:597
+#: ../projectorganizer/src/prjorg-project.c:602
msgid "Ignored file patterns:"
msgstr "Шаблоны игнорируемых файлов:"
-#: ../projectorganizer/src/prjorg-project.c:603
+#: ../projectorganizer/src/prjorg-project.c:608
msgid ""
"Space separated list of patterns that are used to identify files that are "
"not displayed in the project tree."
@@ -5935,11 +5935,11 @@ msgstr ""
"Разделённый пробелами список шаблонов, которые используются для определения "
"файлов, не отображаемых в дереве файлов проекта."
-#: ../projectorganizer/src/prjorg-project.c:609
+#: ../projectorganizer/src/prjorg-project.c:614
msgid "Ignored directory patterns:"
msgstr "Игнорируемые шаблоны каталогов:"
-#: ../projectorganizer/src/prjorg-project.c:615
+#: ../projectorganizer/src/prjorg-project.c:620
msgid ""
"Space separated list of patterns that are used to identify directories that "
"are not scanned for source files."
@@ -5947,23 +5947,23 @@ msgstr ""
"Разделённый пробелами список шаблонов, которые используются для определения "
"каталогов, не просматриваемых на предмет наличия файлов проекта."
-#: ../projectorganizer/src/prjorg-project.c:621
+#: ../projectorganizer/src/prjorg-project.c:626
msgid "Index all project files:"
msgstr "Индексировать все файлы проекта:"
-#: ../projectorganizer/src/prjorg-project.c:624
+#: ../projectorganizer/src/prjorg-project.c:629
msgid "Auto (index if less than 300 files)"
msgstr "Автоматически (индексировать, если файлов не более 300)"
-#: ../projectorganizer/src/prjorg-project.c:625
+#: ../projectorganizer/src/prjorg-project.c:630
msgid "Yes"
msgstr "Да"
-#: ../projectorganizer/src/prjorg-project.c:626
+#: ../projectorganizer/src/prjorg-project.c:631
msgid "No"
msgstr "Нет"
-#: ../projectorganizer/src/prjorg-project.c:630
+#: ../projectorganizer/src/prjorg-project.c:635
msgid ""
"Generate symbol list for all project files instead of only for the currently "
"opened files. Might be slow for big projects."
@@ -5971,7 +5971,7 @@ msgstr ""
"Генерировать список символов для всех файлов проекта, а не только для "
"открытых. Может работать медленно для больших проектов."
-#: ../projectorganizer/src/prjorg-project.c:636
+#: ../projectorganizer/src/prjorg-project.c:641
msgid ""
"Note: the patterns above affect only the sidebar and are not used in the "
"Find in Files\n"
diff --git a/po/tr.po b/po/tr.po
index 0b3d7dbd2..c3816e0fa 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -3396,7 +3396,7 @@ msgstr "Seçimi iptal et"
msgid "Toggle Insert/Overwrite mode"
msgstr ""
-#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:239
+#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:429
msgid "Tab"
msgstr "Sekme"
@@ -5594,7 +5594,7 @@ msgid ""
"installation."
msgstr ""
-#: ../pretty-printer/src/PluginEntry.c:36
+#: ../pretty-printer/src/PluginEntry.c:41
msgid "XML PrettyPrinter"
msgstr ""
@@ -5653,31 +5653,31 @@ msgstr ""
msgid "CDATA"
msgstr "CDATA"
-#: ../pretty-printer/src/ConfigUI.c:205
+#: ../pretty-printer/src/ConfigUI.c:395
msgid "Empty nodes"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:206
+#: ../pretty-printer/src/ConfigUI.c:396
msgid "Concatenation ( to )"
msgstr "Bağlanma ( den e)"
-#: ../pretty-printer/src/ConfigUI.c:207
+#: ../pretty-printer/src/ConfigUI.c:397
msgid "Spacing ( to )"
msgstr "Boşluklama ( den e)"
-#: ../pretty-printer/src/ConfigUI.c:208
+#: ../pretty-printer/src/ConfigUI.c:398
msgid "Expansion ( to )"
msgstr "Genişleme ( den e)"
-#: ../pretty-printer/src/ConfigUI.c:235
+#: ../pretty-printer/src/ConfigUI.c:425
msgid "Indentation"
msgstr "Girinti"
-#: ../pretty-printer/src/ConfigUI.c:240
+#: ../pretty-printer/src/ConfigUI.c:430
msgid "Space"
msgstr "Boşluk"
-#: ../pretty-printer/src/ConfigUI.c:264
+#: ../pretty-printer/src/ConfigUI.c:454
msgid "Line break"
msgstr "Satır sonu"
@@ -5733,72 +5733,72 @@ msgstr ""
msgid "Open Selected File (Project Organizer)"
msgstr "Seçilen Dosyayı Aç (gproject)"
-#: ../projectorganizer/src/prjorg-project.c:573
+#: ../projectorganizer/src/prjorg-project.c:578
msgid "Source patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:579
+#: ../projectorganizer/src/prjorg-project.c:584
msgid ""
"Space separated list of patterns that are used to identify source files. "
"Used for header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:585
+#: ../projectorganizer/src/prjorg-project.c:590
msgid "Header patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:591
+#: ../projectorganizer/src/prjorg-project.c:596
msgid ""
"Space separated list of patterns that are used to identify headers. Used for "
"header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:597
+#: ../projectorganizer/src/prjorg-project.c:602
msgid "Ignored file patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:603
+#: ../projectorganizer/src/prjorg-project.c:608
msgid ""
"Space separated list of patterns that are used to identify files that are "
"not displayed in the project tree."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:609
+#: ../projectorganizer/src/prjorg-project.c:614
msgid "Ignored directory patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:615
+#: ../projectorganizer/src/prjorg-project.c:620
msgid ""
"Space separated list of patterns that are used to identify directories that "
"are not scanned for source files."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:621
+#: ../projectorganizer/src/prjorg-project.c:626
#, fuzzy
msgid "Index all project files:"
msgstr "Proje dosyalarında ara"
-#: ../projectorganizer/src/prjorg-project.c:624
+#: ../projectorganizer/src/prjorg-project.c:629
msgid "Auto (index if less than 300 files)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:625
+#: ../projectorganizer/src/prjorg-project.c:630
#, fuzzy
msgid "Yes"
msgstr "evet"
-#: ../projectorganizer/src/prjorg-project.c:626
+#: ../projectorganizer/src/prjorg-project.c:631
#, fuzzy
msgid "No"
msgstr "No."
-#: ../projectorganizer/src/prjorg-project.c:630
+#: ../projectorganizer/src/prjorg-project.c:635
msgid ""
"Generate symbol list for all project files instead of only for the currently "
"opened files. Might be slow for big projects."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:636
+#: ../projectorganizer/src/prjorg-project.c:641
msgid ""
"Note: the patterns above affect only the sidebar and are not used in the "
"Find in Files\n"
diff --git a/po/zh_CN.po b/po/zh_CN.po
index 95539a92f..a4088fca3 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -3546,7 +3546,7 @@ msgstr "选择字体"
msgid "Toggle Insert/Overwrite mode"
msgstr ""
-#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:239
+#: ../geanymacro/src/geanymacro.c:137 ../pretty-printer/src/ConfigUI.c:429
msgid "Tab"
msgstr ""
@@ -5758,7 +5758,7 @@ msgid ""
"installation."
msgstr ""
-#: ../pretty-printer/src/PluginEntry.c:36
+#: ../pretty-printer/src/PluginEntry.c:41
msgid "XML PrettyPrinter"
msgstr ""
@@ -5818,33 +5818,33 @@ msgstr ""
msgid "CDATA"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:205
+#: ../pretty-printer/src/ConfigUI.c:395
#, fuzzy
msgid "Empty nodes"
msgstr "(空行)"
-#: ../pretty-printer/src/ConfigUI.c:206
+#: ../pretty-printer/src/ConfigUI.c:396
msgid "Concatenation ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:207
+#: ../pretty-printer/src/ConfigUI.c:397
msgid "Spacing ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:208
+#: ../pretty-printer/src/ConfigUI.c:398
msgid "Expansion ( to )"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:235
+#: ../pretty-printer/src/ConfigUI.c:425
#, fuzzy
msgid "Indentation"
msgstr "简报"
-#: ../pretty-printer/src/ConfigUI.c:240
+#: ../pretty-printer/src/ConfigUI.c:430
msgid "Space"
msgstr ""
-#: ../pretty-printer/src/ConfigUI.c:264
+#: ../pretty-printer/src/ConfigUI.c:454
msgid "Line break"
msgstr ""
@@ -5901,71 +5901,71 @@ msgstr ""
msgid "Open Selected File (Project Organizer)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:573
+#: ../projectorganizer/src/prjorg-project.c:578
msgid "Source patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:579
+#: ../projectorganizer/src/prjorg-project.c:584
msgid ""
"Space separated list of patterns that are used to identify source files. "
"Used for header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:585
+#: ../projectorganizer/src/prjorg-project.c:590
msgid "Header patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:591
+#: ../projectorganizer/src/prjorg-project.c:596
msgid ""
"Space separated list of patterns that are used to identify headers. Used for "
"header/source swapping."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:597
+#: ../projectorganizer/src/prjorg-project.c:602
msgid "Ignored file patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:603
+#: ../projectorganizer/src/prjorg-project.c:608
msgid ""
"Space separated list of patterns that are used to identify files that are "
"not displayed in the project tree."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:609
+#: ../projectorganizer/src/prjorg-project.c:614
msgid "Ignored directory patterns:"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:615
+#: ../projectorganizer/src/prjorg-project.c:620
msgid ""
"Space separated list of patterns that are used to identify directories that "
"are not scanned for source files."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:621
+#: ../projectorganizer/src/prjorg-project.c:626
#, fuzzy
msgid "Index all project files:"
msgstr "在项目中查找"
-#: ../projectorganizer/src/prjorg-project.c:624
+#: ../projectorganizer/src/prjorg-project.c:629
msgid "Auto (index if less than 300 files)"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:625
+#: ../projectorganizer/src/prjorg-project.c:630
msgid "Yes"
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:626
+#: ../projectorganizer/src/prjorg-project.c:631
#, fuzzy
msgid "No"
msgstr "No."
-#: ../projectorganizer/src/prjorg-project.c:630
+#: ../projectorganizer/src/prjorg-project.c:635
msgid ""
"Generate symbol list for all project files instead of only for the currently "
"opened files. Might be slow for big projects."
msgstr ""
-#: ../projectorganizer/src/prjorg-project.c:636
+#: ../projectorganizer/src/prjorg-project.c:641
msgid ""
"Note: the patterns above affect only the sidebar and are not used in the "
"Find in Files\n"
diff --git a/workbench/AUTHORS b/workbench/AUTHORS
new file mode 100644
index 000000000..94c363ee2
--- /dev/null
+++ b/workbench/AUTHORS
@@ -0,0 +1 @@
+LarsGit223
diff --git a/workbench/COPYING b/workbench/COPYING
new file mode 100644
index 000000000..8c4c849e2
--- /dev/null
+++ b/workbench/COPYING
@@ -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.
+
+
+ Copyright (C)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin 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.
+
+ , 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.
diff --git a/workbench/ChangeLog b/workbench/ChangeLog
new file mode 100644
index 000000000..e69de29bb
diff --git a/workbench/Makefile.am b/workbench/Makefile.am
new file mode 100644
index 000000000..62210cb9c
--- /dev/null
+++ b/workbench/Makefile.am
@@ -0,0 +1,4 @@
+include $(top_srcdir)/build/vars.auxfiles.mk
+
+SUBDIRS = src icons
+plugin = workbench
diff --git a/workbench/NEWS b/workbench/NEWS
new file mode 100644
index 000000000..b671f54ab
--- /dev/null
+++ b/workbench/NEWS
@@ -0,0 +1,2 @@
+Juli 2017
+* Created plugin, initial commit (work in progress)
diff --git a/workbench/README b/workbench/README
new file mode 100644
index 000000000..f9b5c26ac
--- /dev/null
+++ b/workbench/README
@@ -0,0 +1,151 @@
+=========
+Workbench
+=========
+
+.. contents::
+
+About
+=====
+
+The Workbench plugin is an extension that makes it possible to manage
+multiple projects in geany. You can add geany projects to a workbench.
+From there you can add directories to the project to manage the files
+belonging to the project.
+
+Usage
+=====
+
+Enabling the plugin
+-------------------
+The plugin can be enabled in the plugin manager. After enabling the plugin
+a new tab will be displayed in the Sidebar. There will also be a new entry
+in the menubar. Both are labeled "Workbench".
+
+The Workbench menu
+------------------
+The Workbench menu is the starting point for creating your first workbench.
+Choose "New" from the menu to create your workbench file. Workbench files
+use the prefix ".geanywb". Just like Geany project files they are also
+simple text files containing key-value pairs.
+
+The complete managment of the Workbench file is done using the Workbench
+menu:
+- Item "New": as explained above, creates a new Workbench
+- Item "Open": open a Workbench
+- Item "Save": save the opened Workbench. This "only" saves any changes
+ in the Workbench file. It does not save any changes of the projects
+ belonging to the Workbench.
+- Item "Settings": open the Workbench settings dialog.
+- Item "Close": closes the opened Workbench.
+
+The new Workbench
+-----------------
+A newly created Workbench is completely empty. In the Workbench tab of
+the sidebar you should see the hint "Add a project using the context
+menu". Anything which is dealing with projects and their Workbench
+related settings is done using the context menu on the sidebar. Some
+items in the context menu will only be active if you right click inside
+a project, directory or bookmark.
+
+The Workbench context menu:
+---------------------------
+
+These are the available items:
+
+- "Add project":
+ Add an existing Geany project to the Workbench. Create your projects
+ with Geany. The Workbench plugin does not help you with that. After
+ adding a project you need to save the workbench settings by selecting
+ "Workbench / Save" in the menubar.
+- "Save project":
+ Selecting this item will save the Workbench related project settings in
+ the Geany project file. It is only available if you right clicked inside
+ of a project. It will only save the settings of the project that you
+ clicked on.
+- "Remove project":
+ Remove the project from the Workbench. It is only available if you
+ right clicked inside of a project. After removing a project you need
+ to save the workbench settings by selecting "Workbench / Save" in the
+ menubar.
+- "Fold/unfold project":
+ Fold or unfold the items belonging to the project. It is only available
+ if you right clicked inside of a project.
+
+- "Add directory":
+ Add a directory to the project. It is only available
+ if you right clicked inside of a project. After selecting it a dialog
+ will be opened. Chosse the directory which shall be added. After that
+ the directory will be shown inside the project folder. After adding a
+ new directory, all files and folders beneath the directory will be
+ displayed. See "Directory settings" for more information.
+- "Remove directory":
+ Remove the directory from the project. It is only available if you
+ right clicked inside of a project directory.
+- "Rescan directory":
+ Rescan the directory for files and update the sidebar accordingly.
+ It is only available if you right clicked inside of a project directory.
+ If the content of a directory changes, e.g. a new file is added, then
+ the new file will only be visible in the sidebar after a rescan.
+- "Directory settings":
+ Select this item to change the directory settings. It is only available
+ if you right clicked inside of a project directory. In the directory
+ settings you can set a filter which controls the files and folders
+ that shall be displayed or not.
+- "Fold/unfold directory":
+ Fold or unfold the items belonging to the directory. It is only available
+ if you right clicked inside of a directory.
+
+- "Unfold All":
+ Select this item to unfold all projects, directories and folders.
+- "Collapse All":
+ Select this item to collpase/fold all projects, directories and folders.
+
+- "Add to Workbench Bookmarks":
+ Add the file to the Workbench Bookmarks. It is only available
+ if you right clicked on a file. After adding a file to the Workbench
+ bookmarks a ribbon will be shown for the file in the Workbench sidebar
+ tab in front of the first project. Clicking on it will open the file.
+ Workbench bookmarks give you quick access to files which you often need.
+- "Add to Project Bookmarks":
+ Add the file to the Project Bookmarks. It is only available
+ if you right clicked on a file. After adding a file to the Project
+ bookmarks a ribbon will be shown for the file in the Workbench sidebar
+ tab in front of the first directory of the project. Clicking on it will
+ open the file. Project bookmarks give you quick access to files which
+ you often need in that project.
+- "Remove from Bookmarks":
+ Remove file from the Workbench or project bookmarks. It is only available
+ if you right clicked on a bookmark.
+
+Known issues
+============
+
+None.
+
+License
+=======
+
+The Workbench plugin 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
+=========
+
+The Workbench plugin is not (yet) part of the combined Geany Plugins release.
+So please download it from the github page, see below.
+
+Development Code
+================
+
+Get the code from::
+
+ git clone https://github.com/LarsGit223/geany-plugins
+
+Ideas, questions, patches and bug reports
+=========================================
+
+Please post any ideas, feature requests, questions and bugs in the
+github issue tracker.
diff --git a/workbench/THANKS b/workbench/THANKS
new file mode 100644
index 000000000..0fbb33948
--- /dev/null
+++ b/workbench/THANKS
@@ -0,0 +1,13 @@
+What's this file about?
+-----------------------
+This file lists all external people that have contributed to this project.
+
+Project Organizer Plugin:
+-------------------------
+This plugin is heavily based on code from the Project Organizer plugin.
+So big thanks go to the author Jiří Techet.
+
+Geany and Geany plugins:
+------------------------
+Thanks to all Geany developers for support and writing an simple, lightweight,
+open and extensible IDE.
diff --git a/workbench/icons/16x16/Makefile.am b/workbench/icons/16x16/Makefile.am
new file mode 100644
index 000000000..b3cc7d364
--- /dev/null
+++ b/workbench/icons/16x16/Makefile.am
@@ -0,0 +1,9 @@
+iconsdir = $(datadir)/icons/hicolor/16x16
+icons_appsdir = $(iconsdir)/apps
+
+dist_icons_apps_DATA = \
+ workbench-bookmark.png \
+ workbench-dir.png \
+ workbench-nodirs.png \
+ workbench-project.png \
+ workbench-project-error.png
diff --git a/workbench/icons/16x16/workbench-bookmark.png b/workbench/icons/16x16/workbench-bookmark.png
new file mode 100644
index 000000000..672b1b27e
Binary files /dev/null and b/workbench/icons/16x16/workbench-bookmark.png differ
diff --git a/workbench/icons/16x16/workbench-dir.png b/workbench/icons/16x16/workbench-dir.png
new file mode 100644
index 000000000..993d486ae
Binary files /dev/null and b/workbench/icons/16x16/workbench-dir.png differ
diff --git a/workbench/icons/16x16/workbench-nodirs.png b/workbench/icons/16x16/workbench-nodirs.png
new file mode 100644
index 000000000..52442d047
Binary files /dev/null and b/workbench/icons/16x16/workbench-nodirs.png differ
diff --git a/workbench/icons/16x16/workbench-project-error.png b/workbench/icons/16x16/workbench-project-error.png
new file mode 100644
index 000000000..ec7315431
Binary files /dev/null and b/workbench/icons/16x16/workbench-project-error.png differ
diff --git a/workbench/icons/16x16/workbench-project.png b/workbench/icons/16x16/workbench-project.png
new file mode 100644
index 000000000..e12dc7b53
Binary files /dev/null and b/workbench/icons/16x16/workbench-project.png differ
diff --git a/workbench/icons/24x24/Makefile.am b/workbench/icons/24x24/Makefile.am
new file mode 100644
index 000000000..7f52f0f61
--- /dev/null
+++ b/workbench/icons/24x24/Makefile.am
@@ -0,0 +1,9 @@
+iconsdir = $(datadir)/icons/hicolor/24x24
+icons_appsdir = $(iconsdir)/apps
+
+dist_icons_apps_DATA = \
+ workbench-bookmark.png \
+ workbench-dir.png \
+ workbench-nodirs.png \
+ workbench-project.png \
+ workbench-project-error.png
diff --git a/workbench/icons/24x24/workbench-bookmark.png b/workbench/icons/24x24/workbench-bookmark.png
new file mode 100644
index 000000000..079cd1465
Binary files /dev/null and b/workbench/icons/24x24/workbench-bookmark.png differ
diff --git a/workbench/icons/24x24/workbench-dir.png b/workbench/icons/24x24/workbench-dir.png
new file mode 100644
index 000000000..1be6994cd
Binary files /dev/null and b/workbench/icons/24x24/workbench-dir.png differ
diff --git a/workbench/icons/24x24/workbench-nodirs.png b/workbench/icons/24x24/workbench-nodirs.png
new file mode 100644
index 000000000..35e9a75f1
Binary files /dev/null and b/workbench/icons/24x24/workbench-nodirs.png differ
diff --git a/workbench/icons/24x24/workbench-project-error.png b/workbench/icons/24x24/workbench-project-error.png
new file mode 100644
index 000000000..929157e2f
Binary files /dev/null and b/workbench/icons/24x24/workbench-project-error.png differ
diff --git a/workbench/icons/24x24/workbench-project.png b/workbench/icons/24x24/workbench-project.png
new file mode 100644
index 000000000..62a220cca
Binary files /dev/null and b/workbench/icons/24x24/workbench-project.png differ
diff --git a/workbench/icons/32x32/Makefile.am b/workbench/icons/32x32/Makefile.am
new file mode 100644
index 000000000..aa6fc0cf1
--- /dev/null
+++ b/workbench/icons/32x32/Makefile.am
@@ -0,0 +1,9 @@
+iconsdir = $(datadir)/icons/hicolor/32x32
+icons_appsdir = $(iconsdir)/apps
+
+dist_icons_apps_DATA = \
+ workbench-bookmark.png \
+ workbench-dir.png \
+ workbench-nodirs.png \
+ workbench-project.png \
+ workbench-project-error.png
diff --git a/workbench/icons/32x32/workbench-bookmark.png b/workbench/icons/32x32/workbench-bookmark.png
new file mode 100644
index 000000000..0e9a93f02
Binary files /dev/null and b/workbench/icons/32x32/workbench-bookmark.png differ
diff --git a/workbench/icons/32x32/workbench-dir.png b/workbench/icons/32x32/workbench-dir.png
new file mode 100644
index 000000000..ca4c2e7e0
Binary files /dev/null and b/workbench/icons/32x32/workbench-dir.png differ
diff --git a/workbench/icons/32x32/workbench-nodirs.png b/workbench/icons/32x32/workbench-nodirs.png
new file mode 100644
index 000000000..a242361f6
Binary files /dev/null and b/workbench/icons/32x32/workbench-nodirs.png differ
diff --git a/workbench/icons/32x32/workbench-project-error.png b/workbench/icons/32x32/workbench-project-error.png
new file mode 100644
index 000000000..be1cc2985
Binary files /dev/null and b/workbench/icons/32x32/workbench-project-error.png differ
diff --git a/workbench/icons/32x32/workbench-project.png b/workbench/icons/32x32/workbench-project.png
new file mode 100644
index 000000000..05676e2de
Binary files /dev/null and b/workbench/icons/32x32/workbench-project.png differ
diff --git a/workbench/icons/48x48/Makefile.am b/workbench/icons/48x48/Makefile.am
new file mode 100644
index 000000000..a840cc135
--- /dev/null
+++ b/workbench/icons/48x48/Makefile.am
@@ -0,0 +1,9 @@
+iconsdir = $(datadir)/icons/hicolor/48x48
+icons_appsdir = $(iconsdir)/apps
+
+dist_icons_apps_DATA = \
+ workbench-bookmark.png \
+ workbench-dir.png \
+ workbench-nodirs.png \
+ workbench-project.png \
+ workbench-project-error.png
diff --git a/workbench/icons/48x48/workbench-bookmark.png b/workbench/icons/48x48/workbench-bookmark.png
new file mode 100644
index 000000000..5469b7297
Binary files /dev/null and b/workbench/icons/48x48/workbench-bookmark.png differ
diff --git a/workbench/icons/48x48/workbench-dir.png b/workbench/icons/48x48/workbench-dir.png
new file mode 100644
index 000000000..ceab69b54
Binary files /dev/null and b/workbench/icons/48x48/workbench-dir.png differ
diff --git a/workbench/icons/48x48/workbench-nodirs.png b/workbench/icons/48x48/workbench-nodirs.png
new file mode 100644
index 000000000..cca5804c9
Binary files /dev/null and b/workbench/icons/48x48/workbench-nodirs.png differ
diff --git a/workbench/icons/48x48/workbench-project-error.png b/workbench/icons/48x48/workbench-project-error.png
new file mode 100644
index 000000000..3c4deb34c
Binary files /dev/null and b/workbench/icons/48x48/workbench-project-error.png differ
diff --git a/workbench/icons/48x48/workbench-project.png b/workbench/icons/48x48/workbench-project.png
new file mode 100644
index 000000000..704d962d4
Binary files /dev/null and b/workbench/icons/48x48/workbench-project.png differ
diff --git a/workbench/icons/Makefile.am b/workbench/icons/Makefile.am
new file mode 100644
index 000000000..9c08bc852
--- /dev/null
+++ b/workbench/icons/Makefile.am
@@ -0,0 +1,20 @@
+SUBDIRS = 16x16 24x24 32x32 48x48 scalable
+
+gtk_update_icon_cache = gtk-update-icon-cache -f -t
+
+install-data-hook:
+ @-if test -z "$(DESTDIR)"; then \
+ echo "Updating Gtk icon cache."; \
+ $(gtk_update_icon_cache) "$(datadir)/icons/hicolor"; \
+ $(gtk_update_icon_cache) "$(datadir)/icons/Tango"; \
+ else \
+ echo "*** Icon cache not updated. Remember to run:"; \
+ echo "***"; \
+ echo "*** $(gtk_update_icon_cache) '$(datadir)/icons/hicolor'";\
+ echo "*** $(gtk_update_icon_cache) '$(datadir)/icons/Tango'";\
+ echo "***"; \
+ fi
+
+uninstall-local:
+ rm -f $(DESTDIR)$(datadir)/icons/hicolor/icon-theme.cache
+ rm -f $(DESTDIR)$(datadir)/icons/Tango/icon-theme.cache
diff --git a/workbench/icons/scalable/Makefile.am b/workbench/icons/scalable/Makefile.am
new file mode 100644
index 000000000..777aa19f5
--- /dev/null
+++ b/workbench/icons/scalable/Makefile.am
@@ -0,0 +1,9 @@
+iconsdir = $(datadir)/icons/hicolor/scalable
+icons_appsdir = $(iconsdir)/apps
+
+dist_icons_apps_DATA = \
+ workbench-bookmark.svg \
+ workbench-dir.svg \
+ workbench-nodirs.svg \
+ workbench-project.svg \
+ workbench-project-error.svg
diff --git a/workbench/icons/scalable/workbench-bookmark.svg b/workbench/icons/scalable/workbench-bookmark.svg
new file mode 100644
index 000000000..4e49e3b16
--- /dev/null
+++ b/workbench/icons/scalable/workbench-bookmark.svg
@@ -0,0 +1,17 @@
+
+
+
diff --git a/workbench/icons/scalable/workbench-dir.svg b/workbench/icons/scalable/workbench-dir.svg
new file mode 100644
index 000000000..270a18461
--- /dev/null
+++ b/workbench/icons/scalable/workbench-dir.svg
@@ -0,0 +1,18 @@
+
+
+
diff --git a/workbench/icons/scalable/workbench-nodirs.svg b/workbench/icons/scalable/workbench-nodirs.svg
new file mode 100644
index 000000000..0dc04b56d
--- /dev/null
+++ b/workbench/icons/scalable/workbench-nodirs.svg
@@ -0,0 +1,20 @@
+
+
+
diff --git a/workbench/icons/scalable/workbench-project-error.svg b/workbench/icons/scalable/workbench-project-error.svg
new file mode 100644
index 000000000..5c1d0b757
--- /dev/null
+++ b/workbench/icons/scalable/workbench-project-error.svg
@@ -0,0 +1,17 @@
+
+
+
diff --git a/workbench/icons/scalable/workbench-project.svg b/workbench/icons/scalable/workbench-project.svg
new file mode 100644
index 000000000..d5dc4592b
--- /dev/null
+++ b/workbench/icons/scalable/workbench-project.svg
@@ -0,0 +1,19 @@
+
+
+
diff --git a/workbench/src/Makefile.am b/workbench/src/Makefile.am
new file mode 100644
index 000000000..459bca44a
--- /dev/null
+++ b/workbench/src/Makefile.am
@@ -0,0 +1,31 @@
+include $(top_srcdir)/build/vars.build.mk
+plugin = workbench
+
+geanyplugins_LTLIBRARIES = workbench.la
+
+workbench_la_SOURCES = \
+ plugin_main.c \
+ wb_globals.h \
+ wb_globals.c \
+ workbench.h \
+ workbench.c \
+ wb_project.h \
+ wb_project.c \
+ dialogs.h \
+ dialogs.c \
+ menu.h \
+ menu.c \
+ popup_menu.h \
+ popup_menu.c \
+ sidebar.h \
+ sidebar.c \
+ utils.h \
+ utils.c
+
+workbench_la_CPPFLAGS = $(AM_CPPFLAGS) \
+ -DG_LOG_DOMAIN=\"Workbench\"
+workbench_la_CFLAGS = $(AM_CFLAGS)
+workbench_la_LIBADD = $(COMMONLIBS)
+
+include $(top_srcdir)/build/cppcheck.mk
+
diff --git a/workbench/src/dialogs.c b/workbench/src/dialogs.c
new file mode 100644
index 000000000..2017dcaad
--- /dev/null
+++ b/workbench/src/dialogs.c
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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.
+ */
+
+/*
+ * This file contains all the code for the dialogs.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "wb_globals.h"
+#include "dialogs.h"
+
+extern GeanyPlugin *geany_plugin;
+
+/** Shows the dialog "Create new workbench".
+ *
+ * The dialog lets the user create a new workbench file (filter *.geanywb).
+ *
+ * @return The filename
+ *
+ **/
+gchar *dialogs_create_new_workbench(void)
+{
+ gchar *filename = NULL;
+ GtkWidget *dialog;
+
+ dialog = gtk_file_chooser_dialog_new(_("Create new workbench"),
+ GTK_WINDOW(wb_globals.geany_plugin->geany_data->main_widgets->window), GTK_FILE_CHOOSER_ACTION_SAVE,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("C_reate"), GTK_RESPONSE_ACCEPT, NULL);
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "new.geanywb");
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
+ {
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+ }
+
+ gtk_widget_destroy(dialog);
+
+ return filename;
+}
+
+
+/** Shows the dialog "Open workbench".
+ *
+ * The dialog lets the user choose an existing workbench file (filter *.geanywb).
+ *
+ * @return The filename
+ *
+ **/
+gchar *dialogs_open_workbench(void)
+{
+ gchar *filename = NULL;
+ GtkWidget *dialog;
+ GtkFileFilter *filter;
+
+ dialog = gtk_file_chooser_dialog_new(_("Open workbench"),
+ GTK_WINDOW(wb_globals.geany_plugin->geany_data->main_widgets->window), GTK_FILE_CHOOSER_ACTION_OPEN,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_ACCEPT, NULL);
+
+ filter = gtk_file_filter_new();
+ gtk_file_filter_set_name(filter, _("Workbench files (.geanywb)"));
+ gtk_file_filter_add_pattern(filter, "*.geanywb");
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
+ filter = gtk_file_filter_new();
+ gtk_file_filter_set_name(filter, _("All Files"));
+ gtk_file_filter_add_pattern(filter, "*");
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
+ {
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+ }
+
+ gtk_widget_destroy(dialog);
+
+ return filename;
+}
+
+
+/** Shows the dialog "Add project".
+ *
+ * The dialog lets the user choose an existing project file
+ * (filter *.geany or *).
+ *
+ * @return The filename
+ *
+ **/
+gchar *dialogs_add_project(void)
+{
+ gchar *filename = NULL;
+ GtkWidget *dialog;
+ GtkFileFilter *filter;
+
+ dialog = gtk_file_chooser_dialog_new(_("Add project"),
+ GTK_WINDOW(wb_globals.geany_plugin->geany_data->main_widgets->window), GTK_FILE_CHOOSER_ACTION_OPEN,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Add"), GTK_RESPONSE_ACCEPT, NULL);
+
+ filter = gtk_file_filter_new();
+ gtk_file_filter_set_name(filter, _("Project files (.geany)"));
+ gtk_file_filter_add_pattern(filter, "*.geany");
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
+ filter = gtk_file_filter_new();
+ gtk_file_filter_set_name(filter, _("All Files"));
+ gtk_file_filter_add_pattern(filter, "*");
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
+ {
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+ }
+
+ gtk_widget_destroy(dialog);
+
+ return filename;
+}
+
+
+/** Shows the dialog "Add directory".
+ *
+ * The dialog lets the user choose an existing folder.
+ *
+ * @return The filename
+ *
+ **/
+gchar *dialogs_add_directory(WB_PROJECT *project)
+{
+ gchar *filename = NULL;
+ GtkWidget *dialog;
+
+ dialog = gtk_file_chooser_dialog_new(_("Add directory"),
+ GTK_WINDOW(wb_globals.geany_plugin->geany_data->main_widgets->window), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Add"), GTK_RESPONSE_ACCEPT, NULL);
+ if (project != NULL)
+ {
+ const gchar *path;
+
+ /* Set the current folder to the location of the project file */
+ path = wb_project_get_filename(project);
+ if (path != NULL)
+ {
+ gchar *dirname = g_path_get_dirname(path);
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), dirname);
+ g_free(dirname);
+ }
+ }
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
+ {
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+ }
+
+ gtk_widget_destroy(dialog);
+
+ return filename;
+}
+
+
+/* Split patterns in str into gchar** */
+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;
+}
+
+
+/** Shows the directory settings dialog.
+ *
+ * The dialog lets the user edit the settings for file patterns, irnored file patterns and
+ * ignored directories patterns. On accept the result is directly stored in @a directory.
+ *
+ * @param directory Location of WB_PROJECT_DIR to store the settings into
+ * @return TRUE if the settings have changed, FALSE otherwise
+ *
+ **/
+gboolean dialogs_directory_settings(WB_PROJECT_DIR *directory)
+{
+ GtkWidget *w_file_patterns, *w_ignored_dirs_patterns, *w_ignored_file_patterns;
+ GtkWidget *dialog, *label, *content_area;
+ GtkWidget *vbox, *hbox, *hbox1, *table;
+ GtkDialogFlags flags;
+ gchar *file_patterns_old, *ignored_file_patterns_old, *ignored_dirs_patterns_old;
+ gboolean changed;
+
+ /* Create the widgets */
+ flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
+ dialog = gtk_dialog_new_with_buttons(_("Directory settings"),
+ GTK_WINDOW(wb_globals.geany_plugin->geany_data->main_widgets->window),
+ flags,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_ACCEPT,
+ NULL);
+ content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
+
+ vbox = gtk_vbox_new(FALSE, 0);
+
+ table = gtk_table_new(5, 2, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 5);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 10);
+
+ label = gtk_label_new(_("File patterns:"));
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+ w_file_patterns = gtk_entry_new();
+ ui_table_add_row(GTK_TABLE(table), 0, label, w_file_patterns, NULL);
+ ui_entry_add_clear_icon(GTK_ENTRY(w_file_patterns));
+ gtk_widget_set_tooltip_text(w_file_patterns,
+ _("Space separated list of patterns that are used to identify files "
+ "that shall be displayed in the directory tree."));
+ file_patterns_old = g_strjoinv(" ", wb_project_dir_get_file_patterns(directory));
+ gtk_entry_set_text(GTK_ENTRY(w_file_patterns), file_patterns_old);
+
+ label = gtk_label_new(_("Ignored file patterns:"));
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+ w_ignored_file_patterns = gtk_entry_new();
+ ui_entry_add_clear_icon(GTK_ENTRY(w_ignored_file_patterns));
+ ui_table_add_row(GTK_TABLE(table), 2, label, w_ignored_file_patterns, NULL);
+ gtk_widget_set_tooltip_text(w_ignored_file_patterns,
+ _("Space separated list of patterns that are used to identify files "
+ "that shall not be displayed in the directory tree."));
+ ignored_file_patterns_old = g_strjoinv(" ", wb_project_dir_get_ignored_file_patterns(directory));
+ gtk_entry_set_text(GTK_ENTRY(w_ignored_file_patterns), ignored_file_patterns_old);
+
+ label = gtk_label_new(_("Ignored directory patterns:"));
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+ w_ignored_dirs_patterns = gtk_entry_new();
+ ui_entry_add_clear_icon(GTK_ENTRY(w_ignored_dirs_patterns));
+ ui_table_add_row(GTK_TABLE(table), 3, label, w_ignored_dirs_patterns, NULL);
+ gtk_widget_set_tooltip_text(w_ignored_dirs_patterns,
+ _("Space separated list of patterns that are used to identify directories "
+ "that shall not be scanned for source files."));
+ ignored_dirs_patterns_old = g_strjoinv(" ", wb_project_dir_get_ignored_dirs_patterns(directory));
+ gtk_entry_set_text(GTK_ENTRY(w_ignored_dirs_patterns), ignored_dirs_patterns_old);
+
+ gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 6);
+
+ hbox1 = gtk_hbox_new(FALSE, 0);
+ label = gtk_label_new(_("Note: the patterns above affect only the workbench directory and are not used in the Find in Files\n"
+ "dialog."));
+ gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox1, FALSE, FALSE, 6);
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 6);
+
+ /* Add the label, and show everything we’ve added */
+ gtk_container_add(GTK_CONTAINER (content_area), label);
+ gtk_container_add(GTK_CONTAINER (content_area), hbox);
+
+ gtk_widget_show_all(dialog);
+ gint result = gtk_dialog_run(GTK_DIALOG(dialog));
+ changed = FALSE;
+ if (result == GTK_RESPONSE_ACCEPT)
+ {
+ const gchar *str;
+ gchar **file_patterns, **ignored_dirs_patterns, **ignored_file_patterns;
+
+ str = gtk_entry_get_text(GTK_ENTRY(w_file_patterns));
+ if (g_strcmp0(str, file_patterns_old) != 0)
+ {
+ changed = TRUE;
+ }
+ file_patterns = split_patterns(str);
+
+ str = gtk_entry_get_text(GTK_ENTRY(w_ignored_dirs_patterns));
+ if (g_strcmp0(str, ignored_dirs_patterns_old) != 0)
+ {
+ changed = TRUE;
+ }
+ ignored_dirs_patterns = split_patterns(str);
+
+ str = gtk_entry_get_text(GTK_ENTRY(w_ignored_file_patterns));
+ if (g_strcmp0(str, ignored_file_patterns_old) != 0)
+ {
+ changed = TRUE;
+ }
+ ignored_file_patterns = split_patterns(str);
+
+ wb_project_dir_set_file_patterns(directory, file_patterns);
+ wb_project_dir_set_ignored_dirs_patterns(directory, ignored_dirs_patterns);
+ wb_project_dir_set_ignored_file_patterns(directory, ignored_file_patterns);
+
+ g_strfreev(file_patterns);
+ g_strfreev(ignored_dirs_patterns);
+ g_strfreev(ignored_file_patterns);
+ }
+
+ g_free(file_patterns_old);
+ g_free(ignored_file_patterns_old);
+ g_free(ignored_dirs_patterns_old);
+ gtk_widget_destroy(dialog);
+
+ return changed;
+}
+
+
+/** Shows the workbench settings dialog.
+ *
+ * The dialog lets the user edit the settings for option "Rescan all projects on open".
+ * On accept the result is directly stored in @a workbench.
+ *
+ * @param workbench Location of WORKBENCH to store the settings into
+ * @return TRUE if the settings have changed, FALSE otherwise
+ *
+ **/
+gboolean dialogs_workbench_settings(WORKBENCH *workbench)
+{
+ gint result;
+ GtkWidget *w_rescan_projects_on_open;
+ GtkWidget *dialog, *label, *content_area;
+ GtkWidget *vbox, *hbox, *table;
+ GtkDialogFlags flags;
+ gboolean changed, rescan_projects_on_open, rescan_projects_on_open_old;
+
+ /* Create the widgets */
+ flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
+ dialog = gtk_dialog_new_with_buttons(_("Workbench settings"),
+ GTK_WINDOW(wb_globals.geany_plugin->geany_data->main_widgets->window),
+ flags,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_ACCEPT,
+ NULL);
+ content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
+
+ vbox = gtk_vbox_new(FALSE, 0);
+
+ table = gtk_table_new(5, 2, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 5);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 10);
+
+ label = gtk_label_new(_("Rescan all projects on open:"));
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+ w_rescan_projects_on_open = gtk_check_button_new();
+ ui_table_add_row(GTK_TABLE(table), 0, label, w_rescan_projects_on_open, NULL);
+ gtk_widget_set_tooltip_text(w_rescan_projects_on_open,
+ _("If the option is activated (default), then all projects will be re-scanned"
+ " on opening of the workbench."));
+ rescan_projects_on_open_old = workbench_get_rescan_projects_on_open(workbench);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_rescan_projects_on_open), rescan_projects_on_open_old);
+
+ gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 6);
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 6);
+
+ /* Show everything we’ve added, run dialog */
+ gtk_container_add(GTK_CONTAINER (content_area), hbox);
+ gtk_widget_show_all(dialog);
+ result = gtk_dialog_run(GTK_DIALOG(dialog));
+
+ changed = FALSE;
+ if (result == GTK_RESPONSE_ACCEPT)
+ {
+ rescan_projects_on_open = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w_rescan_projects_on_open));
+ if (rescan_projects_on_open != rescan_projects_on_open_old)
+ {
+ changed = TRUE;
+ workbench_set_rescan_projects_on_open(workbench, rescan_projects_on_open);
+ }
+ }
+
+ gtk_widget_destroy(dialog);
+
+ return changed;
+}
diff --git a/workbench/src/dialogs.h b/workbench/src/dialogs.h
new file mode 100644
index 000000000..025d5d683
--- /dev/null
+++ b/workbench/src/dialogs.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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 __WB_DIALOGS_H__
+#define __WB_DIALOGS_H__
+
+gchar *dialogs_create_new_workbench(void);
+gchar *dialogs_open_workbench(void);
+gchar *dialogs_add_project(void);
+gchar *dialogs_add_directory(WB_PROJECT *project);
+gboolean dialogs_directory_settings(WB_PROJECT_DIR *directory);
+gboolean dialogs_workbench_settings(WORKBENCH *workbench);
+
+#endif
diff --git a/workbench/src/menu.c b/workbench/src/menu.c
new file mode 100644
index 000000000..3509692e0
--- /dev/null
+++ b/workbench/src/menu.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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.
+ */
+
+/*
+ * Code for the "Workbench" menu.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "wb_globals.h"
+#include "dialogs.h"
+#include "menu.h"
+#include "sidebar.h"
+
+extern GeanyPlugin *geany_plugin;
+
+typedef struct
+{
+ GtkWidget *menu;
+ GtkWidget *root_item;
+ GtkWidget *item_new;
+ GtkWidget *item_open;
+ GtkWidget *item_save;
+ GtkWidget *item_settings;
+ GtkWidget *item_close;
+}WB_MENU_DATA;
+static WB_MENU_DATA menu_data;
+
+/** Set the context of the workbench menu.
+ *
+ * The context set controls which items in the menu will be active and
+ * which will be deactive.
+ *
+ * @param context The context/situation in which the menu is/shall be
+ *
+ **/
+void menu_set_context(MENU_CONTEXT context)
+{
+ switch (context)
+ {
+ case MENU_CONTEXT_WB_CREATED:
+ case MENU_CONTEXT_WB_OPENED:
+ gtk_widget_set_sensitive(menu_data.item_new, FALSE);
+ gtk_widget_set_sensitive(menu_data.item_open, FALSE);
+ gtk_widget_set_sensitive(menu_data.item_save, TRUE);
+ gtk_widget_set_sensitive(menu_data.item_settings, TRUE);
+ gtk_widget_set_sensitive(menu_data.item_close, TRUE);
+ break;
+ case MENU_CONTEXT_WB_CLOSED:
+ gtk_widget_set_sensitive(menu_data.item_new, TRUE);
+ gtk_widget_set_sensitive(menu_data.item_open, TRUE);
+ gtk_widget_set_sensitive(menu_data.item_save, FALSE);
+ gtk_widget_set_sensitive(menu_data.item_settings, FALSE);
+ gtk_widget_set_sensitive(menu_data.item_close, FALSE);
+ break;
+ }
+}
+
+/* The function handles the menu item "New workbench" */
+static void item_new_workbench_activate_cb(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ gchar *filename;
+ GError *error = NULL;
+
+ filename = dialogs_create_new_workbench();
+ if (filename == NULL)
+ {
+ return;
+ }
+ wb_globals.opened_wb = workbench_new();
+ workbench_set_filename(wb_globals.opened_wb, filename);
+ if (workbench_save(wb_globals.opened_wb, &error))
+ {
+ menu_set_context(MENU_CONTEXT_WB_CREATED);
+ sidebar_update(SIDEBAR_CONTEXT_WB_CREATED, NULL);
+ }
+ else
+ {
+ dialogs_show_msgbox(GTK_MESSAGE_INFO, _("Could not create new workbench file: %s"), error->message);
+ workbench_free(wb_globals.opened_wb);
+ wb_globals.opened_wb = NULL;
+ }
+ g_free(filename);
+}
+
+
+/* The function handles the menu item "Open workbench" */
+static void item_open_workbench_activate_cb(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ gchar *filename;
+ GError *error = NULL;
+
+ filename = dialogs_open_workbench();
+ if (filename == NULL)
+ {
+ return;
+ }
+ wb_globals.opened_wb = workbench_new();
+ if (workbench_load(wb_globals.opened_wb, filename, &error))
+ {
+ menu_set_context(MENU_CONTEXT_WB_OPENED);
+ sidebar_update(SIDEBAR_CONTEXT_WB_OPENED, NULL);
+ }
+ else
+ {
+ dialogs_show_msgbox(GTK_MESSAGE_INFO, _("Could not open workbench file: %s"), error->message);
+ workbench_free(wb_globals.opened_wb);
+ wb_globals.opened_wb = NULL;
+ }
+ g_free(filename);
+}
+
+
+/* The function handles the menu item "Save workbench" */
+static void item_save_workbench_activate_cb(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ GError *error = NULL;
+
+ if (wb_globals.opened_wb != NULL)
+ {
+ if (!workbench_save(wb_globals.opened_wb, &error))
+ {
+ dialogs_show_msgbox(GTK_MESSAGE_INFO, _("Could not save workbench file: %s"), error->message);
+ }
+ sidebar_update(SIDEBAR_CONTEXT_WB_SAVED, NULL);
+ }
+}
+
+
+/* The function handles the menu item "Settings" */
+static void item_workbench_settings_activate_cb(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ if (wb_globals.opened_wb != NULL)
+ {
+ if (dialogs_workbench_settings(wb_globals.opened_wb))
+ {
+ sidebar_update(SIDEBAR_CONTEXT_WB_SETTINGS_CHANGED, NULL);
+ }
+ }
+}
+
+
+/* The function handles the menu item "Close workbench" */
+static void item_close_workbench_activate_cb(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ workbench_free(wb_globals.opened_wb);
+ wb_globals.opened_wb = NULL;
+
+ menu_set_context(MENU_CONTEXT_WB_CLOSED);
+ sidebar_update(SIDEBAR_CONTEXT_WB_CLOSED, NULL);
+}
+
+
+/** Setup the workbench menu.
+ *
+ **/
+gboolean menu_init(void)
+{
+ /* Create menu and root item/label */
+ menu_data.menu = gtk_menu_new();
+ menu_data.root_item = gtk_menu_item_new_with_label(_("Workbench"));
+ gtk_widget_show(menu_data.root_item);
+
+ /* Create new menu item "New Workbench" */
+ menu_data.item_new = gtk_menu_item_new_with_mnemonic(_("_New..."));
+ gtk_widget_show(menu_data.item_new);
+ gtk_menu_shell_append(GTK_MENU_SHELL (menu_data.menu), menu_data.item_new);
+ g_signal_connect(menu_data.item_new, "activate",
+ G_CALLBACK(item_new_workbench_activate_cb), NULL);
+
+ /* Create new menu item "Open Workbench" */
+ menu_data.item_open = gtk_menu_item_new_with_mnemonic(_("_Open..."));
+ gtk_widget_show(menu_data.item_open);
+ gtk_menu_shell_append(GTK_MENU_SHELL (menu_data.menu), menu_data.item_open);
+ g_signal_connect(menu_data.item_open, "activate",
+ G_CALLBACK(item_open_workbench_activate_cb), NULL);
+
+ /* Create new menu item "Save Workbench" */
+ menu_data.item_save = gtk_menu_item_new_with_mnemonic(_("_Save"));
+ gtk_widget_show(menu_data.item_save);
+ gtk_menu_shell_append(GTK_MENU_SHELL (menu_data.menu), menu_data.item_save);
+ g_signal_connect(menu_data.item_save, "activate",
+ G_CALLBACK(item_save_workbench_activate_cb), NULL);
+
+ /* Create new menu item "Workbench Settings" */
+ menu_data.item_settings = gtk_menu_item_new_with_mnemonic(_("S_ettings"));
+ gtk_widget_show(menu_data.item_settings);
+ gtk_menu_shell_append(GTK_MENU_SHELL (menu_data.menu), menu_data.item_settings);
+ g_signal_connect(menu_data.item_settings, "activate",
+ G_CALLBACK(item_workbench_settings_activate_cb), NULL);
+
+ /* Create new menu item "Close Workbench" */
+ menu_data.item_close = gtk_menu_item_new_with_mnemonic(_("_Close"));
+ gtk_widget_show(menu_data.item_close);
+ gtk_menu_shell_append(GTK_MENU_SHELL (menu_data.menu), menu_data.item_close);
+ g_signal_connect(menu_data.item_close, "activate",
+ G_CALLBACK(item_close_workbench_activate_cb), NULL);
+
+ /* Add our menu to the main window (left of the help menu) */
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_data.root_item), menu_data.menu);
+ gtk_container_add(GTK_CONTAINER(wb_globals.geany_plugin->geany_data->main_widgets->tools_menu), menu_data.root_item);
+
+ return TRUE;
+}
+
+
+/** Cleanup menu data/mem.
+ *
+ **/
+void menu_cleanup (void)
+{
+ gtk_widget_destroy(menu_data.root_item);
+}
diff --git a/workbench/src/menu.h b/workbench/src/menu.h
new file mode 100644
index 000000000..a72897dbb
--- /dev/null
+++ b/workbench/src/menu.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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 __WB_MENU_H__
+#define __WB_MENU_H__
+
+typedef enum
+{
+ MENU_CONTEXT_WB_CREATED,
+ MENU_CONTEXT_WB_OPENED,
+ MENU_CONTEXT_WB_CLOSED,
+}MENU_CONTEXT;
+
+void menu_set_context(MENU_CONTEXT context);
+gboolean menu_init(void);
+void menu_cleanup (void);
+
+#endif
diff --git a/workbench/src/plugin_main.c b/workbench/src/plugin_main.c
new file mode 100644
index 000000000..adfaf84b0
--- /dev/null
+++ b/workbench/src/plugin_main.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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.
+ */
+
+/*
+ * Code for plugin setup/registration.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include
+#include
+
+#include
+
+#include "sidebar.h"
+#include "menu.h"
+#include "popup_menu.h"
+
+GeanyPlugin *geany_plugin;
+GeanyData *geany_data;
+
+/* Callback function for document open */
+static void plugin_workbench_on_doc_open(G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED GeanyDocument * doc,
+ G_GNUC_UNUSED gpointer user_data)
+{
+ WB_PROJECT *project;
+
+ g_return_if_fail(doc != NULL && doc->file_name != NULL);
+
+ project = workbench_file_is_included(wb_globals.opened_wb, doc->file_name);
+ if (project != NULL)
+ {
+ wb_project_remove_single_tm_file(project, doc->file_name);
+ }
+}
+
+
+/* Callback function for document close */
+static void plugin_workbench_on_doc_close(G_GNUC_UNUSED GObject * obj, GeanyDocument * doc,
+ G_GNUC_UNUSED gpointer user_data)
+{
+ WB_PROJECT *project;
+
+ g_return_if_fail(doc != NULL);
+
+ if (doc->file_name == NULL)
+ {
+ return;
+ }
+
+ /* tags of open files managed by geany - when the file gets closed,
+ * we should take care of it */
+ project = workbench_file_is_included(wb_globals.opened_wb, doc->file_name);
+ if (project != NULL)
+ {
+ wb_project_add_single_tm_file(project, doc->file_name);
+ }
+}
+
+
+/* Initialize plugin */
+static gboolean plugin_workbench_init(GeanyPlugin *plugin, G_GNUC_UNUSED gpointer pdata)
+{
+ /* Init/Update globals */
+ workbench_globals_init();
+ wb_globals.geany_plugin = plugin;
+ geany_plugin = plugin;
+ geany_data = plugin->geany_data;
+
+ menu_init();
+ sidebar_init();
+ popup_menu_init();
+
+ /* At start there is no workbench open:
+ deactive save and close menu item and sidebar */
+ menu_set_context(MENU_CONTEXT_WB_CLOSED);
+ sidebar_show_intro_message(_("Create or open a workbench\nusing the workbench menu."), FALSE);
+
+ return TRUE;
+}
+
+
+/* Cleanup plugin */
+static void plugin_workbench_cleanup(G_GNUC_UNUSED GeanyPlugin *plugin, G_GNUC_UNUSED gpointer pdata)
+{
+ menu_cleanup();
+ sidebar_cleanup();
+}
+
+
+/* Show help */
+static void plugin_workbench_help (G_GNUC_UNUSED GeanyPlugin *plugin, G_GNUC_UNUSED gpointer pdata)
+{
+ utils_open_browser("http://plugins.geany.org/workbench.html");
+}
+
+
+static PluginCallback plugin_workbench_callbacks[] = {
+ {"document-open", (GCallback) &plugin_workbench_on_doc_open, TRUE, NULL},
+ {"document-close", (GCallback) &plugin_workbench_on_doc_close, TRUE, NULL},
+ {NULL, NULL, FALSE, NULL}
+};
+
+
+/* Load module */
+G_MODULE_EXPORT
+void geany_load_module(GeanyPlugin *plugin)
+{
+ /* Setup translation */
+ main_locale_init(LOCALEDIR, GETTEXT_PACKAGE);
+
+ /* Set metadata */
+ plugin->info->name = _("Workbench");
+ plugin->info->description = _("Manage and customize multiple projects.");
+ plugin->info->version = "1.0";
+ plugin->info->author = "LarsGit223";
+
+ /* Set functions */
+ plugin->funcs->init = plugin_workbench_init;
+ plugin->funcs->cleanup = plugin_workbench_cleanup;
+ plugin->funcs->help = plugin_workbench_help;
+ plugin->funcs->callbacks = plugin_workbench_callbacks;
+
+ /* Register! */
+ GEANY_PLUGIN_REGISTER(plugin, 225);
+}
diff --git a/workbench/src/popup_menu.c b/workbench/src/popup_menu.c
new file mode 100644
index 000000000..070f70810
--- /dev/null
+++ b/workbench/src/popup_menu.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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.
+ */
+
+/*
+ * Code for the popup menu.
+ */
+#include
+#include
+#include
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#include "wb_globals.h"
+#include "dialogs.h"
+#include "sidebar.h"
+#include "popup_menu.h"
+
+static struct
+{
+ GtkWidget *widget;
+
+ GtkWidget *add_project;
+ GtkWidget *save_project;
+ GtkWidget *remove_project;
+ GtkWidget *fold_unfold_project;
+ GtkWidget *add_directory;
+ GtkWidget *remove_directory;
+ GtkWidget *rescan_directory;
+ GtkWidget *directory_settings;
+ GtkWidget *fold_unfold_directory;
+ GtkWidget *expand_all;
+ GtkWidget *collapse_all;
+ GtkWidget *add_wb_bookmark;
+ GtkWidget *add_prj_bookmark;
+ GtkWidget *remove_bookmark;
+} s_popup_menu;
+
+
+/** Show the popup menu.
+ *
+ * The function shows the popup menu. The value of context decides which popup menu items
+ * are enabled and which are disabled.
+ *
+ * @param context The context/situation in which the popup menu was called
+ * @param event Button event.
+ *
+ **/
+void popup_menu_show(POPUP_CONTEXT context, GdkEventButton *event)
+{
+ switch (context)
+ {
+ case POPUP_CONTEXT_PROJECT:
+ gtk_widget_set_sensitive (s_popup_menu.add_project, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_project, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.fold_unfold_project, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.add_directory, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_directory, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.rescan_directory, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.directory_settings, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.fold_unfold_directory, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.add_wb_bookmark, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.add_prj_bookmark, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_bookmark, FALSE);
+ break;
+ case POPUP_CONTEXT_DIRECTORY:
+ case POPUP_CONTEXT_FOLDER:
+ gtk_widget_set_sensitive (s_popup_menu.add_project, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_project, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.fold_unfold_project, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.add_directory, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_directory, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.rescan_directory, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.directory_settings, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.fold_unfold_directory, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.add_wb_bookmark, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.add_prj_bookmark, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_bookmark, FALSE);
+ break;
+ case POPUP_CONTEXT_FILE:
+ gtk_widget_set_sensitive (s_popup_menu.add_project, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_project, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.fold_unfold_project, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.add_directory, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_directory, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.rescan_directory, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.directory_settings, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.fold_unfold_directory, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.add_wb_bookmark, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.add_prj_bookmark, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_bookmark, FALSE);
+ break;
+ case POPUP_CONTEXT_BACKGROUND:
+ gtk_widget_set_sensitive (s_popup_menu.add_project, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_project, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.fold_unfold_project, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.add_directory, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_directory, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.rescan_directory, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.directory_settings, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.fold_unfold_directory, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.add_wb_bookmark, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.add_prj_bookmark, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_bookmark, FALSE);
+ break;
+ case POPUP_CONTEXT_WB_BOOKMARK:
+ gtk_widget_set_sensitive (s_popup_menu.add_project, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_project, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.fold_unfold_project, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.add_directory, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_directory, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.rescan_directory, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.directory_settings, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.fold_unfold_directory, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.add_wb_bookmark, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.add_prj_bookmark, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_bookmark, TRUE);
+ break;
+ case POPUP_CONTEXT_PRJ_BOOKMARK:
+ gtk_widget_set_sensitive (s_popup_menu.add_project, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_project, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.fold_unfold_project, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.add_directory, TRUE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_directory, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.rescan_directory, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.directory_settings, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.fold_unfold_directory, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.add_wb_bookmark, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.add_prj_bookmark, FALSE);
+ gtk_widget_set_sensitive (s_popup_menu.remove_bookmark, TRUE);
+ break;
+ }
+ gtk_menu_popup(GTK_MENU(s_popup_menu.widget), NULL, NULL, NULL, NULL,
+ event->button, event->time);
+}
+
+
+/* Handle popup menu item "Add project" */
+static void popup_menu_on_add_project(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ gchar *filename;
+
+ filename = dialogs_add_project();
+ if (filename == NULL || wb_globals.opened_wb == NULL)
+ {
+ return;
+ }
+
+ if (workbench_add_project(wb_globals.opened_wb, filename))
+ {
+ sidebar_update(SIDEBAR_CONTEXT_PROJECT_ADDED, NULL);
+ }
+ else
+ {
+ dialogs_show_msgbox(GTK_MESSAGE_INFO, _("Could not add project file: %s"), filename);
+ }
+ g_free(filename);
+}
+
+
+/* Handle popup menu item "Save project" */
+static void popup_menu_on_save_project(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ GError *error = NULL;
+ WB_PROJECT *project;
+
+ project = sidebar_file_view_get_selected_project(NULL);
+ if (project != NULL && wb_globals.opened_wb != NULL)
+ {
+ if (wb_project_save(project, &error))
+ {
+ SIDEBAR_CONTEXT context;
+
+ memset(&context, 0, sizeof(context));
+ context.project = project;
+ sidebar_update(SIDEBAR_CONTEXT_PROJECT_SAVED, &context);
+ }
+ }
+}
+
+
+/* Handle popup menu item "Remove project" */
+static void popup_menu_on_remove_project(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ WB_PROJECT *project;
+
+ project = sidebar_file_view_get_selected_project(NULL);
+ if (project != NULL && wb_globals.opened_wb != NULL)
+ {
+ if (workbench_remove_project_with_address(wb_globals.opened_wb, project))
+ {
+ SIDEBAR_CONTEXT context;
+
+ memset(&context, 0, sizeof(context));
+ context.project = project;
+ sidebar_update(SIDEBAR_CONTEXT_PROJECT_REMOVED, &context);
+ }
+ }
+}
+
+
+/* Handle popup menu item "Expand all" */
+static void popup_menu_on_expand_all(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ sidebar_expand_all();
+}
+
+
+/* Handle popup menu item "Collapse all" */
+static void popup_menu_on_collapse_all(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ sidebar_collapse_all();
+}
+
+
+/* Handle popup menu item "Fold/unfold project" */
+static void popup_menu_on_fold_unfold_project(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ sidebar_toggle_selected_project_expansion();
+}
+
+
+/* Handle popup menu item "Add directory" */
+static void popup_menu_on_add_directory(G_GNUC_UNUSED GtkMenuItem * menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ gchar *dirname;
+ WB_PROJECT *project;
+
+ project = sidebar_file_view_get_selected_project(NULL);
+ if (project != NULL)
+ {
+ dirname = dialogs_add_directory(project);
+ if (dirname != NULL)
+ {
+ SIDEBAR_CONTEXT context;
+
+ memset(&context, 0, sizeof(context));
+ context.project = project;
+ wb_project_add_directory(project, dirname);
+ sidebar_update(SIDEBAR_CONTEXT_DIRECTORY_ADDED, &context);
+ g_free(dirname);
+ }
+ }
+}
+
+
+/* Handle popup menu item "Remove directory" */
+static void popup_menu_on_remove_directory(G_GNUC_UNUSED GtkMenuItem * menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ SIDEBAR_CONTEXT context;
+
+ if (sidebar_file_view_get_selected_context(&context)
+ && context.project != NULL && context.directory != NULL)
+ {
+ wb_project_remove_directory(context.project, context.directory);
+ sidebar_update(SIDEBAR_CONTEXT_DIRECTORY_REMOVED, &context);
+ }
+}
+
+
+/* Handle popup menu item "Rescan directory" */
+static void popup_menu_on_rescan_directory(G_GNUC_UNUSED GtkMenuItem * menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ SIDEBAR_CONTEXT context;
+
+ if (sidebar_file_view_get_selected_context(&context)
+ && context.project != NULL && context.directory != NULL)
+ {
+ wb_project_dir_rescan(context.project, context.directory);
+ sidebar_update(SIDEBAR_CONTEXT_DIRECTORY_RESCANNED, &context);
+ }
+}
+
+
+/* Handle popup menu item "Directory settings" */
+static void popup_menu_on_directory_settings(G_GNUC_UNUSED GtkMenuItem * menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ SIDEBAR_CONTEXT context;
+
+ if (sidebar_file_view_get_selected_context(&context)
+ && context.project != NULL && context.directory != NULL)
+ {
+ if (dialogs_directory_settings(context.directory))
+ {
+ wb_project_set_modified(context.project, TRUE);
+ wb_project_dir_rescan(context.project, context.directory);
+ sidebar_update(SIDEBAR_CONTEXT_DIRECTORY_SETTINGS_CHANGED, &context);
+ }
+ }
+}
+
+
+/* Handle popup menu item "Directory settings" */
+static void popup_menu_on_fold_unfold_directory(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ sidebar_toggle_selected_project_dir_expansion();
+}
+
+
+/* Handle popup menu item "Add to workbench bookmarks" */
+static void popup_menu_on_add_to_workbench_bookmarks(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ SIDEBAR_CONTEXT context;
+
+ if (sidebar_file_view_get_selected_context(&context)
+ && context.file != NULL)
+ {
+ workbench_add_bookmark(wb_globals.opened_wb, context.file);
+ sidebar_update(SIDEBAR_CONTEXT_WB_BOOKMARK_ADDED, &context);
+ }
+}
+
+
+/* Handle popup menu item "Add to project bookmarks" */
+static void popup_menu_on_add_to_project_bookmarks(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ SIDEBAR_CONTEXT context;
+
+ if (sidebar_file_view_get_selected_context(&context)
+ && context.project != NULL && context.file != NULL)
+ {
+ wb_project_add_bookmark(context.project, context.file);
+ sidebar_update(SIDEBAR_CONTEXT_PRJ_BOOKMARK_ADDED, &context);
+ }
+}
+
+
+/* Handle popup menu item "Remove from bookmarks" */
+static void popup_menu_on_remove_from_bookmarks(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
+{
+ SIDEBAR_CONTEXT context;
+
+ if (sidebar_file_view_get_selected_context(&context)
+ && context.wb_bookmark != NULL)
+ {
+ workbench_remove_bookmark(wb_globals.opened_wb, context.wb_bookmark);
+ sidebar_update(SIDEBAR_CONTEXT_WB_BOOKMARK_REMOVED, &context);
+ }
+ if (sidebar_file_view_get_selected_context(&context)
+ && context.project != NULL && context.prj_bookmark != NULL)
+ {
+ wb_project_remove_bookmark(context.project, context.prj_bookmark);
+ sidebar_update(SIDEBAR_CONTEXT_PRJ_BOOKMARK_REMOVED, &context);
+ }
+}
+
+
+/** Setup/Initialize the popup menu.
+ *
+ **/
+void popup_menu_init(void)
+{
+ GtkWidget *item;
+
+ s_popup_menu.widget = gtk_menu_new();
+
+ item = gtk_menu_item_new_with_mnemonic(_("_Add project..."));
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect(item, "activate", G_CALLBACK(popup_menu_on_add_project), NULL);
+ s_popup_menu.add_project = item;
+
+ item = gtk_menu_item_new_with_mnemonic(_("_Save project"));
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect(item, "activate", G_CALLBACK(popup_menu_on_save_project), NULL);
+ s_popup_menu.save_project = item;
+
+ item = gtk_menu_item_new_with_mnemonic(_("_Remove project"));
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect(item, "activate", G_CALLBACK(popup_menu_on_remove_project), NULL);
+ s_popup_menu.remove_project = item;
+
+ item = gtk_menu_item_new_with_mnemonic(_("_Fold/unfold project"));
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect(item, "activate", G_CALLBACK(popup_menu_on_fold_unfold_project), NULL);
+ s_popup_menu.fold_unfold_project = item;
+
+ item = gtk_separator_menu_item_new();
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+
+ item = gtk_menu_item_new_with_mnemonic(_("_Add directory..."));
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect(item, "activate", G_CALLBACK(popup_menu_on_add_directory), NULL);
+ s_popup_menu.add_directory = item;
+
+ item = gtk_menu_item_new_with_mnemonic(_("_Remove directory"));
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect(item, "activate", G_CALLBACK(popup_menu_on_remove_directory), NULL);
+ s_popup_menu.remove_directory = item;
+
+ item = gtk_menu_item_new_with_mnemonic(_("_Rescan directory"));
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect(item, "activate", G_CALLBACK(popup_menu_on_rescan_directory), NULL);
+ s_popup_menu.rescan_directory = item;
+
+ item = gtk_menu_item_new_with_mnemonic(_("_Directory settings"));
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect(item, "activate", G_CALLBACK(popup_menu_on_directory_settings), NULL);
+ s_popup_menu.directory_settings = item;
+
+ item = gtk_menu_item_new_with_mnemonic(_("_Fold/unfold directory"));
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect(item, "activate", G_CALLBACK(popup_menu_on_fold_unfold_directory), NULL);
+ s_popup_menu.fold_unfold_directory = item;
+
+ item = gtk_separator_menu_item_new();
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+
+ item = gtk_menu_item_new_with_mnemonic(_("_Expand all"));
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect(item, "activate", G_CALLBACK(popup_menu_on_expand_all), NULL);
+ s_popup_menu.expand_all = item;
+
+ item = gtk_menu_item_new_with_mnemonic(_("_Collapse all"));
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect(item, "activate", G_CALLBACK(popup_menu_on_collapse_all), NULL);
+ s_popup_menu.collapse_all = item;
+
+ item = gtk_separator_menu_item_new();
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+
+ item = gtk_menu_item_new_with_mnemonic(_("_Add to Workbench Bookmarks"));
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect(item, "activate", G_CALLBACK(popup_menu_on_add_to_workbench_bookmarks), NULL);
+ s_popup_menu.add_wb_bookmark = item;
+
+ item = gtk_menu_item_new_with_mnemonic(_("_Add to Project Bookmarks"));
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect(item, "activate", G_CALLBACK(popup_menu_on_add_to_project_bookmarks), NULL);
+ s_popup_menu.add_prj_bookmark = item;
+
+ item = gtk_menu_item_new_with_mnemonic(_("_Remove from Bookmarks"));
+ gtk_widget_show(item);
+ gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ g_signal_connect(item, "activate", G_CALLBACK(popup_menu_on_remove_from_bookmarks), NULL);
+ s_popup_menu.remove_bookmark = item;
+}
diff --git a/workbench/src/popup_menu.h b/workbench/src/popup_menu.h
new file mode 100644
index 000000000..cb8b52cbd
--- /dev/null
+++ b/workbench/src/popup_menu.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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 __POPUP_MENU_H__
+#define __POPUP_MENU_H__
+
+#include
+
+typedef enum
+{
+ POPUP_CONTEXT_PROJECT,
+ POPUP_CONTEXT_DIRECTORY,
+ POPUP_CONTEXT_FOLDER,
+ POPUP_CONTEXT_FILE,
+ POPUP_CONTEXT_BACKGROUND,
+ POPUP_CONTEXT_WB_BOOKMARK,
+ POPUP_CONTEXT_PRJ_BOOKMARK,
+}POPUP_CONTEXT;
+
+void popup_menu_init(void);
+void popup_menu_show(POPUP_CONTEXT context, GdkEventButton *event);
+
+#endif
diff --git a/workbench/src/sidebar.c b/workbench/src/sidebar.c
new file mode 100644
index 000000000..1170c24f1
--- /dev/null
+++ b/workbench/src/sidebar.c
@@ -0,0 +1,1152 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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.
+ */
+
+/*
+ * Code for setup and control of the sidebar.
+ */
+#include
+#include
+#include
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+#include "wb_globals.h"
+#include
+
+#include "sidebar.h"
+#include "popup_menu.h"
+#include "utils.h"
+
+enum
+{
+ DATA_ID_UNSET = 0,
+ DATA_ID_WB_BOOKMARK,
+ DATA_ID_PROJECT,
+ DATA_ID_PRJ_BOOKMARK,
+ DATA_ID_DIRECTORY,
+ DATA_ID_NO_DIRS,
+ DATA_ID_FOLDER,
+ DATA_ID_FILE,
+};
+
+enum
+{
+ FILEVIEW_COLUMN_ICON,
+ FILEVIEW_COLUMN_NAME,
+ FILEVIEW_COLUMN_DATA_ID,
+ FILEVIEW_COLUMN_ASSIGNED_DATA_POINTER,
+ FILEVIEW_N_COLUMNS,
+};
+
+typedef enum
+{
+ MATCH_FULL,
+ MATCH_PREFIX,
+ MATCH_PATTERN
+} MatchType;
+
+typedef struct
+{
+ GeanyProject *project;
+ GPtrArray *expanded_paths;
+} ExpandData;
+
+typedef struct SIDEBAR
+{
+ GtkWidget *file_view_vbox;
+ GtkWidget *file_view;
+ GtkTreeStore *file_store;
+ GtkWidget *file_view_label;
+}SIDEBAR;
+static SIDEBAR sidebar = {NULL, NULL, NULL, NULL};
+
+/* Remove all child nodes below parent */
+static void sidebar_remove_children(GtkTreeIter *parent)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(sidebar.file_view));
+ if (gtk_tree_model_iter_children (model, &iter, parent))
+ {
+ while (gtk_tree_store_remove (sidebar.file_store, &iter)) {}
+ }
+}
+
+
+/* Create a branch for a folder */
+static void sidebar_create_branch(gint level, const gchar *abs_base_dir, GSList *leaf_list, GtkTreeIter *parent)
+{
+ GSList *dir_list = NULL;
+ GSList *file_list = NULL;
+ GSList *elem;
+ gchar **path_arr;
+
+ foreach_slist (elem, leaf_list)
+ {
+ if (elem->data == NULL)
+ {
+ continue;
+ }
+ 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);
+ }
+ }
+
+ foreach_slist (elem, file_list)
+ {
+ GtkTreeIter iter;
+ gchar *part, *full;
+ GIcon *icon = NULL;
+
+ path_arr = elem->data;
+ gchar *content_type = g_content_type_guess(path_arr[level], NULL, 0, NULL);
+
+ if (content_type)
+ {
+ icon = g_content_type_get_icon(content_type);
+ if (icon)
+ {
+ GtkIconInfo *icon_info;
+
+ icon_info = gtk_icon_theme_lookup_by_gicon(gtk_icon_theme_get_default(), icon, 16, 0);
+ if (!icon_info)
+ {
+ g_object_unref(icon);
+ icon = NULL;
+ }
+ else
+ gtk_icon_info_free(icon_info);
+ }
+ g_free(content_type);
+ }
+
+ /* Build full absolute file name to use it on row activate to
+ open the file. Will be assigned as data pointer, see below. */
+ part = g_build_filenamev(path_arr);
+ full = g_build_filename(abs_base_dir, part, NULL);
+ g_free(part);
+
+ gtk_tree_store_insert_with_values(sidebar.file_store, &iter, parent, 0,
+ FILEVIEW_COLUMN_ICON, icon,
+ FILEVIEW_COLUMN_NAME, path_arr[level],
+ FILEVIEW_COLUMN_DATA_ID, DATA_ID_FILE,
+ FILEVIEW_COLUMN_ASSIGNED_DATA_POINTER, full,
+ -1);
+
+ if (icon)
+ {
+ g_object_unref(icon);
+ }
+ }
+
+ if (dir_list)
+ {
+ GSList *tmp_list = NULL;
+ GtkTreeIter iter;
+ gchar *last_dir_name;
+ GIcon *icon_dir = g_icon_new_for_string("folder", NULL);
+
+ path_arr = dir_list->data;
+ last_dir_name = path_arr[level];
+
+ foreach_slist (elem, dir_list)
+ {
+ 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_insert_with_values(sidebar.file_store, &iter, parent, 0,
+ FILEVIEW_COLUMN_ICON, icon_dir,
+ FILEVIEW_COLUMN_NAME, last_dir_name,
+ FILEVIEW_COLUMN_DATA_ID, DATA_ID_FOLDER,
+ -1);
+
+ sidebar_create_branch(level+1, abs_base_dir, tmp_list, &iter);
+
+ 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_insert_with_values(sidebar.file_store, &iter, parent, 0,
+ FILEVIEW_COLUMN_ICON, icon_dir,
+ FILEVIEW_COLUMN_NAME, last_dir_name,
+ FILEVIEW_COLUMN_DATA_ID, DATA_ID_FOLDER,
+ -1);
+
+ sidebar_create_branch(level+1, abs_base_dir, tmp_list, &iter);
+
+ g_slist_free(tmp_list);
+ g_slist_free(dir_list);
+ if (icon_dir != NULL)
+ {
+ g_object_unref(icon_dir);
+ }
+ }
+
+ g_slist_free(file_list);
+}
+
+
+/* Reverse strcmp */
+static int rev_strcmp(const char *str1, const char *str2)
+{
+ return strcmp(str2, str1);
+}
+
+
+/* Insert given directory/folder into the sidebar file tree */
+static void sidebar_insert_project_directory(WB_PROJECT *prj, WB_PROJECT_DIR *directory, GtkTreeIter *parent)
+{
+ GSList *lst = NULL;
+ GSList *path_list = NULL;
+ GSList *elem;
+ GHashTableIter iter;
+ gpointer key, value;
+ gchar *abs_base_dir;
+
+ g_hash_table_iter_init(&iter, wb_project_dir_get_file_table(directory));
+ abs_base_dir = get_combined_path(wb_project_get_filename(prj), wb_project_dir_get_base_dir(directory));
+ while (g_hash_table_iter_next(&iter, &key, &value))
+ {
+ gchar *path = get_relative_path(abs_base_dir, key);
+ lst = g_slist_prepend(lst, path);
+ }
+ /* sort in reverse order so we can prepend nodes to the tree store -
+ * the store behaves as a linked list and prepending is faster */
+ lst = g_slist_sort(lst, (GCompareFunc) rev_strcmp);
+
+ foreach_slist (elem, lst)
+ {
+ gchar **path_split;
+
+ path_split = g_strsplit_set(elem->data, "/\\", 0);
+ path_list = g_slist_prepend(path_list, path_split);
+ }
+
+ if (path_list != NULL)
+ sidebar_create_branch(0, abs_base_dir, path_list, parent);
+
+ g_slist_free_full(lst, g_free);
+ g_slist_free_full(path_list, (GDestroyNotify) g_strfreev);
+}
+
+
+/* Insert all directories (WB_PROJECT_DIRs) into the sidebar file tree */
+static void sidebar_insert_project_directories (WB_PROJECT *project, GtkTreeIter *parent, gint *position)
+{
+ GtkTreeIter iter;
+ GIcon *icon;
+ GSList *elem, *dirs;
+
+ if (project == NULL)
+ {
+ return;
+ }
+
+ dirs = wb_project_get_directories(project);
+ if (dirs != NULL)
+ {
+ icon = g_icon_new_for_string("workbench-dir", NULL);
+
+ foreach_slist (elem, dirs)
+ {
+ gtk_tree_store_insert_with_values(sidebar.file_store, &iter, parent, *position,
+ FILEVIEW_COLUMN_ICON, icon,
+ FILEVIEW_COLUMN_NAME, wb_project_dir_get_name(elem->data),
+ FILEVIEW_COLUMN_DATA_ID, DATA_ID_DIRECTORY,
+ FILEVIEW_COLUMN_ASSIGNED_DATA_POINTER, elem->data,
+ -1);
+ (*position)++;
+ sidebar_insert_project_directory(project, elem->data, &iter);
+ }
+ }
+ else
+ {
+ icon = g_icon_new_for_string("workbench-nodirs", NULL);
+
+ gtk_tree_store_insert_with_values(sidebar.file_store, &iter, parent, *position,
+ FILEVIEW_COLUMN_ICON, icon,
+ FILEVIEW_COLUMN_NAME, _("No directories"),
+ FILEVIEW_COLUMN_DATA_ID, DATA_ID_NO_DIRS,
+ -1);
+ (*position)++;
+ }
+ if (icon != NULL)
+ {
+ g_object_unref(icon);
+ }
+}
+
+
+/* Get the GtkTreeIter for project prj */
+static gboolean sidebar_get_project_iter(WB_PROJECT *prj, GtkTreeIter *iter)
+{
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(sidebar.file_view));
+ if (gtk_tree_model_get_iter_first (model, iter))
+ {
+ WB_PROJECT *current;
+
+ do
+ {
+ gtk_tree_model_get(model, iter, FILEVIEW_COLUMN_ASSIGNED_DATA_POINTER, ¤t, -1);
+ if (current == prj)
+ {
+ return TRUE;
+ }
+ }while (gtk_tree_model_iter_next(model, iter));
+ }
+ return FALSE;
+}
+
+
+/* Remove all rows from the side bar tree with the given data id */
+static void sidebar_remove_nodes_with_data_id(guint toremove, GtkTreeIter *start)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gboolean has_next;
+ guint dataid;
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(sidebar.file_view));
+ if (start == NULL)
+ {
+ if (!gtk_tree_model_get_iter_first(model, &iter))
+ {
+ return;
+ }
+ }
+ else
+ {
+ iter = *start;
+ }
+
+ do
+ {
+ gtk_tree_model_get(model, &iter, FILEVIEW_COLUMN_DATA_ID, &dataid, -1);
+ if (dataid == toremove)
+ {
+ has_next = gtk_tree_store_remove(sidebar.file_store, &iter);
+ }
+ else
+ {
+ has_next = gtk_tree_model_iter_next(model, &iter);
+ }
+ }
+ while (has_next);
+}
+
+
+/* Insert all project bookmarks into the sidebar file tree */
+static void sidebar_insert_project_bookmarks(WB_PROJECT *project, GtkTreeIter *parent, gint *position)
+{
+ GIcon *icon;
+ guint index, max;
+ GtkTreeIter iter;
+
+ if (project == NULL)
+ return;
+
+ max = wb_project_get_bookmarks_count(project);
+ if (max == 0)
+ return;
+
+ icon = g_icon_new_for_string("workbench-bookmark", NULL);
+ for (index = 0 ; index < max ; index++)
+ {
+ gchar *file, *name;
+
+ file = wb_project_get_bookmark_at_index(project, index);
+ name = get_any_relative_path(wb_project_get_filename(project), file);
+ gtk_tree_store_insert_with_values(sidebar.file_store, &iter, parent, *position,
+ FILEVIEW_COLUMN_ICON, icon,
+ FILEVIEW_COLUMN_NAME, name,
+ FILEVIEW_COLUMN_DATA_ID, DATA_ID_PRJ_BOOKMARK,
+ FILEVIEW_COLUMN_ASSIGNED_DATA_POINTER, file,
+ -1);
+ (*position)++;
+ }
+ if (icon != NULL)
+ {
+ g_object_unref(icon);
+ }
+}
+
+
+/* Update the sidebar for a project only */
+static void sidebar_update_project(WB_PROJECT *project, gboolean title_only)
+{
+ GtkTreeIter iter;
+
+ if (wb_globals.opened_wb == NULL)
+ return;
+
+ if (sidebar_get_project_iter(project, &iter))
+ {
+ /* Update the title */
+ GString *name = g_string_new(wb_project_get_name(project));
+ if (wb_project_is_modified(project))
+ {
+ g_string_append_c(name, '*');
+ }
+
+ gtk_tree_store_set(sidebar.file_store, &iter,
+ FILEVIEW_COLUMN_NAME, name->str,
+ -1);
+ g_string_free(name, TRUE);
+
+ /* Update children/content to? */
+ if (!title_only)
+ {
+ gint position = 0;
+ sidebar_remove_children(&iter);
+ sidebar_insert_project_bookmarks(project, &iter, &position);
+ sidebar_insert_project_directories(project, &iter, &position);
+ }
+ }
+}
+
+
+/* Insert all projects into the sidebar file tree */
+static void sidebar_insert_all_projects(GtkTreeIter *iter, gint *position)
+{
+ GIcon *icon_good, *icon_bad, *icon;
+ guint index, max;
+
+ if (wb_globals.opened_wb == NULL)
+ return;
+
+ icon_good = g_icon_new_for_string("workbench-project", NULL);
+ icon_bad = g_icon_new_for_string("workbench-project-error", NULL);
+
+ max = workbench_get_project_count(wb_globals.opened_wb);
+ for (index = 0 ; index < max ; index++)
+ {
+ gint child_position;
+ WB_PROJECT *project;
+
+ project = workbench_get_project_at_index(wb_globals.opened_wb, index);
+ if (workbench_get_project_status_at_index(wb_globals.opened_wb, index)
+ ==
+ PROJECT_ENTRY_STATUS_OK)
+ {
+ icon = icon_good;
+ }
+ else
+ {
+ icon = icon_bad;
+ }
+
+ GString *name = g_string_new(wb_project_get_name(project));
+ if (wb_project_is_modified(project))
+ {
+ g_string_append_c(name, '*');
+ }
+
+ gtk_tree_store_insert_with_values(sidebar.file_store, iter, NULL, *position,
+ FILEVIEW_COLUMN_ICON, icon,
+ FILEVIEW_COLUMN_NAME, name->str,
+ FILEVIEW_COLUMN_DATA_ID, DATA_ID_PROJECT,
+ FILEVIEW_COLUMN_ASSIGNED_DATA_POINTER, project,
+ -1);
+ g_string_free(name, TRUE);
+
+ child_position = 0;
+ /* Not required here as we build a completely new tree
+ sidebar_remove_children(&iter); */
+ sidebar_insert_project_bookmarks(project, iter, &child_position);
+ sidebar_insert_project_directories(project, iter, &child_position);
+ }
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(sidebar.file_view));
+
+ if (icon_good != NULL)
+ {
+ g_object_unref(icon_good);
+ }
+ if (icon_bad != NULL)
+ {
+ g_object_unref(icon_bad);
+ }
+}
+
+
+/* Insert all workbench bookmarks into the sidebar file tree */
+static void sidebar_insert_workbench_bookmarks(WORKBENCH *workbench, GtkTreeIter *iter, gint *position)
+{
+ GIcon *icon;
+ guint index, max;
+
+ if (workbench == NULL)
+ {
+ return;
+ }
+
+ sidebar_remove_nodes_with_data_id(DATA_ID_WB_BOOKMARK, NULL);
+
+ max = workbench_get_bookmarks_count(workbench);
+ if (max == 0)
+ {
+ return;
+ }
+
+ icon = g_icon_new_for_string("workbench-bookmark", NULL);
+ for (index = 0 ; index < max ; index++)
+ {
+ gchar *file, *name;
+
+ file = workbench_get_bookmark_at_index(workbench, index);
+ name = get_any_relative_path(workbench_get_filename(workbench), file);
+ gtk_tree_store_insert_with_values(sidebar.file_store, iter, NULL, *position,
+ FILEVIEW_COLUMN_ICON, icon,
+ FILEVIEW_COLUMN_NAME, name,
+ FILEVIEW_COLUMN_DATA_ID, DATA_ID_WB_BOOKMARK,
+ FILEVIEW_COLUMN_ASSIGNED_DATA_POINTER, file,
+ -1);
+ (*position)++;
+ }
+
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(sidebar.file_view));
+ if (icon != NULL)
+ {
+ g_object_unref(icon);
+ }
+}
+
+
+/* Reset/Clear/empty the sidebar file tree */
+static void sidebar_reset_tree_store(void)
+{
+ gtk_tree_store_clear(sidebar.file_store);
+}
+
+
+/* Update the workbench part of the sidebar */
+static void sidebar_update_workbench(GtkTreeIter *iter, gint *position)
+{
+ guint count;
+
+ if (wb_globals.opened_wb == NULL)
+ {
+ gtk_label_set_text (GTK_LABEL(sidebar.file_view_label), _("No workbench opened."));
+ gtk_tree_store_clear(sidebar.file_store);
+ sidebar_show_intro_message(_("Create or open a workbench\nusing the workbench menu."), FALSE);
+ sidebar_deactivate();
+ }
+ else
+ {
+ gint length;
+ gchar text[200];
+
+ count = workbench_get_project_count(wb_globals.opened_wb);
+ length = g_snprintf(text, sizeof(text), _("%s: %u Projects"),
+ workbench_get_name(wb_globals.opened_wb), count);
+ if (length < (gint)(sizeof(text)-1) && workbench_is_modified(wb_globals.opened_wb))
+ {
+ text [length] = '*';
+ text [length+1] = '\0';
+ }
+ gtk_label_set_text (GTK_LABEL(sidebar.file_view_label), text);
+ if (count == 0)
+ {
+ gtk_tree_store_clear(sidebar.file_store);
+ sidebar_show_intro_message(_("Add a project\nusing the context menu."), TRUE);
+ }
+ else
+ {
+ /* Add/show workbench bookmarks if any */
+ if (iter != NULL)
+ {
+ sidebar_insert_workbench_bookmarks(wb_globals.opened_wb, iter, position);
+ }
+ }
+ }
+}
+
+
+/** Update the sidebar.
+ *
+ * Update the sidebar according to the given situation/event @a event
+ * and data in @a context.
+ *
+ * @param event Reason for the sidebar update
+ * @param context Data, e.g. actually selected project...
+ *
+ **/
+void sidebar_update (SIDEBAR_EVENT event, SIDEBAR_CONTEXT *context)
+{
+ GtkTreeIter iter;
+ gint position = 0;
+
+ switch (event)
+ {
+ case SIDEBAR_CONTEXT_WB_CREATED:
+ case SIDEBAR_CONTEXT_WB_OPENED:
+ case SIDEBAR_CONTEXT_PROJECT_ADDED:
+ case SIDEBAR_CONTEXT_PROJECT_REMOVED:
+ sidebar_reset_tree_store();
+ sidebar_update_workbench(&iter, &position);
+ sidebar_insert_all_projects(&iter, &position);
+ sidebar_activate();
+ break;
+ case SIDEBAR_CONTEXT_WB_SAVED:
+ case SIDEBAR_CONTEXT_WB_SETTINGS_CHANGED:
+ case SIDEBAR_CONTEXT_WB_CLOSED:
+ sidebar_update_workbench(NULL, &position);
+ break;
+ case SIDEBAR_CONTEXT_PROJECT_SAVED:
+ if (context != NULL && context->project != NULL)
+ {
+ sidebar_update_project(context->project, TRUE);
+ }
+ break;
+ case SIDEBAR_CONTEXT_DIRECTORY_ADDED:
+ case SIDEBAR_CONTEXT_DIRECTORY_REMOVED:
+ case SIDEBAR_CONTEXT_DIRECTORY_RESCANNED:
+ case SIDEBAR_CONTEXT_DIRECTORY_SETTINGS_CHANGED:
+ case SIDEBAR_CONTEXT_PRJ_BOOKMARK_ADDED:
+ case SIDEBAR_CONTEXT_PRJ_BOOKMARK_REMOVED:
+ if (context != NULL && context->project != NULL)
+ {
+ sidebar_update_project(context->project, FALSE);
+ }
+ break;
+ case SIDEBAR_CONTEXT_WB_BOOKMARK_ADDED:
+ case SIDEBAR_CONTEXT_WB_BOOKMARK_REMOVED:
+ {
+ GtkTreeIter first;
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(sidebar.file_view));
+ if (gtk_tree_model_get_iter_first (model, &first))
+ {
+ sidebar_update_workbench(&first, &position);
+ }
+ }
+ break;
+ }
+}
+
+
+/* Callback function for clicking on a sidebar item */
+static void sidebar_filew_view_on_row_activated (GtkTreeView *treeview,
+ GtkTreePath *path, G_GNUC_UNUSED GtkTreeViewColumn *col, G_GNUC_UNUSED gpointer userdata)
+{
+ gchar *info;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ model = gtk_tree_view_get_model(treeview);
+
+ if (gtk_tree_model_get_iter(model, &iter, path))
+ {
+ guint dataid;
+ gchar *name;
+ void *address;
+
+ gtk_tree_model_get(model, &iter, FILEVIEW_COLUMN_NAME, &name, -1);
+ gtk_tree_model_get(model, &iter, FILEVIEW_COLUMN_DATA_ID, &dataid, -1);
+ gtk_tree_model_get(model, &iter, FILEVIEW_COLUMN_ASSIGNED_DATA_POINTER, &address, -1);
+
+ switch (dataid)
+ {
+ case DATA_ID_PROJECT:
+ info = wb_project_get_info((WB_PROJECT *)address);
+ if (workbench_get_project_status_by_address(wb_globals.opened_wb, address)
+ ==
+ PROJECT_ENTRY_STATUS_OK)
+ {
+ dialogs_show_msgbox(GTK_MESSAGE_INFO, "%s", info);
+ }
+ else
+ {
+ dialogs_show_msgbox(GTK_MESSAGE_ERROR, _("%s\nProject file not found!"), info);
+ }
+ g_free(info);
+ break;
+ case DATA_ID_DIRECTORY:
+ info = wb_project_dir_get_info((WB_PROJECT_DIR *)address);
+ dialogs_show_msgbox(GTK_MESSAGE_INFO, "%s", info);
+ break;
+ case DATA_ID_WB_BOOKMARK:
+ case DATA_ID_PRJ_BOOKMARK:
+ case DATA_ID_FILE:
+ document_open_file((char *)address, FALSE, NULL, NULL);
+ break;
+ case DATA_ID_NO_DIRS:
+ dialogs_show_msgbox(GTK_MESSAGE_INFO, _("This project has no directories. Directories can be added to a project using the context menu."));
+ break;
+ default:
+ break;
+ }
+
+ g_free(name);
+ }
+}
+
+
+/* Callbac function for button release, used for the popup menu */
+static gboolean sidebar_file_view_on_button_release(G_GNUC_UNUSED GtkWidget * widget, GdkEventButton * event,
+ G_GNUC_UNUSED gpointer user_data)
+{
+ if (event->button == 3)
+ {
+ SIDEBAR_CONTEXT context;
+ POPUP_CONTEXT popup_context = POPUP_CONTEXT_BACKGROUND;
+
+ if (sidebar_file_view_get_selected_context(&context))
+ {
+ if (context.file != NULL)
+ {
+ popup_context = POPUP_CONTEXT_FILE;
+ }
+ else if (context.folder != NULL)
+ {
+ popup_context = POPUP_CONTEXT_FOLDER;
+ }
+ else if (context.directory != NULL)
+ {
+ popup_context = POPUP_CONTEXT_DIRECTORY;
+ }
+ else if (context.prj_bookmark != NULL)
+ {
+ popup_context = POPUP_CONTEXT_PRJ_BOOKMARK;
+ }
+ else if (context.project != NULL)
+ {
+ popup_context = POPUP_CONTEXT_PROJECT;
+ }
+ else if (context.wb_bookmark != NULL)
+ {
+ popup_context = POPUP_CONTEXT_WB_BOOKMARK;
+ }
+ }
+ popup_menu_show(popup_context, event);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/** Get the selected project and the path to it.
+ *
+ * Get the selected project and return a pointer to it. Also if @a path is not NULL then
+ * the path to the row containing the project is returned in *path.
+ *
+ * @param path @nullable Location to return the path or @c NULL
+ *
+ **/
+WB_PROJECT *sidebar_file_view_get_selected_project(GtkTreePath **path)
+{
+ gboolean has_parent;
+ guint dataid;
+ GtkTreeSelection *treesel;
+ GtkTreeModel *model;
+ GtkTreeIter current, parent;
+ WB_PROJECT *project;
+
+ if (path != NULL)
+ {
+ *path = NULL;
+ }
+ treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(sidebar.file_view));
+ if (gtk_tree_selection_get_selected(treesel, &model, ¤t))
+ {
+ do
+ {
+ gtk_tree_model_get(model, ¤t, FILEVIEW_COLUMN_DATA_ID, &dataid, -1);
+ gtk_tree_model_get(model, ¤t, FILEVIEW_COLUMN_ASSIGNED_DATA_POINTER, &project, -1);
+ if (dataid == DATA_ID_PROJECT && project != NULL)
+ {
+ if (path != NULL)
+ {
+ *path = gtk_tree_model_get_path(model, ¤t);
+ }
+ return project;
+ }
+ has_parent = gtk_tree_model_iter_parent(model,
+ &parent, ¤t);
+ current = parent;
+ }while (has_parent);
+ }
+ return NULL;
+}
+
+
+/** Get the selected project directory and the path to it.
+ *
+ * Get the selected project directory and return a pointer to it. Also if @a path is not NULL then
+ * the path to the row containing the project is returned in *path.
+ *
+ * @param path @nullable Location to return the path or @c NULL
+ *
+ **/
+static WB_PROJECT_DIR *sidebar_file_view_get_selected_project_dir(GtkTreePath **path)
+{
+ gboolean has_parent;
+ guint dataid;
+ GtkTreeSelection *treesel;
+ GtkTreeModel *model;
+ GtkTreeIter current, parent;
+ WB_PROJECT_DIR *directory;
+
+ if (path != NULL)
+ {
+ *path = NULL;
+ }
+ treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(sidebar.file_view));
+ if (gtk_tree_selection_get_selected(treesel, &model, ¤t))
+ {
+ do
+ {
+ gtk_tree_model_get(model, ¤t, FILEVIEW_COLUMN_DATA_ID, &dataid, -1);
+ gtk_tree_model_get(model, ¤t, FILEVIEW_COLUMN_ASSIGNED_DATA_POINTER, &directory, -1);
+ if (dataid == DATA_ID_DIRECTORY && directory != NULL)
+ {
+ if (path != NULL)
+ {
+ *path = gtk_tree_model_get_path(model, ¤t);
+ }
+ return directory;
+ }
+ has_parent = gtk_tree_model_iter_parent(model,
+ &parent, ¤t);
+ current = parent;
+ }while (has_parent);
+ }
+ return NULL;
+}
+
+
+/** Get data corresponding to the current selected tree view selection.
+ *
+ * The function collects all data corresponding to the current selection and stores it in
+ * @a context. This is e.g. the selected file and the directory and project to which it
+ * belongs.
+ *
+ * @param context The location to store the selection context data in.
+ * @return TRUE if anything in the tree is selected, FALSE otherwise.
+ *
+ **/
+gboolean sidebar_file_view_get_selected_context(SIDEBAR_CONTEXT *context)
+{
+ gboolean has_parent;
+ guint dataid;
+ GtkTreeSelection *treesel;
+ GtkTreeModel *model;
+ GtkTreeIter current, parent;
+ gpointer data;
+
+ if (context == NULL)
+ {
+ return FALSE;
+ }
+ memset (context, 0, sizeof(*context));
+
+ treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(sidebar.file_view));
+ if (gtk_tree_selection_get_selected(treesel, &model, ¤t))
+ {
+ /* Search through the parents upwards until we find the project node.
+ Save everthing that's interesting in callers variables... */
+ do
+ {
+ gtk_tree_model_get(model, ¤t, FILEVIEW_COLUMN_DATA_ID, &dataid, -1);
+ gtk_tree_model_get(model, ¤t, FILEVIEW_COLUMN_ASSIGNED_DATA_POINTER, &data, -1);
+ if (data != NULL)
+ {
+ switch (dataid)
+ {
+ case DATA_ID_WB_BOOKMARK:
+ context->wb_bookmark = data;
+ break;
+ case DATA_ID_PROJECT:
+ context->project = data;
+ break;
+ case DATA_ID_PRJ_BOOKMARK:
+ context->prj_bookmark = data;
+ break;
+ case DATA_ID_DIRECTORY:
+ context->directory = data;
+ break;
+ case DATA_ID_NO_DIRS:
+ /* Has not got any data. */
+ break;
+ case DATA_ID_FOLDER:
+ context->folder = data;
+ break;
+ case DATA_ID_FILE:
+ context->file = data;
+ break;
+ }
+ }
+
+ has_parent = gtk_tree_model_iter_parent(model,
+ &parent, ¤t);
+ current = parent;
+ }while (has_parent);
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/** Setup the sidebar.
+ *
+ **/
+void sidebar_init(void)
+{
+ GtkWidget *scrollwin;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *sel;
+ GList *focus_chain = NULL;
+
+ sidebar.file_view_vbox = gtk_vbox_new(FALSE, 0);
+
+ /**** label ****/
+ sidebar.file_view_label = gtk_label_new (_("No workbench opened."));
+ gtk_box_pack_start(GTK_BOX(sidebar.file_view_vbox), sidebar.file_view_label, FALSE, FALSE, 0);
+
+ /**** tree view ****/
+
+ sidebar.file_view = gtk_tree_view_new();
+ g_signal_connect(sidebar.file_view, "row-activated", (GCallback)sidebar_filew_view_on_row_activated, NULL);
+
+ sidebar.file_store = gtk_tree_store_new(FILEVIEW_N_COLUMNS, G_TYPE_ICON, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_POINTER);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(sidebar.file_view), GTK_TREE_MODEL(sidebar.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_add_attribute(column, renderer, "gicon", FILEVIEW_COLUMN_ICON);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(column, renderer, TRUE);
+ gtk_tree_view_column_add_attribute(column, renderer, "text", FILEVIEW_COLUMN_NAME);
+
+ gtk_tree_view_append_column(GTK_TREE_VIEW(sidebar.file_view), column);
+
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(sidebar.file_view), FALSE);
+ gtk_tree_view_set_enable_search(GTK_TREE_VIEW(sidebar.file_view), TRUE);
+ gtk_tree_view_set_search_column(GTK_TREE_VIEW(sidebar.file_view), FILEVIEW_COLUMN_NAME);
+
+ ui_widget_modify_font_from_string(sidebar.file_view,
+ wb_globals.geany_plugin->geany_data->interface_prefs->tagbar_font);
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(sidebar.file_view));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+
+ g_signal_connect(G_OBJECT(sidebar.file_view), "button-release-event",
+ G_CALLBACK(sidebar_file_view_on_button_release), NULL);
+
+ sidebar_deactivate();
+
+ /**** the rest ****/
+ focus_chain = g_list_prepend(focus_chain, sidebar.file_view);
+ gtk_container_set_focus_chain(GTK_CONTAINER(sidebar.file_view_vbox), focus_chain);
+ g_list_free(focus_chain);
+ 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), sidebar.file_view);
+ gtk_box_pack_start(GTK_BOX(sidebar.file_view_vbox), scrollwin, TRUE, TRUE, 0);
+
+ gtk_widget_show_all(sidebar.file_view_vbox);
+ gtk_notebook_append_page(GTK_NOTEBOOK(wb_globals.geany_plugin->geany_data->main_widgets->sidebar_notebook),
+ sidebar.file_view_vbox, gtk_label_new(_("Workbench")));
+}
+
+
+/** Display a text message in the sidebar.
+ *
+ * The function displays the text @a msg in the sidebar and eventually deactivates the sidebar
+ * if @a activate is FALSE.
+ *
+ * @param msg The text to display
+ * @param activate If TRUE the sidebar will be activated, if not it will be deactivated
+ *
+ **/
+void sidebar_show_intro_message(const gchar *msg, gboolean activate)
+{
+ GtkTreeIter iter;
+
+ gtk_tree_store_insert_with_values(sidebar.file_store, &iter, NULL, -1,
+ FILEVIEW_COLUMN_NAME, msg, -1);
+ if (activate)
+ {
+ sidebar_activate();
+ }
+ else
+ {
+ sidebar_deactivate();
+ }
+}
+
+
+/** Activate the sidebar.
+ *
+ **/
+void sidebar_activate(void)
+{
+ gtk_widget_set_sensitive(sidebar.file_view_vbox, TRUE);
+}
+
+
+/** Deactivate the sidebar.
+ *
+ **/
+void sidebar_deactivate(void)
+{
+ gtk_widget_set_sensitive(sidebar.file_view_vbox, FALSE);
+}
+
+
+/** Cleanup the sidebar.
+ *
+ **/
+void sidebar_cleanup(void)
+{
+ gtk_widget_destroy(sidebar.file_view_vbox);
+}
+
+
+/** Expand all rows in the sidebar tree.
+ *
+ **/
+void sidebar_expand_all(void)
+{
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(sidebar.file_view));
+}
+
+
+/** Collapse all rows in the sidebar tree.
+ *
+ **/
+void sidebar_collapse_all(void)
+{
+ gtk_tree_view_collapse_all(GTK_TREE_VIEW(sidebar.file_view));
+}
+
+
+/** Expand all rows in the sidebar tree for the selected project.
+ *
+ **/
+void sidebar_expand_selected_project(void)
+{
+ GtkTreePath *path;
+
+ sidebar_file_view_get_selected_project(&path);
+ if (path != NULL)
+ {
+ gtk_tree_view_expand_row(GTK_TREE_VIEW(sidebar.file_view),
+ path, TRUE);
+ gtk_tree_path_free(path);
+ }
+}
+
+
+/** Collapse all rows in the sidebar tree for the selected project.
+ *
+ **/
+void sidebar_collapse_selected_project(void)
+{
+ GtkTreePath *path;
+
+ sidebar_file_view_get_selected_project(&path);
+ if (path != NULL)
+ {
+ gtk_tree_view_collapse_row(GTK_TREE_VIEW(sidebar.file_view),
+ path);
+ gtk_tree_path_free(path);
+ }
+}
+
+
+/** Toggle state of all rows in the sidebar tree for the selected project.
+ *
+ * If the project is expanded, then it will be collapsed and vice versa.
+ *
+ **/
+void sidebar_toggle_selected_project_expansion(void)
+{
+ GtkTreePath *path;
+
+ sidebar_file_view_get_selected_project(&path);
+ if (path != NULL)
+ {
+ if (gtk_tree_view_row_expanded
+ (GTK_TREE_VIEW(sidebar.file_view),path))
+ {
+ gtk_tree_view_collapse_row(GTK_TREE_VIEW(sidebar.file_view),
+ path);
+ }
+ else
+ {
+ gtk_tree_view_expand_row (GTK_TREE_VIEW(sidebar.file_view),
+ path, TRUE);
+ }
+ gtk_tree_path_free(path);
+ }
+}
+
+
+/** Toggle state of all rows in the sidebar tree for the selected directory.
+ *
+ * If the directory is expanded, then it will be collapsed and vice versa.
+ *
+ **/
+void sidebar_toggle_selected_project_dir_expansion (void)
+{
+ GtkTreePath *path;
+
+ sidebar_file_view_get_selected_project_dir(&path);
+ if (path != NULL)
+ {
+ if (gtk_tree_view_row_expanded
+ (GTK_TREE_VIEW(sidebar.file_view),path))
+ {
+ gtk_tree_view_collapse_row (GTK_TREE_VIEW(sidebar.file_view),
+ path);
+ }
+ else
+ {
+ gtk_tree_view_expand_row (GTK_TREE_VIEW(sidebar.file_view),
+ path, TRUE);
+ }
+ gtk_tree_path_free(path);
+ }
+}
diff --git a/workbench/src/sidebar.h b/workbench/src/sidebar.h
new file mode 100644
index 000000000..26b6265bb
--- /dev/null
+++ b/workbench/src/sidebar.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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 __SIDEBAR_H__
+#define __SIDEBAR_H__
+
+#include
+#include "wb_project.h"
+
+typedef struct
+{
+ WB_PROJECT *project;
+ WB_PROJECT_DIR *directory;
+ gchar *folder;
+ gchar *file;
+ gchar *wb_bookmark;
+ gchar *prj_bookmark;
+}SIDEBAR_CONTEXT;
+
+typedef enum
+{
+ SIDEBAR_CONTEXT_WB_CREATED,
+ SIDEBAR_CONTEXT_WB_OPENED,
+ SIDEBAR_CONTEXT_WB_SAVED,
+ SIDEBAR_CONTEXT_WB_SETTINGS_CHANGED,
+ SIDEBAR_CONTEXT_WB_CLOSED,
+ SIDEBAR_CONTEXT_PROJECT_ADDED,
+ SIDEBAR_CONTEXT_PROJECT_SAVED,
+ SIDEBAR_CONTEXT_PROJECT_REMOVED,
+ SIDEBAR_CONTEXT_DIRECTORY_ADDED,
+ SIDEBAR_CONTEXT_DIRECTORY_REMOVED,
+ SIDEBAR_CONTEXT_DIRECTORY_RESCANNED,
+ SIDEBAR_CONTEXT_DIRECTORY_SETTINGS_CHANGED,
+ SIDEBAR_CONTEXT_WB_BOOKMARK_ADDED,
+ SIDEBAR_CONTEXT_WB_BOOKMARK_REMOVED,
+ SIDEBAR_CONTEXT_PRJ_BOOKMARK_ADDED,
+ SIDEBAR_CONTEXT_PRJ_BOOKMARK_REMOVED,
+}SIDEBAR_EVENT;
+
+void sidebar_init(void);
+void sidebar_cleanup(void);
+
+void sidebar_activate(void);
+void sidebar_deactivate(void);
+
+void sidebar_show_intro_message(const gchar *msg, gboolean activate);
+
+void sidebar_update(SIDEBAR_EVENT event, SIDEBAR_CONTEXT *context);
+
+void sidebar_expand_all(void);
+void sidebar_collapse_all(void);
+void sidebar_expand_selected_project (void);
+void sidebar_collapse_selected_project (void);
+void sidebar_toggle_selected_project_expansion (void);
+void sidebar_toggle_selected_project_dir_expansion (void);
+
+WB_PROJECT *sidebar_file_view_get_selected_project(GtkTreePath **path);
+gboolean sidebar_file_view_get_selected_context(SIDEBAR_CONTEXT *context);
+
+#endif
diff --git a/workbench/src/utils.c b/workbench/src/utils.c
new file mode 100644
index 000000000..12b553908
--- /dev/null
+++ b/workbench/src/utils.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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.
+ */
+
+/*
+ * Utility functions.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include
+#include
+#include
+#include "utils.h"
+
+/** Get the relative path.
+ *
+ * The function examines the relative path of @a utf8_descendant in relation to
+ * @a utf8_parent.
+ *
+ * @param utf8_parent Parent directory.
+ * @param utf8_descendant Sub directory.
+ *
+ **/
+gchar *get_relative_path(const gchar *utf8_parent, const gchar *utf8_descendant)
+{
+ GFile *gf_parent, *gf_descendant;
+ gchar *locale_parent, *locale_descendant;
+ gchar *locale_ret, *utf8_ret;
+
+ locale_parent = utils_get_locale_from_utf8(utf8_parent);
+ locale_descendant = utils_get_locale_from_utf8(utf8_descendant);
+ gf_parent = g_file_new_for_path(locale_parent);
+ gf_descendant = g_file_new_for_path(locale_descendant);
+
+ locale_ret = g_file_get_relative_path(gf_parent, gf_descendant);
+ utf8_ret = utils_get_utf8_from_locale(locale_ret);
+
+ g_object_unref(gf_parent);
+ g_object_unref(gf_descendant);
+ g_free(locale_parent);
+ g_free(locale_descendant);
+ g_free(locale_ret);
+
+ return utf8_ret;
+}
+
+
+/** Check if a string matches a pattern.
+ *
+ * The function checks if @a str matches pattern @a patterns.
+ *
+ * @param patterns Pattern list.
+ * @param str String to check
+ * @return TRUE if str matches the pattern, FALSE otherwise
+ *
+ **/
+gboolean patterns_match(GSList *patterns, const gchar *str)
+{
+ GSList *elem;
+ foreach_slist (elem, patterns)
+ {
+ GPatternSpec *pattern = elem->data;
+ if (g_pattern_match_string(pattern, str))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/** Get precompiled patterns.
+ *
+ * The function builds the precompiled patterns for @a patterns and returns them
+ * as a list.
+ *
+ * @param patterns NULL terminated string array of patterns.
+ * @return Pointer to GSList of patterns or NULL if patterns == NULL
+ *
+ **/
+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;
+}
+
+
+/** Combine an absolute and a relative path.
+ *
+ * The function combines the absolute path @a base with the relative path
+ * @a relative and retunrs it as a string. The caller needs to free the
+ * string with g_free().
+ *
+ * @param base The absolute path.
+ * @param relative The relative path.
+ * @return Combined path or NULL if no memory is available
+ *
+ **/
+gchar *get_combined_path(const gchar *base, const gchar *relative)
+{
+ gchar *basedir, *basedir_end;
+ const gchar *start;
+ gchar *result;
+ gint goback;
+
+ start = relative;
+ basedir = g_path_get_dirname (base);
+ if (relative[0] == '.')
+ {
+ if (strncmp("..", relative, sizeof("..")-1) == 0)
+ {
+ start = &(relative[sizeof("..")-1]);
+ }
+
+ goback = 0;
+ while (*start != '\0')
+ {
+ if (strncmp("..", &(start[1]), sizeof("..")-1) == 0)
+ {
+ start += 1 + (sizeof("..")-1);
+ goback++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ basedir_end = &(basedir[strlen(basedir)]);
+ while (goback > 0)
+ {
+ while (basedir_end > basedir && *basedir_end != G_DIR_SEPARATOR)
+ {
+ basedir_end--;
+ }
+ if (*basedir_end == G_DIR_SEPARATOR)
+ {
+ *basedir_end = '\0';
+ }
+ else
+ {
+ break;
+ }
+ goback--;
+ }
+ }
+
+ result = g_strconcat(basedir, start, NULL);
+ return result;
+}
+
+
+/** Get the relative path.
+ *
+ * The function examines the relative path of @a target in relation to
+ * @a base. It is not required that target is a sub-directory of base.
+ *
+ * @param base Base directory.
+ * @param target Target directory or NULL in case of error.
+ *
+ **/
+gchar *get_any_relative_path (const gchar *base, const gchar *target)
+{
+ guint index = 0, equal, equal_index = 0, base_parts = 0, target_parts = 0, length;
+ GPtrArray *relative;
+ gchar *part;
+ gchar *result = NULL;
+ gchar **splitv_base;
+ gchar **splitv_target;
+
+ /* Split up pathes into parts and count them */
+ splitv_base = g_strsplit (base, G_DIR_SEPARATOR_S, -1);
+ index = 0;
+ while (splitv_base[index] != NULL)
+ {
+ if (strlen(splitv_base[index]) > 0)
+ {
+ base_parts++;
+ }
+ index++;
+ }
+ splitv_target = g_strsplit (target, G_DIR_SEPARATOR_S, -1);
+ index = 0;;
+ while (splitv_target[index] != NULL)
+ {
+ if (strlen(splitv_target[index]) > 0)
+ {
+ target_parts++;
+ }
+ index++;
+ }
+
+ /* Count equal dirs */
+ equal = 0;
+ index = 0;
+ while (splitv_base[index] != NULL && splitv_target[index] != NULL)
+ {
+ if (g_strcmp0 (splitv_base[index], splitv_target[index]) == 0)
+ {
+ /* We might encounter empty strings! */
+ if (strlen(splitv_base[index]) > 0)
+ {
+ equal++;
+ equal_index = index;
+ }
+ }
+ else
+ {
+ break;
+ }
+ index++;
+ }
+
+ length = 0;
+ relative = g_ptr_array_new();
+ if (equal < base_parts)
+ {
+ /* Go back, add ".." */
+ for (index = 0 ; index < (base_parts-equal) ; index++)
+ {
+ if (index == 0)
+ {
+ g_ptr_array_add(relative, g_strdup(".."));
+ length += 2;
+ }
+ else
+ {
+ length += 2 + strlen(G_DIR_SEPARATOR_S);
+ g_ptr_array_add(relative, g_strdup(G_DIR_SEPARATOR_S));
+ g_ptr_array_add(relative, g_strdup(".."));
+ }
+ }
+
+ /* Add directories */
+ index = equal_index + 1;
+ while (splitv_target[index] != NULL)
+ {
+ if (strlen(splitv_target[index]) > 0)
+ {
+ length += strlen(G_DIR_SEPARATOR_S)
+ + strlen(splitv_target[index]);
+ g_ptr_array_add(relative, g_strdup(G_DIR_SEPARATOR_S));
+ g_ptr_array_add(relative, g_strdup(splitv_target[index]));
+ }
+ index++;
+ }
+ }
+
+ /* Copy it all together */
+ result = g_new(char, length+1);
+ if (result != NULL)
+ {
+ guint strpos = 0;
+
+ for (index = 0 ; index < relative->len ; index++)
+ {
+ part = g_ptr_array_index(relative, index);
+ g_strlcpy (&(result[strpos]),
+ part,
+ length+1-strpos);
+ strpos += strlen(part);
+ g_free(part);
+ }
+ }
+ else
+ {
+ for (index = 0 ; index < relative->len ; index++)
+ {
+ part = g_ptr_array_index(relative, index);
+ g_free(part);
+ }
+ result = NULL;
+ }
+ g_ptr_array_free(relative, TRUE);
+
+ return result;
+}
diff --git a/workbench/src/utils.h b/workbench/src/utils.h
new file mode 100644
index 000000000..9c7cfcb3c
--- /dev/null
+++ b/workbench/src/utils.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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 __UTILS_H__
+#define __UTILS_H__
+
+#include
+
+gchar *get_relative_path(const gchar *utf8_parent, const gchar *utf8_descendant);
+gboolean patterns_match(GSList *patterns, const gchar *str);
+GSList *get_precompiled_patterns(gchar **patterns);
+gchar *get_combined_path(const gchar *base, const gchar *relative);
+gchar *get_any_relative_path (const gchar *base, const gchar *target);
+
+#endif
diff --git a/workbench/src/wb_globals.c b/workbench/src/wb_globals.c
new file mode 100644
index 000000000..5085f6759
--- /dev/null
+++ b/workbench/src/wb_globals.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "wb_globals.h"
+
+WB_GLOBALS wb_globals;
+
+void workbench_globals_init(void)
+{
+ memset(&wb_globals, 0, sizeof(wb_globals));
+}
diff --git a/workbench/src/wb_globals.h b/workbench/src/wb_globals.h
new file mode 100644
index 000000000..ce96060d5
--- /dev/null
+++ b/workbench/src/wb_globals.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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 __WB_GLOBALS_H__
+#define __WB_GLOBALS_H__
+
+#include
+#include "workbench.h"
+
+typedef struct
+{
+ GeanyPlugin *geany_plugin;
+ WORKBENCH *opened_wb;
+}WB_GLOBALS;
+
+extern WB_GLOBALS wb_globals;
+
+void workbench_globals_init(void);
+
+#endif
diff --git a/workbench/src/wb_project.c b/workbench/src/wb_project.c
new file mode 100644
index 000000000..a6f892e13
--- /dev/null
+++ b/workbench/src/wb_project.c
@@ -0,0 +1,1468 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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.
+ */
+
+/*
+ * Code for the WB_PROJECT structure.
+ */
+#include
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include
+#include "wb_globals.h"
+#include "wb_project.h"
+#include "utils.h"
+
+extern GeanyData *geany_data;
+
+typedef enum
+{
+ WB_PROJECT_TAG_PREFS_AUTO,
+ WB_PROJECT_TAG_PREFS_YES,
+ WB_PROJECT_TAG_PREFS_NO,
+}WB_PROJECT_TAG_PREFS;
+
+typedef struct
+{
+ GKeyFile *kf;
+ guint dir_count;
+}WB_PROJECT_ON_SAVE_USER_DATA;
+
+struct S_WB_PROJECT_DIR
+{
+ gchar *name;
+ gchar *base_dir;
+ gchar **file_patterns; /**< Array of filename extension patterns. */
+ gchar **ignored_dirs_patterns;
+ gchar **ignored_file_patterns;
+ guint file_count;
+ guint folder_count;
+ GHashTable *file_table; /* contains all file names within base_dir, maps file_name->TMSourceFile */
+};
+
+struct S_WB_PROJECT
+{
+ gchar *filename;
+ gchar *name;
+ gboolean modified;
+ GSList *s_idle_add_funcs;
+ GSList *s_idle_remove_funcs;
+ GSList *directories; /* list of WB_PROJECT_DIR; */
+ WB_PROJECT_TAG_PREFS generate_tag_prefs;
+ GPtrArray *bookmarks;
+};
+
+
+/** Set the projects modified marker.
+ *
+ * @param prj The project
+ * @param value The value to set
+ *
+ **/
+void wb_project_set_modified(WB_PROJECT *prj, gboolean value)
+{
+ if (prj != NULL)
+ {
+ prj->modified = value;
+ }
+}
+
+
+/** Has the project been modified since the last save?
+ *
+ * @param prj The project
+ * @return TRUE if project is modified, FALSE otherwise
+ *
+ **/
+gboolean wb_project_is_modified(WB_PROJECT *prj)
+{
+ if (prj != NULL)
+ {
+ return (prj->modified);
+ }
+ return FALSE;
+}
+
+
+/** Set the filename of a project.
+ *
+ * @param prj The project
+ * @param filename The filename
+ *
+ **/
+void wb_project_set_filename(WB_PROJECT *prj, const gchar *filename)
+{
+ if (prj != NULL)
+ {
+ guint offset;
+ gchar *ext;
+
+ g_free(prj->filename);
+ prj->filename = g_strdup(filename);
+ g_free(prj->name);
+ prj->name = g_path_get_basename (filename);
+ ext = g_strrstr(prj->name, ".geany");
+ if(ext != NULL)
+ {
+ offset = strlen(prj->name);
+ offset -= strlen(".geany");
+ if (ext == prj->name + offset)
+ {
+ /* Strip of file extension by overwriting
+ '.' with string terminator. */
+ prj->name[offset] = '\0';
+ }
+ }
+ }
+}
+
+
+/** Get the filename of a project.
+ *
+ * @param prj The project
+ * @return The filename
+ *
+ **/
+const gchar *wb_project_get_filename(WB_PROJECT *prj)
+{
+ if (prj != NULL)
+ {
+ return prj->filename;
+ }
+ return NULL;
+}
+
+
+/** Get the name of a project.
+ *
+ * @param prj The project
+ * @return The project name
+ *
+ **/
+const gchar *wb_project_get_name(WB_PROJECT *prj)
+{
+ if (prj != NULL)
+ {
+ return prj->name;
+ }
+ return NULL;
+}
+
+
+/** Get the list of directories contained in the project.
+ *
+ * @param prj The project
+ * @return GSList of directories (WB_PROJECT_DIRs)
+ *
+ **/
+GSList *wb_project_get_directories(WB_PROJECT *prj)
+{
+ if (prj != NULL)
+ {
+ return prj->directories;
+ }
+ return NULL;
+}
+
+
+/* Check if filename matches filetpye patterns */
+static gboolean match_basename(gconstpointer pft, gconstpointer user_data)
+{
+ const GeanyFiletype *ft = pft;
+ const gchar *utf8_base_filename = user_data;
+ gint j;
+ gboolean ret = FALSE;
+
+ if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
+ return FALSE;
+
+ for (j = 0; ft->pattern[j] != NULL; j++)
+ {
+ GPatternSpec *pattern = g_pattern_spec_new(ft->pattern[j]);
+
+ if (g_pattern_match_string(pattern, utf8_base_filename))
+ {
+ ret = TRUE;
+ g_pattern_spec_free(pattern);
+ break;
+ }
+ g_pattern_spec_free(pattern);
+ }
+ return ret;
+}
+
+
+/* Clear idle queue */
+static void wb_project_clear_idle_queue(GSList **queue)
+{
+ if (queue == NULL || *queue == NULL)
+ {
+ return;
+ }
+
+ g_slist_free_full(*queue, g_free);
+ *queue = NULL;
+}
+
+
+/* Get the list of files for root */
+static GSList *wb_project_dir_get_file_list(WB_PROJECT_DIR *root, const gchar *utf8_path, GSList *patterns,
+ GSList *ignored_dirs_patterns, GSList *ignored_file_patterns, GHashTable *visited_paths)
+{
+ GSList *list = NULL;
+ GDir *dir;
+ gchar *locale_path = utils_get_locale_from_utf8(utf8_path);
+ gchar *real_path = tm_get_real_path(locale_path);
+
+ dir = g_dir_open(locale_path, 0, NULL);
+ if (!dir || !real_path || g_hash_table_lookup(visited_paths, real_path))
+ {
+ if (dir != NULL)
+ {
+ g_dir_close(dir);
+ }
+ g_free(locale_path);
+ g_free(real_path);
+ return NULL;
+ }
+
+ g_hash_table_insert(visited_paths, real_path, GINT_TO_POINTER(1));
+
+ while (TRUE)
+ {
+ const gchar *locale_name;
+ gchar *locale_filename, *utf8_filename, *utf8_name;
+
+ locale_name = g_dir_read_name(dir);
+ if (!locale_name)
+ break;
+
+ utf8_name = utils_get_utf8_from_locale(locale_name);
+ locale_filename = g_build_filename(locale_path, locale_name, NULL);
+ utf8_filename = utils_get_utf8_from_locale(locale_filename);
+
+ if (g_file_test(locale_filename, G_FILE_TEST_IS_DIR))
+ {
+ GSList *lst;
+
+ if (!patterns_match(ignored_dirs_patterns, utf8_name))
+ {
+ lst = wb_project_dir_get_file_list(root, utf8_filename, patterns, ignored_dirs_patterns,
+ ignored_file_patterns, visited_paths);
+ if (lst)
+ {
+ root->folder_count++;
+ list = g_slist_concat(list, lst);
+ }
+ }
+ }
+ else if (g_file_test(locale_filename, G_FILE_TEST_IS_REGULAR))
+ {
+ if (patterns_match(patterns, utf8_name) && !patterns_match(ignored_file_patterns, utf8_name))
+ {
+ root->file_count++;
+ list = g_slist_prepend(list, g_strdup(utf8_filename));
+ }
+ }
+
+ g_free(utf8_filename);
+ g_free(locale_filename);
+ g_free(utf8_name);
+ }
+
+ g_dir_close(dir);
+ g_free(locale_path);
+
+ return list;
+}
+
+
+/* Create a new project dir with base path "utf8_base_dir" */
+static WB_PROJECT_DIR *wb_project_dir_new(const gchar *utf8_base_dir)
+{
+ guint offset;
+
+ if (utf8_base_dir == NULL)
+ {
+ return NULL;
+ }
+ WB_PROJECT_DIR *dir = g_new0(WB_PROJECT_DIR, 1);
+ dir->base_dir = g_strdup(utf8_base_dir);
+ dir->file_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GFreeFunc)tm_source_file_free);
+
+ offset = strlen(dir->base_dir)-1;
+ while (offset > 0
+ && dir->base_dir[offset] != '\\'
+ && dir->base_dir[offset] != '/')
+ {
+ offset--;
+ }
+ if (offset != 0)
+ {
+ offset++;
+ }
+ dir->name = g_strdup(&(dir->base_dir[offset]));
+ return dir;
+}
+
+
+/* Collect source files */
+static void wb_project_dir_collect_source_files(G_GNUC_UNUSED gchar *filename, TMSourceFile *sf, gpointer user_data)
+{
+ GPtrArray *array = user_data;
+
+ if (sf != NULL)
+ g_ptr_array_add(array, sf);
+}
+
+
+/** Get the name of a project dir.
+ *
+ * @param directory The project dir
+ * @return The name
+ *
+ **/
+const gchar *wb_project_dir_get_name (WB_PROJECT_DIR *directory)
+{
+ if (directory != NULL)
+ {
+ return directory->name;
+ }
+ return NULL;
+}
+
+
+/** Get the file table for the project dir.
+ *
+ * @param directory The project dir
+ * @return A GHashTable of all files containing to the rpoject dir.
+ * Might be empty if dir has not been scanned yet.
+ *
+ **/
+GHashTable *wb_project_dir_get_file_table (WB_PROJECT_DIR *directory)
+{
+ if (directory != NULL)
+ {
+ return directory->file_table;
+ }
+ return NULL;
+}
+
+
+/** Get the base/root dir of a project dir.
+ *
+ * @param directory The project dir
+ * @return The base dir
+ *
+ **/
+gchar *wb_project_dir_get_base_dir (WB_PROJECT_DIR *directory)
+{
+ if (directory != NULL)
+ {
+ return directory->base_dir;
+ }
+ return NULL;
+}
+
+
+/** Get the file patterns of a project dir.
+ *
+ * @param directory The project dir
+ * @return String array of file patterns
+ *
+ **/
+gchar **wb_project_dir_get_file_patterns (WB_PROJECT_DIR *directory)
+{
+ if (directory != NULL)
+ {
+ return directory->file_patterns;
+ }
+ return NULL;
+}
+
+
+/** Set the file patterns of a project dir.
+ *
+ * @param directory The project dir
+ * @param new String array of file patterns to set
+ * @return FALSE if directory is NULL, TRUE otherwise
+ *
+ **/
+gboolean wb_project_dir_set_file_patterns (WB_PROJECT_DIR *directory, gchar **new)
+{
+ if (directory != NULL)
+ {
+ g_strfreev(directory->file_patterns);
+ directory->file_patterns = g_strdupv(new);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/** Get the ignored dirs patterns of a project dir.
+ *
+ * @param directory The project dir
+ * @return String array of ignored dirs patterns
+ *
+ **/
+gchar **wb_project_dir_get_ignored_dirs_patterns (WB_PROJECT_DIR *directory)
+{
+ if (directory != NULL)
+ {
+ return directory->ignored_dirs_patterns;
+ }
+ return NULL;
+}
+
+
+/** Set the ignored dirs patterns of a project dir.
+ *
+ * @param directory The project dir
+ * @param new String array of ignored dirs patterns to set
+ * @return FALSE if directory is NULL, TRUE otherwise
+ *
+ **/
+gboolean wb_project_dir_set_ignored_dirs_patterns (WB_PROJECT_DIR *directory, gchar **new)
+{
+ if (directory != NULL)
+ {
+ g_strfreev(directory->ignored_dirs_patterns);
+ directory->ignored_dirs_patterns = g_strdupv(new);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/** Get the ignored file patterns of a project dir.
+ *
+ * @param directory The project dir
+ * @return String array of ignored file patterns
+ *
+ **/
+gchar **wb_project_dir_get_ignored_file_patterns (WB_PROJECT_DIR *directory)
+{
+ if (directory != NULL)
+ {
+ return directory->ignored_file_patterns;
+ }
+ return NULL;
+}
+
+
+/** Set the ignored file patterns of a project dir.
+ *
+ * @param directory The project dir
+ * @param new String array of ignored dirs patterns to set
+ * @return FALSE if directory is NULL, TRUE otherwise
+ *
+ **/
+gboolean wb_project_dir_set_ignored_file_patterns (WB_PROJECT_DIR *directory, gchar **new)
+{
+ if (directory != NULL)
+ {
+ g_strfreev(directory->ignored_file_patterns);
+ directory->ignored_file_patterns = g_strdupv(new);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/* Remove all files contained in the project dir from the tm-workspace */
+static void wb_project_dir_remove_from_tm_workspace(WB_PROJECT_DIR *root)
+{
+ GPtrArray *source_files;
+
+ source_files = g_ptr_array_new();
+ g_hash_table_foreach(root->file_table, (GHFunc)wb_project_dir_collect_source_files, source_files);
+ tm_workspace_remove_source_files(source_files);
+ g_ptr_array_free(source_files, TRUE);
+}
+
+
+/* Free a project dir */
+static void wb_project_dir_free(WB_PROJECT_DIR *dir)
+{
+ wb_project_dir_remove_from_tm_workspace(dir);
+
+ g_hash_table_destroy(dir->file_table);
+ g_free(dir->base_dir);
+ g_free(dir);
+}
+
+
+/* Compare two project dirs */
+static gint wb_project_dir_comparator(WB_PROJECT_DIR *a, WB_PROJECT_DIR *b)
+{
+ gchar *a_realpath, *b_realpath, *a_locale_base_dir, *b_locale_base_dir;
+ gint res;
+
+ a_locale_base_dir = utils_get_locale_from_utf8(a->base_dir);
+ b_locale_base_dir = utils_get_locale_from_utf8(b->base_dir);
+ a_realpath = tm_get_real_path(a_locale_base_dir);
+ b_realpath = tm_get_real_path(b_locale_base_dir);
+
+ res = g_strcmp0(a_realpath, b_realpath);
+
+ g_free(a_realpath);
+ g_free(b_realpath);
+ g_free(a_locale_base_dir);
+ g_free(b_locale_base_dir);
+
+ return res;
+}
+
+
+/* Get the file count of a project */
+static guint wb_project_get_file_count(WB_PROJECT *prj)
+{
+ GSList *elem;
+ guint filenum = 0;
+
+ foreach_slist(elem, prj->directories)
+ {
+ filenum += ((WB_PROJECT_DIR *)elem->data)->file_count;
+ }
+ return filenum;
+}
+
+/* Rescan/update the file list of a project dir. */
+static guint wb_project_dir_rescan_int(WB_PROJECT *prj, WB_PROJECT_DIR *root)
+{
+ GSList *pattern_list = NULL;
+ GSList *ignored_dirs_list = NULL;
+ GSList *ignored_file_list = NULL;
+ GHashTable *visited_paths;
+ GSList *lst;
+ GSList *elem;
+ guint filenum = 0;
+ gchar *searchdir;
+
+ wb_project_dir_remove_from_tm_workspace(root);
+ g_hash_table_remove_all(root->file_table);
+
+ if (!root->file_patterns || !root->file_patterns[0])
+ {
+ const gchar *all_pattern[] = { "*", NULL };
+ pattern_list = get_precompiled_patterns((gchar **)all_pattern);
+ }
+ else
+ {
+ pattern_list = get_precompiled_patterns(root->file_patterns);
+ }
+
+ ignored_dirs_list = get_precompiled_patterns(root->ignored_dirs_patterns);
+ ignored_file_list = get_precompiled_patterns(root->ignored_file_patterns);
+
+ visited_paths = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+ searchdir = get_combined_path(prj->filename, root->base_dir);
+ root->file_count = 0;
+ root->folder_count = 0;
+ lst = wb_project_dir_get_file_list
+ (root, searchdir, pattern_list, ignored_dirs_list, ignored_file_list, visited_paths);
+ g_hash_table_destroy(visited_paths);
+ g_free(searchdir);
+
+ foreach_slist(elem, lst)
+ {
+ char *path = elem->data;
+
+ if (path)
+ {
+ g_hash_table_insert(root->file_table, g_strdup(path), NULL);
+ filenum++;
+ }
+ }
+
+ 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);
+
+ g_slist_foreach(ignored_file_list, (GFunc) g_pattern_spec_free, NULL);
+ g_slist_free(ignored_file_list);
+
+ return filenum;
+}
+
+
+/* Stolen and modified version from Geany. The only difference is that Geany
+ * first looks at shebang inside the file and then, if it fails, checks the
+ * file extension. Opening every file is too expensive so instead check just
+ * extension and only if this fails, look at the shebang */
+static GeanyFiletype *filetypes_detect(const gchar *utf8_filename)
+{
+ struct stat s;
+ GeanyFiletype *ft = NULL;
+ gchar *locale_filename;
+
+ locale_filename = utils_get_locale_from_utf8(utf8_filename);
+ if (g_stat(locale_filename, &s) != 0 || s.st_size > 10*1024*1024)
+ ft = filetypes[GEANY_FILETYPES_NONE];
+ else
+ {
+ guint i;
+ gchar *utf8_base_filename;
+
+ /* to match against the basename of the file (because of Makefile*) */
+ utf8_base_filename = g_path_get_basename(utf8_filename);
+#ifdef G_OS_WIN32
+ /* use lower case basename */
+ SETPTR(utf8_base_filename, g_utf8_strdown(utf8_base_filename, -1));
+#endif
+
+ for (i = 0; i < geany_data->filetypes_array->len; i++)
+ {
+ GeanyFiletype *ftype = filetypes[i];
+
+ if (match_basename(ftype, utf8_base_filename))
+ {
+ ft = ftype;
+ break;
+ }
+ }
+
+ if (ft == NULL)
+ ft = filetypes_detect_from_file(utf8_filename);
+
+ g_free(utf8_base_filename);
+ }
+
+ g_free(locale_filename);
+
+ return ft;
+}
+
+
+/* Regenerate tags */
+static void wb_project_regenerate_tags(WB_PROJECT_DIR *root, G_GNUC_UNUSED gpointer user_data)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+ GPtrArray *source_files;
+ GHashTable *file_table;
+
+ source_files = g_ptr_array_new();
+ file_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GFreeFunc)tm_source_file_free);
+ g_hash_table_iter_init(&iter, root->file_table);
+ while (g_hash_table_iter_next(&iter, &key, &value))
+ {
+ TMSourceFile *sf;
+ gchar *utf8_path = key;
+ gchar *locale_path = utils_get_locale_from_utf8(utf8_path);
+
+ sf = tm_source_file_new(locale_path, filetypes_detect(utf8_path)->name);
+ if (sf && !document_find_by_filename(utf8_path))
+ g_ptr_array_add(source_files, sf);
+
+ g_hash_table_insert(file_table, g_strdup(utf8_path), sf);
+ g_free(locale_path);
+ }
+ g_hash_table_destroy(root->file_table);
+ root->file_table = file_table;
+
+ tm_workspace_add_source_files(source_files);
+ g_ptr_array_free(source_files, TRUE);
+}
+
+
+/** Rescan/update the file list of a project dir.
+ *
+ * @param project The project to which directory belongs
+ * @param directory The project dir
+ * @return Number of files
+ *
+ **/
+guint wb_project_dir_rescan(WB_PROJECT *prj, WB_PROJECT_DIR *root)
+{
+ guint total, filenum;
+
+ filenum = wb_project_dir_rescan_int(prj, root);
+ total = wb_project_get_file_count(prj);
+ if (prj->generate_tag_prefs == WB_PROJECT_TAG_PREFS_YES || (prj->generate_tag_prefs == WB_PROJECT_TAG_PREFS_AUTO && total < 300))
+ {
+ g_slist_foreach(prj->directories, (GFunc)wb_project_regenerate_tags, NULL);
+ }
+ return filenum;
+}
+
+
+/** Rescan/update the file list of a project.
+ *
+ * @param project The project
+ *
+ **/
+void wb_project_rescan(WB_PROJECT *prj)
+{
+ GSList *elem;
+ guint filenum = 0;
+
+ if (!prj)
+ {
+ return;
+ }
+
+ wb_project_clear_idle_queue(&prj->s_idle_add_funcs);
+ wb_project_clear_idle_queue(&prj->s_idle_remove_funcs);
+
+ foreach_slist(elem, prj->directories)
+ {
+ filenum += wb_project_dir_rescan_int(prj, elem->data);
+ }
+
+ if (prj->generate_tag_prefs == WB_PROJECT_TAG_PREFS_YES || (prj->generate_tag_prefs == WB_PROJECT_TAG_PREFS_AUTO && filenum < 300))
+ {
+ g_slist_foreach(prj->directories, (GFunc)wb_project_regenerate_tags, NULL);
+ }
+}
+
+
+/** Is @a filename included in the project directory?
+ *
+ * @param dir The project directory
+ * @param filename The file
+ * @return TRUE if file is included, FALSE otherwise
+ *
+ **/
+gboolean wb_project_dir_file_is_included(WB_PROJECT_DIR *dir, const gchar *filename)
+{
+ if (filename == NULL || dir == NULL)
+ {
+ return FALSE;
+ }
+
+ if (g_hash_table_lookup_extended (dir->file_table, filename, NULL, NULL))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/** Is @a filename included in the project?
+ *
+ * @param dir The project
+ * @param filename The file
+ * @return TRUE if file is included, FALSE otherwise
+ *
+ **/
+gboolean wb_project_file_is_included(WB_PROJECT *prj, const gchar *filename)
+{
+ GSList *elem;
+
+ if (prj == NULL)
+ {
+ return FALSE;
+ }
+
+ foreach_slist(elem, prj->directories)
+ {
+ if (wb_project_dir_file_is_included(elem->data, filename) == TRUE)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+static gboolean add_tm_idle(gpointer foo)
+{
+ WB_PROJECT *prj;
+ GSList *elem2;
+
+ prj = (WB_PROJECT *)foo;
+ if (prj == NULL || prj->s_idle_add_funcs == NULL)
+ {
+ return FALSE;
+ }
+
+ foreach_slist (elem2, prj->s_idle_add_funcs)
+ {
+ GSList *elem;
+ gchar *utf8_fname = elem2->data;
+
+ foreach_slist (elem, prj->directories)
+ {
+ WB_PROJECT_DIR *dir = elem->data;
+ TMSourceFile *sf = g_hash_table_lookup(dir->file_table, utf8_fname);
+
+ if (sf != NULL && !document_find_by_filename(utf8_fname))
+ {
+ tm_workspace_add_source_file(sf);
+ break; /* single file representation in TM is enough */
+ }
+ }
+ }
+
+ wb_project_clear_idle_queue(&(prj->s_idle_add_funcs));
+
+ return FALSE;
+}
+
+
+/* This function gets called when document is being closed by Geany and we need
+ * to add the TMSourceFile from the tag manager because Geany removes it on
+ * document close.
+ *
+ * Additional problem: The tag removal in Geany happens after this function is called.
+ * To be sure, perform on idle after this happens (even though from my knowledge of TM
+ * this shouldn't probably matter). */
+void wb_project_add_single_tm_file(WB_PROJECT *prj, const gchar *filename)
+{
+ if (prj == NULL)
+ {
+ return;
+ }
+
+ if (prj->s_idle_add_funcs == NULL)
+ {
+ plugin_idle_add(wb_globals.geany_plugin, (GSourceFunc)add_tm_idle, prj);
+ }
+
+ prj->s_idle_add_funcs = g_slist_prepend(prj->s_idle_add_funcs, g_strdup(filename));
+}
+
+
+static gboolean remove_tm_idle(gpointer foo)
+{
+ WB_PROJECT *prj;
+ GSList *elem2;
+
+ prj = (WB_PROJECT *)foo;
+ if (prj == NULL || prj->s_idle_remove_funcs == NULL)
+ {
+ return FALSE;
+ }
+
+ foreach_slist (elem2, prj->s_idle_remove_funcs)
+ {
+ GSList *elem;
+ gchar *utf8_fname = elem2->data;
+
+ foreach_slist (elem, prj->directories)
+ {
+ WB_PROJECT_DIR *dir = elem->data;
+ TMSourceFile *sf = g_hash_table_lookup(dir->file_table, utf8_fname);
+
+ if (sf != NULL)
+ {
+ tm_workspace_remove_source_file(sf);
+ }
+ }
+ }
+
+ wb_project_clear_idle_queue(&(prj->s_idle_remove_funcs));
+ return FALSE;
+}
+
+
+/* This function gets called when document is being opened by Geany and we need
+ * to remove the TMSourceFile from the tag manager because Geany inserts
+ * it for the newly open tab. Even though tag manager would handle two identical
+ * files, the file inserted by the plugin isn't updated automatically in TM
+ * so any changes wouldn't be reflected in the tags array (e.g. removed function
+ * from source file would still be found in TM)
+ *
+ * Additional problem: The document being opened may be caused
+ * by going to tag definition/declaration - tag processing is in progress
+ * when this function is called and if we remove the TmSourceFile now, line
+ * number for the searched tag won't be found. For this reason delay the tag
+ * TmSourceFile removal until idle */
+void wb_project_remove_single_tm_file(WB_PROJECT *prj, const gchar *utf8_filename)
+{
+ if (prj == NULL)
+ {
+ return;
+ }
+
+ if (prj->s_idle_remove_funcs == NULL)
+ {
+ plugin_idle_add(wb_globals.geany_plugin, (GSourceFunc)remove_tm_idle, prj);
+ }
+ prj->s_idle_remove_funcs = g_slist_prepend(prj->s_idle_remove_funcs, g_strdup(utf8_filename));
+}
+
+
+/* Add a directory to the project */
+static WB_PROJECT_DIR *wb_project_add_directory_int(WB_PROJECT *prj, const gchar *dirname, gboolean rescan)
+{
+ if (prj != NULL)
+ {
+ WB_PROJECT_DIR *new_dir = wb_project_dir_new(dirname);
+
+ if (prj->directories != NULL)
+ {
+ GSList *lst = prj->directories->next;
+ lst = g_slist_prepend(lst, new_dir);
+ lst = g_slist_sort(lst, (GCompareFunc)wb_project_dir_comparator);
+ prj->directories->next = lst;
+ }
+ else
+ {
+ prj->directories = g_slist_append(prj->directories, new_dir);
+ }
+
+ if (rescan)
+ {
+ wb_project_rescan(prj);
+ }
+ return new_dir;
+ }
+ return NULL;
+}
+
+
+/** Adds a new project dir to the project.
+ *
+ * @param project The project
+ * @param dirname The base dir of the new project dir
+ * @return TRUE on success, FALSE otherwise
+ *
+ **/
+gboolean wb_project_add_directory(WB_PROJECT *prj, const gchar *dirname)
+{
+ gchar *reldirname;
+
+ /* Convert dirname to path relative to the project file */
+ reldirname = get_any_relative_path(prj->filename, dirname);
+
+ if (wb_project_add_directory_int(prj, reldirname, TRUE) != NULL)
+ {
+ prj->modified = TRUE;
+ return TRUE;
+ }
+
+ g_free(reldirname);
+ return FALSE;
+}
+
+
+/** Remove a project dir from the project.
+ *
+ * @param project The project
+ * @param dir The project dir to remove
+ * @return FALSE on NULL parameter, TRUE otherwise
+ *
+ **/
+gboolean wb_project_remove_directory (WB_PROJECT *prj, WB_PROJECT_DIR *dir)
+{
+ if (prj != NULL && dir != NULL)
+ {
+ prj->directories = g_slist_remove(prj->directories, dir);
+ wb_project_dir_free(dir);
+ wb_project_rescan(prj);
+ prj->modified = TRUE;
+ }
+ return FALSE;
+}
+
+
+/** Get an info string for the project dir.
+ *
+ * @param dir The project dir
+ * @return The info string
+ *
+ **/
+gchar *wb_project_dir_get_info (WB_PROJECT_DIR *dir)
+{
+ gchar *str;
+
+ if (dir == NULL)
+ return g_strdup("");
+
+ GString *temp = g_string_new(NULL);
+ gchar *text;
+ g_string_append_printf(temp, _("Directory-Name: %s\n"), wb_project_dir_get_name(dir));
+ g_string_append_printf(temp, _("Base-Directory: %s\n"), wb_project_dir_get_base_dir(dir));
+
+ g_string_append(temp, _("File Patterns:"));
+ str = g_strjoinv(" ", dir->file_patterns);
+ if (str != NULL )
+ {
+ g_string_append_printf(temp, " %s\n", str);
+ g_free(str);
+ }
+ else
+ {
+ g_string_append(temp, "\n");
+ }
+
+ g_string_append(temp, _("Ignored Dir. Patterns:"));
+ str = g_strjoinv(" ", dir->ignored_dirs_patterns);
+ if (str != NULL )
+ {
+ g_string_append_printf(temp, " %s\n", str);
+ g_free(str);
+ }
+ else
+ {
+ g_string_append(temp, "\n");
+ }
+
+ g_string_append(temp, _("Ignored File Patterns:"));
+ str = g_strjoinv(" ", dir->ignored_file_patterns);
+ if (str != NULL )
+ {
+ g_string_append_printf(temp, " %s\n", str);
+ g_free(str);
+ }
+ else
+ {
+ g_string_append(temp, "\n");
+ }
+
+ g_string_append_printf(temp, _("Number of Sub-Folders: %u\n"), dir->folder_count);
+ g_string_append_printf(temp, _("Number of Files: %u\n"), dir->file_count);
+
+ /* Steal string content */
+ text = temp->str;
+ g_string_free (temp, FALSE);
+
+ return text;
+}
+
+
+/** Get an info string for the project.
+ *
+ * @param prj The project
+ * @return The info string
+ *
+ **/
+gchar *wb_project_get_info (WB_PROJECT *prj)
+{
+ GString *temp = g_string_new(NULL);
+ gchar *text;
+
+ if (prj == NULL)
+ return g_strdup("");
+
+ g_string_append_printf(temp, _("Project: %s\n"), wb_project_get_name(prj));
+ g_string_append_printf(temp, _("File: %s\n"), wb_project_get_filename(prj));
+ g_string_append_printf(temp, _("Number of Directories: %u\n"), g_slist_length(prj->directories));
+ if (wb_project_is_modified(prj))
+ {
+ g_string_append(temp, _("\nThe project contains unsafed changes!\n"));
+ }
+
+ /* Steal string content */
+ text = temp->str;
+ g_string_free (temp, FALSE);
+
+ return text;
+}
+
+
+/* Save directories to key file */
+static void wb_project_save_directories (gpointer data, gpointer user_data)
+{
+ gchar key[250], *str;
+ WB_PROJECT_DIR *dir;
+ WB_PROJECT_ON_SAVE_USER_DATA *tmp;
+
+ if (data == NULL || user_data == NULL)
+ {
+ return;
+ }
+ tmp = (WB_PROJECT_ON_SAVE_USER_DATA *)user_data;
+ dir = (WB_PROJECT_DIR *)data;
+
+ g_snprintf(key, sizeof(key), "Dir%u-BaseDir", tmp->dir_count);
+ g_key_file_set_string(tmp->kf, "Workbench", key, dir->base_dir);
+
+ g_snprintf(key, sizeof(key), "Dir%u-FilePatterns", tmp->dir_count);
+ str = g_strjoinv(";", dir->file_patterns);
+ g_key_file_set_string(tmp->kf, "Workbench", key, str);
+ g_free(str);
+
+ g_snprintf(key, sizeof(key), "Dir%u-IgnoredDirsPatterns", tmp->dir_count);
+ str = g_strjoinv(";", dir->ignored_dirs_patterns);
+ g_key_file_set_string(tmp->kf, "Workbench", key, str);
+ g_free(str);
+
+ g_snprintf(key, sizeof(key), "Dir%u-IgnoredFilePatterns", tmp->dir_count);
+ str = g_strjoinv(";", dir->ignored_file_patterns);
+ g_key_file_set_string(tmp->kf, "Workbench", key, str);
+ g_free(str);
+
+ tmp->dir_count++;
+}
+
+
+/* Add a bookmark to the project */
+static gboolean wb_project_add_bookmark_int(WB_PROJECT *prj, const gchar *filename)
+{
+ if (prj != NULL)
+ {
+ gchar *new;
+
+ new = g_strdup(filename);
+ if (new != NULL)
+ {
+ g_ptr_array_add (prj->bookmarks, new);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/** Add a bookmark to the project.
+ *
+ * @param prj The project
+ * @param filename Bookmark
+ * @return TRUE on success, FALSE otherwise
+ *
+ **/
+gboolean wb_project_add_bookmark(WB_PROJECT *prj, const gchar *filename)
+{
+ if (wb_project_add_bookmark_int(prj, filename) == TRUE)
+ {
+ prj->modified = TRUE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/** Remove a bookmark from the project.
+ *
+ * @param prj The project
+ * @param filename Bookmark
+ * @return TRUE on success, FALSE otherwise
+ *
+ **/
+gboolean wb_project_remove_bookmark(WB_PROJECT *prj, const gchar *filename)
+{
+ if (prj != NULL)
+ {
+ guint index;
+ gchar *current;
+
+ for (index = 0 ; index < prj->bookmarks->len ; index++)
+ {
+ current = g_ptr_array_index(prj->bookmarks, index);
+ if (current == filename)
+ {
+ g_ptr_array_remove_index (prj->bookmarks, index);
+ prj->modified = TRUE;
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+/* Free all bookmarks */
+static gboolean wb_project_free_all_bookmarks(WB_PROJECT *prj)
+{
+ if (prj != NULL)
+ {
+ guint index;
+ gchar *current;
+
+ for (index = 0 ; index < prj->bookmarks->len ; index++)
+ {
+ current = g_ptr_array_index(prj->bookmarks, index);
+ g_free(current);
+ }
+ g_ptr_array_free(prj->bookmarks, TRUE);
+ }
+ return FALSE;
+}
+
+
+/** Get the bookmark of a project at index @a index.
+ *
+ * @param prj The project
+ * @param index The index
+ * @return The filename of the boomark (or NULL if prj is NULL)
+ *
+ **/
+gchar *wb_project_get_bookmark_at_index (WB_PROJECT *prj, guint index)
+{
+ if (prj != NULL)
+ {
+ gchar *file;
+ file = g_ptr_array_index(prj->bookmarks, index);
+ if (file == NULL)
+ {
+ return NULL;
+ }
+ return file;
+ }
+ return NULL;
+}
+
+
+/** Get the number of bookmarks of a project.
+ *
+ * @param prj The project
+ * @return The number of bookmarks
+ *
+ **/
+guint wb_project_get_bookmarks_count(WB_PROJECT *prj)
+{
+ if (prj != NULL && prj->bookmarks != NULL)
+ {
+ return prj->bookmarks->len;
+ }
+ return 0;
+}
+
+
+/** Save a project.
+ *
+ * The function saves the project config file. Only the workbench plugin info
+ * is changed, all other config options remain unchanged.
+ *
+ * @param prj The project
+ * @param error Location to store error info at
+ * @return TRUE on success, FALSE otherwise
+ *
+ **/
+gboolean wb_project_save(WB_PROJECT *prj, GError **error)
+{
+ GKeyFile *kf;
+ guint index;
+ gchar *contents;
+ gsize length, boomarks_size;
+ gboolean success = FALSE;
+ WB_PROJECT_ON_SAVE_USER_DATA tmp;
+
+ g_return_val_if_fail(prj, FALSE);
+
+ /* Load existing data into GKeyFile */
+ kf = g_key_file_new ();
+ if (!g_key_file_load_from_file(kf, prj->filename, G_KEY_FILE_NONE, error))
+ {
+ return FALSE;
+ }
+
+ /* Remove existing, old data from our plugin */
+ g_key_file_remove_group (kf, "Workbench", NULL);
+
+ /* Save Project bookmarks as string list */
+ boomarks_size = wb_project_get_bookmarks_count(prj);
+ if (boomarks_size > 0)
+ {
+ gchar **bookmarks_strings, *file, *rel_path;
+
+ bookmarks_strings = g_new0(gchar *, boomarks_size+1);
+ for (index = 0 ; index < boomarks_size ; index++ )
+ {
+ file = wb_project_get_bookmark_at_index(prj, index);
+ rel_path = get_any_relative_path(prj->filename, file);
+
+ bookmarks_strings[index] = rel_path;
+ }
+ g_key_file_set_string_list
+ (kf, "Workbench", "Bookmarks", (const gchar **)bookmarks_strings, boomarks_size);
+ for (index = 0 ; index < boomarks_size ; index++ )
+ {
+ g_free (bookmarks_strings[index]);
+ }
+ g_free(bookmarks_strings);
+ }
+
+ /* Init tmp data */
+ tmp.kf = kf;
+ tmp.dir_count = 1;
+
+ /* Store our directories */
+ g_slist_foreach(prj->directories, (GFunc)wb_project_save_directories, &tmp);
+
+ /* Get data as string */
+ contents = g_key_file_to_data (kf, &length, error);
+ g_key_file_free(kf);
+
+ /* Save to file */
+ success = g_file_set_contents (prj->filename, contents, length, error);
+ if (success)
+ {
+ prj->modified = FALSE;
+ }
+ g_free (contents);
+
+ return success;
+}
+
+
+/** Load a project.
+ *
+ * The function loads the project data from file @a filename into the
+ * project structure @a prj.
+ *
+ * @param prj The project
+ * @param filename File to load
+ * @param error Location to store error info at
+ * @return TRUE on success, FALSE otherwise
+ *
+ **/
+gboolean wb_project_load(WB_PROJECT *prj, gchar *filename, GError **error)
+{
+ GKeyFile *kf;
+ guint index;
+ gchar *contents;
+ gchar key[100];
+ gsize length;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail(prj, FALSE);
+
+ if (!g_file_get_contents (filename, &contents, &length, error))
+ {
+ return FALSE;
+ }
+
+ kf = g_key_file_new ();
+
+ if (!g_key_file_load_from_data (kf, contents, length,
+ G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
+ error))
+ {
+ g_key_file_free (kf);
+ g_free (contents);
+ return FALSE;
+ }
+
+ if (g_key_file_has_group (kf, "Workbench"))
+ {
+ WB_PROJECT_DIR *new_dir;
+ gchar *str;
+ gchar **splitv, **bookmarks_strings;
+
+ /* Load project bookmarks from string list */
+ bookmarks_strings = g_key_file_get_string_list (kf, "Workbench", "Bookmarks", NULL, error);
+ if (bookmarks_strings != NULL)
+ {
+ gchar **file, *abs_path;
+
+ file = bookmarks_strings;
+ while (*file != NULL)
+ {
+ abs_path = get_combined_path(prj->filename, *file);
+ if (abs_path != NULL)
+ {
+ wb_project_add_bookmark_int(prj, abs_path);
+ g_free(abs_path);
+ }
+ file++;
+ }
+ g_strfreev(bookmarks_strings);
+ }
+
+ /* Load project dirs */
+ for (index = 1 ; index < 1025 ; index++)
+ {
+ g_snprintf(key, sizeof(key), "Dir%u-BaseDir", index);
+
+ str = g_key_file_get_string(kf, "Workbench", key, NULL);
+ if (str == NULL)
+ {
+ break;
+ }
+ new_dir = wb_project_add_directory_int(prj, str, FALSE);
+ if (new_dir == NULL)
+ {
+ break;
+ }
+
+ g_snprintf(key, sizeof(key), "Dir%u-FilePatterns", index);
+ str = g_key_file_get_string(kf, "Workbench", key, NULL);
+ if (str != NULL)
+ {
+ splitv = g_strsplit (str, ";", -1);
+ wb_project_dir_set_file_patterns(new_dir, splitv);
+ }
+ g_free(str);
+
+ g_snprintf(key, sizeof(key), "Dir%u-IgnoredDirsPatterns", index);
+ str = g_key_file_get_string(kf, "Workbench", key, NULL);
+ if (str != NULL)
+ {
+ splitv = g_strsplit (str, ";", -1);
+ wb_project_dir_set_ignored_dirs_patterns(new_dir, splitv);
+ }
+ g_free(str);
+
+ g_snprintf(key, sizeof(key), "Dir%u-IgnoredFilePatterns", index);
+ str = g_key_file_get_string(kf, "Workbench", key, NULL);
+ if (str != NULL)
+ {
+ splitv = g_strsplit (str, ";", -1);
+ wb_project_dir_set_ignored_file_patterns(new_dir, splitv);
+ }
+ g_free(str);
+ }
+ }
+
+ g_key_file_free(kf);
+ g_free (contents);
+ success = TRUE;
+
+ return success;
+}
+
+
+/** Create a new empty project.
+ *
+ * @return Address of the new structure.
+ *
+ **/
+WB_PROJECT *wb_project_new(const gchar *filename)
+{
+ WB_PROJECT *new_prj;
+
+ new_prj = g_malloc0(sizeof *new_prj);
+ new_prj->modified = FALSE;
+ wb_project_set_filename(new_prj, filename);
+ new_prj->bookmarks = g_ptr_array_new();
+ new_prj->generate_tag_prefs = WB_PROJECT_TAG_PREFS_YES;
+
+ return new_prj;
+}
+
+
+/** Free a project.
+ *
+ * @param prj Adress of structure to free
+ *
+ **/
+void wb_project_free(WB_PROJECT *prj)
+{
+ /* Free directories first */
+ g_slist_free_full(prj->directories, (GDestroyNotify)wb_project_dir_free);
+
+ /* Free all bookmarks */
+ wb_project_free_all_bookmarks(prj);
+
+ g_free(prj->filename);
+ g_free(prj->name);
+ g_free(prj);
+}
diff --git a/workbench/src/wb_project.h b/workbench/src/wb_project.h
new file mode 100644
index 000000000..b654eec59
--- /dev/null
+++ b/workbench/src/wb_project.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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 __WB_PROJECT_H__
+#define __WB_PROJECT_H__
+
+#include
+
+typedef struct S_WB_PROJECT WB_PROJECT;
+typedef struct S_WB_PROJECT_DIR WB_PROJECT_DIR;
+
+WB_PROJECT *wb_project_new(const gchar *filename);
+void wb_project_free(WB_PROJECT *prj);
+
+void wb_project_set_modified(WB_PROJECT *prj, gboolean value);
+gboolean wb_project_is_modified(WB_PROJECT *prj);
+
+void wb_project_set_filename(WB_PROJECT *prj, const gchar *filename);
+const gchar *wb_project_get_filename(WB_PROJECT *prj);
+const gchar *wb_project_get_name(WB_PROJECT *prj);
+GSList *wb_project_get_directories(WB_PROJECT *prj);
+gboolean wb_project_add_directory(WB_PROJECT *prj, const gchar *dirname);
+gboolean wb_project_remove_directory (WB_PROJECT *prj, WB_PROJECT_DIR *dir);
+void wb_project_rescan(WB_PROJECT *prj);
+gboolean wb_project_file_is_included(WB_PROJECT *prj, const gchar *filename);
+void wb_project_add_single_tm_file(WB_PROJECT *prj, const gchar *filename);
+void wb_project_remove_single_tm_file(WB_PROJECT *prj, const gchar *filename);
+
+const gchar *wb_project_dir_get_name (WB_PROJECT_DIR *directory);
+GHashTable *wb_project_dir_get_file_table (WB_PROJECT_DIR *directory);
+gchar *wb_project_dir_get_base_dir (WB_PROJECT_DIR *directory);
+gchar **wb_project_dir_get_file_patterns (WB_PROJECT_DIR *directory);
+gboolean wb_project_dir_set_file_patterns (WB_PROJECT_DIR *directory, gchar **new);
+gchar **wb_project_dir_get_ignored_dirs_patterns (WB_PROJECT_DIR *directory);
+gboolean wb_project_dir_set_ignored_dirs_patterns (WB_PROJECT_DIR *directory, gchar **new);
+gchar **wb_project_dir_get_ignored_file_patterns (WB_PROJECT_DIR *directory);
+gboolean wb_project_dir_set_ignored_file_patterns (WB_PROJECT_DIR *directory, gchar **new);
+guint wb_project_dir_rescan(WB_PROJECT *prj, WB_PROJECT_DIR *root);
+gchar *wb_project_dir_get_info (WB_PROJECT_DIR *dir);
+gboolean wb_project_dir_file_is_included(WB_PROJECT_DIR *dir, const gchar *filename);
+
+gboolean wb_project_add_bookmark(WB_PROJECT *prj, const gchar *filename);
+gboolean wb_project_remove_bookmark(WB_PROJECT *prj, const gchar *filename);
+GPtrArray *wb_project_get_bookmarks(WB_PROJECT *prj);
+gchar *wb_project_get_bookmark_at_index (WB_PROJECT *prj, guint index);
+guint wb_project_get_bookmarks_count(WB_PROJECT *prj);
+
+gboolean wb_project_save(WB_PROJECT *prj, GError **error);
+gboolean wb_project_load(WB_PROJECT *prj, gchar *filename, GError **error);
+
+gchar *wb_project_get_info (WB_PROJECT *prj);
+
+#endif
diff --git a/workbench/src/workbench.c b/workbench/src/workbench.c
new file mode 100644
index 000000000..f80831219
--- /dev/null
+++ b/workbench/src/workbench.c
@@ -0,0 +1,803 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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.
+ */
+
+/*
+ * Code for the WORKBENCH structure.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include
+#include
+#include
+#include "workbench.h"
+#include "wb_project.h"
+#include "utils.h"
+
+typedef struct
+{
+ PROJECT_ENTRY_STATUS status;
+ gchar *abs_filename;
+ gchar *rel_filename;
+ gboolean use_abs;
+ WB_PROJECT *project;
+}WB_PROJECT_ENTRY;
+
+struct S_WORKBENCH
+{
+ gchar *filename;
+ gchar *name;
+ gboolean modified;
+ gboolean rescan_projects_on_open;
+ GPtrArray *projects;
+ GPtrArray *bookmarks;
+};
+
+/* Create a new, empty workbench project entry */
+static WB_PROJECT_ENTRY *wb_project_entry_new(void)
+{
+ WB_PROJECT_ENTRY *new_entry;
+
+ new_entry = g_new(WB_PROJECT_ENTRY, 1);
+ memset(new_entry, 0, sizeof(*new_entry));
+ new_entry->status = PROJECT_ENTRY_STATUS_UNKNOWN;
+
+ return new_entry;
+}
+
+
+/* Free a workbench entry */
+static void wb_project_entry_free(WB_PROJECT_ENTRY *entry)
+{
+ wb_project_free(entry->project);
+ g_free(entry->abs_filename);
+ g_free(entry->rel_filename);
+ g_free(entry);
+}
+
+
+/** Create a new empty workbench.
+ *
+ * @return Address of the new structure.
+ *
+ **/
+WORKBENCH *workbench_new(void)
+{
+ WORKBENCH *new_wb;
+
+ new_wb = g_new(WORKBENCH, 1);
+ memset(new_wb, 0, sizeof(*new_wb));
+ new_wb->modified = FALSE;
+ new_wb->rescan_projects_on_open = TRUE;
+ new_wb->projects = g_ptr_array_new();
+ new_wb->bookmarks = g_ptr_array_new();
+
+ return new_wb;
+}
+
+
+/** Free a workbench.
+ *
+ * @param wb The workbench
+ *
+ **/
+void workbench_free(WORKBENCH *wb)
+{
+ WB_PROJECT_ENTRY *entry;
+ guint index;
+
+ if (wb == NULL)
+ {
+ return;
+ }
+
+ /* Free projects and project entries first */
+ for (index = 0 ; index < wb->projects->len ; index++)
+ {
+ entry = g_ptr_array_index(wb->projects, index);
+ if (entry != NULL)
+ {
+ wb_project_entry_free(entry);
+ }
+ }
+
+ g_ptr_array_free (wb->projects, TRUE);
+ g_free(wb);
+}
+
+
+/** Is the workbench empty?
+ *
+ * @param wb The workbench
+ * @return TRUE is workbench is empty or wb == NULL,
+ * FALSE otherwise.
+ *
+ **/
+gboolean workbench_is_empty(WORKBENCH *wb)
+{
+ if (wb != NULL)
+ {
+ return (wb->projects->len == 0);
+ }
+ return TRUE;
+}
+
+
+/** Get the project count.
+ *
+ * @param wb The workbench
+ * @return TRUE is workbench is empty or wb == NULL,
+ * FALSE otherwise.
+ *
+ **/
+guint workbench_get_project_count(WORKBENCH *wb)
+{
+ if (wb != NULL)
+ {
+ return wb->projects->len;
+ }
+ return 0;
+}
+
+
+/** Is the workbench modified?
+ *
+ * @param wb The workbench
+ * @return TRUE if the workbench is modified,
+ * FALSE if not or wb == NULL
+ *
+ **/
+gboolean workbench_is_modified(WORKBENCH *wb)
+{
+ if (wb != NULL)
+ {
+ return wb->modified;
+ }
+ return FALSE;
+}
+
+
+/** Set the "Rescan projects on open" option.
+ *
+ * @param wb The workbench
+ * @param value The value to set
+ *
+ **/
+void workbench_set_rescan_projects_on_open(WORKBENCH *wb, gboolean value)
+{
+ if (wb != NULL)
+ {
+ if (wb->rescan_projects_on_open != value)
+ {
+ wb->rescan_projects_on_open = value;
+ wb->modified = TRUE;
+ }
+ }
+}
+
+
+/** Get the "Rescan projects on open" option.
+ *
+ * @param wb The workbench
+ * @return TRUE = rescan all projects after opening the workbench,
+ * FALSE = don't
+ *
+ **/
+gboolean workbench_get_rescan_projects_on_open(WORKBENCH *wb)
+{
+ if (wb != NULL)
+ {
+ return wb->rescan_projects_on_open;
+ }
+ return FALSE;
+}
+
+
+/** Set the filename.
+ *
+ * @param wb The workbench
+ * @param filename Name of the workbench file
+ *
+ **/
+void workbench_set_filename(WORKBENCH *wb, const gchar *filename)
+{
+ if (wb != NULL)
+ {
+ guint offset;
+ gchar *ext;
+
+ wb->filename = g_strdup(filename);
+ wb->name = g_path_get_basename (filename);
+ ext = g_strrstr(wb->name, ".geanywb");
+ if(ext != NULL)
+ {
+ offset = strlen(wb->name);
+ offset -= strlen(".geanywb");
+ if (ext == wb->name + offset)
+ {
+ /* Strip of file extension by overwriting
+ '.' with string terminator. */
+ wb->name[offset] = '\0';
+ }
+ }
+ }
+}
+
+
+/** Get the filename.
+ *
+ * @param wb The workbench
+ * @return The filename or NULL
+ *
+ **/
+const gchar *workbench_get_filename(WORKBENCH *wb)
+{
+ if (wb != NULL)
+ {
+ return wb->filename;
+ }
+ return NULL;
+}
+
+
+/** Get the name.
+ *
+ * @param wb The workbench
+ * @return The name or NULL
+ *
+ **/
+gchar *workbench_get_name(WORKBENCH *wb)
+{
+ if (wb != NULL)
+ {
+ return wb->name;
+ }
+ return NULL;
+}
+
+
+/** Get the project stored in the workbench at @a index.
+ *
+ * @param wb The workbench
+ * @param index The index
+ * @return Adress of the WB_PROJECT structure or NULL
+ * if wb == NULL or an invalid index
+ *
+ **/
+WB_PROJECT *workbench_get_project_at_index (WORKBENCH *wb, guint index)
+{
+ if (wb != NULL)
+ {
+ WB_PROJECT_ENTRY *entry;
+ entry = g_ptr_array_index(wb->projects, index);
+ if (entry == NULL)
+ {
+ return NULL;
+ }
+ return entry->project;
+ }
+ return NULL;
+}
+
+
+/** Get the project status in the workbench at @a index.
+ *
+ * Actually the project status just gives information about wheter
+ * the project file for the project at @a index was found or not.
+ *
+ * @param wb The workbench
+ * @param index The index
+ * @return the status or PROJECT_ENTRY_STATUS_UNKNOWN if wb == NULL
+ *
+ **/
+PROJECT_ENTRY_STATUS workbench_get_project_status_at_index (WORKBENCH *wb, guint index)
+{
+ if (wb != NULL)
+ {
+ WB_PROJECT_ENTRY *entry;
+ entry = g_ptr_array_index(wb->projects, index);
+ if (entry == NULL)
+ {
+ return PROJECT_ENTRY_STATUS_UNKNOWN;
+ }
+ return entry->status;
+ }
+ return PROJECT_ENTRY_STATUS_UNKNOWN;
+}
+
+
+/** Get the project status in the workbench by address.
+ *
+ * Actually the project status just gives information about wheter
+ * the project file for the project at @a index was found or not.
+ *
+ * @param wb The workbench
+ * @param address Location of the project
+ * @return the status or PROJECT_ENTRY_STATUS_UNKNOWN if wb == NULL
+ *
+ **/
+PROJECT_ENTRY_STATUS workbench_get_project_status_by_address (WORKBENCH *wb, WB_PROJECT *address)
+{
+ guint index;
+ if (wb != NULL || address == NULL)
+ {
+ WB_PROJECT_ENTRY *entry;
+ for (index = 0 ; index < wb->projects->len ; index++)
+ {
+ entry = g_ptr_array_index(wb->projects, index);
+ if (entry != NULL && entry->project == address)
+ {
+ return entry->status;
+ }
+ }
+ }
+ return PROJECT_ENTRY_STATUS_UNKNOWN;
+}
+
+
+/** Add a project to the workbench.
+ *
+ * @param wb The workbench
+ * @param filename Project file
+ * @return TRUE on success, FALSE otherwise
+ *
+ **/
+gboolean workbench_add_project(WORKBENCH *wb, const gchar *filename)
+{
+ if (wb != NULL)
+ {
+ GStatBuf buf;
+ WB_PROJECT *project;
+ WB_PROJECT_ENTRY *entry;
+
+ entry = wb_project_entry_new();
+ if (entry == NULL)
+ {
+ return FALSE;
+ }
+ project = wb_project_new(filename);
+ if (project == NULL)
+ {
+ wb_project_entry_free(entry);
+ return FALSE;
+ }
+
+ /* Set entry data:
+ - absolute and relative filename (relative to workbench file)
+ - per default use relative path
+ - check status of project file
+ - pointer to the project data */
+ entry->abs_filename = g_strdup(filename);
+ entry->rel_filename = get_any_relative_path
+ (wb->filename, filename);
+ entry->use_abs = FALSE;
+ entry->project = project;
+ if (g_stat (filename, &buf) == 0)
+ {
+ entry->status = PROJECT_ENTRY_STATUS_OK;
+ }
+ else
+ {
+ entry->status = PROJECT_ENTRY_STATUS_NOT_FOUND;
+ }
+ g_ptr_array_add (wb->projects, entry);
+
+ wb->modified = TRUE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/** Remove a project from the workbench.
+ *
+ * @param wb The workbench
+ * @param project The Project
+ * @return TRUE on success, FALSE otherwise
+ *
+ **/
+gboolean workbench_remove_project_with_address(WORKBENCH *wb, WB_PROJECT *project)
+{
+ if (wb != NULL && wb->projects != NULL)
+ {
+ guint index;
+ WB_PROJECT_ENTRY *current;
+
+ for (index = 0 ; index < wb->projects->len ; index++)
+ {
+ current = g_ptr_array_index(wb->projects, index);
+ if (current != NULL && current->project == project)
+ {
+ g_ptr_array_remove_index (wb->projects, index);
+ wb_project_entry_free(current);
+ wb->modified = TRUE;
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+/** Is the file included in the workbench?
+ *
+ * @param wb The workbench
+ * @param filename The file
+ * @return Address of project in which the file is included.
+ * NULL if the file is not included in any workbench project.
+ *
+ **/
+WB_PROJECT *workbench_file_is_included (WORKBENCH *wb, const gchar *filename)
+{
+ if (wb != NULL)
+ {
+ guint index;
+ WB_PROJECT_ENTRY *current;
+
+ for (index = 0 ; index < wb->projects->len ; index++)
+ {
+ current = g_ptr_array_index(wb->projects, index);
+ if (current != NULL && wb_project_file_is_included(current->project, filename) == TRUE)
+ {
+ return current->project;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+/* Add a workbench bookmark */
+static gboolean workbench_add_bookmark_int(WORKBENCH *wb, const gchar *filename)
+{
+ if (wb != NULL && filename != NULL)
+ {
+ gchar *new;
+
+ new = g_strdup(filename);
+ g_ptr_array_add (wb->bookmarks, new);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/** Add a bookmark to a workbench.
+ *
+ * @param wb The workbench
+ * @param filename File to bookmark
+ * @return TRUE on success, FALSE otherwise
+ *
+ **/
+gboolean workbench_add_bookmark(WORKBENCH *wb, const gchar *filename)
+{
+ if (workbench_add_bookmark_int(wb, filename) == TRUE)
+ {
+ wb->modified = TRUE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/** Remove a bookmark from a workbench by filename address.
+ *
+ * @param wb The workbench
+ * @param filename File to remove
+ * @return TRUE on success, FALSE otherwise
+ *
+ **/
+gboolean workbench_remove_bookmark(WORKBENCH *wb, const gchar *filename)
+{
+ if (wb != NULL)
+ {
+ guint index;
+ gchar *current;
+
+ for (index = 0 ; index < wb->bookmarks->len ; index++)
+ {
+ current = g_ptr_array_index(wb->bookmarks, index);
+ if (current == filename)
+ {
+ g_ptr_array_remove_index (wb->bookmarks, index);
+ wb->modified = TRUE;
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+/** Get the bookmark at @a index.
+ *
+ * @param wb The workbench
+ * @param index The index
+ * @return Address of filename or NULL
+ *
+ **/
+gchar *workbench_get_bookmark_at_index (WORKBENCH *wb, guint index)
+{
+ if (wb != NULL)
+ {
+ gchar *file;
+ file = g_ptr_array_index(wb->bookmarks, index);
+ if (file == NULL)
+ {
+ return NULL;
+ }
+ return file;
+ }
+ return NULL;
+}
+
+
+/** Get the number of bookmarks in a workbench.
+ *
+ * @param wb The workbench
+ * @return The number of bookmarks or 0 if wb == NULL
+ *
+ **/
+guint workbench_get_bookmarks_count(WORKBENCH *wb)
+{
+ if (wb != NULL && wb->bookmarks != NULL)
+ {
+ return wb->bookmarks->len;
+ }
+ return 0;
+}
+
+
+/** Save a workbench.
+ *
+ * @param wb The workbench
+ * @param error Location for returning an GError
+ * @return TRUE on success, FALSE otherwise
+ *
+ **/
+gboolean workbench_save(WORKBENCH *wb, GError **error)
+{
+ gboolean success = FALSE;
+
+ if (wb != NULL)
+ {
+ GKeyFile *kf;
+ guint index;
+ gchar *contents;
+ gchar group[20];
+ gsize length, boomarks_size;
+ WB_PROJECT_ENTRY *entry;
+
+ kf = g_key_file_new ();
+
+ /* Save common, simple values */
+ g_key_file_set_string(kf, "General", "filetype", "workbench");
+ g_key_file_set_string(kf, "General", "version", "1.0");
+ g_key_file_set_boolean(kf, "General", "RescanProjectsOnOpen", wb->rescan_projects_on_open);
+
+ /* Save Workbench bookmarks as string list */
+ boomarks_size = workbench_get_bookmarks_count(wb);
+ if (boomarks_size > 0)
+ {
+ gchar **bookmarks_strings, *file, *rel_path;
+
+ bookmarks_strings = g_new0(gchar *, boomarks_size+1);
+ for (index = 0 ; index < boomarks_size ; index++ )
+ {
+ file = workbench_get_bookmark_at_index(wb, index);
+ rel_path = get_any_relative_path(wb->filename, file);
+
+ bookmarks_strings[index] = rel_path;
+ }
+ g_key_file_set_string_list
+ (kf, "General", "Bookmarks", (const gchar **)bookmarks_strings, boomarks_size);
+ for (index = 0 ; index < boomarks_size ; index++ )
+ {
+ g_free (bookmarks_strings[index]);
+ }
+ g_free(bookmarks_strings);
+ }
+
+ /* Save projects data */
+ for (index = 0 ; index < wb->projects->len ; index++)
+ {
+ entry = g_ptr_array_index(wb->projects, index);
+ g_snprintf(group, sizeof(group), "Project-%u", (index+1));
+ g_key_file_set_string(kf, group, "AbsFilename", entry->abs_filename);
+ g_key_file_set_string(kf, group, "RelFilename", entry->rel_filename);
+ g_key_file_set_boolean(kf, group, "UseAbsFilename", entry->use_abs);
+ }
+ contents = g_key_file_to_data (kf, &length, error);
+ if (contents != NULL && *error == NULL)
+ {
+ g_key_file_free(kf);
+
+ success = g_file_set_contents (wb->filename, contents, length, error);
+ if (success)
+ {
+ wb->modified = FALSE;
+ }
+ g_free (contents);
+ }
+ }
+ else if (error != NULL)
+ {
+ g_set_error (error, 0, 0,
+ "Internal error: param missing (file: %s, line %d)",
+ __FILE__, __LINE__);
+ }
+
+ return success;
+}
+
+
+/** Load a workbench file.
+ *
+ * The function loads the workbench settings from file @a filename and
+ * stores it into @a wb.
+ *
+ * @param wb The workbench
+ * @param filename File to load from
+ * @param error Location for returning an GError
+ * @return TRUE on success, FALSE otherwise
+ *
+ **/
+gboolean workbench_load(WORKBENCH *wb, const gchar *filename, GError **error)
+{
+ gboolean success = FALSE;
+
+ if (wb != NULL)
+ {
+ GKeyFile *kf;
+ gboolean valid = TRUE;
+ guint index;
+ gchar *contents, **bookmarks_strings;
+ gchar group[20];
+ gsize length;
+ WB_PROJECT_ENTRY *entry;
+
+ if (!g_file_get_contents (filename, &contents, &length, error))
+ {
+ return FALSE;
+ }
+
+ kf = g_key_file_new ();
+
+ if (!g_key_file_load_from_data (kf, contents, length,
+ G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
+ error))
+ {
+ g_key_file_free (kf);
+ g_free (contents);
+ return FALSE;
+ }
+
+ if (g_key_file_has_key (kf, "General", "filetype", NULL)
+ && g_key_file_has_key (kf, "General", "version", NULL))
+ {
+ gchar *check;
+ check = g_key_file_get_string (kf, "General", "filetype", error);
+ if (check == NULL || g_strcmp0(check, "workbench") != 0)
+ {
+ valid = FALSE;
+ }
+ g_free(check);
+ }
+ else
+ {
+ valid = FALSE;
+ }
+
+ if (!valid)
+ {
+ g_set_error (error, 0, 0,
+ _("File %s is not a valid workbench file!"),
+ filename);
+ return FALSE;
+ }
+ workbench_set_filename(wb, filename);
+ wb->rescan_projects_on_open = g_key_file_get_boolean(kf, "General", "RescanProjectsOnOpen", error);
+
+ /* Load Workbench bookmarks from string list */
+ bookmarks_strings = g_key_file_get_string_list (kf, "General", "Bookmarks", NULL, error);
+ if (bookmarks_strings != NULL)
+ {
+ gchar **file, *abs_path;
+
+ file = bookmarks_strings;
+ while (*file != NULL)
+ {
+ abs_path = get_combined_path(wb->filename, *file);
+ if (abs_path != NULL)
+ {
+ workbench_add_bookmark_int(wb, abs_path);
+ g_free(abs_path);
+ }
+ file++;
+ }
+ g_strfreev(bookmarks_strings);
+ }
+
+ /* Load projects data */
+ for (index = 0 ; index < 1024 ; index++)
+ {
+ g_snprintf(group, sizeof(group), "Project-%u", (index+1));
+ if (g_key_file_has_key (kf, group, "AbsFilename", NULL))
+ {
+ gchar *prj_filename;
+ entry = wb_project_entry_new();
+ if (entry == NULL)
+ {
+ continue;
+ }
+ entry->abs_filename = g_key_file_get_string(kf, group, "AbsFilename", error);
+ entry->rel_filename = g_key_file_get_string(kf, group, "RelFilename", error);
+ entry->use_abs = g_key_file_get_boolean(kf, group, "UseAbsFilename", error);
+ if (entry->use_abs == TRUE)
+ {
+ prj_filename = entry->abs_filename;
+ }
+ else
+ {
+ prj_filename = get_combined_path
+ (wb->filename, entry->rel_filename);
+ }
+ if (prj_filename != NULL)
+ {
+ GStatBuf buf;
+
+ entry->project = wb_project_new(prj_filename);
+ if (g_stat (prj_filename, &buf) == 0)
+ {
+ entry->status = PROJECT_ENTRY_STATUS_OK;
+
+ /* ToDo: collect and handle project load errors */
+ wb_project_load(entry->project, prj_filename, error);
+ }
+ else
+ {
+ entry->status = PROJECT_ENTRY_STATUS_NOT_FOUND;
+ }
+ g_ptr_array_add (wb->projects, entry);
+
+ if (wb->rescan_projects_on_open == TRUE)
+ {
+ wb_project_rescan(entry->project);
+ }
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ g_key_file_free(kf);
+ g_free (contents);
+ success = TRUE;
+ }
+ else if (error != NULL)
+ {
+ g_set_error (error, 0, 0,
+ "Internal error: param missing (file: %s, line %d)",
+ __FILE__, __LINE__);
+ }
+
+ return success;
+}
diff --git a/workbench/src/workbench.h b/workbench/src/workbench.h
new file mode 100644
index 000000000..bfa9c0ccd
--- /dev/null
+++ b/workbench/src/workbench.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2017 LarsGit223
+ *
+ * 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 __WB_WORKBENCH_H__
+#define __WB_WORKBENCH_H__
+
+#include
+#include "wb_project.h"
+
+typedef enum
+{
+ PROJECT_ENTRY_STATUS_UNKNOWN,
+ PROJECT_ENTRY_STATUS_OK,
+ PROJECT_ENTRY_STATUS_NOT_FOUND,
+}PROJECT_ENTRY_STATUS;
+
+typedef struct S_WORKBENCH WORKBENCH;
+
+WORKBENCH *workbench_new(void);
+void workbench_free(WORKBENCH *wb);
+
+gboolean workbench_is_empty(WORKBENCH *wb);
+guint workbench_get_project_count(WORKBENCH *wb);
+gboolean workbench_is_modified(WORKBENCH *wb);
+void workbench_set_rescan_projects_on_open(WORKBENCH *wb, gboolean value);
+gboolean workbench_get_rescan_projects_on_open(WORKBENCH *wb);
+WB_PROJECT *workbench_get_project_at_index(WORKBENCH *wb, guint index);
+PROJECT_ENTRY_STATUS workbench_get_project_status_at_index(WORKBENCH *wb, guint index);
+PROJECT_ENTRY_STATUS workbench_get_project_status_by_address(WORKBENCH *wb, WB_PROJECT *address);
+gboolean workbench_add_project(WORKBENCH *wb, const gchar *filename);
+gboolean workbench_remove_project_with_address(WORKBENCH *wb, WB_PROJECT *project);
+WB_PROJECT *workbench_file_is_included (WORKBENCH *wb, const gchar *filename);
+
+void workbench_set_filename(WORKBENCH *wb, const gchar *filename);
+const gchar *workbench_get_filename(WORKBENCH *wb);
+gchar *workbench_get_name(WORKBENCH *wb);
+
+gboolean workbench_save(WORKBENCH *wb, GError **error);
+gboolean workbench_load(WORKBENCH *wb, const gchar *filename, GError **error);
+
+gboolean workbench_add_bookmark(WORKBENCH *wb, const gchar *filename);
+gboolean workbench_remove_bookmark(WORKBENCH *wb, const gchar *filename);
+gchar *workbench_get_bookmark_at_index (WORKBENCH *wb, guint index);
+guint workbench_get_bookmarks_count(WORKBENCH *wb);
+
+#endif