~ubuntu-branches/ubuntu/wily/geany/wily

« back to all changes in this revision

Viewing changes to .pc/20_load_plugins_from_all_paths.patch/src/plugins.c

  • Committer: Package Import Robot
  • Author(s): Chow Loong Jin
  • Date: 2011-12-10 07:43:26 UTC
  • mfrom: (3.3.7 sid)
  • Revision ID: package-import@ubuntu.com-20111210074326-s8yqbew5i20h33tf
Tags: 0.21-1ubuntu1
* Merge from Debian Unstable, remaining changes:
  - debian/patches/20_use_evince_viewer.patch:
     + use evince as viewer for pdf and dvi files
  - debian/patches/20_use_x_terminal_emulator.patch:
     + use x-terminal-emulator as terminal
  - debian/control
     + Add breaks on geany-plugins-common << 0.20
* Also fixes bugs:
  - Filter for MATLAB/Octave files filters everythign (LP: 885505)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *      plugins.c - this file is part of Geany, a fast and lightweight IDE
 
3
 *
 
4
 *      Copyright 2007-2011 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
 
5
 *      Copyright 2007-2011 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
 
6
 *
 
7
 *      This program is free software; you can redistribute it and/or modify
 
8
 *      it under the terms of the GNU General Public License as published by
 
9
 *      the Free Software Foundation; either version 2 of the License, or
 
10
 *      (at your option) any later version.
 
11
 *
 
12
 *      This program is distributed in the hope that it will be useful,
 
13
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
 *      GNU General Public License for more details.
 
16
 *
 
17
 *      You should have received a copy of the GNU General Public License
 
18
 *      along with this program; if not, write to the Free Software
 
19
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
20
 *      MA 02110-1301, USA.
 
21
 *
 
22
 * $Id: plugins.c 5886 2011-08-06 18:34:14Z colombanw $
 
23
 */
 
24
 
 
25
/* Code to manage, load and unload plugins. */
 
26
 
 
27
#include "geany.h"
 
28
 
 
29
#ifdef HAVE_PLUGINS
 
30
 
 
31
#include <string.h>
 
32
 
 
33
#include "Scintilla.h"
 
34
#include "ScintillaWidget.h"
 
35
 
 
36
#include "prefix.h"
 
37
#include "plugins.h"
 
38
#include "plugindata.h"
 
39
#include "support.h"
 
40
#include "utils.h"
 
41
#include "document.h"
 
42
#include "filetypes.h"
 
43
#include "templates.h"
 
44
#include "sciwrappers.h"
 
45
#include "ui_utils.h"
 
46
#include "editor.h"
 
47
#include "dialogs.h"
 
48
#include "msgwindow.h"
 
49
#include "prefs.h"
 
50
#include "geanywraplabel.h"
 
51
#include "build.h"
 
52
#include "encodings.h"
 
53
#include "search.h"
 
54
#include "highlighting.h"
 
55
#include "keybindings.h"
 
56
#include "navqueue.h"
 
57
#include "main.h"
 
58
#include "toolbar.h"
 
59
#include "stash.h"
 
60
#include "symbols.h"
 
61
#include "keyfile.h"
 
62
#include "win32.h"
 
63
#include "pluginutils.h"
 
64
#include "pluginprivate.h"
 
65
 
 
66
 
 
67
GList *active_plugin_list = NULL; /* list of only actually loaded plugins, always valid */
 
68
 
 
69
 
 
70
static gboolean want_plugins = FALSE;
 
71
 
 
72
/* list of all available, loadable plugins, only valid as long as the plugin manager dialog is
 
73
 * opened, afterwards it will be destroyed */
 
74
static GList *plugin_list = NULL;
 
75
static gchar **active_plugins_pref = NULL;      /* list of plugin filenames to load at startup */
 
76
static GList *failed_plugins_list = NULL;       /* plugins the user wants active but can't be used */
 
77
 
 
78
static GtkWidget *menu_separator = NULL;
 
79
 
 
80
static void pm_show_dialog(GtkMenuItem *menuitem, gpointer user_data);
 
81
 
 
82
 
 
83
static PluginFuncs plugin_funcs = {
 
84
        &plugin_add_toolbar_item,
 
85
        &plugin_module_make_resident,
 
86
        &plugin_signal_connect,
 
87
        &plugin_set_key_group,
 
88
        &plugin_show_configure,
 
89
        &plugin_timeout_add,
 
90
        &plugin_timeout_add_seconds,
 
91
        &plugin_idle_add
 
92
};
 
93
 
 
94
static DocumentFuncs doc_funcs = {
 
95
        &document_new_file,
 
96
        &document_get_current,
 
97
        &document_get_from_page,
 
98
        &document_find_by_filename,
 
99
        &document_find_by_real_path,
 
100
        &document_save_file,
 
101
        &document_open_file,
 
102
        &document_open_files,
 
103
        &document_remove_page,
 
104
        &document_reload_file,
 
105
        &document_set_encoding,
 
106
        &document_set_text_changed,
 
107
        &document_set_filetype,
 
108
        &document_close,
 
109
        &document_index,
 
110
        &document_save_file_as,
 
111
        &document_rename_file,
 
112
        &document_get_status_color,
 
113
        &document_get_basename_for_display,
 
114
        &document_get_notebook_page,
 
115
        &document_compare_by_display_name,
 
116
        &document_compare_by_tab_order,
 
117
        &document_compare_by_tab_order_reverse
 
118
};
 
119
 
 
120
static EditorFuncs editor_funcs = {
 
121
        &editor_get_indent_prefs,
 
122
        &editor_create_widget,
 
123
        &editor_indicator_set_on_range,
 
124
        &editor_indicator_set_on_line,
 
125
        &editor_indicator_clear,
 
126
        &editor_set_indent_type,
 
127
        &editor_get_word_at_pos,
 
128
        &editor_get_eol_char_name,
 
129
        &editor_get_eol_char_len,
 
130
        &editor_get_eol_char,
 
131
        &editor_insert_text_block,
 
132
        &editor_get_eol_char_mode,
 
133
        &editor_goto_pos,
 
134
        &editor_find_snippet,
 
135
        &editor_insert_snippet
 
136
};
 
137
 
 
138
static ScintillaFuncs scintilla_funcs = {
 
139
        &scintilla_send_message,
 
140
        &scintilla_new
 
141
};
 
142
 
 
143
/* Macro to prevent a duplicate macro being generated in geanyfunctions.h */
 
144
#define _scintilla_send_message_macro   scintilla_send_message
 
145
 
 
146
static SciFuncs sci_funcs = {
 
147
        &_scintilla_send_message_macro,
 
148
        &sci_send_command,
 
149
        &sci_start_undo_action,
 
150
        &sci_end_undo_action,
 
151
        &sci_set_text,
 
152
        &sci_insert_text,
 
153
        &sci_get_text,
 
154
        &sci_get_length,
 
155
        &sci_get_current_position,
 
156
        &sci_set_current_position,
 
157
        &sci_get_col_from_position,
 
158
        &sci_get_line_from_position,
 
159
        &sci_get_position_from_line,
 
160
        &sci_replace_sel,
 
161
        &sci_get_selected_text,
 
162
        &sci_get_selected_text_length,
 
163
        &sci_get_selection_start,
 
164
        &sci_get_selection_end,
 
165
        &sci_get_selection_mode,
 
166
        &sci_set_selection_mode,
 
167
        &sci_set_selection_start,
 
168
        &sci_set_selection_end,
 
169
        &sci_get_text_range,
 
170
        &sci_get_line,
 
171
        &sci_get_line_length,
 
172
        &sci_get_line_count,
 
173
        &sci_get_line_is_visible,
 
174
        &sci_ensure_line_is_visible,
 
175
        &sci_scroll_caret,
 
176
        &sci_find_matching_brace,
 
177
        &sci_get_style_at,
 
178
        &sci_get_char_at,
 
179
        &sci_get_current_line,
 
180
        &sci_has_selection,
 
181
        &sci_get_tab_width,
 
182
        &sci_indicator_clear,
 
183
        &sci_indicator_set,
 
184
        &sci_get_contents,
 
185
        &sci_get_contents_range,
 
186
        &sci_get_selection_contents,
 
187
        &sci_set_font,
 
188
        &sci_get_line_end_position,
 
189
        &sci_set_target_start,
 
190
        &sci_set_target_end,
 
191
        &sci_replace_target,
 
192
        &sci_set_marker_at_line,
 
193
        &sci_delete_marker_at_line,
 
194
        &sci_is_marker_set_at_line,
 
195
        &sci_goto_line,
 
196
        &sci_find_text,
 
197
        &sci_set_line_indentation,
 
198
        &sci_get_line_indentation,
 
199
        &sci_get_lexer
 
200
};
 
201
 
 
202
static TemplateFuncs template_funcs = {
 
203
        &templates_get_template_fileheader
 
204
};
 
205
 
 
206
static UtilsFuncs utils_funcs = {
 
207
        &utils_str_equal,
 
208
        &utils_string_replace_all,
 
209
        &utils_get_file_list,
 
210
        &utils_write_file,
 
211
        &utils_get_locale_from_utf8,
 
212
        &utils_get_utf8_from_locale,
 
213
        &utils_remove_ext_from_filename,
 
214
        &utils_mkdir,
 
215
        &utils_get_setting_boolean,
 
216
        &utils_get_setting_integer,
 
217
        &utils_get_setting_string,
 
218
        &utils_spawn_sync,
 
219
        &utils_spawn_async,
 
220
        &utils_str_casecmp,
 
221
        &utils_get_date_time,
 
222
        &utils_open_browser,
 
223
        &utils_string_replace_first,
 
224
        &utils_str_middle_truncate,
 
225
        &utils_str_remove_chars,
 
226
        &utils_get_file_list_full,
 
227
        &utils_copy_environment,
 
228
        &utils_find_open_xml_tag,
 
229
        &utils_find_open_xml_tag_pos
 
230
};
 
231
 
 
232
static UIUtilsFuncs uiutils_funcs = {
 
233
        &ui_dialog_vbox_new,
 
234
        &ui_frame_new_with_alignment,
 
235
        &ui_set_statusbar,
 
236
        &ui_table_add_row,
 
237
        &ui_path_box_new,
 
238
        &ui_button_new_with_image,
 
239
        &ui_add_document_sensitive,
 
240
        &ui_widget_set_tooltip_text,
 
241
        &ui_image_menu_item_new,
 
242
        &ui_lookup_widget,
 
243
        &ui_progress_bar_start,
 
244
        &ui_progress_bar_stop,
 
245
        &ui_entry_add_clear_icon,
 
246
        &ui_menu_add_document_items,
 
247
        &ui_widget_modify_font_from_string,
 
248
        &ui_is_keyval_enter_or_return,
 
249
        &ui_get_gtk_settings_integer,
 
250
        &ui_combo_box_add_to_history,
 
251
        &ui_menu_add_document_items_sorted
 
252
};
 
253
 
 
254
static DialogFuncs dialog_funcs = {
 
255
        &dialogs_show_question,
 
256
        &dialogs_show_msgbox,
 
257
        &dialogs_show_save_as,
 
258
        &dialogs_show_input_numeric,
 
259
        &dialogs_show_input
 
260
};
 
261
 
 
262
/* Macro to prevent confusing macro being generated in geanyfunctions.h */
 
263
#define _lookup_widget_macro    ui_lookup_widget
 
264
 
 
265
/* deprecated */
 
266
static SupportFuncs support_funcs = {
 
267
        &_lookup_widget_macro
 
268
};
 
269
 
 
270
static MsgWinFuncs msgwin_funcs = {
 
271
        &msgwin_status_add,
 
272
        &msgwin_compiler_add,
 
273
        &msgwin_msg_add,
 
274
        &msgwin_clear_tab,
 
275
        &msgwin_switch_tab,
 
276
        &msgwin_set_messages_dir
 
277
};
 
278
 
 
279
static EncodingFuncs encoding_funcs = {
 
280
        &encodings_convert_to_utf8,
 
281
        &encodings_convert_to_utf8_from_charset,
 
282
        &encodings_get_charset_from_index
 
283
};
 
284
 
 
285
static KeybindingFuncs keybindings_funcs = {
 
286
        &keybindings_send_command,
 
287
        &keybindings_set_item,
 
288
        &keybindings_get_item
 
289
};
 
290
 
 
291
static TagManagerFuncs tagmanager_funcs = {
 
292
        &tm_get_real_path,
 
293
        &tm_source_file_new,
 
294
        &tm_workspace_add_object,
 
295
        &tm_source_file_update,
 
296
        &tm_work_object_free,
 
297
        &tm_workspace_remove_object
 
298
};
 
299
 
 
300
static SearchFuncs search_funcs = {
 
301
        &search_show_find_in_files_dialog
 
302
};
 
303
 
 
304
static HighlightingFuncs highlighting_funcs = {
 
305
        &highlighting_get_style,
 
306
        &highlighting_set_styles,
 
307
        &highlighting_is_string_style,
 
308
        &highlighting_is_comment_style,
 
309
        &highlighting_is_code_style
 
310
};
 
311
 
 
312
static FiletypeFuncs filetype_funcs = {
 
313
        &filetypes_detect_from_file,
 
314
        &filetypes_lookup_by_name,
 
315
        &filetypes_index,
 
316
        &filetypes_get_display_name,
 
317
        &filetypes_get_sorted_by_name
 
318
};
 
319
 
 
320
static NavQueueFuncs navqueue_funcs = {
 
321
        &navqueue_goto_line
 
322
};
 
323
 
 
324
static MainFuncs main_funcs = {
 
325
        &main_reload_configuration,
 
326
        &main_locale_init,
 
327
        &main_is_realized
 
328
};
 
329
 
 
330
static StashFuncs stash_funcs = {
 
331
        &stash_group_new,
 
332
        &stash_group_add_boolean,
 
333
        &stash_group_add_integer,
 
334
        &stash_group_add_string,
 
335
        &stash_group_add_string_vector,
 
336
        &stash_group_load_from_key_file,
 
337
        &stash_group_save_to_key_file,
 
338
        &stash_group_free,
 
339
        &stash_group_load_from_file,
 
340
        &stash_group_save_to_file,
 
341
        &stash_group_add_toggle_button,
 
342
        &stash_group_add_radio_buttons,
 
343
        &stash_group_add_spin_button_integer,
 
344
        &stash_group_add_combo_box,
 
345
        &stash_group_add_combo_box_entry,
 
346
        &stash_group_add_entry,
 
347
        &stash_group_add_widget_property,
 
348
        &stash_group_display,
 
349
        &stash_group_update
 
350
};
 
351
 
 
352
static SymbolsFuncs symbols_funcs = {
 
353
        &symbols_get_context_separator
 
354
};
 
355
 
 
356
static GeanyFunctions geany_functions = {
 
357
        &doc_funcs,
 
358
        &sci_funcs,
 
359
        &template_funcs,
 
360
        &utils_funcs,
 
361
        &uiutils_funcs,
 
362
        &support_funcs,
 
363
        &dialog_funcs,
 
364
        &msgwin_funcs,
 
365
        &encoding_funcs,
 
366
        &keybindings_funcs,
 
367
        &tagmanager_funcs,
 
368
        &search_funcs,
 
369
        &highlighting_funcs,
 
370
        &filetype_funcs,
 
371
        &navqueue_funcs,
 
372
        &editor_funcs,
 
373
        &main_funcs,
 
374
        &plugin_funcs,
 
375
        &scintilla_funcs,
 
376
        &msgwin_funcs,
 
377
        &stash_funcs,
 
378
        &symbols_funcs
 
379
};
 
380
 
 
381
static GeanyData geany_data;
 
382
 
 
383
 
 
384
static void
 
385
geany_data_init(void)
 
386
{
 
387
        GeanyData gd = {
 
388
                app,
 
389
                &main_widgets,
 
390
                documents_array,
 
391
                filetypes_array,
 
392
                &prefs,
 
393
                &interface_prefs,
 
394
                &toolbar_prefs,
 
395
                &editor_prefs,
 
396
                &file_prefs,
 
397
                &search_prefs,
 
398
                &tool_prefs,
 
399
                &template_prefs,
 
400
                &build_info,
 
401
                filetypes_by_title
 
402
        };
 
403
 
 
404
        geany_data = gd;
 
405
}
 
406
 
 
407
 
 
408
/* Prevent the same plugin filename being loaded more than once.
 
409
 * Note: g_module_name always returns the .so name, even when Plugin::filename is a .la file. */
 
410
static gboolean
 
411
plugin_loaded(GModule *module)
 
412
{
 
413
        gchar *basename_module, *basename_loaded;
 
414
        GList *item;
 
415
 
 
416
        basename_module = g_path_get_basename(g_module_name(module));
 
417
        for (item = plugin_list; item != NULL; item = g_list_next(item))
 
418
        {
 
419
                basename_loaded = g_path_get_basename(
 
420
                        g_module_name(((Plugin*)item->data)->module));
 
421
 
 
422
                if (utils_str_equal(basename_module, basename_loaded))
 
423
                {
 
424
                        g_free(basename_loaded);
 
425
                        g_free(basename_module);
 
426
                        return TRUE;
 
427
                }
 
428
                g_free(basename_loaded);
 
429
        }
 
430
        /* Look also through the list of active plugins. This prevents problems when we have the same
 
431
         * plugin in libdir/geany/ AND in configdir/plugins/ and the one in libdir/geany/ is loaded
 
432
         * as active plugin. The plugin manager list would only take the one in configdir/geany/ and
 
433
         * the plugin manager would list both plugins. Additionally, unloading the active plugin
 
434
         * would cause a crash. */
 
435
        for (item = active_plugin_list; item != NULL; item = g_list_next(item))
 
436
        {
 
437
                basename_loaded = g_path_get_basename(g_module_name(((Plugin*)item->data)->module));
 
438
 
 
439
                if (utils_str_equal(basename_module, basename_loaded))
 
440
                {
 
441
                        g_free(basename_loaded);
 
442
                        g_free(basename_module);
 
443
                        return TRUE;
 
444
                }
 
445
                g_free(basename_loaded);
 
446
        }
 
447
        g_free(basename_module);
 
448
        return FALSE;
 
449
}
 
450
 
 
451
 
 
452
static Plugin *find_active_plugin_by_name(const gchar *filename)
 
453
{
 
454
        GList *item;
 
455
 
 
456
        g_return_val_if_fail(filename, FALSE);
 
457
 
 
458
        for (item = active_plugin_list; item != NULL; item = g_list_next(item))
 
459
        {
 
460
                if (utils_str_equal(filename, ((Plugin*)item->data)->filename))
 
461
                        return item->data;
 
462
        }
 
463
 
 
464
        return NULL;
 
465
}
 
466
 
 
467
 
 
468
static gboolean
 
469
plugin_check_version(GModule *module)
 
470
{
 
471
        gint (*version_check)(gint) = NULL;
 
472
 
 
473
        g_module_symbol(module, "plugin_version_check", (void *) &version_check);
 
474
 
 
475
        if (G_UNLIKELY(! version_check))
 
476
        {
 
477
                geany_debug("Plugin \"%s\" has no plugin_version_check() function - ignoring plugin!",
 
478
                                g_module_name(module));
 
479
                return FALSE;
 
480
        }
 
481
        else
 
482
        {
 
483
                gint result = version_check(GEANY_ABI_VERSION);
 
484
 
 
485
                if (result < 0)
 
486
                {
 
487
                        msgwin_status_add(_("The plugin \"%s\" is not binary compatible with this "
 
488
                                "release of Geany - please recompile it."), g_module_name(module));
 
489
                        geany_debug("Plugin \"%s\" is not binary compatible with this "
 
490
                                "release of Geany - recompile it.", g_module_name(module));
 
491
                        return FALSE;
 
492
                }
 
493
                if (result > GEANY_API_VERSION)
 
494
                {
 
495
                        geany_debug("Plugin \"%s\" requires a newer version of Geany (API >= v%d).",
 
496
                                g_module_name(module), result);
 
497
                        return FALSE;
 
498
                }
 
499
        }
 
500
        return TRUE;
 
501
}
 
502
 
 
503
 
 
504
static void add_callbacks(Plugin *plugin, PluginCallback *callbacks)
 
505
{
 
506
        PluginCallback *cb;
 
507
        guint i, len = 0;
 
508
 
 
509
        while (TRUE)
 
510
        {
 
511
                cb = &callbacks[len];
 
512
                if (!cb->signal_name || !cb->callback)
 
513
                        break;
 
514
                len++;
 
515
        }
 
516
        if (len == 0)
 
517
                return;
 
518
 
 
519
        for (i = 0; i < len; i++)
 
520
        {
 
521
                cb = &callbacks[i];
 
522
 
 
523
                plugin_signal_connect(&plugin->public, NULL, cb->signal_name, cb->after,
 
524
                        cb->callback, cb->user_data);
 
525
        }
 
526
}
 
527
 
 
528
 
 
529
static void read_key_group(Plugin *plugin)
 
530
{
 
531
        GeanyKeyGroupInfo *p_key_info;
 
532
        GeanyKeyGroup **p_key_group;
 
533
 
 
534
        g_module_symbol(plugin->module, "plugin_key_group_info", (void *) &p_key_info);
 
535
        g_module_symbol(plugin->module, "plugin_key_group", (void *) &p_key_group);
 
536
        if (p_key_info && p_key_group)
 
537
        {
 
538
                GeanyKeyGroupInfo *key_info = p_key_info;
 
539
 
 
540
                if (*p_key_group)
 
541
                        geany_debug("Ignoring plugin_key_group symbol for plugin '%s' - "
 
542
                                "use plugin_set_key_group() instead to allocate keybindings dynamically.",
 
543
                                plugin->info.name);
 
544
                else
 
545
                {
 
546
                        if (key_info->count)
 
547
                        {
 
548
                                GeanyKeyGroup *key_group =
 
549
                                        plugin_set_key_group(&plugin->public, key_info->name, key_info->count, NULL);
 
550
                                if (key_group)
 
551
                                        *p_key_group = key_group;
 
552
                        }
 
553
                        else
 
554
                                geany_debug("Ignoring plugin_key_group_info symbol for plugin '%s' - "
 
555
                                        "count field is zero. Maybe use plugin_set_key_group() instead?",
 
556
                                        plugin->info.name);
 
557
                }
 
558
        }
 
559
        else if (p_key_info || p_key_group)
 
560
                geany_debug("Ignoring only one of plugin_key_group[_info] symbols defined for plugin '%s'. "
 
561
                        "Maybe use plugin_set_key_group() instead?",
 
562
                        plugin->info.name);
 
563
}
 
564
 
 
565
 
 
566
static gint cmp_plugin_names(gconstpointer a, gconstpointer b)
 
567
{
 
568
        const Plugin *pa = a;
 
569
        const Plugin *pb = b;
 
570
 
 
571
        return strcmp(pa->info.name, pb->info.name);
 
572
}
 
573
 
 
574
 
 
575
static void
 
576
plugin_init(Plugin *plugin)
 
577
{
 
578
        GeanyPlugin **p_geany_plugin;
 
579
        PluginCallback *callbacks;
 
580
        PluginInfo **p_info;
 
581
        PluginFields **plugin_fields;
 
582
 
 
583
        /* set these symbols before plugin_init() is called
 
584
         * we don't set geany_functions and geany_data since they are set directly by plugin_new() */
 
585
        g_module_symbol(plugin->module, "geany_plugin", (void *) &p_geany_plugin);
 
586
        if (p_geany_plugin)
 
587
                *p_geany_plugin = &plugin->public;
 
588
        g_module_symbol(plugin->module, "plugin_info", (void *) &p_info);
 
589
        if (p_info)
 
590
                *p_info = &plugin->info;
 
591
        g_module_symbol(plugin->module, "plugin_fields", (void *) &plugin_fields);
 
592
        if (plugin_fields)
 
593
                *plugin_fields = &plugin->fields;
 
594
        read_key_group(plugin);
 
595
 
 
596
        /* start the plugin */
 
597
        g_return_if_fail(plugin->init);
 
598
        plugin->init(&geany_data);
 
599
 
 
600
        /* store some function pointers for later use */
 
601
        g_module_symbol(plugin->module, "plugin_configure", (void *) &plugin->configure);
 
602
        g_module_symbol(plugin->module, "plugin_configure_single", (void *) &plugin->configure_single);
 
603
        if (app->debug_mode && plugin->configure && plugin->configure_single)
 
604
                g_warning("Plugin '%s' implements plugin_configure_single() unnecessarily - "
 
605
                        "only plugin_configure() will be used!",
 
606
                        plugin->info.name);
 
607
 
 
608
        g_module_symbol(plugin->module, "plugin_help", (void *) &plugin->help);
 
609
        g_module_symbol(plugin->module, "plugin_cleanup", (void *) &plugin->cleanup);
 
610
        if (plugin->cleanup == NULL)
 
611
        {
 
612
                if (app->debug_mode)
 
613
                        g_warning("Plugin '%s' has no plugin_cleanup() function - there may be memory leaks!",
 
614
                                plugin->info.name);
 
615
        }
 
616
 
 
617
        /* now read any plugin-owned data that might have been set in plugin_init() */
 
618
 
 
619
        if (plugin->fields.flags & PLUGIN_IS_DOCUMENT_SENSITIVE)
 
620
        {
 
621
                ui_add_document_sensitive(plugin->fields.menu_item);
 
622
        }
 
623
 
 
624
        g_module_symbol(plugin->module, "plugin_callbacks", (void *) &callbacks);
 
625
        if (callbacks)
 
626
                add_callbacks(plugin, callbacks);
 
627
 
 
628
        /* remember which plugins are active.
 
629
         * keep list sorted so tools menu items and plugin preference tabs are
 
630
         * sorted by plugin name */
 
631
        active_plugin_list = g_list_insert_sorted(active_plugin_list, plugin, cmp_plugin_names);
 
632
 
 
633
        geany_debug("Loaded:   %s (%s)", plugin->filename,
 
634
                NVL(plugin->info.name, "<Unknown>"));
 
635
}
 
636
 
 
637
 
 
638
/* Load and optionally init a plugin.
 
639
 * init_plugin decides whether the plugin's plugin_init() function should be called or not. If it is
 
640
 * called, the plugin will be started, if not the plugin will be read only (for the list of
 
641
 * available plugins in the plugin manager).
 
642
 * When add_to_list is set, the plugin will be added to the plugin manager's plugin_list. */
 
643
static Plugin*
 
644
plugin_new(const gchar *fname, gboolean init_plugin, gboolean add_to_list)
 
645
{
 
646
        Plugin *plugin;
 
647
        GModule *module;
 
648
        GeanyData **p_geany_data;
 
649
        GeanyFunctions **p_geany_functions;
 
650
        void (*plugin_set_info)(PluginInfo*);
 
651
 
 
652
        g_return_val_if_fail(fname, NULL);
 
653
        g_return_val_if_fail(g_module_supported(), NULL);
 
654
 
 
655
        /* find the plugin in the list of already loaded, active plugins and use it, otherwise
 
656
         * load the module */
 
657
        plugin = find_active_plugin_by_name(fname);
 
658
        if (plugin != NULL)
 
659
        {
 
660
                geany_debug("Plugin \"%s\" already loaded.", fname);
 
661
                if (add_to_list)
 
662
                {
 
663
                        /* do not add the to list twice */
 
664
                        if (g_list_find(plugin_list, plugin) != NULL)
 
665
                                return NULL;
 
666
 
 
667
                        plugin_list = g_list_prepend(plugin_list, plugin);
 
668
                }
 
669
                return plugin;
 
670
        }
 
671
 
 
672
        /* Don't use G_MODULE_BIND_LAZY otherwise we can get unresolved symbols at runtime,
 
673
         * causing a segfault. Without that flag the module will safely fail to load.
 
674
         * G_MODULE_BIND_LOCAL also helps find undefined symbols e.g. app when it would
 
675
         * otherwise not be detected due to the shadowing of Geany's app variable.
 
676
         * Also without G_MODULE_BIND_LOCAL calling public functions e.g. the old info()
 
677
         * function from a plugin will be shadowed. */
 
678
        module = g_module_open(fname, G_MODULE_BIND_LOCAL);
 
679
        if (! module)
 
680
        {
 
681
                geany_debug("Can't load plugin: %s", g_module_error());
 
682
                return NULL;
 
683
        }
 
684
 
 
685
        if (plugin_loaded(module))
 
686
        {
 
687
                geany_debug("Plugin \"%s\" already loaded.", fname);
 
688
 
 
689
                if (! g_module_close(module))
 
690
                        g_warning("%s: %s", fname, g_module_error());
 
691
                return NULL;
 
692
        }
 
693
 
 
694
        if (! plugin_check_version(module))
 
695
        {
 
696
                if (! g_module_close(module))
 
697
                        g_warning("%s: %s", fname, g_module_error());
 
698
                return NULL;
 
699
        }
 
700
 
 
701
        g_module_symbol(module, "plugin_set_info", (void *) &plugin_set_info);
 
702
        if (plugin_set_info == NULL)
 
703
        {
 
704
                geany_debug("No plugin_set_info() defined for \"%s\" - ignoring plugin!", fname);
 
705
 
 
706
                if (! g_module_close(module))
 
707
                        g_warning("%s: %s", fname, g_module_error());
 
708
                return NULL;
 
709
        }
 
710
 
 
711
        plugin = g_new0(Plugin, 1);
 
712
 
 
713
        /* set basic fields here to allow plugins to call Geany functions in set_info() */
 
714
        g_module_symbol(module, "geany_data", (void *) &p_geany_data);
 
715
        if (p_geany_data)
 
716
                *p_geany_data = &geany_data;
 
717
        g_module_symbol(module, "geany_functions", (void *) &p_geany_functions);
 
718
        if (p_geany_functions)
 
719
                *p_geany_functions = &geany_functions;
 
720
 
 
721
        /* read plugin name, etc. */
 
722
        plugin_set_info(&plugin->info);
 
723
        if (G_UNLIKELY(! NZV(plugin->info.name)))
 
724
        {
 
725
                geany_debug("No plugin name set in plugin_set_info() for \"%s\" - ignoring plugin!",
 
726
                        fname);
 
727
 
 
728
                if (! g_module_close(module))
 
729
                        g_warning("%s: %s", fname, g_module_error());
 
730
                g_free(plugin);
 
731
                return NULL;
 
732
        }
 
733
 
 
734
        g_module_symbol(module, "plugin_init", (void *) &plugin->init);
 
735
        if (plugin->init == NULL)
 
736
        {
 
737
                geany_debug("Plugin '%s' has no plugin_init() function - ignoring plugin!",
 
738
                        plugin->info.name);
 
739
 
 
740
                if (! g_module_close(module))
 
741
                        g_warning("%s: %s", fname, g_module_error());
 
742
                g_free(plugin);
 
743
                return NULL;
 
744
        }
 
745
        /*geany_debug("Initializing plugin '%s'", plugin->info.name);*/
 
746
 
 
747
        plugin->filename = g_strdup(fname);
 
748
        plugin->module = module;
 
749
        plugin->public.info = &plugin->info;
 
750
        plugin->public.priv = plugin;
 
751
 
 
752
        if (init_plugin)
 
753
                plugin_init(plugin);
 
754
 
 
755
        if (add_to_list)
 
756
                plugin_list = g_list_prepend(plugin_list, plugin);
 
757
 
 
758
        return plugin;
 
759
}
 
760
 
 
761
 
 
762
static void remove_callbacks(Plugin *plugin)
 
763
{
 
764
        GArray *signal_ids = plugin->signal_ids;
 
765
        SignalConnection *sc;
 
766
 
 
767
        if (signal_ids == NULL)
 
768
                return;
 
769
 
 
770
        foreach_array(SignalConnection, sc, signal_ids)
 
771
                g_signal_handler_disconnect(sc->object, sc->handler_id);
 
772
 
 
773
        g_array_free(signal_ids, TRUE);
 
774
}
 
775
 
 
776
 
 
777
static void remove_sources(Plugin *plugin)
 
778
{
 
779
        GList *item;
 
780
 
 
781
        item = plugin->sources;
 
782
        while (item != NULL)
 
783
        {
 
784
                GList *next = item->next; /* cache the next pointer because current item will be freed */
 
785
 
 
786
                g_source_destroy(item->data);
 
787
                item = next;
 
788
        }
 
789
        /* don't free the list here, it is allocated inside each source's data */
 
790
}
 
791
 
 
792
 
 
793
static gboolean is_active_plugin(Plugin *plugin)
 
794
{
 
795
        return (g_list_find(active_plugin_list, plugin) != NULL);
 
796
}
 
797
 
 
798
 
 
799
/* Clean up anything used by an active plugin  */
 
800
static void
 
801
plugin_cleanup(Plugin *plugin)
 
802
{
 
803
        GtkWidget *widget;
 
804
 
 
805
        if (plugin->cleanup)
 
806
                plugin->cleanup();
 
807
 
 
808
        remove_callbacks(plugin);
 
809
        remove_sources(plugin);
 
810
 
 
811
        if (plugin->key_group)
 
812
                keybindings_free_group(plugin->key_group);
 
813
 
 
814
        widget = plugin->toolbar_separator.widget;
 
815
        if (widget)
 
816
                gtk_widget_destroy(widget);
 
817
 
 
818
        geany_debug("Unloaded: %s", plugin->filename);
 
819
}
 
820
 
 
821
 
 
822
static void
 
823
plugin_free(Plugin *plugin)
 
824
{
 
825
        g_return_if_fail(plugin);
 
826
        g_return_if_fail(plugin->module);
 
827
 
 
828
        if (is_active_plugin(plugin))
 
829
                plugin_cleanup(plugin);
 
830
 
 
831
        active_plugin_list = g_list_remove(active_plugin_list, plugin);
 
832
 
 
833
        if (! g_module_close(plugin->module))
 
834
                g_warning("%s: %s", plugin->filename, g_module_error());
 
835
 
 
836
        plugin_list = g_list_remove(plugin_list, plugin);
 
837
 
 
838
        g_free(plugin->filename);
 
839
        g_free(plugin);
 
840
        plugin = NULL;
 
841
}
 
842
 
 
843
 
 
844
/* load active plugins at startup */
 
845
static void
 
846
load_active_plugins(void)
 
847
{
 
848
        guint i, len;
 
849
 
 
850
        if (active_plugins_pref == NULL || (len = g_strv_length(active_plugins_pref)) == 0)
 
851
                return;
 
852
 
 
853
        for (i = 0; i < len; i++)
 
854
        {
 
855
                const gchar *fname = active_plugins_pref[i];
 
856
 
 
857
                if (NZV(fname) && g_file_test(fname, G_FILE_TEST_EXISTS))
 
858
                {
 
859
                        if (plugin_new(fname, TRUE, FALSE) == NULL)
 
860
                                failed_plugins_list = g_list_prepend(failed_plugins_list, g_strdup(fname));
 
861
                }
 
862
        }
 
863
}
 
864
 
 
865
 
 
866
static void
 
867
load_plugins_from_path(const gchar *path)
 
868
{
 
869
        GSList *list, *item;
 
870
        gchar *fname, *tmp;
 
871
        gint count = 0;
 
872
 
 
873
        list = utils_get_file_list(path, NULL, NULL);
 
874
 
 
875
        for (item = list; item != NULL; item = g_slist_next(item))
 
876
        {
 
877
                tmp = strrchr(item->data, '.');
 
878
                if (tmp == NULL || utils_str_casecmp(tmp, "." G_MODULE_SUFFIX) != 0)
 
879
                        continue;
 
880
 
 
881
                fname = g_strconcat(path, G_DIR_SEPARATOR_S, item->data, NULL);
 
882
                if (plugin_new(fname, FALSE, TRUE))
 
883
                        count++;
 
884
                g_free(fname);
 
885
        }
 
886
 
 
887
        g_slist_foreach(list, (GFunc) g_free, NULL);
 
888
        g_slist_free(list);
 
889
 
 
890
        if (count)
 
891
                geany_debug("Found %d plugin(s) in '%s'.", count, path);
 
892
}
 
893
 
 
894
 
 
895
static gchar *get_plugin_path(void)
 
896
{
 
897
#ifdef G_OS_WIN32
 
898
        gchar *path;
 
899
        gchar *install_dir = win32_get_installation_dir();
 
900
 
 
901
        path = g_strconcat(install_dir, "\\lib", NULL);
 
902
        g_free(install_dir);
 
903
 
 
904
        return path;
 
905
#else
 
906
        return g_strconcat(GEANY_LIBDIR, G_DIR_SEPARATOR_S "geany", NULL);
 
907
#endif
 
908
}
 
909
 
 
910
 
 
911
static gboolean validate_custom_plugin_path(const gchar *plugin_path_custom,
 
912
                                                                                        const gchar *plugin_path_config,
 
913
                                                                                        const gchar *plugin_path_system)
 
914
{
 
915
        /* check whether the custom plugin path is one of the system or user plugin paths
 
916
         * and abort if so */
 
917
        if (utils_str_equal(plugin_path_custom, plugin_path_config) ||
 
918
                utils_str_equal(plugin_path_custom, plugin_path_system))
 
919
                return FALSE;
 
920
 
 
921
        return TRUE;
 
922
}
 
923
 
 
924
 
 
925
/* Load (but don't initialize) all plugins for the Plugin Manager dialog */
 
926
static void load_all_plugins(void)
 
927
{
 
928
        gchar *plugin_path_config;
 
929
        gchar *plugin_path_system;
 
930
 
 
931
        plugin_path_config = g_strconcat(app->configdir, G_DIR_SEPARATOR_S, "plugins", NULL);
 
932
        plugin_path_system = get_plugin_path();
 
933
 
 
934
        /* first load plugins in ~/.config/geany/plugins/ */
 
935
        load_plugins_from_path(plugin_path_config);
 
936
 
 
937
        /* load plugins from a custom path */
 
938
        if (NZV(prefs.custom_plugin_path))
 
939
        {
 
940
                gchar *plugin_path_custom = utils_get_locale_from_utf8(prefs.custom_plugin_path);
 
941
                utils_tidy_path(plugin_path_custom);
 
942
 
 
943
                if (validate_custom_plugin_path(plugin_path_custom, plugin_path_config, plugin_path_system))
 
944
                        load_plugins_from_path(plugin_path_custom);
 
945
 
 
946
                g_free(plugin_path_custom);
 
947
        }
 
948
 
 
949
        /* finally load plugins from $prefix/lib/geany */
 
950
        load_plugins_from_path(plugin_path_system);
 
951
 
 
952
        g_free(plugin_path_config);
 
953
        g_free(plugin_path_system);
 
954
}
 
955
 
 
956
 
 
957
static void on_tools_menu_show(GtkWidget *menu_item, G_GNUC_UNUSED gpointer user_data)
 
958
{
 
959
        GList *item, *list = gtk_container_get_children(GTK_CONTAINER(menu_item));
 
960
        guint i = 0;
 
961
        gboolean have_plugin_menu_items = FALSE;
 
962
 
 
963
        for (item = list; item != NULL; item = g_list_next(item))
 
964
        {
 
965
                if (item->data == menu_separator)
 
966
                {
 
967
                        if (i < g_list_length(list) - 1)
 
968
                        {
 
969
                                have_plugin_menu_items = TRUE;
 
970
                                break;
 
971
                        }
 
972
                }
 
973
                i++;
 
974
        }
 
975
        g_list_free(list);
 
976
 
 
977
        ui_widget_show_hide(menu_separator, have_plugin_menu_items);
 
978
}
 
979
 
 
980
 
 
981
/* Calling this starts up plugin support */
 
982
void plugins_load_active(void)
 
983
{
 
984
        GtkWidget *widget;
 
985
 
 
986
        want_plugins = TRUE;
 
987
 
 
988
        geany_data_init();
 
989
 
 
990
        widget = gtk_separator_menu_item_new();
 
991
        gtk_widget_show(widget);
 
992
        gtk_container_add(GTK_CONTAINER(main_widgets.tools_menu), widget);
 
993
 
 
994
        widget = gtk_menu_item_new_with_mnemonic(_("_Plugin Manager"));
 
995
        gtk_container_add(GTK_CONTAINER(main_widgets.tools_menu), widget);
 
996
        gtk_widget_show(widget);
 
997
        g_signal_connect(widget, "activate", G_CALLBACK(pm_show_dialog), NULL);
 
998
 
 
999
        menu_separator = gtk_separator_menu_item_new();
 
1000
        gtk_container_add(GTK_CONTAINER(main_widgets.tools_menu), menu_separator);
 
1001
        g_signal_connect(main_widgets.tools_menu, "show", G_CALLBACK(on_tools_menu_show), NULL);
 
1002
 
 
1003
        load_active_plugins();
 
1004
}
 
1005
 
 
1006
 
 
1007
static void update_active_plugins_pref(void)
 
1008
{
 
1009
        gint i = 0;
 
1010
        GList *list;
 
1011
        gsize count = g_list_length(active_plugin_list) + g_list_length(failed_plugins_list);
 
1012
 
 
1013
        g_strfreev(active_plugins_pref);
 
1014
 
 
1015
        if (count == 0)
 
1016
        {
 
1017
                active_plugins_pref = NULL;
 
1018
                return;
 
1019
        }
 
1020
 
 
1021
        active_plugins_pref = g_new0(gchar*, count + 1);
 
1022
 
 
1023
        for (list = g_list_first(active_plugin_list); list != NULL; list = list->next)
 
1024
        {
 
1025
                Plugin *plugin = list->data;
 
1026
 
 
1027
                active_plugins_pref[i] = g_strdup(plugin->filename);
 
1028
                i++;
 
1029
        }
 
1030
        for (list = g_list_first(failed_plugins_list); list != NULL; list = list->next)
 
1031
        {
 
1032
                const gchar *fname = list->data;
 
1033
 
 
1034
                active_plugins_pref[i] = g_strdup(fname);
 
1035
                i++;
 
1036
        }
 
1037
        active_plugins_pref[i] = NULL;
 
1038
}
 
1039
 
 
1040
 
 
1041
static void on_save_settings(GKeyFile *config)
 
1042
{
 
1043
        /* if plugins are disabled, don't clear list of active plugins */
 
1044
        if (want_plugins)
 
1045
                update_active_plugins_pref();
 
1046
}
 
1047
 
 
1048
 
 
1049
/* called even if plugin support is disabled */
 
1050
void plugins_init(void)
 
1051
{
 
1052
        StashGroup *group;
 
1053
 
 
1054
        group = stash_group_new("plugins");
 
1055
        configuration_add_pref_group(group, TRUE);
 
1056
 
 
1057
        stash_group_add_toggle_button(group, &prefs.load_plugins,
 
1058
                "load_plugins", TRUE, "check_plugins");
 
1059
        stash_group_add_entry(group, &prefs.custom_plugin_path,
 
1060
                "custom_plugin_path", "", "extra_plugin_path_entry");
 
1061
 
 
1062
        g_signal_connect(geany_object, "save-settings", G_CALLBACK(on_save_settings), NULL);
 
1063
        stash_group_add_string_vector(group, &active_plugins_pref, "active_plugins", NULL);
 
1064
}
 
1065
 
 
1066
 
 
1067
/* called even if plugin support is disabled */
 
1068
void plugins_finalize(void)
 
1069
{
 
1070
        if (failed_plugins_list != NULL)
 
1071
        {
 
1072
                g_list_foreach(failed_plugins_list, (GFunc) g_free,     NULL);
 
1073
                g_list_free(failed_plugins_list);
 
1074
        }
 
1075
        if (active_plugin_list != NULL)
 
1076
        {
 
1077
                g_list_foreach(active_plugin_list, (GFunc) plugin_free, NULL);
 
1078
                g_list_free(active_plugin_list);
 
1079
        }
 
1080
        g_strfreev(active_plugins_pref);
 
1081
}
 
1082
 
 
1083
 
 
1084
/* Check whether there are any plugins loaded which provide a configure symbol */
 
1085
gboolean plugins_have_preferences(void)
 
1086
{
 
1087
        GList *item;
 
1088
 
 
1089
        if (active_plugin_list == NULL)
 
1090
                return FALSE;
 
1091
 
 
1092
        foreach_list(item, active_plugin_list)
 
1093
        {
 
1094
                Plugin *plugin = item->data;
 
1095
                if (plugin->configure != NULL || plugin->configure_single != NULL)
 
1096
                        return TRUE;
 
1097
        }
 
1098
 
 
1099
        return FALSE;
 
1100
}
 
1101
 
 
1102
 
 
1103
/* Plugin Manager */
 
1104
 
 
1105
enum
 
1106
{
 
1107
        PLUGIN_COLUMN_CHECK = 0,
 
1108
        PLUGIN_COLUMN_NAME,
 
1109
        PLUGIN_COLUMN_FILE,
 
1110
        PLUGIN_COLUMN_PLUGIN,
 
1111
        PLUGIN_N_COLUMNS,
 
1112
        PM_BUTTON_CONFIGURE,
 
1113
        PM_BUTTON_HELP
 
1114
};
 
1115
 
 
1116
typedef struct
 
1117
{
 
1118
        GtkWidget *dialog;
 
1119
        GtkWidget *tree;
 
1120
        GtkListStore *store;
 
1121
        GtkWidget *plugin_label;
 
1122
        GtkWidget *description_label;
 
1123
        GtkWidget *author_label;
 
1124
        GtkWidget *configure_button;
 
1125
        GtkWidget *help_button;
 
1126
}
 
1127
PluginManagerWidgets;
 
1128
 
 
1129
static PluginManagerWidgets pm_widgets;
 
1130
 
 
1131
 
 
1132
static void pm_update_buttons(Plugin *p)
 
1133
{
 
1134
        gboolean is_active;
 
1135
 
 
1136
        is_active = is_active_plugin(p);
 
1137
        gtk_widget_set_sensitive(pm_widgets.configure_button,
 
1138
                (p->configure || p->configure_single) && is_active);
 
1139
        gtk_widget_set_sensitive(pm_widgets.help_button, p->help != NULL && is_active);
 
1140
}
 
1141
 
 
1142
 
 
1143
static void pm_selection_changed(GtkTreeSelection *selection, gpointer user_data)
 
1144
{
 
1145
        GtkTreeIter iter;
 
1146
        GtkTreeModel *model;
 
1147
        Plugin *p;
 
1148
 
 
1149
        if (gtk_tree_selection_get_selected(selection, &model, &iter))
 
1150
        {
 
1151
                gtk_tree_model_get(model, &iter, PLUGIN_COLUMN_PLUGIN, &p, -1);
 
1152
 
 
1153
                if (p != NULL)
 
1154
                {
 
1155
                        gchar *text;
 
1156
                        PluginInfo *pi;
 
1157
 
 
1158
                        pi = &p->info;
 
1159
                        /* Translators: <plugin name> <plugin version> */
 
1160
                        text = g_strdup_printf(_("%s %s"), pi->name, pi->version);
 
1161
                        gtk_label_set_text(GTK_LABEL(pm_widgets.plugin_label), text);
 
1162
                        gtk_label_set_text(GTK_LABEL(pm_widgets.description_label), pi->description);
 
1163
                        gtk_label_set_text(GTK_LABEL(pm_widgets.author_label), pi->author);
 
1164
                        g_free(text);
 
1165
 
 
1166
                        pm_update_buttons(p);
 
1167
                }
 
1168
        }
 
1169
}
 
1170
 
 
1171
 
 
1172
static void pm_plugin_toggled(GtkCellRendererToggle *cell, gchar *pth, gpointer data)
 
1173
{
 
1174
        gboolean old_state, state;
 
1175
        gchar *file_name;
 
1176
        GtkTreeIter iter;
 
1177
        GtkTreePath *path = gtk_tree_path_new_from_string(pth);
 
1178
        Plugin *p;
 
1179
 
 
1180
        gtk_tree_model_get_iter(GTK_TREE_MODEL(pm_widgets.store), &iter, path);
 
1181
        gtk_tree_path_free(path);
 
1182
 
 
1183
        gtk_tree_model_get(GTK_TREE_MODEL(pm_widgets.store), &iter,
 
1184
                PLUGIN_COLUMN_CHECK, &old_state, PLUGIN_COLUMN_PLUGIN, &p, -1);
 
1185
 
 
1186
        /* no plugins item */
 
1187
        if (p == NULL)
 
1188
                return;
 
1189
 
 
1190
        state = ! old_state; /* toggle the state */
 
1191
 
 
1192
        /* save the filename of the plugin */
 
1193
        file_name = g_strdup(p->filename);
 
1194
 
 
1195
        /* unload plugin module */
 
1196
        if (!state)
 
1197
                /* save shortcuts (only need this group, but it doesn't take long) */
 
1198
                keybindings_write_to_file();
 
1199
 
 
1200
        plugin_free(p);
 
1201
 
 
1202
        /* reload plugin module and initialize it if item is checked */
 
1203
        p = plugin_new(file_name, state, TRUE);
 
1204
        if (!p)
 
1205
        {
 
1206
                /* plugin file may no longer be on disk, or is now incompatible */
 
1207
                gtk_list_store_remove(pm_widgets.store, &iter);
 
1208
        }
 
1209
        else
 
1210
        {
 
1211
                if (state)
 
1212
                        keybindings_load_keyfile();             /* load shortcuts */
 
1213
 
 
1214
                /* update model */
 
1215
                gtk_list_store_set(pm_widgets.store, &iter,
 
1216
                        PLUGIN_COLUMN_CHECK, state,
 
1217
                        PLUGIN_COLUMN_PLUGIN, p, -1);
 
1218
 
 
1219
                /* set again the sensitiveness of the configure and help buttons */
 
1220
                pm_update_buttons(p);
 
1221
        }
 
1222
        g_free(file_name);
 
1223
}
 
1224
 
 
1225
 
 
1226
static void pm_prepare_treeview(GtkWidget *tree, GtkListStore *store)
 
1227
{
 
1228
        GtkCellRenderer *text_renderer, *checkbox_renderer;
 
1229
        GtkTreeViewColumn *column;
 
1230
        GtkTreeIter iter;
 
1231
        GList *list;
 
1232
        GtkTreeSelection *sel;
 
1233
 
 
1234
        checkbox_renderer = gtk_cell_renderer_toggle_new();
 
1235
        column = gtk_tree_view_column_new_with_attributes(
 
1236
                _("Active"), checkbox_renderer, "active", PLUGIN_COLUMN_CHECK, NULL);
 
1237
        gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
 
1238
        g_signal_connect(checkbox_renderer, "toggled", G_CALLBACK(pm_plugin_toggled), NULL);
 
1239
 
 
1240
        text_renderer = gtk_cell_renderer_text_new();
 
1241
        column = gtk_tree_view_column_new_with_attributes(
 
1242
                _("Plugin"), text_renderer, "text", PLUGIN_COLUMN_NAME, NULL);
 
1243
        gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
 
1244
 
 
1245
        text_renderer = gtk_cell_renderer_text_new();
 
1246
        g_object_set(text_renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
 
1247
        column = gtk_tree_view_column_new_with_attributes(
 
1248
                _("File"), text_renderer, "text", PLUGIN_COLUMN_FILE, NULL);
 
1249
        gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
 
1250
 
 
1251
        gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE);
 
1252
        gtk_tree_view_set_enable_search(GTK_TREE_VIEW(tree), FALSE);
 
1253
        gtk_tree_sortable_set_sort_column_id(
 
1254
                GTK_TREE_SORTABLE(store), PLUGIN_COLUMN_NAME, GTK_SORT_ASCENDING);
 
1255
 
 
1256
        /* selection handling */
 
1257
        sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
 
1258
        gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
 
1259
        g_signal_connect(sel, "changed", G_CALLBACK(pm_selection_changed), NULL);
 
1260
 
 
1261
        list = g_list_first(plugin_list);
 
1262
        if (list == NULL)
 
1263
        {
 
1264
                gtk_list_store_append(store, &iter);
 
1265
                gtk_list_store_set(store, &iter, PLUGIN_COLUMN_CHECK, FALSE,
 
1266
                                PLUGIN_COLUMN_NAME, _("No plugins available."),
 
1267
                                PLUGIN_COLUMN_FILE, "", PLUGIN_COLUMN_PLUGIN, NULL, -1);
 
1268
        }
 
1269
        else
 
1270
        {
 
1271
                Plugin *p;
 
1272
                for (; list != NULL; list = list->next)
 
1273
                {
 
1274
                        p = list->data;
 
1275
 
 
1276
                        gtk_list_store_append(store, &iter);
 
1277
                        gtk_list_store_set(store, &iter,
 
1278
                                PLUGIN_COLUMN_CHECK, is_active_plugin(p),
 
1279
                                PLUGIN_COLUMN_NAME, p->info.name,
 
1280
                                PLUGIN_COLUMN_FILE, p->filename,
 
1281
                                PLUGIN_COLUMN_PLUGIN, p,
 
1282
                                -1);
 
1283
                }
 
1284
        }
 
1285
        gtk_tree_view_set_model(GTK_TREE_VIEW(tree), GTK_TREE_MODEL(store));
 
1286
        g_object_unref(store);
 
1287
}
 
1288
 
 
1289
 
 
1290
static void pm_on_plugin_button_clicked(GtkButton *button, gpointer user_data)
 
1291
{
 
1292
        GtkTreeModel *model;
 
1293
        GtkTreeSelection *selection;
 
1294
        GtkTreeIter iter;
 
1295
        Plugin *p;
 
1296
 
 
1297
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pm_widgets.tree));
 
1298
        if (gtk_tree_selection_get_selected(selection, &model, &iter))
 
1299
        {
 
1300
                gtk_tree_model_get(model, &iter, PLUGIN_COLUMN_PLUGIN, &p, -1);
 
1301
 
 
1302
                if (p != NULL)
 
1303
                {
 
1304
                        if (GPOINTER_TO_INT(user_data) == PM_BUTTON_CONFIGURE)
 
1305
                                plugin_show_configure(&p->public);
 
1306
                        else if (GPOINTER_TO_INT(user_data) == PM_BUTTON_HELP && p->help != NULL)
 
1307
                                p->help();
 
1308
                }
 
1309
        }
 
1310
}
 
1311
 
 
1312
 
 
1313
static void
 
1314
free_non_active_plugin(gpointer data, gpointer user_data)
 
1315
{
 
1316
        Plugin *plugin = data;
 
1317
 
 
1318
        /* don't do anything when closing the plugin manager and it is an active plugin */
 
1319
        if (is_active_plugin(plugin))
 
1320
                return;
 
1321
 
 
1322
        plugin_free(plugin);
 
1323
}
 
1324
 
 
1325
 
 
1326
static void pm_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
 
1327
{
 
1328
        if (plugin_list != NULL)
 
1329
        {
 
1330
                /* remove all non-active plugins from the list */
 
1331
                g_list_foreach(plugin_list, free_non_active_plugin, NULL);
 
1332
                g_list_free(plugin_list);
 
1333
                plugin_list = NULL;
 
1334
        }
 
1335
        gtk_widget_destroy(GTK_WIDGET(dialog));
 
1336
}
 
1337
 
 
1338
 
 
1339
static GtkWidget *create_table_label(const gchar *text)
 
1340
{
 
1341
        GtkWidget *label;
 
1342
        PangoAttrList *attrs;
 
1343
 
 
1344
        attrs = pango_attr_list_new();
 
1345
        pango_attr_list_insert(attrs, pango_attr_weight_new(PANGO_WEIGHT_BOLD));
 
1346
        label = gtk_label_new(text);
 
1347
        gtk_label_set_attributes(GTK_LABEL(label), attrs);
 
1348
        gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
 
1349
        pango_attr_list_unref(attrs);
 
1350
 
 
1351
        return label;
 
1352
}
 
1353
 
 
1354
 
 
1355
static void pm_show_dialog(GtkMenuItem *menuitem, gpointer user_data)
 
1356
{
 
1357
        GtkWidget *vbox, *vbox2, *label_vbox, *hbox, *swin, *label, *label2, *desc_win, *table, *paned;
 
1358
 
 
1359
        /* before showing the dialog, we need to create the list of available plugins */
 
1360
        load_all_plugins();
 
1361
 
 
1362
        pm_widgets.dialog = gtk_dialog_new_with_buttons(_("Plugins"), GTK_WINDOW(main_widgets.window),
 
1363
                                                GTK_DIALOG_DESTROY_WITH_PARENT,
 
1364
                                                GTK_STOCK_OK, GTK_RESPONSE_CANCEL, NULL);
 
1365
        vbox = ui_dialog_vbox_new(GTK_DIALOG(pm_widgets.dialog));
 
1366
        gtk_widget_set_name(pm_widgets.dialog, "GeanyDialog");
 
1367
        gtk_box_set_spacing(GTK_BOX(vbox), 6);
 
1368
 
 
1369
        gtk_window_set_default_size(GTK_WINDOW(pm_widgets.dialog), 500, 450);
 
1370
 
 
1371
        pm_widgets.tree = gtk_tree_view_new();
 
1372
        pm_widgets.store = gtk_list_store_new(
 
1373
                PLUGIN_N_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
 
1374
        pm_prepare_treeview(pm_widgets.tree, pm_widgets.store);
 
1375
 
 
1376
        swin = gtk_scrolled_window_new(NULL, NULL);
 
1377
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
 
1378
                GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 
1379
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin), GTK_SHADOW_IN);
 
1380
        gtk_container_add(GTK_CONTAINER(swin), pm_widgets.tree);
 
1381
 
 
1382
        label = geany_wrap_label_new(_("Choose which plugins should be loaded at startup:"));
 
1383
 
 
1384
        pm_widgets.configure_button = gtk_button_new_from_stock(GTK_STOCK_PREFERENCES);
 
1385
        gtk_widget_set_sensitive(pm_widgets.configure_button, FALSE);
 
1386
        g_signal_connect(pm_widgets.configure_button, "clicked",
 
1387
                G_CALLBACK(pm_on_plugin_button_clicked), GINT_TO_POINTER(PM_BUTTON_CONFIGURE));
 
1388
 
 
1389
        pm_widgets.help_button = gtk_button_new_from_stock(GTK_STOCK_HELP);
 
1390
        gtk_widget_set_sensitive(pm_widgets.help_button, FALSE);
 
1391
        g_signal_connect(pm_widgets.help_button, "clicked",
 
1392
                G_CALLBACK(pm_on_plugin_button_clicked), GINT_TO_POINTER(PM_BUTTON_HELP));
 
1393
 
 
1394
        label2 = gtk_label_new(_("<b>Plugin details:</b>"));
 
1395
        gtk_label_set_use_markup(GTK_LABEL(label2), TRUE);
 
1396
        gtk_misc_set_alignment(GTK_MISC(label2), 0, 0.5);
 
1397
 
 
1398
        table = gtk_table_new(3, 2, FALSE);
 
1399
        gtk_table_set_col_spacings(GTK_TABLE(table), 6);
 
1400
        pm_widgets.plugin_label = geany_wrap_label_new(NULL);
 
1401
        pm_widgets.description_label = geany_wrap_label_new(NULL);
 
1402
        pm_widgets.author_label = geany_wrap_label_new(NULL);
 
1403
        gtk_table_attach(GTK_TABLE(table), create_table_label(_("Plugin:")), 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
 
1404
        gtk_table_attach(GTK_TABLE(table), create_table_label(_("Description:")), 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
 
1405
        gtk_table_attach(GTK_TABLE(table), create_table_label(_("Author(s):")), 0, 1, 2, 3, GTK_FILL, GTK_FILL, 0, 0);
 
1406
        gtk_table_attach(GTK_TABLE(table), pm_widgets.plugin_label, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
 
1407
        gtk_table_attach(GTK_TABLE(table), pm_widgets.description_label, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
 
1408
        gtk_table_attach(GTK_TABLE(table), pm_widgets.author_label, 1, 2, 2, 3, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
 
1409
 
 
1410
        desc_win = gtk_scrolled_window_new(NULL, NULL);
 
1411
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(desc_win),
 
1412
                GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 
1413
        gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(desc_win), table);
 
1414
 
 
1415
        hbox = gtk_hbox_new(FALSE, 0);
 
1416
        gtk_box_pack_start(GTK_BOX(hbox), label2, TRUE, TRUE, 0);
 
1417
        gtk_box_pack_start(GTK_BOX(hbox), pm_widgets.help_button, FALSE, FALSE, 4);
 
1418
        gtk_box_pack_start(GTK_BOX(hbox), pm_widgets.configure_button, FALSE, FALSE, 0);
 
1419
 
 
1420
        label_vbox = gtk_vbox_new(FALSE, 3);
 
1421
        gtk_box_pack_start(GTK_BOX(label_vbox), hbox, FALSE, FALSE, 0);
 
1422
        gtk_box_pack_start(GTK_BOX(label_vbox), desc_win, TRUE, TRUE, 0);
 
1423
 
 
1424
        paned = gtk_vpaned_new();
 
1425
        gtk_paned_pack1(GTK_PANED(paned), swin, TRUE, FALSE);
 
1426
        gtk_paned_pack2(GTK_PANED(paned), label_vbox, FALSE, FALSE);
 
1427
 
 
1428
        vbox2 = gtk_vbox_new(FALSE, 3);
 
1429
        gtk_box_pack_start(GTK_BOX(vbox2), label, FALSE, FALSE, 5);
 
1430
        gtk_box_pack_start(GTK_BOX(vbox2), paned, TRUE, TRUE, 0);
 
1431
 
 
1432
        g_signal_connect(pm_widgets.dialog, "response", G_CALLBACK(pm_dialog_response), NULL);
 
1433
 
 
1434
        gtk_container_add(GTK_CONTAINER(vbox), vbox2);
 
1435
        gtk_widget_show_all(pm_widgets.dialog);
 
1436
}
 
1437
 
 
1438
 
 
1439
#endif