/* * GUI file selection module * Mainly, handling GtkFileSelection widget. * * Copyright INOUE Seiichiro , licensed under the GPL. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "diff.h" #include "misc.h" #include "gui.h" #include "gdwin.h" #include "dirview.h" #include "onepaneview.h" #include "multipaneview.h" #include "mergeview.h" /* Note: * File dialog box has its internal state, * because it has to accept multiple file names. */ /* Constant strings */ static const char *FILE_SEL_CAPTION[MAX_NUM_COMPARE_FILES] = { N_("First file selection"), N_("Second file selection"), N_("Third file selection"), }; /* Key for gtk_object_[get|set]_data() */ #define FILEDIALOGDATA_KEY "fddkey" /* Data structure definitions */ /* Internal data in GtkFileSelection instance. * Kept by gtk_object_set_data() and gtk_object_get_data(). */ typedef struct { /* For reference */ GDiffWindow *gdwin; /* Dialog box state */ int num_files; gboolean is_dir; WhichFile file_count; GtkToggleButton *file_button; GtkToggleButton *dir_button; GtkToggleButton *diff2_button; GtkToggleButton *diff3_button; GtkLabel *fname_label[MAX_NUM_COMPARE_FILES - 1]; } FileDialogData; /* Private function declarations */ static void file_create_filesel(GDiffWindow *gdwin); static void file_selection_ok(GtkWidget *w, gpointer data); static void file_selection_cancel(GtkWidget *w, gpointer data); static void dir_toggle(GtkToggleButton *w, gpointer data); static void diff3_toggle(GtkToggleButton *w, gpointer data); static FileDialogData* filedialogdata_new(GDiffWindow *gdwin); static void filedialogdata_reset(FileDialogData *fdd); static void filedialogdata_addfile(FileDialogData *fdd, WhichFile whichfile, const char *fname); static void filedialogdata_do_job(FileDialogData *fdd, const char *last_fname); /** * filesel_open: * An entry point to open files(directories). * At present, called only from [menu]-[File]-[Open]. * * If the instance of dialog has not created yet, create it and show it. * If it exists and has been hidden, just show it. * If it has been shown, just raise it. **/ void filesel_open(GDiffWindow *gdwin) { FileDialogData *fdd; if (gdwin->filesel) {/* The dialog has already been created */ if (!GTK_WIDGET_VISIBLE(gdwin->filesel)) { fdd = gtk_object_get_data(GTK_OBJECT(gdwin->filesel), FILEDIALOGDATA_KEY); gtk_window_set_title(GTK_WINDOW(gdwin->filesel), _(FILE_SEL_CAPTION[fdd->file_count])); gtk_widget_show(GTK_WIDGET(gdwin->filesel)); } else { gdk_window_raise(GTK_WIDGET(gdwin->filesel)->window); } } else { file_create_filesel(gdwin); } } /* ---The followings are private functions--- */ /** * file_create_filesel: * Create filesel widget. **/ static void file_create_filesel(GDiffWindow *gdwin) { GtkWidget *filesel; FileDialogData *fdd; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *frame; GtkWidget *hbox2; GtkWidget *rbutton; GtkWidget *num_rbutton; GtkWidget *label; /* Create GtkFileSelection dialog */ filesel = gtk_file_selection_new(_(FILE_SEL_CAPTION[FIRST_FILE])); gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(filesel)); gtk_window_set_position(GTK_WINDOW(filesel), GTK_WIN_POS_MOUSE); gdwin->filesel = GTK_FILE_SELECTION(filesel); /* Create FileDialogData as an internal data of file dialog widget */ fdd = filedialogdata_new(gdwin); gtk_object_set_data(GTK_OBJECT(filesel), FILEDIALOGDATA_KEY, fdd); vbox = gtk_vbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(filesel)->action_area), vbox, FALSE, FALSE, 0); hbox = gtk_hbox_new(TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); /* Checkbutton to specify directory selection */ frame = gtk_frame_new(_("Compare")); gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, FALSE, 0); hbox2 = gtk_hbox_new(TRUE, 0); gtk_container_add(GTK_CONTAINER(frame), hbox2); rbutton = gtk_radio_button_new_with_label(NULL, _("File")); fdd->file_button = GTK_TOGGLE_BUTTON(rbutton); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rbutton), TRUE); gtk_box_pack_start(GTK_BOX(hbox2), rbutton, TRUE, TRUE, 0); rbutton = gtk_radio_button_new_with_label( gtk_radio_button_group(GTK_RADIO_BUTTON(rbutton)), _("Directory")); gtk_box_pack_start(GTK_BOX(hbox2), rbutton, TRUE, TRUE, 0); fdd->dir_button = GTK_TOGGLE_BUTTON(rbutton); gtk_signal_connect(GTK_OBJECT(rbutton), "toggled", GTK_SIGNAL_FUNC(dir_toggle), filesel); /* Checkbutton to specify the number of files */ frame = gtk_frame_new(_("Number")); gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, FALSE, 0); hbox2 = gtk_hbox_new(TRUE, 0); gtk_container_add(GTK_CONTAINER(frame), hbox2); num_rbutton = gtk_radio_button_new_with_label(NULL, _("Two")); fdd->diff2_button = GTK_TOGGLE_BUTTON(num_rbutton); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(num_rbutton), TRUE); gtk_box_pack_start(GTK_BOX(hbox2), num_rbutton, TRUE, TRUE, 0); num_rbutton = gtk_radio_button_new_with_label( gtk_radio_button_group(GTK_RADIO_BUTTON(num_rbutton)), _("Three")); gtk_box_pack_start(GTK_BOX(hbox2), num_rbutton, TRUE, TRUE, 0); fdd->diff3_button = GTK_TOGGLE_BUTTON(num_rbutton); gtk_signal_connect(GTK_OBJECT(num_rbutton), "toggled", GTK_SIGNAL_FUNC(diff3_toggle), filesel); /* Label to show file names, while you're selecting remaining files. */ label = gtk_label_new(""); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); fdd->fname_label[FIRST_FILE] = GTK_LABEL(label); label = gtk_label_new(""); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); fdd->fname_label[SECOND_FILE] = GTK_LABEL(label); gtk_signal_connect(GTK_OBJECT(filesel), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &gdwin->filesel); gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked", GTK_SIGNAL_FUNC(file_selection_ok), filesel); gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button), "clicked", GTK_SIGNAL_FUNC(file_selection_cancel), filesel); gtk_widget_show_all(filesel); } /** * file_selection_ok: * The behavior depends on the internal state. * If you need to select more files, change its state. * If you selected whole files, do the job. **/ static void file_selection_ok(GtkWidget *w, gpointer data) { GtkFileSelection *fs = data; FileDialogData *fdd; const char *fname; FileType ft; fdd = gtk_object_get_data(GTK_OBJECT(fs), FILEDIALOGDATA_KEY); fname = gtk_file_selection_get_filename(fs); ft = check_filetype(fname); if (ft == OTHERFILE) { g_message(_("Wrong file type\n")); return; } else if (ft == REGULARFILE && fdd->is_dir) { g_message(_("You have to select a directory. Or don't check the option button.\n")); return; } else if (ft == DIRECTORY && !(fdd->is_dir)) { g_message(_("You have to select a (regular)file. Or check the option button.\n")); return; } if (fdd->file_count != fdd->num_files - 1) { /* implictly, fdd->file_count is incremented */ filedialogdata_addfile(fdd, fdd->file_count, fname); gtk_window_set_title(GTK_WINDOW(fs), _(FILE_SEL_CAPTION[fdd->file_count])); gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), ""); } else { filedialogdata_do_job(fdd, fname); } } /** * file_selection_cancel: * Reset all internal states. **/ static void file_selection_cancel(GtkWidget *w, gpointer data) { GtkFileSelection *fs = data; FileDialogData *fdd; fdd = gtk_object_get_data(GTK_OBJECT(fs), FILEDIALOGDATA_KEY); filedialogdata_reset(fdd); gtk_widget_hide(GTK_WIDGET(fs)); } /** * dir_toggle: * Select comparing directories. * If a user has already selected the first one, don't change the mode. **/ static void dir_toggle(GtkToggleButton *w, gpointer data) { GtkFileSelection *fs = data; FileDialogData *fdd; fdd = gtk_object_get_data(GTK_OBJECT(fs), FILEDIALOGDATA_KEY); if (gtk_toggle_button_get_active(w)) {/* Directory */ if (fdd->num_files == 3) {/* current limitation */ fdd->num_files = 2; gtk_toggle_button_set_active(fdd->diff2_button, TRUE); } if (fdd->file_count == FIRST_FILE) { fdd->is_dir = TRUE; gtk_widget_set_sensitive(fs->file_list, FALSE); } else /* Don't change */ w->active = !w->active;/* Is this a proper way ?*/ } else {/* File */ fdd->is_dir = FALSE; gtk_widget_set_sensitive(fs->file_list, TRUE); } } static void diff3_toggle(GtkToggleButton *w, gpointer data) { GtkFileSelection *fs = data; FileDialogData *fdd; fdd = gtk_object_get_data(GTK_OBJECT(fs), FILEDIALOGDATA_KEY); if (gtk_toggle_button_get_active(w)) {/* diff3 */ if (fdd->is_dir) {/* current limitaion */ fdd->is_dir = FALSE; gtk_toggle_button_set_active(fdd->file_button, TRUE); w->active = !w->active;/* Don't change */ return; } fdd->num_files = 3; } else { /* diff */ if (fdd->file_count == THIRD_FILE) filedialogdata_do_job(fdd, NULL); else fdd->num_files = 2; } } /* Routines for dialog box's own private data, i.e. FileDialogData */ /** * filedialogdata_new: * Allocate FileDialogData, intialize it and return its pointer. * Input: * GDiffWindow *gdwin; kept by FileDialogData. * Output: * Return value; allocated FileDialogData pointer. **/ static FileDialogData* filedialogdata_new(GDiffWindow *gdwin) { FileDialogData *fdd; fdd = g_new(FileDialogData, 1); fdd->gdwin = gdwin; fdd->num_files = 2; fdd->is_dir = FALSE; fdd->file_count = FIRST_FILE; fdd->fname_label[FIRST_FILE] = NULL; fdd->fname_label[SECOND_FILE] = NULL; return fdd; } /** * filedialogdata_reset: * Reset FileDialogData. * Called when a user closes a dialog while he has selected some files, * but hasn't selected whole files. **/ static void filedialogdata_reset(FileDialogData *fdd) { gtk_widget_hide(GTK_WIDGET(fdd->fname_label[FIRST_FILE])); gtk_widget_hide(GTK_WIDGET(fdd->fname_label[SECOND_FILE])); fdd->file_count = FIRST_FILE; } /** * filedialogdata_addfile: * Add a file to FileDialogData. * Called when a user selects a file. **/ static void filedialogdata_addfile(FileDialogData *fdd, WhichFile whichfile, const char *fname) { gtk_label_set_text(fdd->fname_label[whichfile], fname); gtk_widget_show(GTK_WIDGET(fdd->fname_label[whichfile])); fdd->file_count++; } /** * filedialogdata_do_job: * Called when you selected whole files. * If you have selected two files and change from diff3 to diff, @last_fname is NULL. **/ static void filedialogdata_do_job(FileDialogData *fdd, const char *last_fname) { GDiffWindow *gdwin; GtkWidget *new_view = NULL; DiffDir *diffdir; const char *fnames[MAX_NUM_COMPARE_FILES]; gdwin = fdd->gdwin; gtk_label_get(fdd->fname_label[FIRST_FILE], (gchar**)&fnames[FIRST_FILE]); if (fdd->num_files == 2 || last_fname == NULL) { if (last_fname) fnames[SECOND_FILE] = last_fname; else gtk_label_get(fdd->fname_label[SECOND_FILE], (gchar**)&fnames[SECOND_FILE]); diffdir = diffdir_new(fnames[FIRST_FILE], fnames[SECOND_FILE], NULL, g_pref.diff_args); } else { gtk_label_get(fdd->fname_label[SECOND_FILE], (gchar**)&fnames[SECOND_FILE]); fnames[THIRD_FILE] = last_fname; diffdir = diffdir_new(fnames[FIRST_FILE], fnames[SECOND_FILE], fnames[THIRD_FILE], g_pref.diff3_args); } if (fdd->is_dir) { g_assert(fdd->num_files != 3); /* Current limitation */ new_view = gdiff_dirview_new(diffdir); } else { DiffFiles *dfiles = g_slist_nth_data(diffdir->dfiles_list, 0); if (g_pref.fvpref.view_type & ONEPANE_MASK_VIEW) new_view = gdiff_onepview_new(diffdir, dfiles, FALSE); else if (g_pref.fvpref.view_type & MULTIPANE_MASK_VIEW) new_view = gdiff_multipview_new(diffdir, dfiles, FALSE); else if (g_pref.fvpref.view_type & MERGE_MASK_VIEW) new_view = gdiff_mergeview_new(diffdir, dfiles, FALSE); else g_assert_not_reached(); } filedialogdata_reset(fdd); gtk_widget_hide(GTK_WIDGET(fdd->gdwin->filesel));/* ugly */ /* Do the job */ gdwin_add_view(gdwin, new_view); }