2 * geanyvc.c - Plugin to geany light IDE to work with vc
4 * Copyright 2007 Frank Lanitz <frank(at)frank(dot)uvena(dot)de>
5 * Copyright 2007 Enrico Tröger <enrico.troeger@uvena.de>
6 * Copyright 2007 Nick Treleaven <nick.treleaven@btinternet.com>
7 * Copyright 2007 Yura Siamashka <yurand2@gmail.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 /* This plugin allow to works with cvs/svn/git inside geany light IDE. */
29 #include "plugindata.h"
31 #include "filetypes.h"
34 #include "pluginmacros.h"
36 PluginFields
*plugin_fields
;
37 GeanyData
*geany_data
;
41 PLUGIN_INFO(_("VC"), _("Interface to different Version Control systems."), VERSION
,
42 _("Yura Siamashka <yurand2@gmail.com>"));
48 VC_COMMAND_DIFF_PROJECT
,
49 VC_COMMAND_REVERT_FILE
,
55 /* The addresses of these strings act as enums, their contents are not used. */
56 static const gchar DIRNAME
[] = "*DIRNAME*";
57 static const gchar FILENAME
[] = "*FILENAME*";
58 static const gchar BASE_FILENAME
[] = "*BASE_FILENAME*";
60 /* this string is used when action require to run several commands */
61 static const gchar CMD_SEPARATOR
[] = "*CMD-SEPARATOR*";
64 static const gchar
*SVN_CMD_DIFF_FILE
[] = { "svn", "diff", "--non-interactive", FILENAME
, NULL
};
65 static const gchar
*SVN_CMD_DIFF_DIR
[] = { "svn", "diff", "--non-interactive", DIRNAME
, NULL
};
66 static const gchar
*SVN_CMD_DIFF_PROJECT
[] = { "svn", "diff", "--non-interactive", DIRNAME
, NULL
};
67 static const gchar
*SVN_CMD_REVERT_FILE
[] = { "svn", "revert", BASE_FILENAME
, NULL
};
68 static const gchar
*SVN_CMD_STATUS
[] = { "svn", "status", NULL
};
69 static const gchar
*SVN_CMD_ADD
[] = { "svn", "add", BASE_FILENAME
, NULL
};
70 static const gchar
*SVN_CMD_REMOVE
[] = { "svn", "rm", BASE_FILENAME
, NULL
};
72 static void *SVN_COMMANDS
[] = { SVN_CMD_DIFF_FILE
,
81 static const gchar
*CVS_CMD_DIFF_FILE
[] = { "cvs", "diff", "-u", BASE_FILENAME
, NULL
};
82 static const gchar
*CVS_CMD_DIFF_DIR
[] = { "cvs", "diff", "-u", NULL
};
83 static const gchar
*CVS_CMD_DIFF_PROJECT
[] = { "cvs", "diff", "-u", NULL
};
84 static const gchar
*CVS_CMD_REVERT_FILE
[] = { "cvs", "update", "-C", BASE_FILENAME
, NULL
};
85 static const gchar
*CVS_CMD_STATUS
[] = { "cvs", "status", NULL
};
86 static const gchar
*CVS_CMD_ADD
[] = { "cvs", "add", BASE_FILENAME
, NULL
};
87 static const gchar
*CVS_CMD_REMOVE
[] = { "cvs", "remove", BASE_FILENAME
, NULL
};
89 static void *CVS_COMMANDS
[] = { CVS_CMD_DIFF_FILE
,
98 static const gchar
*GIT_CMD_DIFF_FILE
[] = { "git", "diff", "--", BASE_FILENAME
, NULL
};
99 static const gchar
*GIT_CMD_DIFF_DIR
[] = { "git", "diff", NULL
};
100 static const gchar
*GIT_CMD_DIFF_PROJECT
[] = { "git", "diff", NULL
};
101 static const gchar
*GIT_CMD_REVERT_FILE
[] = { "git", "checkout", "--", BASE_FILENAME
, NULL
};
102 static const gchar
*GIT_CMD_STATUS
[] = { "git", "status", NULL
};
103 static const gchar
*GIT_CMD_ADD
[] = { "git", "add", "--", BASE_FILENAME
, NULL
};
104 static const gchar
*GIT_CMD_REMOVE
[] =
105 { "git", "rm", "-f", "--", BASE_FILENAME
, CMD_SEPARATOR
, "git", "reset", "HEAD", "--",
109 static const gchar
*GIT_ENV_DIFF_FILE
[] = { "PAGER=cat", NULL
};
110 static const gchar
*GIT_ENV_DIFF_DIR
[] = { "PAGER=cat", NULL
};
111 static const gchar
*GIT_ENV_DIFF_PROJECT
[] = { "PAGER=cat", NULL
};
112 static const gchar
*GIT_ENV_REVERT_FILE
[] = { "PAGER=cat", NULL
};
113 static const gchar
*GIT_ENV_STATUS
[] = { "PAGER=cat", NULL
};
114 static const gchar
*GIT_ENV_ADD
[] = { "PAGER=cat", NULL
};
115 static const gchar
*GIT_ENV_REMOVE
[] = { "PAGER=cat", NULL
};
118 static void *GIT_COMMANDS
[] = { GIT_CMD_DIFF_FILE
,
120 GIT_CMD_DIFF_PROJECT
,
127 static void *GIT_ENV
[] = { GIT_ENV_DIFF_FILE
,
129 GIT_ENV_DIFF_PROJECT
,
137 in_vc_true(const gchar
* filename
)
143 in_vc_git(const gchar
* filename
)
146 gchar
*argv
[] = { "git", "ls-files", "--", NULL
, NULL
};
149 gboolean ret
= FALSE
;
152 if (g_file_test(filename
, G_FILE_TEST_IS_DIR
))
154 dir
= g_strdup(filename
);
155 base_name
= g_strdup(".");
159 dir
= g_path_get_dirname(filename
);
160 base_name
= g_path_get_basename(filename
);
164 g_spawn_sync(dir
, argv
, NULL
,
165 G_SPAWN_SEARCH_PATH
| G_SPAWN_STDERR_TO_DEV_NULL
,
166 NULL
, NULL
, &std_output
, NULL
, &exit_code
, NULL
);
179 typedef struct VC_RECORD
183 const gchar
*program
;
184 const gchar
*subdir
; // version control subdirectory, e.g. ".svn"
185 gboolean check_parents
; // check parent dirs to find subdir
186 gboolean(*in_vc
) (const gchar
* path
); // check if file in VC
190 static void *NO_ENV
[] = { NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
192 /* Adding another VC system should be as easy as adding another entry in this array. */
193 static VC_RECORD VC
[] = {
194 {SVN_COMMANDS
, NO_ENV
, "svn", ".svn", FALSE
, in_vc_true
},
195 {CVS_COMMANDS
, NO_ENV
, "cvs", "CVS", FALSE
, in_vc_true
},
196 {GIT_COMMANDS
, GIT_ENV
, "git", ".git", TRUE
, in_vc_git
},
201 find_subdir(const gchar
* filename
, const gchar
* subdir
)
203 gboolean ret
= FALSE
;
206 gchar
*base_prev
= g_strdup(":");
208 if (g_file_test(filename
, G_FILE_TEST_IS_DIR
))
209 base
= g_strdup(filename
);
211 base
= g_path_get_dirname(filename
);
213 while (strcmp(base
, base_prev
) != 0)
215 gitdir
= g_build_path("/", base
, subdir
, NULL
);
216 ret
= g_file_test(gitdir
, G_FILE_TEST_IS_DIR
);
222 base
= g_path_get_dirname(base
);
232 check_filename(const gchar
* filename
, VC_RECORD
* vc
)
242 path
= g_find_program_in_path(vc
->program
);
247 if (vc
->check_parents
)
249 ret
= find_subdir(filename
, vc
->subdir
);
253 if (g_file_test(filename
, G_FILE_TEST_IS_DIR
))
254 base
= g_strdup(filename
);
256 base
= g_path_get_dirname(filename
);
257 dir
= g_build_path("/", base
, vc
->subdir
, NULL
);
259 ret
= g_file_test(dir
, G_FILE_TEST_IS_DIR
);
266 ret
= vc
->in_vc(filename
);
271 static const VC_RECORD
*
272 find_vc(const char *filename
)
275 for (i
= 0; i
< G_N_ELEMENTS(VC
); i
++)
277 if (check_filename(filename
, &VC
[i
]))
286 find_cmd_env(gint cmd_type
, gboolean cmd
, const gchar
* filename
)
289 vc
= find_vc(filename
);
293 return vc
->commands
[cmd_type
];
295 return vc
->envs
[cmd_type
];
301 get_cmd_env(gint cmd_type
, gboolean cmd
, const gchar
* filename
)
308 gchar
*base_filename
;
311 argv
= find_cmd_env(cmd_type
, cmd
, filename
);
315 if (g_file_test(filename
, G_FILE_TEST_IS_DIR
))
317 dir
= g_strdup(filename
);
321 dir
= g_path_get_dirname(filename
);
323 base_filename
= g_path_get_basename(filename
);
327 if (argv
[len
] == NULL
)
331 ret
= g_malloc(sizeof(gchar
*) * (len
+ 1));
332 memset(ret
, 0, sizeof(gchar
*) * (len
+ 1));
334 head
= g_slist_alloc();
337 for (i
= 0, j
= 0; i
< len
; i
++, j
++)
339 if (argv
[i
] == CMD_SEPARATOR
)
341 ret
= g_malloc(sizeof(gchar
*) * (len
+ 1));
342 memset(ret
, 0, sizeof(gchar
*) * (len
+ 1));
344 head
= g_slist_append(head
, ret
);
346 else if (argv
[i
] == DIRNAME
)
348 ret
[j
] = g_strdup(dir
);
350 else if (argv
[i
] == FILENAME
)
352 ret
[j
] = g_strdup(filename
);
354 else if (argv
[i
] == BASE_FILENAME
)
356 ret
[j
] = g_strdup(base_filename
);
359 ret
[j
] = g_strdup(argv
[i
]);
362 g_free(base_filename
);
368 find_by_filename(const gchar
* filename
)
372 for (i
= 0; i
< doc_array
->len
; i
++)
374 if (doc_list
[i
].is_valid
&& doc_list
[i
].file_name
&&
375 strcmp(doc_list
[i
].file_name
, filename
) == 0)
382 /* name_prefix should be in UTF-8, and can have a path. */
384 show_output(const gchar
* std_output
, const gchar
* name
, const gchar
* force_encoding
)
386 gchar
*text
, *detect_enc
= NULL
;
390 // need to convert input text from the encoding of the original file into
391 // UTF-8 because internally Geany always needs UTF-8
394 text
= encodings
->convert_to_utf8_from_charset(std_output
, -1, force_encoding
,
399 text
= encodings
->convert_to_utf8(std_output
, -1, &detect_enc
);
403 idx
= find_by_filename(name
);
406 idx
= documents
->new_file(name
,
407 geany_data
->filetypes
[GEANY_FILETYPES_DIFF
],
412 scintilla
->set_text(doc_list
[idx
].sci
, text
);
413 book
= GTK_NOTEBOOK(app
->notebook
);
414 page
= gtk_notebook_page_num(book
, GTK_WIDGET(doc_list
[idx
].sci
));
415 gtk_notebook_set_current_page(book
, page
);
416 doc_list
[idx
].changed
= FALSE
;
417 documents
->set_text_changed(idx
);
420 documents
->set_encoding(idx
, force_encoding
? force_encoding
: detect_enc
);
424 ui
->set_statusbar(FALSE
, _("Could not parse the output of the diff"));
431 execute_command(const gchar
* filename
, gint cmd
)
433 gchar
*std_output
= NULL
;
434 gchar
*std_error
= NULL
;
439 gchar
**env
= find_cmd_env(cmd
, FALSE
, filename
);
440 GSList
*largv
= get_cmd_env(cmd
, TRUE
, filename
);
448 if (g_file_test(filename
, G_FILE_TEST_IS_DIR
))
450 dir
= g_strdup(filename
);
454 dir
= g_path_get_dirname(filename
);
457 for (cur
= largv
; cur
!= NULL
; cur
= g_slist_next(cur
))
460 if (cur
!= g_slist_last(largv
))
462 g_spawn_sync(dir
, cur
->data
, env
,
463 G_SPAWN_SEARCH_PATH
| G_SPAWN_STDOUT_TO_DEV_NULL
|
464 G_SPAWN_STDERR_TO_DEV_NULL
, NULL
, NULL
, NULL
, NULL
, &exit_code
,
467 else if (g_spawn_sync
468 (dir
, cur
->data
, env
, G_SPAWN_SEARCH_PATH
, NULL
, NULL
, &std_output
,
469 &std_error
, &exit_code
, NULL
))
471 // CVS dump stuff to stderr when diff nested dirs
472 if (strcmp(argv
[0], "cvs") != 0 && NZV(std_error
))
474 dialogs
->show_msgbox(1,
475 _("%s exited with an error: \n%s."), argv
[0],
476 g_strstrip(std_error
));
478 else if (NZV(std_output
))
485 ui
->set_statusbar(FALSE
, _("Something went really wrong."));
487 g_strfreev(cur
->data
);
496 /* Make a diff from the current directory */
498 vcdirectory_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
501 gchar
*base_name
= NULL
;
502 gchar
*locale_filename
= NULL
;
505 idx
= documents
->get_cur_idx();
507 g_return_if_fail(DOC_IDX_VALID(idx
) && doc_list
[idx
].file_name
!= NULL
);
509 if (doc_list
[idx
].changed
)
511 documents
->save_file(idx
, FALSE
);
514 locale_filename
= utils
->get_locale_from_utf8(doc_list
[idx
].file_name
);
515 base_name
= g_path_get_dirname(locale_filename
);
517 text
= execute_command(base_name
, VC_COMMAND_DIFF_DIR
);
520 setptr(base_name
, g_strconcat(base_name
, ".vc.diff", NULL
));
521 show_output(text
, base_name
, NULL
);
526 g_free(locale_filename
);
530 /* Callback if menu item for the current project was activated */
532 vcproject_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
535 gchar
*locale_filename
= NULL
;
539 idx
= documents
->get_cur_idx();
541 g_return_if_fail(project
!= NULL
&& NZV(project
->base_path
));
543 if (DOC_IDX_VALID(idx
) && doc_list
[idx
].changed
&& doc_list
[idx
].file_name
!= NULL
)
545 documents
->save_file(idx
, FALSE
);
548 locale_filename
= utils
->get_locale_from_utf8(project
->base_path
);
549 text
= execute_command(locale_filename
, VC_COMMAND_DIFF_PROJECT
);
552 name
= g_strconcat(project
->name
, ".vc.diff", NULL
);
553 show_output(text
, name
, NULL
);
557 g_free(locale_filename
);
561 /* Callback if menu item for a single file was activated */
563 vcfile_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
566 gchar
*locale_filename
, *text
;
569 idx
= documents
->get_cur_idx();
571 g_return_if_fail(DOC_IDX_VALID(idx
) && doc_list
[idx
].file_name
!= NULL
);
573 if (doc_list
[idx
].changed
)
575 documents
->save_file(idx
, FALSE
);
578 locale_filename
= utils
->get_locale_from_utf8(doc_list
[idx
].file_name
);
580 text
= execute_command(locale_filename
, VC_COMMAND_DIFF_FILE
);
583 name
= g_strconcat(doc_list
[idx
].file_name
, ".vc.diff", NULL
);
584 show_output(text
, name
, doc_list
[idx
].encoding
);
588 g_free(locale_filename
);
591 /* Show status from the current directory */
593 vcstatus_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
596 gchar
*base_name
= NULL
;
597 gchar
*locale_filename
= NULL
;
600 idx
= documents
->get_cur_idx();
602 g_return_if_fail(DOC_IDX_VALID(idx
) && doc_list
[idx
].file_name
!= NULL
);
604 if (doc_list
[idx
].changed
)
606 documents
->save_file(idx
, FALSE
);
609 locale_filename
= utils
->get_locale_from_utf8(doc_list
[idx
].file_name
);
610 base_name
= g_path_get_dirname(locale_filename
);
612 text
= execute_command(base_name
, VC_COMMAND_STATUS
);
615 show_output(text
, "*VC-STATUS*", NULL
);
620 g_free(locale_filename
);
624 command_with_question_activated(GtkMenuItem
* menuitem
, gpointer gdata
, gint cmd
,
625 const gchar
* question
)
628 gchar
*locale_filename
, *text
;
632 idx
= documents
->get_cur_idx();
634 g_return_if_fail(DOC_IDX_VALID(idx
) && doc_list
[idx
].file_name
!= NULL
);
636 if (doc_list
[idx
].changed
)
638 documents
->save_file(idx
, FALSE
);
641 locale_filename
= utils
->get_locale_from_utf8(doc_list
[idx
].file_name
);
643 dialog
= gtk_message_dialog_new(GTK_WINDOW(app
->window
),
644 GTK_DIALOG_DESTROY_WITH_PARENT
,
645 GTK_MESSAGE_QUESTION
,
646 GTK_BUTTONS_YES_NO
, question
, locale_filename
);
647 result
= gtk_dialog_run(GTK_DIALOG(dialog
));
648 gtk_widget_destroy(dialog
);
650 if (result
== GTK_RESPONSE_YES
)
652 text
= execute_command(locale_filename
, cmd
);
658 g_free(locale_filename
);
659 return (result
== GTK_RESPONSE_YES
) ? TRUE
: FALSE
;
663 vcadd_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
665 command_with_question_activated(menuitem
, gdata
, VC_COMMAND_ADD
,
666 _("Do you really want to add: %s"));
670 vcremove_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
674 ret
= command_with_question_activated(menuitem
, gdata
, VC_COMMAND_REMOVE
,
675 _("Do you really want to remove: %s"));
678 documents
->remove(gtk_notebook_get_current_page(GTK_NOTEBOOK(app
->notebook
)));
683 vcrevert_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
687 ret
= command_with_question_activated(menuitem
, gdata
, VC_COMMAND_REVERT_FILE
,
688 _("Do you really want to revert: %s"));
691 idx
= documents
->get_cur_idx();
692 documents
->reload_file(idx
, NULL
);
697 static GtkWidget
*menu_vc_diff_file
= NULL
;
698 static GtkWidget
*menu_vc_diff_dir
= NULL
;
699 static GtkWidget
*menu_vc_diff_project
= NULL
;
700 static GtkWidget
*menu_vc_status
= NULL
;
701 static GtkWidget
*menu_vc_revert_file
= NULL
;
702 static GtkWidget
*menu_vc_add_file
= NULL
;
703 static GtkWidget
*menu_vc_remove_file
= NULL
;
710 gboolean project_have_vc
= FALSE
;
711 gboolean d_have_vc
= FALSE
;
712 gboolean f_have_vc
= FALSE
;
716 doc
= documents
->get_current();
717 have_file
= doc
&& doc
->file_name
&& g_path_is_absolute(doc
->file_name
);
721 dir
= g_path_get_dirname(doc
->file_name
);
722 if (find_cmd_env(VC_COMMAND_DIFF_FILE
, TRUE
, dir
))
725 if (find_cmd_env(VC_COMMAND_DIFF_FILE
, TRUE
, doc
->file_name
))
730 if (project
!= NULL
&& NZV(project
->base_path
) &&
731 find_cmd_env(VC_COMMAND_DIFF_DIR
, TRUE
, project
->base_path
))
732 project_have_vc
= TRUE
;
734 gtk_widget_set_sensitive(menu_vc_diff_file
, f_have_vc
);
735 gtk_widget_set_sensitive(menu_vc_diff_dir
, d_have_vc
);
736 gtk_widget_set_sensitive(menu_vc_revert_file
, f_have_vc
);
737 gtk_widget_set_sensitive(menu_vc_remove_file
, f_have_vc
);
738 gtk_widget_set_sensitive(menu_vc_add_file
, d_have_vc
&& !f_have_vc
);
740 gtk_widget_set_sensitive(menu_vc_status
, d_have_vc
);
741 gtk_widget_set_sensitive(menu_vc_diff_project
, project_have_vc
);
745 /* Called by Geany to initialize the plugin */
747 init(GeanyData
* data
)
749 GtkWidget
*menu_vc
= NULL
;
750 GtkWidget
*menu_vc_menu
= NULL
;
751 GtkTooltips
*tooltips
= NULL
;
753 tooltips
= gtk_tooltips_new();
755 menu_vc
= gtk_image_menu_item_new_with_mnemonic(_("_VC"));
756 gtk_container_add(GTK_CONTAINER(data
->tools_menu
), menu_vc
);
758 g_signal_connect((gpointer
) menu_vc
, "activate", G_CALLBACK(update_menu_items
), NULL
);
760 menu_vc_menu
= gtk_menu_new();
761 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_vc
), menu_vc_menu
);
764 menu_vc_diff_file
= gtk_menu_item_new_with_mnemonic(_("Diff From Current File"));
765 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_diff_file
);
766 gtk_tooltips_set_tip(tooltips
, menu_vc_diff_file
,
767 _("Make a diff from the current active file"), NULL
);
769 g_signal_connect((gpointer
) menu_vc_diff_file
, "activate",
770 G_CALLBACK(vcfile_activated
), NULL
);
773 menu_vc_diff_dir
= gtk_menu_item_new_with_mnemonic(_("Diff From Current Directory"));
774 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_diff_dir
);
775 gtk_tooltips_set_tip(tooltips
, menu_vc_diff_dir
,
776 _("Make a diff from the directory of the current active file"), NULL
);
778 g_signal_connect((gpointer
) menu_vc_diff_dir
, "activate",
779 G_CALLBACK(vcdirectory_activated
), NULL
);
782 menu_vc_diff_project
= gtk_menu_item_new_with_mnemonic(_("Diff From Current Project"));
783 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_diff_project
);
784 gtk_tooltips_set_tip(tooltips
, menu_vc_diff_project
,
785 _("Make a diff from the current project's base path"), NULL
);
787 g_signal_connect((gpointer
) menu_vc_diff_project
, "activate",
788 G_CALLBACK(vcproject_activated
), NULL
);
791 menu_vc_status
= gtk_menu_item_new_with_mnemonic(_("Status"));
792 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_status
);
793 gtk_tooltips_set_tip(tooltips
, menu_vc_status
, _("Show status."), NULL
);
795 g_signal_connect((gpointer
) menu_vc_status
, "activate",
796 G_CALLBACK(vcstatus_activated
), NULL
);
799 menu_vc_revert_file
= gtk_menu_item_new_with_mnemonic(_("Revert File"));
800 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_revert_file
);
801 gtk_tooltips_set_tip(tooltips
, menu_vc_revert_file
,
802 _("Restore pristine working copy file (undo local edits)."), NULL
);
804 g_signal_connect((gpointer
) menu_vc_revert_file
, "activate",
805 G_CALLBACK(vcrevert_activated
), NULL
);
808 menu_vc_add_file
= gtk_menu_item_new_with_mnemonic(_("Add File"));
809 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_add_file
);
810 gtk_tooltips_set_tip(tooltips
, menu_vc_add_file
, _("Add file to repositary."), NULL
);
812 g_signal_connect((gpointer
) menu_vc_add_file
, "activate",
813 G_CALLBACK(vcadd_activated
), NULL
);
816 menu_vc_remove_file
= gtk_menu_item_new_with_mnemonic(_("Remove File"));
817 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_remove_file
);
818 gtk_tooltips_set_tip(tooltips
, menu_vc_remove_file
,
819 _("Remove file from repositary."), NULL
);
821 g_signal_connect((gpointer
) menu_vc_remove_file
, "activate",
822 G_CALLBACK(vcremove_activated
), NULL
);
824 gtk_widget_show_all(menu_vc
);
826 plugin_fields
->menu_item
= menu_vc
;
827 plugin_fields
->flags
= PLUGIN_IS_DOCUMENT_SENSITIVE
;
831 /* Called by Geany before unloading the plugin. */
835 // remove the menu item added in init()
836 gtk_widget_destroy(plugin_fields
->menu_item
);