/* * GDiffWindow module * See "gdwin.h" for the details of data structure. * * Copyright INOUE Seiichiro , licensed under the GPL. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "diff.h" #include "gui.h" #include "gdwin.h" #include "dirview.h" #include "onepaneview.h" #include "multipaneview.h" #include "mergeview.h" #include "viewmisc.h" #include "menu-tool-bar.h" /* Private function declarations */ static void select_file_cb(GdiffDirView *dirview, DiffFiles *dfiles, gpointer data); static void switch_page_cb(GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer data); static GtkWidget* make_label_for_notebook(ViewType vtype, const char **fnames); static gboolean is_files_displayable(DiffFiles *files); /** * gdwin_new: * Allocate GDiffWindow, initialize it, and return its pointer. * No related back-end data. * So all this function does is almost related to GUI components. * Now, the return value is not used directly. * Instead, the instance is implicitly used in various callback functions. * Input: * const char *geometry_string; **/ GDiffWindow* gdwin_new(const char *geometry_string) { GDiffWindow *gdwin; GtkWidget *app; GtkAccelGroup *accelg; GtkWidget *notebook; gdwin = g_new(GDiffWindow, 1); /* Initialize */ gdwin->filesel = NULL; gdwin->view_list = NULL; gdwin->pref = g_pref.winpref; /* Main window */ app = gnome_app_new(APPNAME, _("Gtkdiff")); gdwin->app = GNOME_APP(app); gtk_window_set_wmclass(GTK_WINDOW(app), "gtkdiff", "gtkdiff"); gtk_window_set_policy(GTK_WINDOW(app), TRUE, TRUE, FALSE); gtk_widget_set_name(app, "gtkdiff"); accelg = gtk_accel_group_new(); gdwin->accelg = accelg; gtk_window_add_accel_group(GTK_WINDOW(app), accelg); if (geometry_string) { gint x, y, w, h; if (gnome_parse_geometry(geometry_string, &x, &y, &w, &h)) { if ((x != -1) && (y != -1)) gtk_widget_set_uposition(GTK_WIDGET(app), x, y); if ((w != -1) && (h != -1)) gtk_window_set_default_size(GTK_WINDOW(app), w, h); } else { gnome_app_error(GNOME_APP(app), _("Couldn't understand geometry (position and size)\n" " specified on command line")); } } else { /* Default geometry */ gtk_window_set_default_size(GTK_WINDOW(app), 500, 400); gtk_widget_set_uposition(app, 20, 20); } /* It's a good idea to do this for all windows. */ /*TODO: for supporting multiple windows, to connect gdwin_delete*/ gtk_signal_connect(GTK_OBJECT(app), "destroy", GTK_SIGNAL_FUNC(gtk_main_quit), NULL); gtk_signal_connect(GTK_OBJECT(app), "delete_event", GTK_SIGNAL_FUNC(gtk_main_quit), NULL); /* notebook */ notebook = gtk_notebook_new(); gdwin->notebook = GTK_NOTEBOOK(notebook); gtk_signal_connect(GTK_OBJECT(notebook), "switch_page", GTK_SIGNAL_FUNC(switch_page_cb), gdwin); gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), gdwin->pref.show_tabs); gtk_notebook_set_homogeneous_tabs(GTK_NOTEBOOK(notebook), FALSE); gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE); gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook)); gnome_app_set_contents(GNOME_APP(app), notebook); /* I need to set NULL, because menubar_create() implicitly gets access to them. */ gdwin->toolbar = NULL; gdwin->searchbar = NULL; gdwin->app->statusbar = NULL; menubar_create(gdwin); toolbar_create(gdwin); gdwin->searchbar = searchbar_new(gdwin); statusbar_create(gdwin); menubar_install_hints_for_statusbar(gdwin); gtk_widget_show_all(app); /* I have to take care of toolbar's visibility after showing app widget. */ bars_modify(gdwin); return gdwin; } /* place holder */ void gdwin_delete(GDiffWindow *gdwin) { return; } /** * gdwin_add_view: **/ void gdwin_add_view(GDiffWindow *gdwin, GtkWidget *view) { GtkWidget *label; const char *fnames[MAX_NUM_COMPARE_FILES]; gint page; view_get_fnames(view, fnames); if (GDIFF_IS_DIRVIEW(view)) { label = make_label_for_notebook(DIR_VIEW, fnames); gtk_notebook_append_page(gdwin->notebook, GTK_WIDGET(view), label); gdiff_dirview_adjust_column(GDIFF_DIRVIEW(view), GTK_WIDGET(gdwin->notebook)->allocation.width); GDIFF_DIRVIEW(view)->gdwin = gdwin; gtk_signal_connect(GTK_OBJECT(view), "select_file", GTK_SIGNAL_FUNC(select_file_cb), gdwin); } else if (GDIFF_IS_ONEPVIEW(view)) { if (GDIFF_ONEPVIEW_NUM_FILES(view) == 2) { label = make_label_for_notebook(ONEPANE2_VIEW, fnames); } else { label = make_label_for_notebook(ONEPANE3_VIEW, fnames); } gtk_notebook_append_page(gdwin->notebook, GTK_WIDGET(view), label); GDIFF_ONEPVIEW(view)->gdwin = gdwin; } else if (GDIFF_IS_MULTIPVIEW(view)) { if (GDIFF_MULTIPVIEW_NUM_FILES(view) == 2) { label = make_label_for_notebook(MULTIPANE2_VIEW, fnames); } else { label = make_label_for_notebook(MULTIPANE3_VIEW, fnames); } gtk_notebook_append_page(gdwin->notebook, GTK_WIDGET(view), label); GDIFF_MULTIPVIEW(view)->gdwin = gdwin; } else if (GDIFF_IS_MERGEVIEW(view)) { if (GDIFF_MERGEVIEW_NUM_FILES(view) == 2) { label = make_label_for_notebook(MERGE2_VIEW, fnames); } else { label = make_label_for_notebook(MERGE3_VIEW, fnames); } gtk_notebook_append_page(gdwin->notebook, GTK_WIDGET(view), label); GDIFF_MERGEVIEW(view)->gdwin = gdwin; } gtk_widget_add_accelerator(view, "scrollup", gdwin->accelg, GDK_Page_Up, 0, 0); gtk_widget_add_accelerator(view, "scrolldown", gdwin->accelg, GDK_Page_Down, 0, 0); gtk_widget_show(view); /* Is this a proper way ? */ page = gtk_notebook_page_num(gdwin->notebook, view); gtk_notebook_set_page(gdwin->notebook, page); gdwin->view_list = g_list_prepend(gdwin->view_list, view); } /** * gdwin_remove_view: **/ void gdwin_remove_view(GDiffWindow *gdwin, GtkWidget *view) { GList *list; list = gdwin->view_list; while (list) { GList *next = list->next; GtkWidget *v = GTK_WIDGET(list->data); if (v == view) { gint page; page = gtk_notebook_page_num(gdwin->notebook, view); gdwin->view_list = g_list_remove(gdwin->view_list, view); gtk_notebook_remove_page(gdwin->notebook, page); } list = next; } /* When every view is closed, update menu */ if (gdwin->view_list == NULL) menubar_update(gdwin, &g_pref, NO_VIEW); } /** * gdwin_current_view: * Search the current view in the notebook, and return it. * Output: * Return value; Current view(widget). If no view, NULL. **/ GtkWidget* gdwin_current_view(const GDiffWindow *gdwin) { GList *list; GtkNotebook *notebook = gdwin->notebook; GtkWidget *w; gint page; page = gtk_notebook_current_page(notebook); if (page < 0) return NULL; w = gtk_notebook_get_nth_page(notebook, page); for (list = gdwin->view_list; list; list = list->next) { GtkWidget *view = GTK_WIDGET(list->data); if (view == w) { return view; } } return NULL; } /** * gdwin_find_dirview_with_diffdir: **/ GtkWidget* gdwin_find_dirview_with_diffdir(const GDiffWindow *gdwin, const DiffDir *diffdir) { GList *list; for (list = gdwin->view_list; list; list = list->next) { GtkWidget *view = GTK_WIDGET(list->data); if (GDIFF_IS_DIRVIEW(view) && GDIFF_DIRVIEW_DIFFDIR(view) == diffdir) return view; } return NULL; } /** * gdwin_find_fview_with_diffdir: **/ GtkWidget* gdwin_find_fview_with_diffdir(const GDiffWindow *gdwin, const DiffDir *diffdir) { GList *list; for (list = gdwin->view_list; list; list = list->next) { GtkWidget *view = GTK_WIDGET(list->data); if (GDIFF_IS_ONEPVIEW(view) && GDIFF_ONEPVIEW_DIFFDIR(view) == diffdir) return view; if (GDIFF_IS_MULTIPVIEW(view) && GDIFF_MULTIPVIEW_DIFFDIR(view) == diffdir) return view; if (GDIFF_IS_MERGEVIEW(view) && GDIFF_MERGEVIEW_DIFFDIR(view) == diffdir) return view; } return NULL; } /** * gdwin_find_fview_with_dfiles: **/ GtkWidget* gdwin_find_fview_with_dfiles(const GDiffWindow *gdwin, const DiffFiles *dfiles, gboolean visible_only) { GList *list; for (list = gdwin->view_list; list; list = list->next) { GtkWidget *view = GTK_WIDGET(list->data); if (GDIFF_IS_ONEPVIEW(view) && GDIFF_ONEPVIEW_DFILES(view) == dfiles) { if (visible_only == TRUE && !GTK_WIDGET_VISIBLE(view)) continue; return view; } else if (GDIFF_IS_MULTIPVIEW(view) && GDIFF_MULTIPVIEW_DFILES(view) == dfiles) { if (visible_only == TRUE && !GTK_WIDGET_VISIBLE(view)) continue; return view; } else if (GDIFF_IS_MERGEVIEW(view) && GDIFF_MERGEVIEW_DFILES(view) == dfiles) { if (visible_only == TRUE && !GTK_WIDGET_VISIBLE(view)) continue; return view; } } return NULL; } /** * gdwin_find_fview_with_vtype: **/ GtkWidget* gdwin_find_fview_with_vtype(const GDiffWindow *gdwin, const DiffFiles *dfiles, ViewType vtype) { GList *list; for (list = gdwin->view_list; list; list = list->next) { GtkWidget *view = GTK_WIDGET(list->data); if (vtype & ONEPANE_MASK_VIEW) { if (GDIFF_IS_ONEPVIEW(view) && GDIFF_ONEPVIEW_DFILES(view) == dfiles) return view; } else if (vtype & MULTIPANE_MASK_VIEW) { if (GDIFF_IS_MULTIPVIEW(view) && GDIFF_MULTIPVIEW_DFILES(view) == dfiles) return view; } else if (vtype & MERGE_MASK_VIEW) { if (GDIFF_IS_MERGEVIEW(view) && GDIFF_MERGEVIEW_DFILES(view) == dfiles) return view; } else { g_assert_not_reached(); } } return NULL; } /* ---The followings are private functions--- */ /** * select_file_cb: * Callback function when a user selects one in the directory view. **/ static void select_file_cb(GdiffDirView *dirview, DiffFiles *dfiles, gpointer data) { GDiffWindow *gdwin = data; GList *list; GtkWidget *new_view; if (is_files_displayable(dfiles) == FALSE) return; /* Look for the file view among all opened views */ for (list = gdwin->view_list; list; list = list->next) { GtkWidget *view = GTK_WIDGET(list->data); /* if desirable view is already open, focus it */ if ((GDIFF_IS_ONEPVIEW(view) && GDIFF_ONEPVIEW_DFILES(view) == dfiles && GDIFF_ONEPVIEW_PREF(view).view_type == g_pref.fvpref.view_type) || (GDIFF_IS_MULTIPVIEW(view) && GDIFF_MULTIPVIEW_DFILES(view) == dfiles && GDIFF_MULTIPVIEW_PREF(view).view_type == g_pref.fvpref.view_type) || (GDIFF_IS_MERGEVIEW(view) && GDIFF_MERGEVIEW_DFILES(view) == dfiles && GDIFF_MERGEVIEW_PREF(view).view_type == g_pref.fvpref.view_type)) { gint page; page = gtk_notebook_page_num(gdwin->notebook, view); if (!GTK_WIDGET_VISIBLE(view)) { gtk_widget_show(view); } gtk_notebook_set_page(gdwin->notebook, page); return; } } /* if desirable view is not open, open it */ if (g_pref.fvpref.view_type & ONEPANE_MASK_VIEW) new_view = gdiff_onepview_new(GDIFF_DIRVIEW_DIFFDIR(dirview), dfiles, TRUE); else if (g_pref.fvpref.view_type & MULTIPANE_MASK_VIEW) new_view = gdiff_multipview_new(GDIFF_DIRVIEW_DIFFDIR(dirview), dfiles, TRUE); else if (g_pref.fvpref.view_type & MERGE_MASK_VIEW) new_view = gdiff_mergeview_new(GDIFF_DIRVIEW_DIFFDIR(dirview), dfiles, TRUE); else return; gdwin_add_view(gdwin, new_view); } /** * switch_page_cb: * Update menu-bar and status-bar. **/ static void switch_page_cb(GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer data) { GDiffWindow *gdwin = data; GtkWidget *view = page->child; Preference pref = g_pref;/* copy to local */ char *sbar_msg = NULL;/* status-bar message */ const char *fnames[MAX_NUM_COMPARE_FILES]; view_get_fnames(view, fnames); if (GDIFF_IS_DIRVIEW(view)) { /* Deal with dirty-bit */ if (GDIFF_DIRVIEW(view)->b_dirty == TRUE) { gdiff_dirview_redisplay(GDIFF_DIRVIEW(view)); } pref.dvpref = GDIFF_DIRVIEW_PREF(view); menubar_update(gdwin, &pref, DIR_VIEW); sbar_msg = sbar_create_msg(2, fnames, NULL, NULL); } else if (GDIFF_IS_ONEPVIEW(view)) { pref.fvpref = GDIFF_ONEPVIEW_PREF(view); if (GDIFF_ONEPVIEW_NUM_FILES(view) == 2) { menubar_update(gdwin, &pref, ONEPANE2_VIEW); } else { menubar_update(gdwin, &pref, ONEPANE3_VIEW); } sbar_msg = sbar_create_msg(GDIFF_ONEPVIEW_NUM_FILES(view), fnames, NULL, NULL); } else if (GDIFF_IS_MULTIPVIEW(view)) { pref.fvpref = GDIFF_MULTIPVIEW_PREF(view); if (GDIFF_MULTIPVIEW_NUM_FILES(view) == 2) { menubar_update(gdwin, &pref, MULTIPANE2_VIEW); } else { menubar_update(gdwin, &pref, MULTIPANE3_VIEW); } sbar_msg = sbar_create_msg(GDIFF_MULTIPVIEW_NUM_FILES(view), fnames, NULL, NULL); } else if (GDIFF_IS_MERGEVIEW(view)) { pref.fvpref = GDIFF_MERGEVIEW_PREF(view); if (GDIFF_MERGEVIEW_NUM_FILES(view) == 2) { menubar_update(gdwin, &pref, MERGE2_VIEW); } else { menubar_update(gdwin, &pref, MERGE3_VIEW); } sbar_msg = sbar_create_msg(GDIFF_MERGEVIEW_NUM_FILES(view), fnames, NULL, NULL); } /* Update status-bar */ if (sbar_msg) { statusbar_update(gdwin, sbar_msg); g_free(sbar_msg); } if (GDIFF_IS_ONEPVIEW(view)) { searchbar_show_button(gdwin->searchbar, 1); } else if (GDIFF_IS_MULTIPVIEW(view)) { searchbar_show_button(gdwin->searchbar, GDIFF_MULTIPVIEW_NUM_FILES(view)); } else if (GDIFF_IS_MERGEVIEW(view)) { searchbar_show_button(gdwin->searchbar, GDIFF_MERGEVIEW_NUM_FILES(view)); } } /** * make_label_for_notebook: * From the file names and the current view info., * make a GtkLabel widget for adding to GtkNotebook widget. **/ static GtkWidget* make_label_for_notebook(ViewType vtype, const char **fnames) { char *buf = NULL; GtkWidget *label = NULL; int n; for (n = 0; n < MAX_NUM_COMPARE_FILES; n++) { if (fnames[n] == NULL) fnames[n] = ""; } if (vtype == DIR_VIEW) buf = g_strdup_printf("[%s : %s]", fnames[0], fnames[1]); else if (vtype == ONEPANE2_VIEW) buf = g_strdup_printf("%s : %s [1]", fnames[0], fnames[1]); else if (vtype == ONEPANE3_VIEW) buf = g_strdup_printf("%s : %s : %s [1]", fnames[0], fnames[1], fnames[2]); else if (vtype == MULTIPANE2_VIEW) buf = g_strdup_printf("%s : %s [2]", fnames[0], fnames[1]); else if (vtype == MULTIPANE3_VIEW) buf = g_strdup_printf("%s : %s : %s [3]", fnames[0], fnames[1], fnames[2]); else if (vtype == MERGE2_VIEW) buf = g_strdup_printf("%s : %s [merge]", fnames[0], fnames[1]); else if (vtype == MERGE3_VIEW) buf = g_strdup_printf("%s : %s : %s [merge]", fnames[0], fnames[1], fnames[2]); else g_assert_not_reached(); if (buf) { label = gtk_label_new(buf); g_free(buf); } return label; } /** * is_files_displayable: * Check the files are displayable. * If they are not displayable, show message box. * Output: * Return value; TRUE if they are displayable. **/ static gboolean is_files_displayable(DiffFiles *files) { FilesStatus fsst; fsst = dfiles_get_status(files); switch (fsst) { case DIFFERENT_FILES: case ONLY_FILE1_EXISTS: case ONLY_FILE2_EXISTS: return TRUE; case IDENTICAL_FILES: g_message(_("Identical files\n")); case BINARY_FILES: g_message(_("Binary files\n")); break; case DIRECTORIES: default: g_assert_not_reached(); break; } return FALSE; }