r1996: Cope slightly better with invalid filenames in various places (reported by
[rox-filer.git] / ROX-Filer / src / abox.c
blob8ee0514ca70a7ec8eb73ca2b687fd335f1821ac4
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* abox.c - the dialog box widget used for filer operations.
24 * The actual code for specific operations is in action.c.
27 #include "config.h"
29 #include <string.h>
31 #include "global.h"
33 #include "main.h"
34 #include "abox.h"
35 #include "gui_support.h"
36 #include "filer.h"
37 #include "display.h"
38 #include "support.h"
40 #define RESPONSE_QUIET 1
42 /* Static prototypes */
43 static void abox_class_init(GObjectClass *gclass, gpointer data);
44 static void abox_init(GTypeInstance *object, gpointer gclass);
45 static void response(GtkDialog *dialog, gint response_id);
46 static void abox_finalise(GObject *object);
47 static void shade(ABox *abox);
49 GType abox_get_type(void)
51 static GType type = 0;
53 if (!type)
55 static const GTypeInfo info =
57 sizeof (ABoxClass),
58 NULL, /* base_init */
59 NULL, /* base_finalise */
60 (GClassInitFunc) abox_class_init,
61 NULL, /* class_finalise */
62 NULL, /* class_data */
63 sizeof(ABox),
64 0, /* n_preallocs */
65 (GInstanceInitFunc) abox_init
68 type = g_type_register_static(GTK_TYPE_DIALOG,
69 "ABox", &info, 0);
72 return type;
75 GtkWidget* abox_new(const gchar *title, gboolean quiet)
77 GtkWidget *widget;
78 ABox *abox;
80 widget = GTK_WIDGET(gtk_widget_new(abox_get_type(), NULL));
81 abox = (ABox *) widget;
83 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(abox->quiet), quiet);
85 gtk_window_set_title(GTK_WINDOW(widget), title);
86 gtk_dialog_set_has_separator(GTK_DIALOG(widget), FALSE);
88 return widget;
91 static void abox_class_init(GObjectClass *gclass, gpointer data)
93 GtkDialogClass *dialog = (GtkDialogClass *) gclass;
95 dialog->response = response;
97 g_signal_new("flag_toggled", G_TYPE_FROM_CLASS(gclass),
98 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(ABoxClass, flag_toggled),
99 NULL, NULL, g_cclosure_marshal_VOID__INT,
100 G_TYPE_NONE, 1, G_TYPE_INT);
102 gclass->finalize = abox_finalise;
105 static void abox_init(GTypeInstance *object, gpointer gclass)
107 GtkWidget *frame, *text, *scrollbar, *button;
108 ABox *abox = ABOX(object);
109 GtkDialog *dialog = GTK_DIALOG(object);
111 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
113 abox->dir_label = gtk_label_new(_("<dir>"));
114 gtk_widget_set_size_request(abox->dir_label, 8, -1);
115 abox->results = NULL;
116 abox->entry = NULL;
117 abox->next_dir = NULL;
118 abox->next_timer = 0;
119 abox->question = FALSE;
120 gtk_misc_set_alignment(GTK_MISC(abox->dir_label), 0.5, 0.5);
121 gtk_box_pack_start(GTK_BOX(dialog->vbox),
122 abox->dir_label, FALSE, TRUE, 0);
124 abox->log_hbox = gtk_hbox_new(FALSE, 0);
125 gtk_box_pack_start(GTK_BOX(dialog->vbox),
126 abox->log_hbox, TRUE, TRUE, 4);
128 frame = gtk_frame_new(NULL);
129 gtk_box_pack_start_defaults(GTK_BOX(abox->log_hbox), frame);
131 text = gtk_text_view_new();
132 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
133 gtk_container_add(GTK_CONTAINER(frame), text);
135 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
136 abox->log = text;
138 scrollbar = gtk_vscrollbar_new(NULL);
139 gtk_widget_set_scroll_adjustments(text, NULL,
140 gtk_range_get_adjustment(GTK_RANGE(scrollbar)));
141 gtk_text_buffer_create_tag(
142 gtk_text_view_get_buffer(GTK_TEXT_VIEW(abox->log)),
143 "error", "foreground", "red",
144 NULL);
145 gtk_text_buffer_create_tag(
146 gtk_text_view_get_buffer(GTK_TEXT_VIEW(abox->log)),
147 "question", "weight", "bold",
148 NULL);
149 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
150 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE);
151 gtk_widget_set_size_request(text, 400, 100);
153 gtk_box_pack_start(GTK_BOX(abox->log_hbox), scrollbar, FALSE, TRUE, 0);
155 gtk_dialog_add_buttons(dialog,
156 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
157 GTK_STOCK_NO, GTK_RESPONSE_NO,
158 GTK_STOCK_YES, GTK_RESPONSE_YES,
159 NULL);
161 abox->flag_box = gtk_hbox_new(FALSE, 16);
162 gtk_box_pack_end(GTK_BOX(dialog->vbox),
163 abox->flag_box, FALSE, TRUE, 2);
165 button = button_new_mixed(GTK_STOCK_GOTO_LAST, _("_Quiet"));
166 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
167 gtk_dialog_add_action_widget(dialog, button, RESPONSE_QUIET);
168 gtk_dialog_set_default_response(dialog, RESPONSE_QUIET);
170 gtk_widget_show_all(dialog->vbox);
172 abox->quiet = abox_add_flag(abox,
173 _("Quiet"), _("Don't confirm every operation"),
174 'Q', FALSE);
176 shade(abox);
179 static void flag_toggled(GtkToggleButton *toggle, ABox *abox)
181 gint code;
183 code = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toggle),
184 "abox-response"));
185 if (code == 'Q')
186 shade(abox);
188 g_signal_emit_by_name(abox, "flag_toggled", code);
192 GtkWidget *abox_add_flag(ABox *abox, const gchar *label, const gchar *tip,
193 gint response, gboolean default_value)
195 GtkWidget *check;
197 check = gtk_check_button_new_with_label(label);
198 gtk_tooltips_set_tip(tooltips, check, tip, NULL);
199 g_object_set_data(G_OBJECT(check), "abox-response",
200 GINT_TO_POINTER(response));
201 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), default_value);
202 g_signal_connect(check, "toggled", G_CALLBACK(flag_toggled), abox);
203 gtk_box_pack_end(GTK_BOX(abox->flag_box), check, FALSE, TRUE, 0);
204 gtk_widget_show(check);
206 return check;
209 static void response(GtkDialog *dialog, gint response_id)
211 ABox *abox = ABOX(dialog);
213 if (response_id == GTK_RESPONSE_CANCEL)
214 gtk_widget_destroy(GTK_WIDGET(dialog));
215 else if (response_id == RESPONSE_QUIET)
217 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(abox->quiet),
218 TRUE);
219 gtk_dialog_response(dialog, GTK_RESPONSE_YES);
221 else if (response_id == GTK_RESPONSE_YES ||
222 response_id == GTK_RESPONSE_NO)
224 abox->question = FALSE;
225 shade(abox);
229 /* Display the question. Unshade the Yes, No and entry box (if any).
230 * Will send a response signal when the user makes a choice.
232 void abox_ask(ABox *abox, const gchar *question)
234 g_return_if_fail(abox != NULL);
235 g_return_if_fail(question != NULL);
236 g_return_if_fail(IS_ABOX(abox));
238 abox_log(abox, question, "question");
240 abox->question = TRUE;
241 shade(abox);
244 void abox_cancel_ask(ABox *abox)
246 g_return_if_fail(abox != NULL);
247 g_return_if_fail(IS_ABOX(abox));
249 abox->question = FALSE;
250 shade(abox);
253 void abox_log(ABox *abox, const gchar *message, const gchar *style)
255 GtkTextIter end;
256 GtkTextBuffer *text_buffer;
257 GtkTextView *log = GTK_TEXT_VIEW(abox->log);
258 gchar *u8 = NULL;
260 if (!g_utf8_validate(message, -1, NULL))
261 u8 = to_utf8(message);
263 text_buffer = gtk_text_view_get_buffer(log);
265 gtk_text_buffer_get_end_iter(text_buffer, &end);
266 gtk_text_buffer_insert_with_tags_by_name(text_buffer,
267 &end, u8 ? u8 : message, -1, style, NULL);
268 gtk_text_view_scroll_to_mark(
269 log,
270 gtk_text_buffer_get_mark(text_buffer, "insert"),
271 0.0, FALSE, 0, 0);
273 g_free(u8);
276 static void abox_finalise(GObject *object)
278 GObjectClass *parent_class;
279 ABox *abox = ABOX(object);
281 if (abox->next_dir)
283 null_g_free(&abox->next_dir);
284 gtk_timeout_remove(abox->next_timer);
287 parent_class = gtk_type_class(GTK_TYPE_DIALOG);
289 if (G_OBJECT_CLASS(parent_class)->finalize)
290 (*G_OBJECT_CLASS(parent_class)->finalize)(object);
293 static gboolean show_next_dir(gpointer data)
295 ABox *abox = (ABox *) data;
297 gtk_label_set_text(GTK_LABEL(abox->dir_label), abox->next_dir);
298 null_g_free(&abox->next_dir);
300 return FALSE;
303 /* Display this message in the current-object area.
304 * The display won't update too fast, even if you call this very often.
306 void abox_set_current_object(ABox *abox, const gchar *message)
308 g_return_if_fail(abox != NULL);
309 g_return_if_fail(message != NULL);
310 g_return_if_fail(IS_ABOX(abox));
312 /* If a string is already set then replace it, but assume the
313 * timer is already running...
316 if (abox->next_dir)
317 g_free(abox->next_dir);
318 else
320 gtk_label_set_text(GTK_LABEL(abox->dir_label), message);
321 abox->next_timer = gtk_timeout_add(500, show_next_dir, abox);
324 abox->next_dir = g_strdup(message);
327 static void lost_preview(GtkWidget *window, ABox *abox)
329 abox->preview = NULL;
332 static void select_row_callback(GtkTreeView *treeview,
333 GtkTreePath *path,
334 GtkTreeViewColumn *col,
335 ABox *abox)
337 GtkTreeModel *model;
338 GtkTreeIter iter;
339 char *leaf, *dir;
341 model = gtk_tree_view_get_model(GTK_TREE_VIEW(abox->results));
342 gtk_tree_model_get_iter(model, &iter, path);
343 gtk_tree_model_get(model, &iter, 0, &leaf, 1, &dir, -1);
345 if (abox->preview)
347 if (strcmp(abox->preview->real_path, dir) == 0)
348 display_set_autoselect(abox->preview, leaf);
349 else
350 filer_change_to(abox->preview, dir, leaf);
351 goto out;
354 abox->preview = filer_opendir(dir, NULL, NULL);
355 if (abox->preview)
357 display_set_autoselect(abox->preview, leaf);
358 g_signal_connect_object(abox->preview->window, "destroy",
359 G_CALLBACK(lost_preview), abox, 0);
362 out:
363 g_free(dir);
364 g_free(leaf);
367 /* Add a list-of-results area. You must use this before adding files
368 * with abox_add_filename().
370 void abox_add_results(ABox *abox)
372 GtkTreeViewColumn *column;
373 GtkWidget *scroller, *frame;
374 GtkListStore *model;
375 GtkCellRenderer *cell_renderer;
377 g_return_if_fail(abox != NULL);
378 g_return_if_fail(IS_ABOX(abox));
379 g_return_if_fail(abox->results == NULL);
381 scroller = gtk_scrolled_window_new(NULL, NULL);
382 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
383 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
385 frame = gtk_frame_new(NULL);
386 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
387 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(abox)->vbox),
388 frame, TRUE, TRUE, 4);
390 gtk_container_add(GTK_CONTAINER(frame), scroller);
392 model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
393 abox->results = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
394 g_object_unref(G_OBJECT(model));
396 cell_renderer = gtk_cell_renderer_text_new();
397 column = gtk_tree_view_column_new_with_attributes(
398 _("Name"), cell_renderer, "text", 0, NULL);
399 gtk_tree_view_column_set_resizable(column, TRUE);
400 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
401 gtk_tree_view_append_column(GTK_TREE_VIEW(abox->results), column);
402 gtk_tree_view_insert_column_with_attributes(
403 GTK_TREE_VIEW(abox->results),
404 1, (gchar *) _("Directory"), cell_renderer,
405 "text", 1, NULL);
407 gtk_container_add(GTK_CONTAINER(scroller), abox->results);
409 gtk_widget_set_size_request(abox->results, 100, 100);
410 gtk_box_set_child_packing(GTK_BOX(GTK_DIALOG(abox)->vbox),
411 abox->log_hbox, FALSE, TRUE, 4, GTK_PACK_START);
413 g_signal_connect(abox->results, "row-activated",
414 G_CALLBACK(select_row_callback), abox);
417 void abox_add_filename(ABox *abox, const gchar *path)
419 GtkTreeModel *model;
420 GtkTreeIter iter;
421 gchar *dir;
423 model = gtk_tree_view_get_model(GTK_TREE_VIEW(abox->results));
425 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
427 dir = g_dirname(path);
428 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
429 0, g_basename(path),
430 1, dir, -1);
431 g_free(dir);
434 /* Clear search results area */
435 void abox_clear_results(ABox *abox)
437 GtkTreeModel *model;
439 g_return_if_fail(abox != NULL);
440 g_return_if_fail(IS_ABOX(abox));
442 model = gtk_tree_view_get_model(GTK_TREE_VIEW(abox->results));
444 gtk_list_store_clear(GTK_LIST_STORE(model));
447 void abox_add_combo(ABox *abox, GList *presets,
448 const gchar *text, GtkWidget *help_button)
450 GtkWidget *hbox, *label, *combo;
452 g_return_if_fail(abox != NULL);
453 g_return_if_fail(IS_ABOX(abox));
454 g_return_if_fail(abox->entry == NULL);
456 hbox = gtk_hbox_new(FALSE, 0);
457 label = gtk_label_new(_("Command:"));
458 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
460 combo = gtk_combo_new();
461 gtk_combo_disable_activate(GTK_COMBO(combo));
462 gtk_combo_set_use_arrows_always(GTK_COMBO(combo), TRUE);
463 gtk_combo_set_popdown_strings(GTK_COMBO(combo), presets);
464 abox->entry = GTK_COMBO(combo)->entry;
465 gtk_entry_set_activates_default(GTK_ENTRY(abox->entry), TRUE);
467 gtk_entry_set_text(GTK_ENTRY(abox->entry), text);
468 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 4);
470 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(abox)->vbox), hbox,
471 FALSE, TRUE, 0);
472 gtk_box_pack_start(GTK_BOX(hbox), help_button, FALSE, TRUE, 4);
474 gtk_widget_show_all(hbox);
476 shade(abox);
479 void abox_add_entry(ABox *abox, const gchar *text, GtkWidget *help_button)
481 GtkWidget *hbox, *label;
483 g_return_if_fail(abox != NULL);
484 g_return_if_fail(IS_ABOX(abox));
485 g_return_if_fail(abox->entry == NULL);
487 hbox = gtk_hbox_new(FALSE, 0);
488 label = gtk_label_new(_("Expression:"));
489 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
490 abox->entry = gtk_entry_new();
491 gtk_widget_set_name(abox->entry, "fixed-style");
492 gtk_entry_set_text(GTK_ENTRY(abox->entry), text);
493 gtk_box_pack_start(GTK_BOX(hbox), abox->entry, TRUE, TRUE, 4);
494 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(abox)->vbox),
495 hbox, FALSE, TRUE, 4);
496 gtk_box_pack_start(GTK_BOX(hbox), help_button,
497 FALSE, TRUE, 4);
499 gtk_entry_set_activates_default(GTK_ENTRY(abox->entry), TRUE);
501 gtk_widget_show_all(hbox);
503 shade(abox);
506 static void shade(ABox *abox)
508 GtkDialog *dialog = (GtkDialog *) abox;
509 gboolean quiet, on = abox->question;
511 quiet = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(abox->quiet));
513 gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_YES, on);
514 gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_NO, on);
516 if (on && !quiet)
517 gtk_dialog_set_response_sensitive(dialog, RESPONSE_QUIET, TRUE);
518 else
519 gtk_dialog_set_response_sensitive(dialog,
520 RESPONSE_QUIET, FALSE);
522 /* Unsetting the focus means that set_default will put it in the
523 * right place...
525 gtk_window_set_focus(GTK_WINDOW(abox), NULL);
526 /* Note: Gtk+-2.0.0 will segfault on Return if an insensitive
527 * widget is the default.
529 if (quiet)
530 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_YES);
531 else
532 gtk_dialog_set_default_response(dialog, RESPONSE_QUIET);
534 if (abox->entry)
536 gtk_widget_set_sensitive(abox->entry, on);
537 if (on)
538 gtk_widget_grab_focus(abox->entry);