Removing stupid debug code
[geanyvc.git] / geanyvc.c
blob6b91042c468e305b8bb58c2ce98d4114880bace9
1 /*
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.
24 /* VC plugin */
25 /* This plugin allow to works with cvs/svn/git inside geany light IDE. */
27 #include "geany.h"
28 #include "support.h"
29 #include "plugindata.h"
30 #include "document.h"
31 #include "filetypes.h"
32 #include "utils.h"
33 #include "project.h"
34 #include "pluginmacros.h"
36 PluginFields *plugin_fields;
37 GeanyData *geany_data;
40 VERSION_CHECK(27);
41 PLUGIN_INFO(_("VC"), _("Interface to different Version Control systems."), VERSION,
42 _("Yura Siamashka <yurand2@gmail.com>"));
44 enum
46 VC_COMMAND_DIFF_FILE,
47 VC_COMMAND_DIFF_DIR,
48 VC_COMMAND_DIFF_PROJECT,
49 VC_COMMAND_REVERT_FILE,
50 VC_COMMAND_STATUS,
51 VC_COMMAND_ADD,
52 VC_COMMAND_REMOVE,
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,
73 SVN_CMD_DIFF_DIR,
74 SVN_CMD_DIFF_PROJECT,
75 SVN_CMD_REVERT_FILE,
76 SVN_CMD_STATUS,
77 SVN_CMD_ADD,
78 SVN_CMD_REMOVE,
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,
90 CVS_CMD_DIFF_DIR,
91 CVS_CMD_DIFF_PROJECT,
92 CVS_CMD_REVERT_FILE,
93 CVS_CMD_STATUS,
94 CVS_CMD_ADD,
95 CVS_CMD_REMOVE,
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", "--",
106 BASE_FILENAME, NULL
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,
119 GIT_CMD_DIFF_DIR,
120 GIT_CMD_DIFF_PROJECT,
121 GIT_CMD_REVERT_FILE,
122 GIT_CMD_STATUS,
123 GIT_CMD_ADD,
124 GIT_CMD_REMOVE,
127 static void *GIT_ENV[] = { GIT_ENV_DIFF_FILE,
128 GIT_ENV_DIFF_DIR,
129 GIT_ENV_DIFF_PROJECT,
130 GIT_ENV_REVERT_FILE,
131 GIT_ENV_STATUS,
132 GIT_ENV_ADD,
133 GIT_ENV_REMOVE,
136 static gboolean
137 in_vc_true(const gchar * filename)
139 return TRUE;
142 static gboolean
143 in_vc_git(const gchar * filename)
145 gint exit_code;
146 gchar *argv[] = { "git", "ls-files", "--", NULL, NULL };
147 gchar *dir;
148 gchar *base_name;
149 gboolean ret = FALSE;
150 gchar *std_output;
152 if (g_file_test(filename, G_FILE_TEST_IS_DIR))
154 dir = g_strdup(filename);
155 base_name = g_strdup(".");
157 else
159 dir = g_path_get_dirname(filename);
160 base_name = g_path_get_basename(filename);
162 argv[3] = base_name;
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);
167 if (NZV(std_output))
169 ret = TRUE;
170 g_free(std_output);
173 g_free(base_name);
174 g_free(dir);
176 return ret;
179 typedef struct VC_RECORD
181 void **commands;
182 void **envs;
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
187 } VC_RECORD;
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},
200 static gboolean
201 find_subdir(const gchar * filename, const gchar * subdir)
203 gboolean ret = FALSE;
204 gchar *base;
205 gchar *gitdir;
206 gchar *base_prev = g_strdup(":");
208 if (g_file_test(filename, G_FILE_TEST_IS_DIR))
209 base = g_strdup(filename);
210 else
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);
217 g_free(gitdir);
218 if (ret)
219 break;
220 g_free(base_prev);
221 base_prev = base;
222 base = g_path_get_dirname(base);
225 g_free(base);
226 g_free(base_prev);
227 return ret;
231 static gboolean
232 check_filename(const gchar * filename, VC_RECORD * vc)
234 gboolean ret;
235 gchar *base;
236 gchar *dir;
237 gchar *path;
239 if (!filename)
240 return FALSE;
242 path = g_find_program_in_path(vc->program);
243 if (!path)
244 return FALSE;
245 g_free(path);
247 if (vc->check_parents)
249 ret = find_subdir(filename, vc->subdir);
251 else
253 if (g_file_test(filename, G_FILE_TEST_IS_DIR))
254 base = g_strdup(filename);
255 else
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);
261 g_free(base);
262 g_free(dir);
265 if (ret)
266 ret = vc->in_vc(filename);
268 return ret;
271 static const VC_RECORD *
272 find_vc(const char *filename)
274 guint i;
275 for (i = 0; i < G_N_ELEMENTS(VC); i++)
277 if (check_filename(filename, &VC[i]))
279 return &VC[i];
282 return NULL;
285 static void *
286 find_cmd_env(gint cmd_type, gboolean cmd, const gchar * filename)
288 const VC_RECORD *vc;
289 vc = find_vc(filename);
290 if (vc)
292 if (cmd)
293 return vc->commands[cmd_type];
294 else
295 return vc->envs[cmd_type];
297 return NULL;
300 static GSList *
301 get_cmd_env(gint cmd_type, gboolean cmd, const gchar * filename)
303 gint i, j;
304 gint len = 0;
305 gchar **argv;
306 gchar **ret;
307 gchar *dir;
308 gchar *base_filename;
309 GSList *head = NULL;
311 argv = find_cmd_env(cmd_type, cmd, filename);
312 if (!argv)
313 return NULL;
315 if (g_file_test(filename, G_FILE_TEST_IS_DIR))
317 dir = g_strdup(filename);
319 else
321 dir = g_path_get_dirname(filename);
323 base_filename = g_path_get_basename(filename);
325 while (1)
327 if (argv[len] == NULL)
328 break;
329 len++;
331 ret = g_malloc(sizeof(gchar *) * (len + 1));
332 memset(ret, 0, sizeof(gchar *) * (len + 1));
334 head = g_slist_alloc();
335 head->data = ret;
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));
343 j = -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);
358 else
359 ret[j] = g_strdup(argv[i]);
361 g_free(dir);
362 g_free(base_filename);
363 return head;
367 static int
368 find_by_filename(const gchar * filename)
370 guint i;
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)
376 return i;
378 return -1;
382 /* name_prefix should be in UTF-8, and can have a path. */
383 static void
384 show_output(const gchar * std_output, const gchar * name, const gchar * force_encoding)
386 gchar *text, *detect_enc = NULL;
387 gint idx, page;
388 GtkNotebook *book;
390 // need to convert input text from the encoding of the original file into
391 // UTF-8 because internally Geany always needs UTF-8
392 if (force_encoding)
394 text = encodings->convert_to_utf8_from_charset(std_output, -1, force_encoding,
395 TRUE);
397 else
399 text = encodings->convert_to_utf8(std_output, -1, &detect_enc);
401 if (text)
403 idx = find_by_filename(name);
404 if (idx == -1)
406 idx = documents->new_file(name,
407 geany_data->filetypes[GEANY_FILETYPES_DIFF],
408 text);
410 else
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);
422 else
424 ui->set_statusbar(FALSE, _("Could not parse the output of the diff"));
426 g_free(text);
427 g_free(detect_enc);
430 static gchar *
431 execute_command(const gchar * filename, gint cmd)
433 gchar *std_output = NULL;
434 gchar *std_error = NULL;
435 gint exit_code;
436 gchar *text = NULL;
437 gchar *dir;
438 GSList *cur;
439 gchar **env = find_cmd_env(cmd, FALSE, filename);
440 GSList *largv = get_cmd_env(cmd, TRUE, filename);
441 gchar **argv;
443 if (!largv)
445 return NULL;
448 if (g_file_test(filename, G_FILE_TEST_IS_DIR))
450 dir = g_strdup(filename);
452 else
454 dir = g_path_get_dirname(filename);
457 for (cur = largv; cur != NULL; cur = g_slist_next(cur))
459 argv = cur->data;
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,
465 NULL);
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))
480 text = std_output;
483 else
485 ui->set_statusbar(FALSE, _("Something went really wrong."));
487 g_strfreev(cur->data);
489 g_free(dir);
490 g_free(std_error);
491 g_slist_free(largv);
492 return text;
496 /* Make a diff from the current directory */
497 static void
498 vcdirectory_activated(GtkMenuItem * menuitem, gpointer gdata)
500 gint idx;
501 gchar *base_name = NULL;
502 gchar *locale_filename = NULL;
503 gchar *text;
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);
518 if (text)
520 setptr(base_name, g_strconcat(base_name, ".vc.diff", NULL));
521 show_output(text, base_name, NULL);
522 g_free(text);
525 g_free(base_name);
526 g_free(locale_filename);
530 /* Callback if menu item for the current project was activated */
531 static void
532 vcproject_activated(GtkMenuItem * menuitem, gpointer gdata)
534 gint idx;
535 gchar *locale_filename = NULL;
536 gchar *text;
537 gchar *name;
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);
550 if (text)
552 name = g_strconcat(project->name, ".vc.diff", NULL);
553 show_output(text, name, NULL);
554 g_free(text);
555 g_free(name);
557 g_free(locale_filename);
561 /* Callback if menu item for a single file was activated */
562 static void
563 vcfile_activated(GtkMenuItem * menuitem, gpointer gdata)
565 gint idx;
566 gchar *locale_filename, *text;
567 gchar *name;
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);
581 if (text)
583 name = g_strconcat(doc_list[idx].file_name, ".vc.diff", NULL);
584 show_output(text, name, doc_list[idx].encoding);
585 g_free(text);
586 g_free(name);
588 g_free(locale_filename);
591 /* Show status from the current directory */
592 static void
593 vcstatus_activated(GtkMenuItem * menuitem, gpointer gdata)
595 gint idx;
596 gchar *base_name = NULL;
597 gchar *locale_filename = NULL;
598 gchar *text;
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);
613 if (text)
615 show_output(text, "*VC-STATUS*", NULL);
616 g_free(text);
619 g_free(base_name);
620 g_free(locale_filename);
623 static gboolean
624 command_with_question_activated(GtkMenuItem * menuitem, gpointer gdata, gint cmd,
625 const gchar * question)
627 gint idx;
628 gchar *locale_filename, *text;
629 GtkWidget *dialog;
630 gint result;
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);
653 if (text)
655 g_free(text);
658 g_free(locale_filename);
659 return (result == GTK_RESPONSE_YES) ? TRUE : FALSE;
662 static void
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"));
669 static void
670 vcremove_activated(GtkMenuItem * menuitem, gpointer gdata)
672 gboolean ret;
674 ret = command_with_question_activated(menuitem, gdata, VC_COMMAND_REMOVE,
675 _("Do you really want to remove: %s"));
676 if (ret)
678 documents->remove(gtk_notebook_get_current_page(GTK_NOTEBOOK(app->notebook)));
682 static void
683 vcrevert_activated(GtkMenuItem * menuitem, gpointer gdata)
685 gint idx;
686 gboolean ret;
687 ret = command_with_question_activated(menuitem, gdata, VC_COMMAND_REVERT_FILE,
688 _("Do you really want to revert: %s"));
689 if (ret)
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;
705 static void
706 update_menu_items()
708 document *doc;
709 gboolean have_file;
710 gboolean project_have_vc = FALSE;
711 gboolean d_have_vc = FALSE;
712 gboolean f_have_vc = FALSE;
714 gchar *dir;
716 doc = documents->get_current();
717 have_file = doc && doc->file_name && g_path_is_absolute(doc->file_name);
719 if (have_file)
721 dir = g_path_get_dirname(doc->file_name);
722 if (find_cmd_env(VC_COMMAND_DIFF_FILE, TRUE, dir))
723 d_have_vc = TRUE;
725 if (find_cmd_env(VC_COMMAND_DIFF_FILE, TRUE, doc->file_name))
726 f_have_vc = TRUE;
727 g_free(dir);
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 */
746 void
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);
763 // Single file
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);
772 // Directory
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);
781 // Project
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);
790 // Status
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);
798 // Revert file
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);
807 // Add file
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);
815 // Remove file
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. */
832 void
833 cleanup()
835 // remove the menu item added in init()
836 gtk_widget_destroy(plugin_fields->menu_item);