1 /*
2 *
3 * GeanyGenDoc, a Geany plugin to ease generation of source code documentation
4 * Copyright (C) 2010-2011 Colomban Wendling <ban@herbesfolles.org>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h" /* for the gettext domain */
23 #endif
24
25 #include "ggd-plugin.h"
26
27 #include <glib.h>
28 #include <glib/gi18n-lib.h>
29 #include <gdk/gdkkeysyms.h> /* for the key bindings */
30 #include <ctpl/ctpl.h>
31 #include <geanyplugin.h>
32
33 #include "ggd.h"
34 #include "ggd-file-type.h"
35 #include "ggd-file-type-manager.h"
36 #include "ggd-tag-utils.h"
37 #include "ggd-options.h"
38
39
40
41 /*
42 * Questions:
43 * * how to update tag list? (tm_source_file_buffer_update() is not found in
44 * symbols table)
45 */
46
47 /* These items are set by Geany before plugin_init() is called. */
48 GeanyPlugin *geany_plugin;
49 GeanyData *geany_data;
50
51 /* TODO check minimum requierment */
52 PLUGIN_VERSION_CHECK (227)
53
54 PLUGIN_SET_TRANSLATABLE_INFO (
55 LOCALEDIR, GETTEXT_PACKAGE,
56 _("Documentation Generator"),
57 _("Generates documentation basis from source code"),
58 GGD_PLUGIN_VERSION,
59 "Colomban Wendling <ban@herbesfolles.org>"
60 )
61
62 enum
63 {
64 KB_INSERT,
65 NUM_KB
66 };
67
68 typedef struct _PluginData
69 {
70 GgdOptGroup *config;
71 GeanyKeyGroup *kb_group;
72
73 gint editor_menu_popup_line;
74
75 GtkWidget *separator_item;
76 GtkWidget *edit_menu_item;
77 GtkWidget *tools_menu_item;
78 gulong edit_menu_item_hid;
79 } PluginData;
80
81 #define plugin (&plugin_data)
82 static PluginData plugin_data = {
83 NULL, NULL, 0, NULL, NULL, NULL, 0l
84 };
85
86 /* global plugin options
87 * default values that needs to be set dynamically goes at the top of
88 * load_configuration() */
89 gchar *GGD_OPT_doctype[GEANY_MAX_BUILT_IN_FILETYPES] = { NULL };
90 gboolean GGD_OPT_save_to_refresh = FALSE;
91 gboolean GGD_OPT_indent = TRUE;
92 gchar *GGD_OPT_environ = NULL;
93
94
95 /* Gets an element of GGD_OPT_doctype, falling back to GGD_OPT_doctype[0]
96 * (default) if the requested element is not set */
97 static const gchar *
ggd_plugin_get_doctype(GeanyFiletypeID id)98 ggd_plugin_get_doctype (GeanyFiletypeID id)
99 {
100 const gchar *doctype;
101
102 g_return_val_if_fail (id >= 0 && id < GEANY_MAX_BUILT_IN_FILETYPES, NULL);
103
104 doctype = GGD_OPT_doctype[id];
105 if (! doctype || ! *doctype) {
106 doctype = GGD_OPT_doctype[0];
107 }
108
109 return doctype;
110 }
111
112
113 /* FIXME: tm_source_file_buffer_update() is not found in symbols table */
114 /* (tries to) refresh the tag list to the file's current state */
115 static void
refresh_tag_list(TMSourceFile * tm_wo,ScintillaObject * sci,GeanyDocument * doc)116 refresh_tag_list (TMSourceFile *tm_wo,
117 ScintillaObject *sci,
118 GeanyDocument *doc)
119 {
120 /*
121 gint len;
122 guchar *buf;
123
124 len = sci_get_length (sci);
125 buf = g_malloc (len + 1);
126 sci_get_text (sci, len + 1, (gchar *)buf);
127 tm_source_file_buffer_update (tm_wo, buf, len, TRUE);
128 g_free (buf);
129 //*/
130 if (GGD_OPT_save_to_refresh) {
131 document_save_file (doc, FALSE);
132 }
133 }
134
135 /* tries to insert a comment in the current document
136 * @line: The line for which insert comment, or -1 for the current line */
137 static void
insert_comment(gint line)138 insert_comment (gint line)
139 {
140 GeanyDocument *doc;
141
142 doc = document_get_current ();
143 if (DOC_VALID (doc)) {
144 /* try to ensure tags corresponds to the actual state of the file */
145 refresh_tag_list (doc->tm_file, doc->editor->sci, doc);
146 if (line < 0) {
147 line = sci_get_current_line (doc->editor->sci);
148 }
149 ggd_insert_comment (doc, line, ggd_plugin_get_doctype (doc->file_type->id));
150 }
151 }
152
153 /* tries to insert comments for all the document */
154 static void
insert_all_comments(void)155 insert_all_comments (void)
156 {
157 GeanyDocument *doc;
158
159 doc = document_get_current ();
160 if (DOC_VALID (doc)) {
161 /* try to ensure tags corresponds to the actual state of the file */
162 refresh_tag_list (doc->tm_file, doc->editor->sci, doc);
163 ggd_insert_all_comments (doc, ggd_plugin_get_doctype (doc->file_type->id));
164 }
165 }
166
167 /* Escapes a string to use it safely as a GKeyFile key.
168 * It currently replaces only "=" and "#" since GKeyFile supports UTF-8 keys. */
169 static gchar *
normalize_key(const gchar * key)170 normalize_key (const gchar *key)
171 {
172 GString *nkey;
173
174 nkey = g_string_new (NULL);
175 for (; *key; key++) {
176 switch (*key) {
177 case '=': g_string_append (nkey, "Equal"); break;
178 case '#': g_string_append (nkey, "Sharp"); break;
179 default: g_string_append_c (nkey, *key); break;
180 }
181 }
182
183 return g_string_free (nkey, FALSE);
184 }
185
186 static gboolean
load_configuration(void)187 load_configuration (void)
188 {
189 gboolean success = FALSE;
190 gchar *conffile;
191 GError *err = NULL;
192 guint i;
193
194 /* default options that needs to be set dynamically */
195 GGD_OPT_doctype[0] = g_strdup ("doxygen");
196
197 plugin->config = ggd_opt_group_new ("General");
198 ggd_opt_group_add_string (plugin->config, &GGD_OPT_doctype[0], "doctype");
199 for (i = 1; i < GEANY_MAX_BUILT_IN_FILETYPES; i++) {
200 gchar *name;
201 gchar *normal_ftname;
202
203 normal_ftname = normalize_key (filetypes[i]->name);
204 name = g_strconcat ("doctype_", normal_ftname, NULL);
205 ggd_opt_group_add_string (plugin->config, &GGD_OPT_doctype[i], name);
206 g_free (name);
207 g_free (normal_ftname);
208 }
209 ggd_opt_group_add_boolean (plugin->config, &GGD_OPT_save_to_refresh, "save_to_refresh");
210 ggd_opt_group_add_boolean (plugin->config, &GGD_OPT_indent, "indent");
211 ggd_opt_group_add_string (plugin->config, &GGD_OPT_environ, "environ");
212 conffile = ggd_get_config_file ("ggd.conf", NULL, GGD_PERM_R, &err);
213 if (conffile) {
214 success = ggd_opt_group_load_from_file (plugin->config, conffile, &err);
215 }
216 if (err) {
217 GLogLevelFlags level = G_LOG_LEVEL_WARNING;
218
219 if (err->domain == G_FILE_ERROR && err->code == G_FILE_ERROR_NOENT) {
220 level = G_LOG_LEVEL_INFO;
221 }
222 g_log (G_LOG_DOMAIN, level,
223 _("Failed to load configuration: %s"), err->message);
224 g_error_free (err);
225 }
226 g_free (conffile);
227 /* init filetype manager */
228 ggd_file_type_manager_init ();
229
230 return success;
231 }
232
233 static void
unload_configuration(void)234 unload_configuration (void)
235 {
236 gchar *conffile;
237 GError *err = NULL;
238
239 conffile = ggd_get_config_file ("ggd.conf", NULL, GGD_PERM_RW, &err);
240 if (conffile) {
241 ggd_opt_group_write_to_file (plugin->config, conffile, &err);
242 }
243 if (err) {
244 g_warning (_("Failed to save configuration: %s"), err->message);
245 g_error_free (err);
246 }
247 g_free (conffile);
248 ggd_opt_group_free (plugin->config, TRUE);
249 plugin->config = NULL;
250 /* uninit filetype manager */
251 ggd_file_type_manager_uninit ();
252 }
253
254 /* forces reloading of configuration files */
255 static gboolean
reload_configuration(void)256 reload_configuration (void)
257 {
258 unload_configuration ();
259 return load_configuration ();
260 }
261
262
263 /* --- Actual Geany interaction --- */
264
265 /* handler for the editor's popup menu entry the plugin adds */
266 static void
editor_menu_acivated_handler(GtkMenuItem * menu_item,PluginData * pdata)267 editor_menu_acivated_handler (GtkMenuItem *menu_item,
268 PluginData *pdata)
269 {
270 (void)menu_item;
271
272 insert_comment (pdata->editor_menu_popup_line);
273 }
274
275 /* hanlder for the keybinding that inserts a comment */
276 static void
insert_comment_keybinding_handler(guint key_id)277 insert_comment_keybinding_handler (guint key_id)
278 {
279 (void)key_id;
280
281 insert_comment (-1);
282 }
283
284 /* handler for the GeanyDocument::update-editor-menu signal.
285 * It is used to get the line at which menu popuped */
286 static void
update_editor_menu_handler(GObject * obj,const gchar * word,gint pos,GeanyDocument * doc,PluginData * pdata)287 update_editor_menu_handler (GObject *obj,
288 const gchar *word,
289 gint pos,
290 GeanyDocument *doc,
291 PluginData *pdata)
292 {
293 pdata->editor_menu_popup_line = sci_get_line_from_position (doc->editor->sci,
294 pos);
295 }
296
297 /* handler that opens the current filetype's configuration file */
298 static void
open_current_filetype_conf_handler(GtkWidget * widget,gpointer data)299 open_current_filetype_conf_handler (GtkWidget *widget,
300 gpointer data)
301 {
302 GeanyDocument *doc;
303
304 (void)widget;
305 (void)data;
306
307 doc = document_get_current ();
308 if (DOC_VALID (doc)) {
309 gchar *path_read;
310 gchar *path_write;
311 GError *err = NULL;
312
313 path_write = ggd_file_type_manager_get_conf_path (doc->file_type->id,
314 GGD_PERM_W | GGD_PERM_NOCREAT,
315 &err);
316 if (! path_write) {
317 msgwin_status_add (_("Failed to find configuration file "
318 "for file type \"%s\": %s"),
319 doc->file_type->name, err->message);
320 g_error_free (err);
321 } else {
322 gchar *text = NULL;
323 gchar *path_write_u8;
324
325 path_read = ggd_file_type_manager_get_conf_path (doc->file_type->id,
326 GGD_PERM_R, &err);
327 if (! path_read) {
328 text = g_strdup (_(
329 "# Configuration for this file type doesn't exist yet.\n"
330 "# To create it, just write it in this file and save it. For the description\n"
331 "# of the syntax of this file, please refer to the manual.\n"
332 ));
333 } else {
334 gchar *content = NULL;
335 gsize length;
336
337 if (! g_file_get_contents (path_read, &content, &length, &err)) {
338 gchar *display_path_read;
339
340 display_path_read = g_filename_display_name (path_read);
341 g_warning (_("Failed to load file \"%s\": %s"),
342 display_path_read, err->message);
343 g_free (display_path_read);
344 g_error_free (err);
345 } else {
346 text = encodings_convert_to_utf8 (content, length, NULL);
347 g_free (content);
348 }
349 g_free (path_read);
350 }
351 path_write_u8 = utils_get_utf8_from_locale (path_write);
352 /* It's no Ruby, but it is the closest one I've found. It has:
353 * - # comments
354 * - multi-line double-quoted strings
355 */
356 document_new_file (path_write_u8, filetypes[GEANY_FILETYPES_RUBY], text);
357 g_free (path_write_u8);
358 g_free (text);
359 g_free (path_write);
360 }
361 }
362 }
363
364 static void
open_manual_handler(GtkWidget * widget,gpointer data)365 open_manual_handler (GtkWidget *widget,
366 gpointer data)
367 {
368 #ifdef G_OS_WIN32
369 gchar *prefix = g_win32_get_package_installation_directory_of_module (NULL);
370 #else
371 gchar *prefix = NULL;
372 #endif
373 gchar *path = g_build_filename (prefix ? prefix : "", PLUGINDOCDIR,
374 "/html/manual.html", NULL);
375
376 utils_open_browser (path);
377
378 g_free (path);
379 g_free (prefix);
380 }
381
382 /* handler that reloads the configuration */
383 static void
reload_configuration_hanlder(GtkWidget * widget,gpointer data)384 reload_configuration_hanlder (GtkWidget *widget,
385 gpointer data)
386 {
387 (void)widget;
388 (void)data;
389
390 reload_configuration ();
391 }
392
393 /* handler that documents current symbol */
394 static void
document_current_symbol_handler(GObject * obj,gpointer data)395 document_current_symbol_handler (GObject *obj,
396 gpointer data)
397 {
398 insert_comment (-1);
399 }
400
401 /* handler that documents all symbols */
402 static void
document_all_symbols_handler(GObject * obj,gpointer data)403 document_all_symbols_handler (GObject *obj,
404 gpointer data)
405 {
406 insert_all_comments ();
407 }
408
409
410 /* FIXME: make menu item appear in the edit menu too */
411 /* adds the menu item in the editor's popup menu */
412 static void
add_edit_menu_item(PluginData * pdata)413 add_edit_menu_item (PluginData *pdata)
414 {
415 GtkWidget *parent_menu;
416
417 parent_menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (
418 ui_lookup_widget (geany->main_widgets->editor_menu, "comments")));
419 if (! parent_menu) {
420 parent_menu = geany->main_widgets->editor_menu;
421 pdata->separator_item = gtk_separator_menu_item_new ();
422 gtk_menu_shell_append (GTK_MENU_SHELL (parent_menu), pdata->separator_item);
423 gtk_widget_show (pdata->separator_item);
424 }
425 pdata->edit_menu_item = gtk_menu_item_new_with_label (_("Insert Documentation Comment"));
426 pdata->edit_menu_item_hid = g_signal_connect (pdata->edit_menu_item, "activate",
427 G_CALLBACK (editor_menu_acivated_handler),
428 pdata);
429 gtk_menu_shell_append (GTK_MENU_SHELL (parent_menu), pdata->edit_menu_item);
430 gtk_widget_show (pdata->edit_menu_item);
431 /* make item document-presence sensitive */
432 ui_add_document_sensitive (pdata->edit_menu_item);
433 /* and attach a keybinding */
434 keybindings_set_item (pdata->kb_group, KB_INSERT, insert_comment_keybinding_handler,
435 GDK_d, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
436 "instert_doc", _("Insert Documentation Comment"),
437 pdata->edit_menu_item);
438 }
439
440 /* removes the menu item in the editor's popup menu */
441 static void
remove_edit_menu_item(PluginData * pdata)442 remove_edit_menu_item (PluginData *pdata)
443 {
444 g_signal_handler_disconnect (pdata->edit_menu_item, pdata->edit_menu_item_hid);
445 pdata->edit_menu_item_hid = 0l;
446 if (pdata->separator_item) {
447 gtk_widget_destroy (pdata->separator_item);
448 }
449 gtk_widget_destroy (pdata->edit_menu_item);
450 }
451
452 static GtkWidget *
menu_add_item(GtkMenuShell * menu,const gchar * mnemonic,const gchar * tooltip,const gchar * stock_image,GCallback activate_handler,gpointer activate_data)453 menu_add_item (GtkMenuShell *menu,
454 const gchar *mnemonic,
455 const gchar *tooltip,
456 const gchar *stock_image,
457 GCallback activate_handler,
458 gpointer activate_data)
459 {
460 GtkWidget *item;
461
462 if (! stock_image) {
463 item = gtk_menu_item_new_with_mnemonic (mnemonic);
464 } else {
465 item = gtk_image_menu_item_new_with_mnemonic (mnemonic);
466 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
467 gtk_image_new_from_stock (stock_image,
468 GTK_ICON_SIZE_MENU));
469 }
470 gtk_widget_set_tooltip_text (item, tooltip);
471 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
472 if (activate_handler) {
473 g_signal_connect (item, "activate", activate_handler, activate_data);
474 }
475
476 return item;
477 }
478
479 /* creates plugin's tool's menu */
480 static GtkWidget *
create_tools_menu_item(void)481 create_tools_menu_item (void)
482 {
483 GtkWidget *menu;
484 GtkWidget *item;
485
486 /* build submenu */
487 menu = gtk_menu_new ();
488 /* build "document current symbol" item */
489 item = menu_add_item (GTK_MENU_SHELL (menu),
490 _("_Document Current Symbol"),
491 _("Generate documentation for the current symbol"),
492 NULL,
493 G_CALLBACK (document_current_symbol_handler), NULL);
494 ui_add_document_sensitive (item);
495 /* build "document all" item */
496 item = menu_add_item (GTK_MENU_SHELL (menu),
497 _("Document _All Symbols"),
498 _("Generate documentation for all symbols in the "
499 "current document"),
500 NULL,
501 G_CALLBACK (document_all_symbols_handler), NULL);
502 ui_add_document_sensitive (item);
503 /* separator */
504 item = gtk_separator_menu_item_new ();
505 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
506 /* build "reload" item */
507 item = menu_add_item (GTK_MENU_SHELL (menu),
508 _("_Reload Configuration Files"),
509 _("Force reloading of the configuration files"),
510 GTK_STOCK_REFRESH,
511 G_CALLBACK (reload_configuration_hanlder), NULL);
512 /* language filetypes opener */
513 item = menu_add_item (GTK_MENU_SHELL (menu),
514 _("_Edit Current Language Configuration"),
515 _("Open the current language configuration file for "
516 "editing"),
517 GTK_STOCK_EDIT,
518 G_CALLBACK (open_current_filetype_conf_handler), NULL);
519 ui_add_document_sensitive (item);
520 /* separator */
521 item = gtk_separator_menu_item_new ();
522 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
523 /* help/manual opening */
524 item = menu_add_item (GTK_MENU_SHELL (menu),
525 _("Open _Manual"),
526 _("Open the manual in a browser"),
527 GTK_STOCK_HELP,
528 G_CALLBACK (open_manual_handler), NULL);
529 /* build tools menu item */
530 item = gtk_menu_item_new_with_mnemonic (_("_Documentation Generator"));
531 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
532 gtk_widget_show_all (item);
533
534 return item;
535 }
536
537 /* build plugin's menus */
538 static void
build_menus(PluginData * pdata)539 build_menus (PluginData *pdata)
540 {
541 add_edit_menu_item (pdata);
542 pdata->tools_menu_item = create_tools_menu_item ();
543 gtk_menu_shell_append (GTK_MENU_SHELL (geany->main_widgets->tools_menu),
544 pdata->tools_menu_item);
545 }
546
547 /* destroys plugin's menus */
548 static void
destroy_menus(PluginData * pdata)549 destroy_menus (PluginData *pdata)
550 {
551 gtk_widget_destroy (pdata->tools_menu_item);
552 pdata->tools_menu_item = NULL;
553 remove_edit_menu_item (pdata);
554 }
555
556 void
plugin_init(GeanyData * data G_GNUC_UNUSED)557 plugin_init (GeanyData *data G_GNUC_UNUSED)
558 {
559 plugin->kb_group = plugin_set_key_group (geany_plugin, GGD_PLUGIN_CNAME,
560 NUM_KB, NULL);
561 load_configuration ();
562 build_menus (plugin);
563 plugin_signal_connect (geany_plugin, NULL, "update-editor-menu", FALSE,
564 G_CALLBACK (update_editor_menu_handler), plugin);
565 }
566
567 void
plugin_cleanup(void)568 plugin_cleanup (void)
569 {
570 destroy_menus (plugin);
571 unload_configuration ();
572 plugin->kb_group = NULL;
573 }
574
575 void
plugin_help(void)576 plugin_help (void)
577 {
578 open_manual_handler (NULL, NULL);
579 }
580
581
582 /* --- Configuration dialog --- */
583
584 #include "ggd-widget-frame.h"
585 #include "ggd-widget-doctype-selector.h"
586
587 static GtkWidget *GGD_W_doctype_selector;
588
589 static void
conf_dialog_response_handler(GtkDialog * dialog,gint response_id,PluginData * pdata)590 conf_dialog_response_handler (GtkDialog *dialog,
591 gint response_id,
592 PluginData *pdata)
593 {
594 switch (response_id) {
595 case GTK_RESPONSE_ACCEPT:
596 case GTK_RESPONSE_APPLY:
597 case GTK_RESPONSE_OK:
598 case GTK_RESPONSE_YES: {
599 guint i;
600
601 ggd_opt_group_sync_from_proxies (pdata->config);
602 for (i = 0; i < GEANY_MAX_BUILT_IN_FILETYPES; i++) {
603 g_free (GGD_OPT_doctype[i]);
604 GGD_OPT_doctype[i] = ggd_doctype_selector_get_doctype (GGD_DOCTYPE_SELECTOR (GGD_W_doctype_selector),
605 i);
606 }
607 break;
608 }
609
610 default: break;
611 }
612 }
613
614 GtkWidget *
plugin_configure(GtkDialog * dialog)615 plugin_configure (GtkDialog *dialog)
616 {
617 GtkWidget *box;
618 GtkWidget *box2;
619 GtkWidget *frame;
620 GtkWidget *widget;
621 guint i;
622
623 g_signal_connect (dialog, "response",
624 G_CALLBACK (conf_dialog_response_handler), plugin);
625
626 box = gtk_vbox_new (FALSE, 12);
627
628 /* General */
629 frame = ggd_frame_new (_("General"));
630 gtk_box_pack_start (GTK_BOX (box), frame, FALSE, FALSE, 0);
631 box2 = gtk_vbox_new (FALSE, 0);
632 gtk_container_add (GTK_CONTAINER (frame), box2);
633 /* auto-save */
634 widget = gtk_check_button_new_with_mnemonic (_("_Save file before generating "
635 "documentation"));
636 gtk_widget_set_tooltip_text (widget,
637 _("Whether the current document should be saved to disc before generating "
638 "the documentation. This is a technical detail, but it is currently "
639 "needed to have an up-to-date tag list. If you disable this option and "
640 "ask for documentation generation on a modified document, the behavior "
641 "may be surprising since the comment will be generated for the last "
642 "saved state of this document and not the current one."));
643 ggd_opt_group_set_proxy_gtktogglebutton (plugin->config, &GGD_OPT_save_to_refresh,
644 widget);
645 gtk_box_pack_start (GTK_BOX (box2), widget, FALSE, FALSE, 0);
646 /* indent */
647 widget = gtk_check_button_new_with_mnemonic (_("_Indent inserted documentation"));
648 gtk_widget_set_tooltip_text (widget,
649 _("Whether the inserted documentation should be indented to fit the "
650 "indentation at the insertion position."));
651 ggd_opt_group_set_proxy_gtktogglebutton (plugin->config, &GGD_OPT_indent,
652 widget);
653 gtk_box_pack_start (GTK_BOX (box2), widget, FALSE, FALSE, 0);
654
655 /* Documentation type */
656 frame = ggd_frame_new (_("Documentation type"));
657 gtk_box_pack_start (GTK_BOX (box), frame, TRUE, TRUE, 0);
658 box2 = gtk_vbox_new (FALSE, 0);
659 gtk_container_add (GTK_CONTAINER (frame), box2);
660 GGD_W_doctype_selector = ggd_doctype_selector_new ();
661 for (i = 0; i < GEANY_MAX_BUILT_IN_FILETYPES; i++) {
662 ggd_doctype_selector_set_doctype (GGD_DOCTYPE_SELECTOR (GGD_W_doctype_selector),
663 i, GGD_OPT_doctype[i]);
664 }
665 gtk_widget_set_tooltip_text (GGD_W_doctype_selector,
666 _("Choose the documentation type to use with each file type. The special "
667 "language \"All\" on top of the list is used to choose the default "
668 "documentation type, used for all languages that haven't one set."));
669 gtk_box_pack_start (GTK_BOX (box2), GGD_W_doctype_selector, TRUE, TRUE, 0);
670
671 /* Environ editor */
672 widget = ggd_frame_new (_("Global environment"));
673 gtk_widget_set_tooltip_text (widget,
674 _("Global environment overrides and additions. This environment will be "
675 "merged with the file-type-specific ones."));
676 {
677 GtkWidget *scrolled;
678 GtkWidget *view;
679 GObject *proxy;
680
681 scrolled = gtk_scrolled_window_new (NULL, NULL);
682 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
683 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
684 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
685 GTK_SHADOW_IN);
686 gtk_container_add (GTK_CONTAINER (widget), scrolled);
687 view = gtk_text_view_new ();
688 proxy = G_OBJECT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
689 ggd_opt_group_set_proxy (plugin->config, &GGD_OPT_environ, proxy, "text");
690 gtk_container_add (GTK_CONTAINER (scrolled), view);
691 }
692 gtk_box_pack_start (GTK_BOX (box), widget, TRUE, TRUE, 0);
693
694
695 gtk_widget_show_all (box);
696
697 return box;
698 }
699