/*
* (SLIK) SimpLIstic sKin functions
* (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2012 The Geeqie Team
*
* Author: John Ellis
*
#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
-#include "gqview.h"
+#include "main.h"
#include "ui_tabcomp.h"
-#include "ui_bookmark.h"
+#include "history_list.h"
+#include "misc.h" /* expand_tilde() */
#include "ui_fileops.h"
#include "ui_spinner.h"
#include "ui_utildlg.h"
* #define TAB_COMPLETION_ENABLE_POPUP_MENU
*/
#define TAB_COMPLETION_ENABLE_POPUP_MENU 1
-#define TAB_COMP_POPUP_MAX 500
+#define TAB_COMP_POPUP_MAX 1000
#ifdef TAB_COMPLETION_ENABLE_POPUP_MENU
#include "ui_menu.h"
/* ----------------------------------------------------------------
Tab completion routines, can be connected to any gtkentry widget
using the tab_completion_add_to_entry() function.
- Use remove_trailing_slash() to strip the trailing '/'.
+ Use remove_trailing_slash() to strip the trailing G_DIR_SEPARATOR.
----------------------------------------------------------------*/
typedef struct _TabCompData TabCompData;
GList *file_list;
void (*enter_func)(const gchar *, gpointer);
void (*tab_func)(const gchar *, gpointer);
+ void (*tab_append_func)(const gchar *, gpointer, gint);
+
gpointer enter_data;
gpointer tab_data;
-
+ gpointer tab_append_data;
+
GtkWidget *combo;
- gint has_history;
+ gboolean has_history;
gchar *history_key;
gint history_levels;
FileDialog *fd;
gchar *fd_title;
- gint fd_folders_only;
+ gboolean fd_folders_only;
GtkWidget *fd_button;
+
+ guint choices;
};
static void tab_completion_select_show(TabCompData *td);
+static gint tab_completion_do(TabCompData *td);
static void tab_completion_free_list(TabCompData *td)
{
list = td->file_list;
- while(list)
+ while (list)
{
g_free(list->data);
list = list->next;
static void tab_completion_read_dir(TabCompData *td, const gchar *path)
{
- DIR *dp;
- struct dirent *dir;
- GList *list = NULL;
+ DIR *dp;
+ struct dirent *dir;
+ GList *list = NULL;
gchar *pathl;
tab_completion_free_list(td);
pathl = path_from_utf8(path);
- dp = opendir(pathl);
- g_free(pathl);
+ dp = opendir(pathl);
if (!dp)
- {
- /* dir not found */
- return;
- }
- while ((dir = readdir(dp)) != NULL)
- {
+ {
+ /* dir not found */
+ g_free(pathl);
+ return;
+ }
+ while ((dir = readdir(dp)) != NULL)
+ {
gchar *name = dir->d_name;
if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0)
{
- list = g_list_prepend(list, path_to_utf8(name));
+ gchar *abspath = g_build_filename(pathl, name, NULL);
+
+ if (g_file_test(abspath, G_FILE_TEST_IS_DIR))
+ {
+ gchar *dname = g_strconcat(name, G_DIR_SEPARATOR_S, NULL);
+ list = g_list_prepend(list, path_to_utf8(dname));
+ g_free(dname);
+ }
+ else
+ {
+ list = g_list_prepend(list, path_to_utf8(name));
+ }
+ g_free(abspath);
}
}
- closedir(dp);
+ closedir(dp);
td->dir_path = g_strdup(path);
td->file_list = list;
+ g_free(pathl);
}
static void tab_completion_destroy(GtkWidget *widget, gpointer data)
g_free(td);
}
-static gint tab_completion_emit_enter_signal(TabCompData *td)
+static gchar *tab_completion_get_text(TabCompData *td)
{
gchar *text;
- if (!td->enter_func) return FALSE;
text = g_strdup(gtk_entry_get_text(GTK_ENTRY(td->entry)));
if (text[0] == '~')
{
gchar *t = text;
- text = g_strconcat(homedir(), t + 1, NULL);
+ text = expand_tilde(text);
g_free(t);
}
+ return text;
+}
+
+static gboolean tab_completion_emit_enter_signal(TabCompData *td)
+{
+ gchar *text;
+ if (!td->enter_func) return FALSE;
+
+ text = tab_completion_get_text(td);
td->enter_func(text, td->enter_data);
g_free(text);
gchar *text;
if (!td->tab_func) return;
- text = g_strdup(gtk_entry_get_text(GTK_ENTRY(td->entry)));
-
- if (text[0] == '~')
- {
- gchar *t = text;
- text = g_strconcat(homedir(), t + 1, NULL);
- g_free(t);
- }
-
+ text = tab_completion_get_text(td);
td->tab_func(text, td->tab_data);
g_free(text);
}
#ifdef TAB_COMPLETION_ENABLE_POPUP_MENU
+void tab_completion_iter_menu_items(GtkWidget *widget, gpointer data)
+{
+ TabCompData *td = data;
+ GtkWidget *child;
+
+ if (!gtk_widget_get_visible(widget)) return;
+
+ child = gtk_bin_get_child(GTK_BIN(widget));
+ if (GTK_IS_LABEL(child)) {
+ const gchar *text = gtk_label_get_text(GTK_LABEL(child));
+ const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(td->entry));
+ const gchar *prefix = filename_from_path(entry_text);
+ guint prefix_len = strlen(prefix);
+
+ if (strlen(text) < prefix_len || strncmp(text, prefix, prefix_len))
+ {
+ /* Hide menu items not matching */
+ gtk_widget_hide(widget);
+ }
+ else
+ {
+ /* Count how many choices are left in the menu */
+ td->choices++;
+ }
+ }
+}
-static gint tab_completion_popup_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
+static gboolean tab_completion_popup_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
{
TabCompData *td = data;
- if (event->keyval == GDK_Tab ||
- event->keyval == GDK_BackSpace ||
+ if (event->keyval == GDK_KEY_Tab ||
+ event->keyval == GDK_KEY_BackSpace ||
(event->keyval >= 0x20 && event->keyval <= 0xFF) )
{
if (event->keyval >= 0x20 && event->keyval <= 0xFF)
buf[1] = '\0';
gtk_editable_insert_text(GTK_EDITABLE(td->entry), buf, 1, &p);
gtk_editable_set_position(GTK_EDITABLE(td->entry), -1);
+
+ /* Reduce the number of entries in the menu */
+ td->choices = 0;
+ gtk_container_foreach(GTK_CONTAINER(widget), tab_completion_iter_menu_items, (gpointer) td);
+ if (td->choices > 1) return TRUE; /* multiple choices */
+ if (td->choices > 0) tab_completion_do(td); /* one choice */
}
-
- /*close the menu */
+
+ /* close the menu */
gtk_menu_popdown(GTK_MENU(widget));
/* doing this does not emit the "selection done" signal, unref it ourselves */
- gtk_widget_unref(widget);
-
+ g_object_unref(widget);
return TRUE;
}
gchar *name = data;
TabCompData *td;
gchar *buf;
- gchar *ptr;
td = g_object_get_data(G_OBJECT(widget), "tab_completion_data");
if (!td) return;
- ptr = td->dir_path + strlen(td->dir_path) - 1;
- buf = g_strconcat(td->dir_path, (ptr[0] == '/') ? "" : "/", name, NULL);
+ buf = g_build_filename(td->dir_path, name, NULL);
gtk_entry_set_text(GTK_ENTRY(td->entry), buf);
gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(buf));
g_free(buf);
GdkScreen *screen;
gint monitor_num;
GdkRectangle monitor;
+ GtkRequisition requisition;
+ GtkAllocation allocation;
- gdk_window_get_origin(td->entry->window, x, y);
+ gdk_window_get_origin(gtk_widget_get_window(GTK_WIDGET(td->entry)), x, y);
screen = gtk_widget_get_screen(GTK_WIDGET(menu));
- monitor_num = gdk_screen_get_monitor_at_window(screen, td->entry->window);
+ monitor_num = gdk_screen_get_monitor_at_window(screen, gtk_widget_get_window(GTK_WIDGET(td->entry)));
gdk_screen_get_monitor_geometry(screen, monitor_num, &monitor);
gtk_widget_size_request(GTK_WIDGET(menu), &req);
*x += strong_pos.x / PANGO_SCALE + xoffset;
- height = MIN(td->entry->requisition.height, td->entry->allocation.height);
+ gtk_widget_get_requisition(td->entry, &requisition);
+ gtk_widget_get_allocation(td->entry, &allocation);
+
+ height = MIN(requisition.height, allocation.height);
if (req.height > monitor.y + monitor.height - *y - height &&
*y - monitor.y > monitor.y + monitor.height - *y)
static gint simple_sort(gconstpointer a, gconstpointer b)
{
- return CASE_SORT((gchar *)a, (gchar *)b);
+ return CASE_SORT((gchar *)a, (gchar *)b);
}
#endif
-static gint tab_completion_do(TabCompData *td)
+static gboolean tab_completion_do(TabCompData *td)
{
const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(td->entry));
const gchar *entry_file;
gchar *entry_dir;
gchar *ptr;
- gint home_exp = FALSE;
+ gboolean home_exp = FALSE;
+
+ if (entry_text[0] == '\0')
+ {
+ entry_dir = g_strdup(G_DIR_SEPARATOR_S); /* FIXME: root directory win32 */
+ gtk_entry_set_text(GTK_ENTRY(td->entry), entry_dir);
+ gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(entry_dir));
+ g_free(entry_dir);
+ return FALSE;
+ }
/* home dir expansion */
if (entry_text[0] == '~')
{
- entry_dir = g_strconcat(homedir(), entry_text + 1, NULL);
+ entry_dir = expand_tilde(entry_text);
home_exp = TRUE;
}
else
entry_dir = g_strdup(entry_text);
}
- entry_file = filename_from_path(entry_text);
-
if (isfile(entry_dir))
{
if (home_exp)
g_free(entry_dir);
return home_exp;
}
+
+ entry_file = filename_from_path(entry_text);
+
if (isdir(entry_dir) && strcmp(entry_file, ".") != 0 && strcmp(entry_file, "..") != 0)
{
ptr = entry_dir + strlen(entry_dir) - 1;
- if (ptr[0] == '/')
+ if (ptr[0] == G_DIR_SEPARATOR)
{
if (home_exp)
{
const gchar *file;
file = td->file_list->data;
- buf = g_strconcat(entry_dir, file, NULL);
+ buf = g_build_filename(entry_dir, file, NULL);
if (isdir(buf))
{
+ gchar *tmp = g_strconcat(buf, G_DIR_SEPARATOR_S, NULL);
g_free(buf);
- buf = g_strconcat(entry_dir, file, "/", NULL);
+ buf = tmp;
}
gtk_entry_set_text(GTK_ENTRY(td->entry), buf);
gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(buf));
}
else
{
- gchar *buf = g_strconcat(entry_dir, "/", NULL);
+ gchar *buf = g_strconcat(entry_dir, G_DIR_SEPARATOR_S, NULL);
gtk_entry_set_text(GTK_ENTRY(td->entry), buf);
gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(buf));
g_free(buf);
ptr = (gchar *)filename_from_path(entry_dir);
if (ptr > entry_dir) ptr--;
ptr[0] = '\0';
-
+
if (strlen(entry_dir) == 0)
{
g_free(entry_dir);
- entry_dir = g_strdup("/");
+ entry_dir = g_strdup(G_DIR_SEPARATOR_S); /* FIXME: win32 */
}
if (isdir(entry_dir))
tab_completion_read_dir(td, entry_dir);
}
- if (strcmp(entry_dir, "/") == 0) entry_dir[0] = '\0';
-
list = td->file_list;
- while(list)
+ while (list)
{
gchar *file = list->data;
if (strncmp(entry_file, file, l) == 0)
gchar *file = poss->data;
gchar *buf;
- buf = g_strconcat(entry_dir, "/", file, NULL);
-
- if (isdir(buf))
- {
- g_free(buf);
- buf = g_strconcat(entry_dir, "/", file, "/", NULL);
- }
+ buf = g_build_filename(entry_dir, file, NULL);
gtk_entry_set_text(GTK_ENTRY(td->entry), buf);
gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(buf));
g_free(buf);
}
else
{
- gint c = strlen(entry_file);
- gint done = FALSE;
+ gsize c = strlen(entry_file);
+ gboolean done = FALSE;
gchar *test_file = poss->data;
while (!done)
{
list = poss;
if (!list) done = TRUE;
- while(list && !done)
+ while (list && !done)
{
gchar *file = list->data;
if (strlen(file) < c || strncmp(test_file, file, c) != 0)
gchar *buf;
file = g_strdup(test_file);
file[c] = '\0';
- buf = g_strconcat(entry_dir, "/", file, NULL);
+ buf = g_build_filename(entry_dir, file, NULL);
gtk_entry_set_text(GTK_ENTRY(td->entry), buf);
gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(buf));
return FALSE;
}
-static gint tab_completion_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
+static gboolean tab_completion_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
{
TabCompData *td = data;
- gint stop_signal = FALSE;
+ gboolean stop_signal = FALSE;
switch (event->keyval)
{
- case GDK_Tab:
+ case GDK_KEY_Tab:
if (!(event->state & GDK_CONTROL_MASK))
{
if (tab_completion_do(td))
stop_signal = TRUE;
}
break;
- case GDK_Return: case GDK_KP_Enter:
+ case GDK_KEY_Return: case GDK_KEY_KP_Enter:
if (td->fd_button &&
(event->state & GDK_CONTROL_MASK))
{
if (!td) return;
- if (!GTK_WIDGET_HAS_FOCUS(entry))
+ if (!gtk_widget_has_focus(entry))
{
gtk_widget_grab_focus(entry);
}
static void tab_completion_button_size_allocate(GtkWidget *button, GtkAllocation *allocation, gpointer data)
{
GtkWidget *parent = data;
+ GtkAllocation parent_allocation;
+ gtk_widget_get_allocation(parent, &parent_allocation);
- if (allocation->height > parent->allocation.height)
+ if (allocation->height > parent_allocation.height)
{
GtkAllocation button_allocation;
- button_allocation = button->allocation;
- button_allocation.height = parent->allocation.height;
- button_allocation.y = parent->allocation.y +
- (parent->allocation.height - parent->allocation.height) / 2;
+ gtk_widget_get_allocation(button, &button_allocation);
+ button_allocation.height = parent_allocation.height;
+ button_allocation.y = parent_allocation.y +
+ (parent_allocation.height - parent_allocation.height) / 2;
gtk_widget_size_allocate(button, &button_allocation);
}
}
GdkPixbuf *pixbuf;
button = gtk_button_new();
- GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
+ gtk_widget_set_can_focus(button, FALSE);
g_signal_connect(G_OBJECT(button), "size_allocate",
G_CALLBACK(tab_completion_button_size_allocate), parent);
g_signal_connect(G_OBJECT(button), "clicked",
pixbuf = gdk_pixbuf_new_from_inline(-1, icon_tabcomp, FALSE, NULL);
icon = gtk_image_new_from_pixbuf(pixbuf);
- gdk_pixbuf_unref(pixbuf);
+ g_object_unref(pixbuf);
+
gtk_container_add(GTK_CONTAINER(button), icon);
gtk_widget_show(icon);
box = gtk_hbox_new(FALSE, 0);
- combo = gtk_combo_box_entry_new_text();
+ combo = gtk_combo_box_text_new_with_entry();
gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
gtk_widget_show(combo);
- combo_entry = GTK_BIN(combo)->child;
+ combo_entry = gtk_bin_get_child(GTK_BIN(combo));
#if 0
gtk_combo_set_case_sensitive(GTK_COMBO(combo), TRUE);
gtk_combo_set_use_arrows(GTK_COMBO(combo), FALSE);
work = history_list_get_by_key(history_key);
while (work)
{
- gtk_combo_box_append_text(GTK_COMBO_BOX(combo), (gchar *)work->data);
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), (gchar *)work->data);
work = work->next;
n++;
}
TabCompData *td;
GtkTreeModel *store;
GList *work;
+ gint n = 0;
td = g_object_get_data(G_OBJECT(entry), "tab_completion_data");
work = history_list_get_by_key(td->history_key);
while (work)
{
- gtk_combo_box_append_text(GTK_COMBO_BOX(td->combo), (gchar *)work->data);
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(td->combo), (gchar *)work->data);
work = work->next;
+ n++;
}
+
+ if (td->tab_append_func) {
+ td->tab_append_func(path, td->tab_append_data, n);
+ }
}
GtkWidget *tab_completion_new(GtkWidget **entry, const gchar *text,
TabCompData *td;
if (!entry)
{
- printf("Tab completion error: entry != NULL\n");
+ log_printf("Tab completion error: entry != NULL\n");
return;
}
td = g_new0(TabCompData, 1);
+
td->entry = entry;
- td->dir_path = NULL;
- td->file_list = NULL;
td->enter_func = enter_func;
td->enter_data = data;
- td->tab_func = NULL;
- td->tab_data = NULL;
-
- td->has_history = FALSE;
- td->history_key = NULL;
- td->history_levels = 0;
g_object_set_data(G_OBJECT(td->entry), "tab_completion_data", td);
td->tab_data = data;
}
+/* Add a callback function called when a new entry is appended to the list */
+void tab_completion_add_append_func(GtkWidget *entry, void (*tab_append_func)(const gchar *, gpointer, gint), gpointer data)
+{
+ TabCompData *td = g_object_get_data(G_OBJECT(entry), "tab_completion_data");
+
+ if (!td) return;
+
+ td->tab_append_func = tab_append_func;
+ td->tab_append_data = data;
+}
+
gchar *remove_trailing_slash(const gchar *path)
{
gint l;
if (!path) return NULL;
l = strlen(path);
- while (l > 1 && path[l - 1] == '/') l--;
+ while (l > 1 && path[l - 1] == G_DIR_SEPARATOR) l--;
return g_strndup(path, l);
}
}
title = (td->fd_title) ? td->fd_title : _("Select path");
- td->fd = file_dialog_new(title, GQ_WMCLASS, "select_path", td->entry,
+ td->fd = file_dialog_new(title, "select_path", td->entry,
tab_completion_select_cancel_cb, td);
file_dialog_add_button(td->fd, GTK_STOCK_OK, NULL,
tab_completion_select_ok_cb, TRUE);
tab_completion_select_show(td);
}
-void tab_completion_add_select_button(GtkWidget *entry, const gchar *title, gint folders_only)
+void tab_completion_add_select_button(GtkWidget *entry, const gchar *title, gboolean folders_only)
{
TabCompData *td;
GtkWidget *parent;
gtk_widget_show(td->fd_button);
}
-
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */