static void fm_path_entry_completion_render_func(GtkCellLayout *cell_layout, GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { gchar *model_file_name; int model_file_name_len; FmPathEntryPrivate *priv = FM_PATH_ENTRY_GET_PRIVATE( FM_PATH_ENTRY(data) ); gtk_tree_model_get(GTK_TREE_MODEL(model), iter, COL_BASENAME, &model_file_name, -1); model_file_name_len = strlen(model_file_name); if( priv->highlight_completion_match ) { int buf_len = model_file_name_len + 14 + 1; gchar* markup = g_malloc(buf_len); gchar *trail = g_stpcpy(markup, "<b><u>"); trail = strncpy(trail, model_file_name, priv->typed_basename_len) + priv->typed_basename_len; trail = g_stpcpy(trail, "</u></b>"); trail = g_stpcpy(trail, model_file_name + priv->typed_basename_len); g_object_set(cell, "markup", markup, NULL); g_free(markup); } /* FIXME: We don't need a custom render func if we don't hightlight */ else g_object_set(cell, "text", model_file_name, NULL); g_free(model_file_name); }
static gboolean fm_path_entry_match_selected(GtkEntryCompletion *widget, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data) { GtkWidget *entry = gtk_entry_completion_get_entry(widget); gchar new_text[PATH_MAX]; FmPathEntryPrivate *priv = FM_PATH_ENTRY_GET_PRIVATE( FM_PATH_ENTRY(entry) ); gchar *model_file_name; gchar *new_path; gtk_tree_model_get(GTK_TREE_MODEL(model), iter, COL_FILE_NAME, &model_file_name, -1); /* FIXME: should we use UTF-8 encoded display name here? */ new_path = fm_path_to_str(priv->completion_model->dir->dir_path); g_sprintf(new_text, "%s/%s", /* prevent leading double slash */ g_str_equal(new_path, "/") ? "" : new_path, model_file_name); g_free(new_path); priv->completion_len = 0; gtk_entry_set_text(GTK_ENTRY(entry), new_text); /* move the cursor to the end of entry */ gtk_editable_set_position(GTK_EDITABLE(entry), -1); return TRUE; }
static void fm_path_entry_paste_and_go(GtkMenuItem *menuitem, GtkEntry *entry) { GtkClipboard* clipboard = gtk_clipboard_get_for_display( gtk_widget_get_display (GTK_WIDGET (menuitem)),GDK_SELECTION_CLIPBOARD); gchar* full_path = gtk_clipboard_wait_for_text(clipboard); if (full_path) { FmPathEntryPrivate *priv = FM_PATH_ENTRY_GET_PRIVATE(entry); if(priv->path) fm_path_unref(priv->path); /* special handling for home dir */ if(full_path[0] == '~' && full_path[1] == G_DIR_SEPARATOR) priv->path = fm_path_new_relative(fm_path_get_home(), full_path + 2); else if(full_path[0] == '~' && full_path[1] == 0) priv->path = fm_path_ref(fm_path_get_home()); else priv->path = fm_path_new_for_str(full_path); gchar * disp_name = fm_path_display_name(priv->path, FALSE); gtk_entry_set_text(entry, disp_name); g_free(disp_name); gtk_editable_set_position(GTK_EDITABLE(entry), -1); g_free(full_path); fm_path_entry_activate(FM_PATH_ENTRY(entry)); } }
static gboolean fm_path_entry_match_func(GtkEntryCompletion *completion, const gchar *key, GtkTreeIter *iter, gpointer user_data) { gboolean ret; GtkTreeModel *model = gtk_entry_completion_get_model(completion); FmPathEntry *pe = FM_PATH_ENTRY( gtk_entry_completion_get_entry(completion) ); FmPathEntryPrivate *priv = FM_PATH_ENTRY_GET_PRIVATE(pe); FmFileInfo *model_file_info; gchar *model_file_name; /* get original key (case sensitive) */ const gchar *original_key = gtk_entry_get_text( GTK_ENTRY(pe) ); gboolean is_dir; /* find sep in key */ gchar *key_last_slash = strrchr(original_key, G_DIR_SEPARATOR); /* no model based completion possible */ if( (!model) || (key_last_slash == NULL) ) return FALSE; priv->completion_len = strlen(key_last_slash + 1); /* get filename, info from model */ gtk_tree_model_get(GTK_TREE_MODEL(model), iter, COL_FILE_NAME, &model_file_name, COL_FILE_INFO, &model_file_info, -1); ret = fm_file_info_is_dir(model_file_info) && g_str_has_prefix(model_file_name, key_last_slash + 1); g_free(model_file_name); return ret; }
static void fm_path_entry_changed(GtkEditable *editable, gpointer user_data) { FmPathEntry *entry = FM_PATH_ENTRY(editable); FmPathEntryPrivate *priv = FM_PATH_ENTRY_GET_PRIVATE(entry); GtkWidget* widget = GTK_WIDGET(entry); const gchar *path_str, *sep; /* find parent dir of current path */ path_str = gtk_entry_get_text( GTK_ENTRY(entry) ); sep = g_utf8_strrchr(path_str, -1, G_DIR_SEPARATOR); if(sep) /* we found a parent dir */ { int parent_len = (sep - path_str) + 1; /* includes the dir separator / */ if(!priv->parent_dir || priv->parent_len != parent_len || strncmp(priv->parent_dir, path_str, parent_len )) { /* parent dir has been changed, reload dir list */ ListSubDirNames* data = g_slice_new0(ListSubDirNames); g_free(priv->parent_dir); priv->parent_dir = g_strndup(path_str, parent_len); priv->parent_len = parent_len; /* g_debug("parent dir is changed to %s", priv->parent_dir); */ /* FIXME: convert utf-8 encoded path to on-disk encoding. */ data->entry = entry; if(priv->parent_dir[0] == '~') /* special case for home dir */ { char* expand = g_strconcat(g_get_home_dir(), priv->parent_dir + 1, NULL); data->dir = g_file_new_for_commandline_arg(expand); g_free(expand); } else data->dir = g_file_new_for_commandline_arg(priv->parent_dir); /* clear current model */ gtk_list_store_clear(GTK_LIST_STORE(priv->model)); /* cancel running dir-listing jobs */ if(priv->cancellable) { g_cancellable_cancel(priv->cancellable); g_object_unref(priv->cancellable); } /* launch a new job to do dir listing */ data->cancellable = g_cancellable_new(); priv->cancellable = (GCancellable*)g_object_ref(data->cancellable); g_io_scheduler_push_job(list_sub_dirs, data, (GDestroyNotify)list_sub_dir_names_free, G_PRIORITY_LOW, data->cancellable); } /* calculate the length of remaining part after / */ priv->typed_basename_len = strlen(sep + 1); } else /* clear all autocompletion thing. */ clear_completion(priv); }
static void fm_path_entry_changed(GtkEditable *editable) { FmPathEntry *entry = FM_PATH_ENTRY(editable); FmPathEntryPrivate *priv = FM_PATH_ENTRY_GET_PRIVATE(entry); const gchar *original_key = gtk_entry_get_text( GTK_ENTRY(entry) ); /* len of directory part */ gint key_dir_len; gchar *last_slash = strrchr(original_key, G_DIR_SEPARATOR); if( priv->in_change || !priv->path ) return; /* not path -> keep current completion model */ if( last_slash == NULL ) return; /* Check if path entry is not part of current completion folder model */ key_dir_len = last_slash - original_key; if( !fm_path_equal_str(priv->path, original_key, key_dir_len) ) { gchar* new_path = g_path_get_dirname(original_key); FmPath *new_fm_path = fm_path_new(new_path); g_free(new_path); if( new_fm_path != NULL ) { /* set hidden parameter based on prev. model */ /* FIXME: this is not very good */ gboolean show_hidden = priv->completion_model ? fm_folder_model_get_show_hidden(priv->completion_model) : FALSE; if(priv->completion_model) g_object_unref(priv->completion_model); if(priv->model && fm_path_equal(priv->model->dir->dir_path, new_fm_path)) { if(priv->path) fm_path_unref(priv->path); priv->path = fm_path_ref(priv->model->dir->dir_path); fm_path_unref(new_fm_path); priv->completion_model = g_object_ref(priv->model); } else { FmFolder *new_fm_folder = fm_folder_get_for_path(new_fm_path); FmFolderModel *new_fm = fm_folder_model_new(new_fm_folder, show_hidden); g_object_unref(new_fm_folder); priv->completion_model = new_fm; if(priv->path) fm_path_unref(priv->path); priv->path = new_fm_path; } gtk_entry_completion_set_model( priv->completion, GTK_TREE_MODEL(priv->completion_model) ); } else { /* FIXME: Handle invalid Paths */ g_warning("Invalid Path: %s", new_path); } } }
static void on_view_loaded( FmFolderView* view, FmPath* path, gpointer user_data) { const FmNavHistoryItem* item; FmMainWin* win = (FmMainWin*)user_data; FmPathEntry* entry = FM_PATH_ENTRY(win->location); fm_path_entry_set_model( entry, path, view->model ); /* scroll to recorded position */ item = fm_nav_history_get_cur(win->nav_history); gtk_adjustment_set_value( gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(view)), item->scroll_pos); }
static gboolean fm_path_entry_focus_in_event(GtkWidget *widget, GdkEvent *event) { FmPathEntry *entry = FM_PATH_ENTRY(widget); FmPathEntryPrivate *priv = FM_PATH_ENTRY_GET_PRIVATE(entry); /* activate auto-completion */ gtk_entry_set_completion(entry, priv->completion); /* listen to 'changed' signal for auto-completion */ g_signal_connect(entry, "changed", G_CALLBACK(fm_path_entry_changed), NULL); return GTK_WIDGET_CLASS(fm_path_entry_parent_class)->focus_in_event(widget, event); }
static gboolean fm_path_entry_key_press(GtkWidget *widget, GdkEventKey *event) { FmPathEntry *entry = FM_PATH_ENTRY(widget); switch( event->keyval ) { case GDK_Tab: /* place the cursor at the end */ gtk_editable_set_position(GTK_EDITABLE(entry), -1); return TRUE; } return FALSE; }
static gboolean fm_path_entry_focus_out_event(GtkWidget *widget, GdkEvent *event) { FmPathEntry *entry = FM_PATH_ENTRY(widget); FmPathEntryPrivate *priv = FM_PATH_ENTRY_GET_PRIVATE(entry); /* de-activate auto-completion */ gtk_entry_set_completion(entry, NULL); /* release all resources allocated for completion. */ clear_completion(priv); /* disconnect from 'changed' signal since we don't do auto-completion * when we have no keyboard focus. */ g_signal_handlers_disconnect_by_func(entry, fm_path_entry_changed, NULL); return GTK_WIDGET_CLASS(fm_path_entry_parent_class)->focus_out_event(widget, event); }
static void fm_path_entry_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { FmPathEntry *entry = FM_PATH_ENTRY(object); FmPathEntryPrivate *priv = FM_PATH_ENTRY_GET_PRIVATE(entry); switch( prop_id ) { case PROP_HIGHLIGHT_COMPLETION_MATCH: g_value_set_boolean(value, priv->highlight_completion_match); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } }
static gboolean fm_path_entry_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data) { FmPathEntry *entry = FM_PATH_ENTRY(widget); FmPathEntryPrivate *priv = FM_PATH_ENTRY_GET_PRIVATE(entry); char* text; int pos; switch( event->keyval ) { case GDK_Tab: { gtk_entry_completion_insert_prefix(priv->completion); gtk_editable_set_position(GTK_EDITABLE(entry), -1); return TRUE; } } return FALSE; }
static void fm_path_entry_do_insert_text(GtkEditable *editable, const gchar *new_text, gint new_text_length, gint *position) { FmPathEntry *entry = FM_PATH_ENTRY(editable); FmPathEntryPrivate *priv = FM_PATH_ENTRY_GET_PRIVATE(entry); /* let the GtkEntry class handle the insert */ (parent_editable_interface->do_insert_text)(editable, new_text, new_text_length, position); if( GTK_WIDGET_HAS_FOCUS(editable) && priv->completion_model ) { /* we have a common suffix -> add idle function */ if( (priv->common_suffix_append_idle_id < 0) && ( fm_path_entry_update_expand_path(entry) ) ) priv->common_suffix_append_idle_id = g_idle_add_full(G_PRIORITY_HIGH, fm_path_entry_suffix_append_idle, entry, fm_path_entry_suffix_append_idle_destroy); } }
static gboolean fm_path_entry_suffix_append_idle(gpointer user_data) { FmPathEntry *entry = FM_PATH_ENTRY(user_data); FmPathEntryPrivate *priv = FM_PATH_ENTRY_GET_PRIVATE(entry); const gchar *original_key = gtk_entry_get_text( GTK_ENTRY(entry) ); /* we have a common suffix -> insert/select it */ fm_path_entry_update_expand_path(entry); if( priv->common_suffix[0] ) { gint suffix_offset = g_utf8_strlen(original_key, -1); gint suffix_offset_save = suffix_offset; /* dont recur */ priv->in_change = TRUE; gtk_editable_insert_text(GTK_EDITABLE(entry), priv->common_suffix, -1, &suffix_offset); gtk_editable_select_region(GTK_EDITABLE(entry), suffix_offset_save, -1); priv->in_change = FALSE; } /* don't call again */ return FALSE; }
static gboolean fm_path_entry_match_func(GtkEntryCompletion *completion, const gchar *key, GtkTreeIter *iter, gpointer user_data) { gboolean ret; GtkTreeModel *model = gtk_entry_completion_get_model(completion); FmPathEntry *entry = FM_PATH_ENTRY( gtk_entry_completion_get_entry(completion) ); FmPathEntryPrivate *priv = FM_PATH_ENTRY_GET_PRIVATE(entry); char *model_basename; const char* typed_basename; /* we don't use the case-insensitive key provided by entry completion here */ typed_basename = gtk_entry_get_text(entry) + priv->parent_len; gtk_tree_model_get(model, iter, COL_BASENAME, &model_basename, -1); if(model_basename[0] == '.' && typed_basename[0] != '.') ret = FALSE; /* ignore hidden files when needed. */ else ret = g_str_has_prefix(model_basename, typed_basename); /* FIXME: should we be case insensitive here? */ g_free(model_basename); return ret; }
static void fm_path_entry_suffix_append_idle_destroy(gpointer user_data) { FmPathEntry *entry = FM_PATH_ENTRY(user_data); FM_PATH_ENTRY_GET_PRIVATE(entry)->common_suffix_append_idle_id = -1; }