/* $Id$ Copyright (C) 2006-2009 tooar This file is part of emelFM2. emelFM2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. emelFM2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with emelFM2; see the file GPL. If not, contact the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ //#define STANDALONE #ifdef STANDALONE # ifndef INCLUDED_IN_PARENT # define INCLUDED_IN_PARENT # endif #endif #ifdef INCLUDED_IN_PARENT #include "emelfm2.h" //#ifdef E2_VFS #include #include "e2_dialog.h" #include "e2_fileview.h" #include "e2_filelist.h" #ifdef STAHDALONE # include "e2_fs.h" # include "e2_vfs.h" #endif // data struct for single-type dialog typedef struct _E2_VFSDialogRuntime { GtkWidget *dialog; //the created dialog GtkWidget *protocol_combo; GtkWidget *hostentry; //entry for hostname GtkWidget *direntry; //entry for initial directory of host GtkWidget *userentry; //entry for username GtkWidget *pwentry; //entry for password GtkWidget *portentry; //entry for port GtkWidget *aliasentry; //entry for short-name GtkListStore *backupstore; //in case the user Escapes out of the dialog ViewInfo *view; E2_VFSDialogType type; //dialog type-enumerator gint hist_index; //row index in history store; } E2_VFSDialogRuntime; typedef struct { GtkWidget *dialog; GtkTreeSelection *selection; //the dialog view selection // GtkWidget *cell_entry; //set to cell entry when esiting starts GtkListStore *protocolstore; //store for protocol combo-cell-renderer data GtkListStore *backupstore; //in case the user Escapes out of the dialog ViewInfo *view; } E2_VFSHistoryDialogRuntime; #ifdef STANDALONE gchar *remote_protocols[]; gchar *archive_protocols[]; GList *remote_history; GList *arch_history; #ifdef E2_VFS_COLLECTIONS GList *synth_history; #endif #endif /** @brief backup protocol combo history when closing the dialog @param combo widget which selects the protocol @param type enumerator of which type of dialog and history @return */ static void _e2_vfs_dialog_save_proto_history (GtkWidget *combo, guint type) { GList *newlist = NULL; GList **oldlist; GtkTreeIter iter; GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(combo)); if (gtk_tree_model_get_iter_first (model, &iter)) { gchar *protoname; do { gtk_tree_model_get (model, &iter, 0, protoname); newlist = g_list_append (newlist, protoname); } while (gtk_tree_model_iter_next (model, &iter)); } switch (type) { case REMOTE_DLG: oldlist = &remote_history; break; case ARCH_DLG: oldlist = &arch_history; break; #ifdef E2_VFS_COLLECTIONS case SYNTH_DLG: oldlist = &synth_history; #endif default: oldlist = NULL; break; } if (oldlist != NULL) { e2_list_free_with_data (oldlist); *oldlist = newlist; } } /********** single-type dialog functions **********/ /*********************/ /***** utilities *****/ /*********************/ /** @brief save dialog data in vfs history store @param iter pointer to data for the store row with data to update, or NULL to append @param rt data struct for the dialog @return */ static void _e2_vfs_dialog_store_data (GtkTreeIter *iter, E2_VFSDialogRuntime *rt) { gboolean freep; const gchar *data[VFSDISPLAY]; data[VFSPROTONAME] = gtk_combo_box_get_active_text (GTK_COMBO_BOX (rt->protocol_combo)); if (data[VFSPROTONAME] == NULL) { data[VFSPROTONAME] = ""; freep = FALSE; } else freep = TRUE; data[VFSHOSTNAME] = (rt->hostentry != NULL) ? gtk_entry_get_text (GTK_ENTRY (rt->hostentry)) : ""; data[VFSPATH] = gtk_entry_get_text (GTK_ENTRY (rt->direntry)); data[VFSUSER] = (rt->userentry != NULL) ? gtk_entry_get_text (GTK_ENTRY (rt->userentry)) : ""; data[VFSPASSWORD] = gtk_entry_get_text (GTK_ENTRY (rt->pwentry)); data[VFSPORT] = (rt->portentry != NULL) ? gtk_entry_get_text (GTK_ENTRY (rt->portentry)) : ""; data[VFSALIAS] = gtk_entry_get_text (GTK_ENTRY (rt->aliasentry)); _e2_vfs_store_history_data (iter, data); if (freep) g_free ((gchar *)data[VFSPROTONAME]); } /** @brief set all displayed dialog data from data store @param mdl the data model to interrogate @param iter pointer to the row with data to use @param rt data struct for the dialog @return */ static void _e2_vfs_dialog_set_data (GtkTreeModel *mdl, GtkTreeIter *iter, E2_VFSDialogRuntime *rt) { gchar *host, *path, *user, *password, *port, *alias; gtk_tree_model_get (mdl, iter, VFSHOSTNAME, &host, VFSPATH, &path, VFSUSER, &user, VFSPASSWORD, &password, //plaintext VFSPORT, &port, //string VFSALIAS, &alias, -1); //update the entries that exist for this dialog if (rt->hostentry != NULL) gtk_entry_set_text (GTK_ENTRY (rt->hostentry), host); gtk_entry_set_text (GTK_ENTRY (rt->direntry), path); if (rt->userentry != NULL) gtk_entry_set_text (GTK_ENTRY (rt->userentry), user); if (rt->pwentry != NULL) gtk_entry_set_text (GTK_ENTRY (rt->pwentry), password); if (rt->portentry != NULL) gtk_entry_set_text (GTK_ENTRY (rt->portentry), port); gtk_entry_set_text (GTK_ENTRY (rt->aliasentry), alias); g_free (host); g_free (path); g_free (user); g_free (password); g_free (port); g_free (alias); } /** @brief cleanup after dialog @param rt pointer to dialog data struct @return */ static void _e2_vfs_dialog_clean (E2_VFSDialogRuntime *rt) { if (GTK_IS_DIALOG(rt->dialog)) //not explicitly closed by the user gtk_widget_destroy (rt->dialog); DEALLOCATE (E2_VFSDialogRuntime, rt); } /*******************/ /**** callbacks ****/ /*******************/ /** @brief edit dialog protocol change callback This changes widget sensitivies according to protocol, and ... @param combo_box the combobox where the response was triggered @param rt data struct for the dialog @return */ static void _e2_vfs_proto_change_cb (GtkComboBox *combo_box, E2_VFSDialogRuntime *rt) { gchar *protname = gtk_combo_box_get_active_text (combo_box); if (protname == NULL) return; E2_VFSProtocol proto = 0; //FIXME match it switch (rt->type) { case REMOTE_DLG: switch (proto) { default: break; } break; case ARCH_DLG: proto += E2_VPROTO_MINARCHIVE; switch (proto) { case E2_VPROTO_ZIP: //FIXME make this work with dynamic protocol history // case E2_VPROTO_7Z: // case E2_VPROTO_ARJ: // case E2_VPROTO_RAR: gtk_widget_set_sensitive (rt->pwentry, TRUE); break; default: gtk_entry_set_visibility (GTK_ENTRY (rt->pwentry), FALSE); gtk_widget_set_sensitive (rt->pwentry, FALSE); break; } break; #ifdef E2_VFS_COLLECTIONS case SYNTH_DLG: proto += E2_VPROTO_MINSYNTH; switch (proto) { default: break; } break; #endif } /* //FIXME change widget sensitivity if () { gtk_widget_set_sensitive (rt->hostentry, FALSE); } if () { gtk_widget_set_sensitive (rt->direntry, FALSE); } if () { gtk_widget_set_sensitive (rt->userentry, FALSE); } if () { gtk_widget_set_sensitive (rt->portentry, FALSE); } if (!rt->remote) { gchar *ext = archive_protocols[index]; const gchar *path = gtk_entry_get_text (GTK_ENTRY (rt->direntry)); if (strstr (path, ext) == NULL) { / *FIXME this deletes the initial text at dialog start gint start, end; if (gtk_editable_get_selection_bounds (GTK_EDITABLE (rt->direntry), &start, &end)) { gtk_editable_delete_text (GTK_EDITABLE (rt->direntry), start, end); gtk_editable_insert_text (GTK_EDITABLE (rt->direntry), ext, strlen (ext), &start); } else * / { gchar *newtxt = g_strconcat (path, ext, NULL); gtk_entry_set_text (GTK_ENTRY (rt->direntry), newtxt); g_free (newtxt); } } } */ } /** @brief "key-press" signal callback to ensure that text entered in protocol combo is valid Only lower-case alphanumerics are allowed @param entry the entry widget for protocol name @param event pointer to event data struct @param user_data UNUSED pointer specified when callback was connected @return TRUE if the text was manipulated and entered, or ignored */ static gboolean _e2_vfs_dialog_protokey_press_cb (GtkWidget *entry, GdkEventKey *event, gpointer user_data) { //compare this to tabular dialog, where protocol is cleaned up after editing is finished #ifdef USE_GTK2_10 if (event->is_modifier) #else if (e2_utils_key_is_modifier (event)) #endif return FALSE; //keep waiting if (event->keyval > 0xEFFF && event->keyval < 0x10000) return FALSE; //don't interfere with non-text chars gunichar uc = gdk_keyval_to_unicode (event->keyval); //ignore invalid chars if (g_unichar_isalnum (uc)) { if (g_unichar_islower (uc)) return FALSE; //allow it to be handled normally uc = g_unichar_tolower (uc); //convert to utf8 gchar outbuf[8]; gint utflen = g_unichar_to_utf8 (uc, outbuf); *(outbuf + utflen) = '\0'; //insert at cursor gint position = gtk_editable_get_position (GTK_EDITABLE (entry)); gtk_editable_insert_text (GTK_EDITABLE (entry), outbuf, utflen, &position); } return TRUE; } /** @brief handle v keypresses in the password-input entry @param entry the entry widget for password data @param event pointer to event data struct @param rt pointer to dialog data struct @return TRUE if the keypress was v */ static gboolean _e2_vfs_dialog_pwkey_press_cb (GtkWidget *entry, GdkEventKey *event, E2_VFSDialogRuntime *rt) { guint modifiers = gtk_accelerator_get_default_mod_mask (); if ((event->state & modifiers) == GDK_MOD1_MASK) { if (event->keyval == GDK_v) { gboolean state = gtk_entry_get_visibility (GTK_ENTRY (rt->pwentry)); gtk_entry_set_visibility (GTK_ENTRY (rt->pwentry), !state); return TRUE; } } return FALSE; } /** @brief adjust directory string in @a entry After a keypress, this clears any selection and completes the path. If the current content is not an absolute path, the active-pane directory is referenced for completion. Completion is local only, so this is not relevant for remote completion @param entry the entry widget for directory data @param event pointer to event data struct @param rt UNUSED pointer to dialog data struct @return TRUE if the key was non-modified and < 0xF000 */ static gboolean _e2_vfs_dialog_pathkey_press_cb (GtkWidget *entry, GdkEventKey *event, E2_VFSDialogRuntime *rt) { if ((event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) == 0 && (event->keyval < 0xF000 || event->keyval > 0xFFFF)) { //work around gtk's failure to replace selected text gint start, end; if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end)) { gtk_editable_delete_text (GTK_EDITABLE (entry), start, end); gtk_editable_set_position (GTK_EDITABLE (entry), start); } e2_fs_complete_dir (entry, event->keyval, 0); //default is active pane return TRUE; } return FALSE; } /** @brief dialog response callback @param dialog the dialog where the response was triggered @param response the number assigned the activated widget @param rt data struct for the dialog @return */ static void _e2_vfs_dialog_response_cb (GtkDialog *dialog, gint response, E2_VFSDialogRuntime *rt) { gint hist; gint index; const gchar *value; gchar *used, *local; GtkTreeIter iter; switch (response) { case E2_RESPONSE_USER1: //back button-click //get prior entry with same protocol from this-pane history list, update dialog fields hist = rt->hist_index; if (hist < 1) //invalid (-1) or at start of list break; if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (vfs.vtab), &iter, NULL, hist)) { index = 0; gchar *protoname, *curr; gtk_tree_model_get (GTK_TREE_MODEL (vfs.vtab), &iter, VFSPROTONAME, &protoname, -1); //FIXME scan pane history, not the whole store while (e2_tree_iter_previous (GTK_TREE_MODEL (vfs.vtab), &iter)) { index++; gtk_tree_model_get (GTK_TREE_MODEL (vfs.vtab), &iter, VFSPROTONAME, &curr, -1); if (g_str_equal (curr, protoname)) { //replace all data in the dialog _e2_vfs_dialog_set_data (GTK_TREE_MODEL (vfs.vtab), &iter, rt); rt->hist_index -= index; g_free (curr); break; } g_free (curr); } g_free (protoname); } switch (rt->type) { case REMOTE_DLG: gtk_widget_grab_focus (rt->hostentry); break; case ARCH_DLG: gtk_widget_grab_focus (rt->direntry); break; #ifdef E2_VFS_COLLECTIONS case SYNTH_DLG: // gtk_widget_grab_focus (rt->direntry); #endif default: break; } break; case E2_RESPONSE_USER2: //forward button-click //get next entry with same protocol from this-pane history list, update dialog fields hist = rt->hist_index; if (hist == -1) //invalid index is not welcome here ... { if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (vfs.vtab), &iter)) { _e2_vfs_dialog_set_data (GTK_TREE_MODEL (vfs.vtab), &iter, rt); hist = 0; break; } } if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (vfs.vtab), &iter, NULL, hist)) { index = 0; gchar *protoname, *curr; gtk_tree_model_get (GTK_TREE_MODEL (vfs.vtab), &iter, VFSPROTONAME, &protoname, -1); //FIXME scan pane history, not the whole store while (gtk_tree_model_iter_next (GTK_TREE_MODEL (vfs.vtab), &iter)) { index++; gtk_tree_model_get (GTK_TREE_MODEL (vfs.vtab), &iter, VFSPROTONAME, &curr, -1); if (g_str_equal (curr, protoname)) { //replace all data in the dialog _e2_vfs_dialog_set_data (GTK_TREE_MODEL (vfs.vtab), &iter, rt); rt->hist_index += index; g_free (curr); break; } g_free (curr); } g_free (protoname); } switch (rt->type) { case REMOTE_DLG: gtk_widget_grab_focus (rt->hostentry); break; case ARCH_DLG: gtk_widget_grab_focus (rt->direntry); break; #ifdef E2_VFS_COLLECTIONS case SYNTH_DLG: // gtk_widget_grab_focus (rt->direntry); #endif default: break; } break; case GTK_RESPONSE_REJECT: //open local gtk_widget_hide (rt->dialog); gtk_list_store_clear (rt->backupstore); g_object_unref (G_OBJECT (rt->backupstore)); //backup protocol store into type-specific lists //FIXME _e2_vfs_protohistory_update (rt->protocolstore); //update all vptrs to reflect any change in store contents _e2_vfs_update_mtablist (); //update history lists to reflect any change in store contents _e2_vfs_clean_placehistories (); //E2_VFSTMP //FIXME update toggle-btn data e2_vfs_set_space (rt->view, NULL, TRUE); _e2_vfs_dialog_clean (rt); break; case GTK_RESPONSE_ACCEPT: //open place case E2_RESPONSE_USER3: //store without opening //a little validation of entered data switch (rt->type) { gint len; case REMOTE_DLG: //dir = / if empty value = gtk_entry_get_text (GTK_ENTRY (rt->direntry)); if (!g_path_is_absolute (value)) used = g_strconcat (G_DIR_SEPARATOR_S, value, NULL); else used = g_strdup (value); //make sure the user didn't enter a 'dirty' path string used = e2_utils_path_clean (used); //strip trailer len = strlen (used); if (len > 1) *(used + len - 1) = '\0'; gtk_entry_set_text (GTK_ENTRY (rt->direntry), used); g_free (used); //password for secure protocols ?? //default port value supplied when site opened //default email supplied when site opened //non-empty hostname value = gtk_entry_get_text (GTK_ENTRY (rt->hostentry)); if (*value == '\0') //FIXME prevent code following switch e2_output_print_error (_("You must specify the site to open"), FALSE); break; case ARCH_DLG: value = gtk_entry_get_text (GTK_ENTRY (rt->direntry)); //FIXME check protocol is consistent with name (append protocol if needed) if (!g_path_is_absolute (value)) #ifdef E2_VFSTMP //FIXME dir when not mounted local #else used = g_strconcat (curr_view->dir, value, NULL); #endif else used = g_strdup (value); //make sure the user didn't enter a 'dirty' path string used = e2_utils_path_clean (used); //strip trailer len = strlen (used); if (len > 1) *(used + len - 1) = '\0'; gtk_entry_set_text (GTK_ENTRY (rt->direntry), used); if (response == GTK_RESPONSE_ACCEPT) { local = F_FILENAME_TO_LOCALE (used); E2_ERR_DECLARE #ifdef E2_VFSTMP //FIXME correct spacedata #endif VPATH ddata = { local, NULL } ; if (e2_fs_access (&ddata, R_OK E2_ERR_PTR())) { #ifdef E2_VFSTMP //FIXME handle vfs error E2_ERR_CLEAR #endif gchar *msg = g_strdup_printf (_("Cannot open '%s'"), used); e2_output_print_error (msg, TRUE); //FIXME prevent code following switch } F_FREE (local, used); } g_free (used); break; #ifdef E2_VFS_COLLECTIONS case SYNTH_DLG: break; #endif } if (1) //validation process succeeded { //save data if appropriate //FIXME decide when to use, replace, append //CHECKME limit store row count ? _e2_vfs_dialog_store_data (NULL, rt); if (response == GTK_RESPONSE_ACCEPT) { //if added //need dialog data for a while yet, but not in our face ... gtk_widget_hide (rt->dialog); //finishes with backup gtk_list_store_clear (rt->backupstore); g_object_unref (G_OBJECT (rt->backupstore)); rt->hist_index = gtk_tree_model_iter_n_children ( GTK_TREE_MODEL (vfs.vtab), NULL) - 1; #ifdef E2_VFSTMP PlaceInfo *spacedata = ; CHECKME any backup for vptrs removed from vfs.vmtab FIXME create & list in vfs.vmtab a vptr for place/dir associated with tpath, if N/A now //backup protocol store into type-specific list //FIXME _e2_vfs_protohistory_update (rt->protocolstore); //update all vptrs to reflect any change in store contents _e2_vfs_update_mtablist (); //update history lists to reflect any change in store contents _e2_vfs_clean_placehistories (); //E2_VFSTMP //FIXME update toggle-btn data //open the place/dir for the vptr e2_vfs_set_space (rt->view, spacedata, TRUE); #endif _e2_vfs_dialog_clean (rt); } } break; case GTK_RESPONSE_CLOSE: gtk_list_store_clear (rt->backupstore); g_object_unref (G_OBJECT (rt->backupstore)); break; default: //apply backup store in case any change was made gtk_list_store_clear (vfs.vtab); g_object_unref (G_OBJECT (vfs.vtab)); //E2_VFSTMP fix any treerowrefs in vptrs vfs.vtab = rt->backupstore; break; } } /** @brief create vfs data dialog, for one category or places This expects BGL on/closed, and filelist refreshing disabled @param view data structure for the view to which the file list belongs @return */ void e2_vfs_dialog_create (E2_VFSDialogType type, ViewInfo *view) { E2_VFSDialogRuntime rt; if (vfs.vtab == NULL) //should never fail _e2_vfs_create_history_store (); //backup data in case user cancels e2_tree_store_copy (GTK_TREE_MODEL (vfs.vtab), FALSE, (gpointer *)&rt.backupstore); rt.view = view; rt.type = type; rt.dialog = e2_dialog_create (NULL, NULL, _("vfs data"), _e2_vfs_dialog_response_cb, &rt); //work with copy of index until the dir is actually refreshed //FIXME rt.hist_index = (remote) ? view->remotevfs_curr : view->archvfs_curr; // if (rt.hist_index == -1) rt.hist_index = 0; //don't bother to find the real first compatible protocol ... GtkSizeGroup *sgrp = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); GtkWidget *hbox, *dialog_vbox, *label; gchar *text = NULL; //warning prevention #ifdef USE_GTK2_14 dialog_vbox = gtk_dialog_get_content_area (GTK_DIALOG (rt.dialog)); #else dialog_vbox = GTK_DIALOG (rt.dialog)->vbox; #endif hbox = e2_widget_add_box (dialog_vbox, TRUE, E2_PADDING, FALSE, FALSE, E2_PADDING); label = e2_widget_add_mid_label (hbox, _("alias"), 0.0, FALSE, E2_PADDING); gtk_size_group_add_widget (sgrp, label); rt.aliasentry = e2_widget_add_entry (hbox, "", FALSE, FALSE); gtk_widget_set_size_request (rt.aliasentry, 180, -1); #ifdef USE_GTK2_12TIPS gtk_widget_set_tooltip_text ( #else e2_widget_set_tooltip (NULL, #endif rt.aliasentry, _("You may provide a short-name for use in labels and matching")); #ifdef E2_ASSISTED e2_widget_set_label_relations (label, rt.aliasentry); #endif //setup protocol combo box hbox = e2_widget_add_box (dialog_vbox, TRUE, E2_PADDING, FALSE, FALSE, E2_PADDING); gint wide = 0; //warning prevention GList *usehist = NULL; switch (rt.type) { case REMOTE_DLG: text = _("protocol"); wide = 80; usehist = remote_history; break; case ARCH_DLG: text = _("type"); wide = 100; usehist = arch_history; break; #ifdef E2_VFS_COLLECTIONS case SYNTH_DLG: text = _("type"); //FIXME wide = 100; usehist = synth_history; break; #endif } label = e2_widget_add_mid_label (hbox, text, 0.0, FALSE, E2_PADDING); gtk_size_group_add_widget (sgrp, label); /* rt.protocol_combo = e2_combobox_get (NULL, NULL, NULL, E2_COMBOBOX_MENU_STYLE); gtk_box_pack_start (GTK_BOX (hbox), rt.protocol_combo, FALSE, FALSE, 0); */ E2_ComboBoxFlags flags = E2_COMBOBOX_MENU_STYLE; if (usehist == NULL || g_list_length (usehist) == 0) flags |= E2_COMBOBOX_HAS_ENTRY; rt.protocol_combo = e2_combobox_add (hbox, FALSE, E2_PADDING, NULL, NULL, NULL, flags); e2_combobox_append_history (rt.protocol_combo, usehist); gtk_widget_set_size_request (rt.protocol_combo, wide, -1); GTK_WIDGET_SHOW (rt.protocol_combo); //connect to key-press, so we can correct mistakes g_signal_connect (G_OBJECT (GTK_BIN (rt.protocol_combo)->child), "key-press-event", G_CALLBACK (_e2_vfs_dialog_protokey_press_cb), NULL); //connect to changed signal, so we can adjust sensitivities g_signal_connect (G_OBJECT (rt.protocol_combo), "changed", G_CALLBACK (_e2_vfs_proto_change_cb), &rt); //setup host name hbox = e2_widget_add_box (dialog_vbox, TRUE, E2_PADDING, FALSE, FALSE, E2_PADDING); text = (rt.type == REMOTE_DLG) ? _("host name") : _("parent identifier"); label = e2_widget_add_mid_label (hbox, text, 0.0, FALSE, E2_PADDING); gtk_size_group_add_widget (sgrp, label); //init hostname from history, later rt.hostentry = e2_widget_add_entry (hbox, "", TRUE, FALSE); text = (rt.type == REMOTE_DLG) ? _("Domain name of the site e.g. your.computer.net"): _("Full URI for the parent space, if any"); #ifdef USE_GTK2_12TIPS gtk_widget_set_tooltip_text ( #else e2_widget_set_tooltip (NULL, #endif rt.hostentry, text); #ifdef E2_ASSISTED e2_widget_set_label_relations (label, rt.hostentry); #endif //setup startup directory or archive path hbox = e2_widget_add_box (dialog_vbox, TRUE, E2_PADDING, FALSE, FALSE, E2_PADDING); #ifdef E2_VFSTMP what is relevance of initial remote directory, when cacheing happens ? override cached value if not empty #endif switch (rt.type) { case ARCH_DLG: text = _("path to archive"); break; case REMOTE_DLG: #ifdef E2_VFS_COLLECTIONS case SYNTH_DLG: #endif text = _("initial directory"); #ifndef E2_VFS_COLLECTIONS default: #endif break; /*#ifdef E2_VFS_COLLECTIONS case SYNTH_DLG: text = _("initial directory"); //"FIXME"; break; #endif */ } label = e2_widget_add_mid_label (hbox, text, 0.0, FALSE, E2_PADDING); gtk_size_group_add_widget (sgrp, label); E2_VFSProtocol index; if (rt.type == REMOTE_DLG) { //get last history values later text = ""; index = E2_VPROTO_FTP; //FIXME } else #ifdef E2_VFS_COLLECTIONS if (rt.type == ARCH_DLG) #endif { index = E2_VPROTO_INVALID; //best if refreshing disabled so info is not disrupted FileInfo *info = e2_fileview_get_selected_first_local (curr_view, FALSE); if (info != NULL) index = _e2_vfs_get_protonum_for_file (info->filename); if (index != E2_VPROTO_INVALID) { gchar *utf = F_FILENAME_FROM_LOCALE (info->filename); #ifdef E2_VFSTMP //FIXME dir when not mounted local #else text = g_strconcat (curr_view->dir, utf, NULL); #endif F_FREE (utf, info->filename); index -= E2_VPROTO_MINARCHIVE; //get back to 0-base } else { //suffix is appended when protocol is set at end of setup #ifdef E2_VFSTMP //FIXME dir when not mounted local #else text = g_strconcat (curr_view->dir, "<*>.", NULL); #endif index = 0; } } #ifdef E2_VFS_COLLECTIONS else { index = 0; //FIXME text = ""; } #endif rt.direntry = e2_widget_add_entry (hbox, text, TRUE, FALSE); if (*text != '\0') g_free (text); switch (rt.type) { case REMOTE_DLG: text = _("Absolute path of directory at the site to be initially displayed"); break; case ARCH_DLG: text = _("Absolute path of archive in this space, including archoive's full name"); break; #ifdef E2_VFS_COLLECTIONS case SYNTH_DLG: text = _("Path to be initially displayed, if any"); break; #endif } #ifdef USE_GTK2_12TIPS gtk_widget_set_tooltip_text ( #else e2_widget_set_tooltip (NULL, #endif rt.direntry, text); #ifdef E2_ASSISTED e2_widget_set_label_relations (label, rt.direntry); #endif if (rt.type != REMOTE_DLG) //need to interpret keypresses for completion CHECKME remote too ? g_signal_connect (G_OBJECT (rt.direntry), "key-press-event", G_CALLBACK (_e2_vfs_dialog_pathkey_press_cb), &rt); if (rt.type == REMOTE_DLG) { //setup username for remote sites //FIXME archive could be in remote place hbox = e2_widget_add_box (dialog_vbox, TRUE, E2_PADDING, FALSE, FALSE, E2_PADDING); label = e2_widget_add_mid_label (hbox, _("user"), 0.0, FALSE, E2_PADDING); gtk_size_group_add_widget (sgrp, label); //init name from history later rt.userentry = e2_widget_add_entry (hbox, "", FALSE, FALSE); gtk_widget_set_size_request (rt.userentry, 180, -1); #ifdef USE_GTK2_12TIPS gtk_widget_set_tooltip_text ( #else e2_widget_set_tooltip (NULL, #endif rt.userentry, _("Username needed to log on to the specified site, or leave blank for anonymous")); #ifdef E2_ASSISTED e2_widget_set_label_relations (label, rt.userntry); #endif } else rt.userentry = NULL; #ifdef E2_VFS_COLLECTIONS if (rt.type != SYNTH_DLG) { #endif //setup password hbox = e2_widget_add_box (dialog_vbox, TRUE, E2_PADDING, FALSE, FALSE, E2_PADDING); label = e2_widget_add_mid_label (hbox, _("password"), 0.0, FALSE, E2_PADDING); gtk_size_group_add_widget (sgrp, label); //init value from history later rt.pwentry = e2_widget_add_entry (hbox, "", FALSE, FALSE); gtk_widget_set_size_request (rt.pwentry, 180, -1); gtk_entry_set_visibility (GTK_ENTRY (rt.pwentry), FALSE); switch (rt.type) { case REMOTE_DLG: text = _("Password needed to log on to the specified site, or leave blank for email address"); break; case ARCH_DLG: text = _("Password, if any, needed to open the archive"); default: break; } #ifdef USE_GTK2_12TIPS gtk_widget_set_tooltip_text ( #else e2_widget_set_tooltip (NULL, #endif rt.pwentry, text); #ifdef E2_ASSISTED e2_widget_set_label_relations (label, rt.pwentry); #endif //we also need to interpret some password keypresses g_signal_connect (G_OBJECT (rt.pwentry), "key-press-event", G_CALLBACK (_e2_vfs_dialog_pwkey_press_cb), &rt); #ifdef E2_VFS_COLLECTIONS } else rt.pwentry = NULL; #endif //setup port //FIXME archive could be in remote place if (rt.type == REMOTE_DLG) { hbox = e2_widget_add_box (dialog_vbox, TRUE, E2_PADDING, FALSE, FALSE, E2_PADDING); label = e2_widget_add_mid_label (hbox, _("port"), 0.0, FALSE, E2_PADDING); gtk_size_group_add_widget (sgrp, label); //set value from history later rt.portentry = e2_widget_add_entry (hbox, "", FALSE, FALSE); gtk_widget_set_size_request (rt.portentry, 60, -1); #ifdef USE_GTK2_12TIPS gtk_widget_set_tooltip_text ( #else e2_widget_set_tooltip (NULL, #endif rt.portentry, _("Port number to access the specified site, or leave blank for default")); #ifdef E2_ASSISTED e2_widget_set_label_relations (label, rt.portentry); #endif } else rt.portentry = NULL; g_object_unref (G_OBJECT(sgrp)); //now that all widgets are in place, finalise strings, widget sensitivies etc GtkTreeIter iter; GtkTreeModel *mdl = GTK_TREE_MODEL (vfs.vtab); //E2_VFSTMP FIXME find index for this type of dialog if (gtk_tree_model_iter_nth_child (mdl, &iter, NULL, rt.hist_index)) { _e2_vfs_dialog_set_data (mdl, &iter, &rt); gtk_tree_model_get (mdl, &iter, //FIXME VFSPROTO, &index, -1); if (index >= E2_VPROTO_MINARCHIVE) index -= E2_VPROTO_MINARCHIVE; #ifdef E2_VFSTMP FIXME dynamic protocols #endif } else index = -1; gtk_combo_box_set_active (GTK_COMBO_BOX (rt.protocol_combo), index); //now the buttons //FIXME a button to revert to local namespace e2_dialog_add_custom_button_full (rt.dialog, FALSE, E2_RESPONSE_USER1, _("_Previous"), GTK_STOCK_GO_BACK, _("Show data for previously-used place with same protocol/type"), NULL, NULL); e2_dialog_add_custom_button_full (rt.dialog, FALSE, E2_RESPONSE_USER2, _("_Next"), GTK_STOCK_GO_FORWARD, _("Show data for next-used place with same protocol/type"), NULL, NULL); /* e2_dialog_add_custom_button_full (rt.dialog, FALSE, E2_RESPONSE_USER3), _("_Bookmark"), "bookmark"E2ICONTB, _(""), NULL, NULL); */ GtkWidget *btn = e2_dialog_add_custom_button_full (rt.dialog, FALSE, GTK_RESPONSE_REJECT, _("_Local"), "vfs_off"E2ICONTB, _("Revert to native filesystem"), NULL, NULL); if (view->spacedata == NULL) gtk_widget_set_sensitive (btn, FALSE); e2_dialog_add_custom_button_full (rt.dialog, FALSE, E2_RESPONSE_USER3, _("_Save"), GTK_STOCK_SAVE, _("Store this data but do not open the place"), NULL, NULL); e2_dialog_add_defined_button (rt.dialog, &E2_BUTTON_CLOSE); e2_dialog_add_custom_button_full (rt.dialog, FALSE, GTK_RESPONSE_ACCEPT, _("_Open"), GTK_STOCK_OPEN, _("Open this place"), NULL, NULL); e2_dialog_run (rt.dialog, NULL, 0); // if (rt.type == REMOTE_DLG) // gtk_widget_grab_focus (rt.hostentry); // else // gtk_widget_grab_focus (rt.direntry); gtk_widget_grab_focus (rt.aliasentry); e2_dialog_wait (rt.dialog, TRUE, TRUE, FALSE, FALSE); //CHECKME TRUE maincontext _e2_vfs_dialog_clean (&rt); } /********************/ /** tabular dialog **/ /********************/ /* TODO change focused iter, if its protocol not in rt->protocolstore, append it with ask-what-type if that's not discoverable backup from rt->protocolstore to protocol history lists when user applies or ok's add "open local" button enable deleting any or all protocol history by some process involving the combo render */ static void _e2_vfs_protohistory_update (GtkListStore *pstore) { GtkTreeIter iter; GtkTreeModel *mdl = GTK_TREE_MODEL (pstore); if (gtk_tree_model_get_iter_first (mdl, &iter)) { gchar *pname; E2_VFSDialogType ptype; GList *anew = NULL; GList *rnew = NULL; #ifdef E2_VFS_COLLECTIONS GList *snew = NULL; #endif do { gtk_tree_model_get (mdl, &iter, 0, &pname, 1, &ptype, -1); switch (ptype) { case ARCH_DLG: anew = g_list_append (anew, pname); break; case REMOTE_DLG: rnew = g_list_append (rnew, pname); break; #ifdef E2_VFS_COLLECTIONS case SYNTH_DLG: snew = g_list_append (snew, pname); #endif default: break; } } while (gtk_tree_model_iter_next (mdl, &iter)); if (anew != NULL) { if (arch_history != NULL) e2_list_free_with_data (&arch_history); arch_history = anew; } if (rnew != NULL) { if (remote_history != NULL) e2_list_free_with_data (&remote_history); remote_history = rnew; } #ifdef E2_VFS_COLLECTIONS if (snew != NULL) { if (synth_history != NULL) e2_list_free_with_data (&synth_history); synth_history = snew; } #endif } } /** @brief change key handling when cell editing starts @param renderer the renderer for the cell @param editable the interface to @a renderer @param path_string string form of gtk tree path to the row to be amended @param user_data UNUSED pointer to data specified when callback was connected @return */ static void _e2_vfs_history_editstart_cb (GtkCellRenderer *cell, GtkCellEditable *editable, gchar *path_string, E2_VFSHistoryDialogRuntime *rt) { // printd (DEBUG, "start cell edit cb"); g_signal_handlers_block_by_func (G_OBJECT (rt->dialog), e2_dialog_key_neg_cb, rt->dialog); //CHECKME - connect a key-press cb to the entry widget, use that to filter stuff //and/or delete history // if (GTK_IS_ENTRY (editable)) // { // rt->cell_entry = GTK_ENTRY (editable); // gtk_entry_set_completion (entry, completion); // } } /** @brief revert key handling when cell editing is finished @param renderer the renderer for the cell @param user_data UNUSED pointer to data specified when callback was connected @return */ static void _e2_vfs_history_editcancel_cb (GtkCellRenderer *cell, E2_VFSHistoryDialogRuntime *rt) { // printd (DEBUG, "cancel cell edit cb"); g_signal_handlers_unblock_by_func (G_OBJECT (rt->dialog), e2_dialog_key_neg_cb, rt->dialog); } /** @brief save edited text value, if it's an acceptable format, in the underlying store @param UNUSED cell the renderer for the cell @param path_string string form of gtk tree path to the row to be amended @param new_text replacement text string for the cell @param rt pointer to dialog data struct @return */ static void _e2_vfs_historyproto_edited_cb (GtkCellRendererText *cell, gchar *path_string, gchar *new_text, E2_VFSHistoryDialogRuntime *rt) { g_signal_handlers_unblock_by_func (G_OBJECT (rt->dialog), e2_dialog_key_neg_cb, rt->dialog); gchar *lc; GtkTreeIter iter; if (new_text == NULL) //this probably can't happen { printd (DEBUG, "protocol edited cb, new text is NULL"); lc = g_strdup (""); } else { printd (DEBUG, "protocol edited cb, new text is %s", new_text); lc = g_strdup (new_text); g_strstrip (lc); if (*lc != '\0') { //validate the new string gchar *p = lc; while (*p != '\0') { gunichar uchr = g_utf8_get_char (p); if (!g_unichar_isalnum (uchr)) //what about unstripped pre- or post- ? { //warn user? bad unstored text is visible in the dialog g_free (lc); //FIXME change the dialog too if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rt->protocolstore), &iter) && !e2_tree_find_iter_from_str_same (GTK_TREE_MODEL (rt->protocolstore), 0, new_text, &iter)) { printd (DEBUG, "protocol edited cb, protocol %s combo cleanup to FIXME", lc); // lc = "FIXME"; //FIXME doesn't change text visible in the combo entry and dialog // gtk_list_store_insert_with_values (rt->protocolstore, &iter, -1, // 0, lc, 1, 4, -1); gtk_list_store_remove (rt->protocolstore, &iter); } return; } p = g_utf8_next_char (p); } p = lc; lc = g_utf8_strdown (p, -1); g_free (p); } } //if new protocol is not identifiable type, ask user what it is E2_VFSDialogType thistype; if ((thistype = _e2_vfs_match_prototype (lc)) == (E2_VFSDialogType) E2_VPROTO_INVALID) { if (_e2_vfs_ask_prototype (lc, &thistype)!= OK) { g_free (lc); return; } // thistype = REMOTE_DLG; } //update underlying data store if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (vfs.vtab), &iter, path_string)) { printd (DEBUG, "protocol edited cb, %s protocol stored", lc); gtk_list_store_set (vfs.vtab, &iter, VFSPROTONAME, lc, VFSEDITED, TRUE, -1); } //update protocol-combo history if (*lc != '\0' && !g_str_equal (new_text, lc)) { if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rt->protocolstore), &iter) && !e2_tree_find_iter_from_str_same (GTK_TREE_MODEL (rt->protocolstore), 0, new_text, &iter)) { printd (DEBUG, "protocol edited cb, protocol %s combo cleanup", lc); //FIXME doesn't change text visible in the combo entry and dialog gtk_list_store_insert_with_values (rt->protocolstore, &iter, -1, 0, lc, 1, 4, -1); } } else if (*lc != '\0' && gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rt->protocolstore), &iter) && !e2_tree_find_iter_from_str_same (GTK_TREE_MODEL (rt->protocolstore), 0, lc, &iter)) { printd (DEBUG, "protocol edited cb, protocol %s added to combo", lc); //log as "type 4" item for history-list-assignment when dialog closes gtk_list_store_insert_with_values (rt->protocolstore, &iter, -1, 0, lc, 1, 4, -1); } g_free (lc); } /** @brief treeview cell edited callback @param renderer the renderer that was used @param path_string string form of path of edited cell @param new_text the replacement content for the edited cell @param col pointerised column no @return */ static void _e2_vfs_historycell_edited_cb (GtkCellRendererText *renderer, gchar *path_string, gchar *new_text, E2_VFSHistoryDialogRuntime *rt) { g_signal_handlers_unblock_by_func (G_OBJECT (rt->dialog), e2_dialog_key_neg_cb, rt->dialog); if (new_text == NULL) return; //the user did not finish editing a new cell GtkTreeIter iter; if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (vfs.vtab), &iter, path_string)) { gpointer col = g_object_get_data (G_OBJECT (renderer), "model_column"); gint colnum = GPOINTER_TO_INT (col); gtk_list_store_set (vfs.vtab, &iter, colnum, new_text, VFSEDITED, TRUE, -1); } } /** @brief process result from vfs history dialog @param dialog the dialog where the response was triggered @param response the response from the dialog @param rt pointer to dialog data struct @return */ static void _e2_vfs_historydlg_response_cb (GtkDialog *dialog, gint response, E2_VFSHistoryDialogRuntime *rt) { gint index; GtkTreeIter iter; GtkTreePath *tpath; GtkTreeModel *model; GtkTreeSelection *sel = rt->selection; switch (response) { case E2_RESPONSE_USER3: //insert after if (gtk_tree_selection_get_selected (sel, &model, &iter)) { tpath = gtk_tree_model_get_path (model, &iter); index = *gtk_tree_path_get_indices (tpath); gtk_tree_path_free (tpath); gtk_tree_selection_unselect_all (sel); GtkTreeIter iter2; gtk_list_store_insert_after (vfs.vtab, &iter2, &iter); iter = iter2; } else gtk_list_store_append (vfs.vtab, &iter); gtk_tree_selection_select_iter (sel, &iter); break; case E2_RESPONSE_REMOVE: if (gtk_tree_selection_get_selected (sel, &model, &iter)) { tpath = gtk_tree_model_get_path (model, &iter); index = *gtk_tree_path_get_indices (tpath); gtk_tree_path_free (tpath); gtk_list_store_remove (vfs.vtab, &iter); } break; case GTK_RESPONSE_OK: //open CHECKME _ACCEPT ? tpath = (gtk_tree_selection_get_selected (sel, &model, &iter)) ? gtk_tree_model_get_path (model, &iter) : NULL; case GTK_RESPONSE_REJECT: //revert to local fs gtk_widget_destroy (rt->dialog); gtk_list_store_clear (rt->backupstore); g_object_unref (G_OBJECT (rt->backupstore)); //backup protocol store into type-specific lists _e2_vfs_protohistory_update (rt->protocolstore); //update all vptrs to reflect any change in store contents _e2_vfs_update_mtablist (); //update history lists to reflect any change in store contents _e2_vfs_clean_placehistories (); //E2_VFSTMP //FIXME update toggle-btn data if (response == E2_RESPONSE_USER1) e2_vfs_set_space (rt->view, NULL, TRUE); else //OK if (tpath != NULL) { #ifdef E2_VFSTMP PlaceInfo *spacedata = ; CHECKME any backup for vptrs removed from vfs.vmtab FIXME create & list in vfs.vmtab a vptr for place/dir associated with tpath, if N/A now //open the place/dir for the vptr e2_vfs_set_space (rt->view, spacedata, TRUE); #endif } DEALLOCATE (E2_VFSHistoryDialogRuntime, rt); break; case E2_RESPONSE_APPLY: //close wihout changing store data gtk_widget_destroy (rt->dialog); gtk_list_store_clear (rt->backupstore); g_object_unref (G_OBJECT (rt->backupstore)); DEALLOCATE (E2_VFSHistoryDialogRuntime, rt); break; default: gtk_widget_destroy (rt->dialog); //??GtkListStore * rt->protocolstore; //FIXME treerowrefs in vptrs, if the store is replaced //in case the store was edited, apply the backup OR check all VFSEDIIED values gtk_list_store_clear (vfs.vtab); g_object_unref (G_OBJECT (vfs.vtab)); vfs.vtab = rt->backupstore; DEALLOCATE (E2_VFSHistoryDialogRuntime, rt); break; } } /** @brief mouse button double-click callback This opens the place described in the clicked row, in the active pane @param treeview the widget where the button was pressed @param path model path to the clicked row @param col UNUSED clicked view column @param view UNUSED rt data for the view to be worked on @return */ static void _e2_vfs_historydlg_activated_cb (GtkTreeView *treeview, GtkTreePath *tpath, GtkTreeViewColumn *column, E2_VFSHistoryDialogRuntime *rt) { GtkTreeIter iter; GtkTreeModel *model = GTK_TREE_MODEL (vfs.vtab); if (gtk_tree_model_get_iter (model, &iter, tpath)) { // gtk_tree_selection_select_iter (rt->selection, &iter); _e2_vfs_historydlg_response_cb (GTK_DIALOG (rt->dialog), GTK_RESPONSE_OK, rt); //CHECKME _ACCEPT // CHECKME ISSUE RESPONSE TO CLOSE DIALOG ? } } /** @brief create tabular dialog Expects BGL closed @param view pointer to data struct for view to change if the user elects to do so @return */ void e2_vfs_create_history_dialog (ViewInfo *view) { E2_VFSHistoryDialogRuntime *rt = ALLOCATE (E2_VFSHistoryDialogRuntime); CHECKALLOCATEDWARN (rt, return); if (vfs.vtab == NULL) //should never fail _e2_vfs_create_history_store (); //backup data in case user cancels e2_tree_store_copy (GTK_TREE_MODEL (vfs.vtab), FALSE, (gpointer *)&rt->backupstore); rt->view = view; //which view to change rt->dialog = e2_dialog_create (NULL, NULL, _("vfs history"), _e2_vfs_historydlg_response_cb, rt); GtkWidget *sw = e2_widget_add_sw ( #ifdef USE_GTK2_14 gtk_dialog_get_content_area (GTK_DIALOG (rt->dialog)), #else GTK_DIALOG (rt->dialog)->vbox, #endif GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, TRUE, E2_PADDING_SMALL); GtkWidget *tvw = gtk_tree_view_new (); GtkCellRenderer *renderer; //setup treeview for visible columns of vfs data store gchar *titles [VFSDISPLAY] = { _("Protocol"), _("Host"), _("Path"), _("User"), _("Password"), _("Port"), _("Alias"), }; gint widths [VFSDISPLAY] = { 70, 150, 220, 100, 100, 50, 100 }; guint i; for (i = 0; i < VFSDISPLAY; i++) { if (i == VFSPROTONAME) { GtkTreeIter iter; //> 1 column with flags to assist later disaggregation rt->protocolstore = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_UINT); GList *member; for (member = remote_history; member != NULL; member = member->next) gtk_list_store_insert_with_values (rt->protocolstore, &iter, -1, 0, (gchar *)member->data, 1, REMOTE_DLG, -1); for (member = arch_history; member != NULL; member = member->next) gtk_list_store_insert_with_values (rt->protocolstore, &iter, -1, 0, (gchar *)member->data, 1, ARCH_DLG, -1); #ifdef E2_VFS_COLLECTIONS for (member = synth_history; member != NULL; member = member->next) gtk_list_store_insert_with_values (rt->protocolstore, &iter, -1, 0, (gchar *)member->data, 1, SYNTH_DLG, -1); #endif GtkTreeSortable *sort = GTK_TREE_SORTABLE (rt->protocolstore); gtk_tree_sortable_set_sort_column_id (sort, 0, GTK_SORT_ASCENDING); renderer = gtk_cell_renderer_combo_new (); g_object_set (G_OBJECT (renderer), "model", GTK_TREE_MODEL (rt->protocolstore), "text-column", 0, "editable", TRUE, "has-entry", TRUE, NULL); g_signal_connect (G_OBJECT (renderer), "edited", G_CALLBACK (_e2_vfs_historyproto_edited_cb), rt); } else { // gboolean visible = (i != 4); //password hidden renderer = gtk_cell_renderer_text_new (); g_object_set (G_OBJECT (renderer), // "visible", visible, "editable", TRUE, "yalign", 0.0, "ypad", 0, //supposedly default, but this makes the rows closer NULL); g_object_set_data (G_OBJECT (renderer), "model_column", GUINT_TO_POINTER (i)); g_signal_connect (G_OBJECT (renderer), "edited", G_CALLBACK (_e2_vfs_historycell_edited_cb), rt); } g_signal_connect (G_OBJECT (renderer), "editing-started", G_CALLBACK (_e2_vfs_history_editstart_cb), rt); g_signal_connect (G_OBJECT (renderer), "editing-canceled", G_CALLBACK (_e2_vfs_history_editcancel_cb), rt); GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes ( titles[i], renderer, "text", i, NULL); g_object_set (col, "alignment", 0.5, // "clickable", TRUE, sorting kills the indices // "sort-indicator", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_FIXED, "resizable", TRUE, "fixed-width", widths[i], "expand", TRUE, NULL); gtk_tree_view_insert_column (GTK_TREE_VIEW (tvw), col, -1); } gtk_tree_view_set_model (GTK_TREE_VIEW (tvw), GTK_TREE_MODEL (vfs.vtab)); GTK_WIDGET_SHOW (tvw); rt->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tvw)); gtk_container_add (GTK_CONTAINER (sw), tvw); gint initwidth = 0; for (i = 0; i < VFSDISPLAY; i++) initwidth += widths[i]; if (initwidth > 650) initwidth = 650; gtk_widget_set_size_request (sw, initwidth, 60); gtk_window_set_default_size (GTK_WINDOW (rt->dialog), 600, 150); //activate callback to open an activate row g_signal_connect (tvw, "row-activated", G_CALLBACK (_e2_vfs_historydlg_activated_cb), rt); //CHECKME dnd etc stuffs up current index ? //add buttons, custom and standard E2_Button local_btn; /* local_btn.label = _("_Up"); local_btn.stock = GTK_STOCK_GO_UP; local_btn.tip = _("Move the selected row one place up"); local_btn.showflags = E2_BTN_TIPPED; local_btn.response = E2_RESPONSE_USER1; e2_dialog_add_defined_button (rt->dialog, &local_btn); local_btn.label = _("_Down"); local_btn.stock = GTK_STOCK_GO_DOWN; local_btn.tip = _("Move the selected row one place down"); local_btn.response = E2_RESPONSE_USER2; e2_dialog_add_defined_button (rt->dialog, &local_btn); */ local_btn = E2_BUTTON_REMOVE; local_btn.tip = _("Delete the selected row"); local_btn.showflags = E2_BTN_TIPPED; e2_dialog_add_defined_button (rt->dialog, &local_btn); local_btn.label = _("_Add"); local_btn.stock = GTK_STOCK_ADD; local_btn.tip = _("Add a row after the selected one"); local_btn.response = E2_RESPONSE_USER3; e2_dialog_add_defined_button (rt->dialog, &local_btn); local_btn.label = _("_Local"); local_btn.stock = "vfs_off"E2ICONTB; local_btn.tip = _("Revert to native filesystem"); local_btn.showflags |= E2_BTN_DEFAULT; local_btn.response = GTK_RESPONSE_REJECT; GtkWidget *btn = e2_dialog_add_defined_button (rt->dialog, &local_btn); if (view->spacedata == NULL) gtk_widget_set_sensitive (btn, FALSE); local_btn.label = _("_Open"); local_btn.stock = GTK_STOCK_OPEN; local_btn.tip = _("Open the place described in the selected row"); local_btn.showflags = E2_BTN_TIPPED; local_btn.response = GTK_RESPONSE_OK; //CHECKME _ACCEPT E2_Button apply_btn; apply_btn = E2_BUTTON_APPLY; apply_btn.tip = _("Save and close"); apply_btn.showflags |= E2_BTN_TIPPED; e2_dialog_show (rt->dialog, app.main_window, 0, &E2_BUTTON_CANCEL, &apply_btn, &local_btn, NULL); } //#endif //def E2_VFS #endif //def INCLUDED_IN_PARENT