nautilus-3.6.3/src/nautilus-query-editor.c

No issues found

   1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
   2 /*
   3  * Copyright (C) 2005 Red Hat, Inc.
   4  *
   5  * Nautilus is free software; you can redistribute it and/or
   6  * modify it under the terms of the GNU General Public License as
   7  * published by the Free Software Foundation; either version 2 of the
   8  * License, or (at your option) any later version.
   9  *
  10  * Nautilus is distributed in the hope that it will be useful,
  11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13  * General Public License for more details.
  14  *
  15  * You should have received a copy of the GNU General Public
  16  * License along with this program; see the file COPYING.  If not,
  17  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  18  * Boston, MA 02111-1307, USA.
  19  *
  20  * Author: Alexander Larsson <alexl@redhat.com>
  21  *
  22  */
  23 
  24 #include <config.h>
  25 #include "nautilus-query-editor.h"
  26 
  27 #include <string.h>
  28 #include <glib/gi18n.h>
  29 #include <gio/gio.h>
  30 #include <gdk/gdkkeysyms.h>
  31 #include <gtk/gtk.h>
  32 
  33 #include <eel/eel-glib-extensions.h>
  34 #include <libnautilus-private/nautilus-file-utilities.h>
  35 
  36 typedef enum {
  37 	NAUTILUS_QUERY_EDITOR_ROW_TYPE,
  38 	
  39 	NAUTILUS_QUERY_EDITOR_ROW_LAST
  40 } NautilusQueryEditorRowType;
  41 
  42 typedef struct {
  43 	NautilusQueryEditorRowType type;
  44 	NautilusQueryEditor *editor;
  45 	GtkWidget *toolbar;
  46 	GtkWidget *hbox;
  47 	GtkWidget *combo;
  48 
  49 	GtkWidget *type_widget;
  50 } NautilusQueryEditorRow;
  51 
  52 
  53 typedef struct {
  54 	const char *name;
  55 	GtkWidget * (*create_widgets)      (NautilusQueryEditorRow *row);
  56 	void        (*add_to_query)        (NautilusQueryEditorRow *row,
  57 					    NautilusQuery          *query);
  58 	void        (*add_rows_from_query) (NautilusQueryEditor *editor,
  59 					    NautilusQuery *query);
  60 } NautilusQueryEditorRowOps;
  61 
  62 struct NautilusQueryEditorDetails {
  63 	GtkWidget *entry;
  64 	gboolean change_frozen;
  65 	guint typing_timeout_id;
  66 
  67 	GtkWidget *search_current_button;
  68 	GtkWidget *search_all_button;
  69 	char *current_uri;
  70 
  71 	GList *rows;
  72 	gboolean got_preedit;
  73 
  74 	NautilusQuery *query;
  75 };
  76 
  77 enum {
  78 	ACTIVATED,
  79 	CHANGED,
  80 	CANCEL,
  81 	LAST_SIGNAL
  82 }; 
  83 
  84 static guint signals[LAST_SIGNAL];
  85 
  86 static void entry_activate_cb (GtkWidget *entry, NautilusQueryEditor *editor);
  87 static void entry_changed_cb  (GtkWidget *entry, NautilusQueryEditor *editor);
  88 static void nautilus_query_editor_changed_force (NautilusQueryEditor *editor,
  89 						 gboolean             force);
  90 static void nautilus_query_editor_changed (NautilusQueryEditor *editor);
  91 static NautilusQueryEditorRow * nautilus_query_editor_add_row (NautilusQueryEditor *editor,
  92 							       NautilusQueryEditorRowType type);
  93 
  94 static GtkWidget *type_row_create_widgets      (NautilusQueryEditorRow *row);
  95 static void       type_row_add_to_query        (NautilusQueryEditorRow *row,
  96 					        NautilusQuery          *query);
  97 static void       type_add_rows_from_query     (NautilusQueryEditor    *editor,
  98 					        NautilusQuery          *query);
  99 
 100 
 101 
 102 static NautilusQueryEditorRowOps row_type[] = {
 103 	{ N_("File Type"),
 104 	  type_row_create_widgets,
 105 	  type_row_add_to_query,
 106 	  type_add_rows_from_query
 107 	},
 108 };
 109 
 110 G_DEFINE_TYPE (NautilusQueryEditor, nautilus_query_editor, GTK_TYPE_BOX);
 111 
 112 /* taken from gtk/gtktreeview.c */
 113 static void
 114 send_focus_change (GtkWidget *widget,
 115                    GdkDevice *device,
 116 		   gboolean   in)
 117 {
 118 	GdkDeviceManager *device_manager;
 119 	GList *devices, *d;
 120 
 121 	device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
 122 	devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
 123 	devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
 124 	devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_FLOATING));
 125 
 126 	for (d = devices; d; d = d->next) {
 127 		GdkDevice *dev = d->data;
 128 		GdkEvent *fevent;
 129 		GdkWindow *window;
 130 
 131 		if (gdk_device_get_source (dev) != GDK_SOURCE_KEYBOARD)
 132 			continue;
 133 
 134 		window = gtk_widget_get_window (widget);
 135 
 136 		/* Skip non-master keyboards that haven't
 137 		 * selected for events from this window
 138 		 */
 139 		if (gdk_device_get_device_type (dev) != GDK_DEVICE_TYPE_MASTER &&
 140 		    !gdk_window_get_device_events (window, dev))
 141 			continue;
 142 
 143 		fevent = gdk_event_new (GDK_FOCUS_CHANGE);
 144 
 145 		fevent->focus_change.type = GDK_FOCUS_CHANGE;
 146 		fevent->focus_change.window = g_object_ref (window);
 147 		fevent->focus_change.in = in;
 148 		gdk_event_set_device (fevent, device);
 149 
 150 		gtk_widget_send_focus_change (widget, fevent);
 151 
 152 		gdk_event_free (fevent);
 153 	}
 154 
 155 	g_list_free (devices);
 156 }
 157 
 158 static void
 159 entry_focus_hack (GtkWidget *entry,
 160 		  GdkDevice *device)
 161 {
 162 	GtkEntryClass *entry_class;
 163 	GtkWidgetClass *entry_parent_class;
 164 
 165 	/* Grab focus will select all the text.  We don't want that to happen, so we
 166 	 * call the parent instance and bypass the selection change.  This is probably
 167 	 * really non-kosher. */
 168 	entry_class = g_type_class_peek (GTK_TYPE_ENTRY);
 169 	entry_parent_class = g_type_class_peek_parent (entry_class);
 170 	(entry_parent_class->grab_focus) (entry);
 171 
 172 	/* send focus-in event */
 173 	send_focus_change (entry, device, TRUE);
 174 }
 175 
 176 static void
 177 entry_preedit_changed_cb (GtkEntry            *entry,
 178 			  gchar               *preedit,
 179 			  NautilusQueryEditor *editor)
 180 {
 181 	editor->details->got_preedit = TRUE;
 182 }
 183 
 184 gboolean
 185 nautilus_query_editor_handle_event (NautilusQueryEditor *editor,
 186 				    GdkEventKey         *event)
 187 {
 188 	GdkEvent *new_event;
 189 	gboolean handled = FALSE;
 190 	gulong id;
 191 	gboolean retval;
 192 	gboolean text_changed;
 193 	char *old_text;
 194 	const char *new_text;
 195 
 196 	/* if we're focused already, no need to handle the event manually */
 197 	if (gtk_widget_has_focus (editor->details->entry)) {
 198 		return FALSE;
 199 	}
 200 
 201 	/* never handle these events */
 202 	if (event->keyval == GDK_KEY_slash || event->keyval == GDK_KEY_Delete) {
 203 		return FALSE;
 204 	}
 205 
 206 	/* don't activate search for these events */
 207 	if (!gtk_widget_get_visible (GTK_WIDGET (editor)) && event->keyval == GDK_KEY_space) {
 208 		return FALSE;
 209 	}
 210 
 211 	editor->details->got_preedit = FALSE;
 212 	if (!gtk_widget_get_realized (editor->details->entry)) {
 213 		gtk_widget_realize (editor->details->entry);
 214 	}
 215 
 216 	old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (editor->details->entry)));
 217 
 218 	id = g_signal_connect (editor->details->entry, "preedit-changed",
 219 			       G_CALLBACK (entry_preedit_changed_cb), editor);
 220 
 221 	new_event = gdk_event_copy ((GdkEvent *) event);
 222 	g_object_unref (((GdkEventKey *) new_event)->window);
 223 	((GdkEventKey *) new_event)->window = g_object_ref
 224 		(gtk_widget_get_window (editor->details->entry));
 225 	retval = gtk_widget_event (editor->details->entry, new_event);
 226 	gdk_event_free (new_event);
 227 
 228 	g_signal_handler_disconnect (editor->details->entry, id);
 229 
 230 	new_text = gtk_entry_get_text (GTK_ENTRY (editor->details->entry));
 231 	text_changed = strcmp (old_text, new_text) != 0;
 232 	g_free (old_text);
 233 
 234 	handled = (editor->details->got_preedit) || (retval && text_changed);
 235 	editor->details->got_preedit = FALSE;
 236 
 237 	return handled;
 238 }
 239 
 240 static void
 241 row_destroy (NautilusQueryEditorRow *row)
 242 {
 243 	gtk_widget_destroy (row->toolbar);
 244 	g_free (row);
 245 }
 246 
 247 static void
 248 nautilus_query_editor_dispose (GObject *object)
 249 {
 250 	NautilusQueryEditor *editor;
 251 
 252 	editor = NAUTILUS_QUERY_EDITOR (object);
 253 
 254 	if (editor->details->typing_timeout_id > 0) {
 255 		g_source_remove (editor->details->typing_timeout_id);
 256 		editor->details->typing_timeout_id = 0;
 257 	}
 258 
 259 	g_clear_object (&editor->details->query);
 260 
 261 	g_list_free_full (editor->details->rows, (GDestroyNotify) row_destroy);
 262 	editor->details->rows = NULL;
 263 
 264 	G_OBJECT_CLASS (nautilus_query_editor_parent_class)->dispose (object);
 265 }
 266 
 267 static void
 268 nautilus_query_editor_grab_focus (GtkWidget *widget)
 269 {
 270 	NautilusQueryEditor *editor = NAUTILUS_QUERY_EDITOR (widget);
 271 
 272 	if (gtk_widget_get_visible (widget)) {
 273 		entry_focus_hack (editor->details->entry, gtk_get_current_event_device ());
 274 	}
 275 }
 276 
 277 static void
 278 nautilus_query_editor_class_init (NautilusQueryEditorClass *class)
 279 {
 280 	GObjectClass *gobject_class;
 281 	GtkWidgetClass *widget_class;
 282 	GtkBindingSet *binding_set;
 283 
 284 	gobject_class = G_OBJECT_CLASS (class);
 285         gobject_class->dispose = nautilus_query_editor_dispose;
 286 
 287 	widget_class = GTK_WIDGET_CLASS (class);
 288 	widget_class->grab_focus = nautilus_query_editor_grab_focus;
 289 
 290 	signals[CHANGED] =
 291 		g_signal_new ("changed",
 292 		              G_TYPE_FROM_CLASS (class),
 293 		              G_SIGNAL_RUN_LAST,
 294 		              G_STRUCT_OFFSET (NautilusQueryEditorClass, changed),
 295 		              NULL, NULL,
 296 		              g_cclosure_marshal_generic,
 297 		              G_TYPE_NONE, 2, NAUTILUS_TYPE_QUERY, G_TYPE_BOOLEAN);
 298 
 299 	signals[CANCEL] =
 300 		g_signal_new ("cancel",
 301 		              G_TYPE_FROM_CLASS (class),
 302 		              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
 303 		              G_STRUCT_OFFSET (NautilusQueryEditorClass, cancel),
 304 		              NULL, NULL,
 305 		              g_cclosure_marshal_VOID__VOID,
 306 		              G_TYPE_NONE, 0);
 307 
 308 	signals[ACTIVATED] =
 309 		g_signal_new ("activated",
 310 		              G_TYPE_FROM_CLASS (class),
 311 		              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
 312 		              G_STRUCT_OFFSET (NautilusQueryEditorClass, activated),
 313 		              NULL, NULL,
 314 		              g_cclosure_marshal_VOID__VOID,
 315 		              G_TYPE_NONE, 0);
 316 
 317 	binding_set = gtk_binding_set_by_class (class);
 318 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "cancel", 0);
 319 
 320 	g_type_class_add_private (class, sizeof (NautilusQueryEditorDetails));
 321 }
 322 
 323 GFile *
 324 nautilus_query_editor_get_location (NautilusQueryEditor *editor)
 325 {
 326 	GFile *file = NULL;
 327 	if (editor->details->current_uri != NULL)
 328 		file = g_file_new_for_uri (editor->details->current_uri);
 329 	return file;
 330 }
 331 
 332 static void
 333 entry_activate_cb (GtkWidget *entry, NautilusQueryEditor *editor)
 334 {
 335 	g_signal_emit (editor, signals[ACTIVATED], 0);
 336 }
 337 
 338 static gboolean
 339 typing_timeout_cb (gpointer user_data)
 340 {
 341 	NautilusQueryEditor *editor;
 342 
 343 	editor = NAUTILUS_QUERY_EDITOR (user_data);
 344 	editor->details->typing_timeout_id = 0;
 345 
 346 	nautilus_query_editor_changed (editor);
 347 
 348 	return FALSE;
 349 }
 350 
 351 #define TYPING_TIMEOUT 250
 352 
 353 static void
 354 entry_changed_cb (GtkWidget *entry, NautilusQueryEditor *editor)
 355 {
 356 	if (editor->details->change_frozen) {
 357 		return;
 358 	}
 359 
 360 	if (editor->details->typing_timeout_id > 0) {
 361 		g_source_remove (editor->details->typing_timeout_id);
 362 	}
 363 
 364 	editor->details->typing_timeout_id =
 365 		g_timeout_add (TYPING_TIMEOUT,
 366 			       typing_timeout_cb,
 367 			       editor);
 368 }
 369 
 370 /* Type */
 371 
 372 static gboolean
 373 type_separator_func (GtkTreeModel      *model,
 374 		     GtkTreeIter       *iter,
 375 		     gpointer           data)
 376 {
 377 	char *text;
 378 	gboolean res;
 379 	
 380 	gtk_tree_model_get (model, iter, 0, &text, -1);
 381 
 382 	res = text != NULL && strcmp (text, "---") == 0;
 383 	
 384 	g_free (text);
 385 	return res;
 386 }
 387 
 388 struct {
 389 	char *name;
 390 	char *mimetypes[20];
 391 } mime_type_groups[] = {
 392 	{ N_("Documents"),
 393 	  { "application/rtf",
 394 	    "application/msword",
 395 	    "application/vnd.sun.xml.writer",
 396 	    "application/vnd.sun.xml.writer.global",
 397 	    "application/vnd.sun.xml.writer.template",
 398 	    "application/vnd.oasis.opendocument.text",
 399 	    "application/vnd.oasis.opendocument.text-template",
 400 	    "application/x-abiword",
 401 	    "application/x-applix-word",
 402 	    "application/x-mswrite",
 403 	    "application/docbook+xml",
 404 	    "application/x-kword",
 405 	    "application/x-kword-crypt",
 406 	    "application/x-lyx",
 407 	    NULL
 408 	  }
 409 	},
 410 	{ N_("Music"),
 411 	  { "application/ogg",
 412 	    "audio/x-vorbis+ogg",
 413 	    "audio/ac3",
 414 	    "audio/basic",
 415 	    "audio/midi",
 416 	    "audio/x-flac",
 417 	    "audio/mp4",
 418 	    "audio/mpeg",
 419 	    "audio/x-mpeg",
 420 	    "audio/x-ms-asx",
 421 	    "audio/x-pn-realaudio",
 422 	    NULL
 423 	  }
 424 	},
 425 	{ N_("Video"),
 426 	  { "video/mp4",
 427 	    "video/3gpp",
 428 	    "video/mpeg",
 429 	    "video/quicktime",
 430 	    "video/vivo",
 431 	    "video/x-avi",
 432 	    "video/x-mng",
 433 	    "video/x-ms-asf",
 434 	    "video/x-ms-wmv",
 435 	    "video/x-msvideo",
 436 	    "video/x-nsv",
 437 	    "video/x-real-video",
 438 	    NULL
 439 	  }
 440 	},
 441 	{ N_("Picture"),
 442 	  { "application/vnd.oasis.opendocument.image",
 443 	    "application/x-krita",
 444 	    "image/bmp",
 445 	    "image/cgm",
 446 	    "image/gif",
 447 	    "image/jpeg",
 448 	    "image/jpeg2000",
 449 	    "image/png",
 450 	    "image/svg+xml",
 451 	    "image/tiff",
 452 	    "image/x-compressed-xcf",
 453 	    "image/x-pcx",
 454 	    "image/x-photo-cd",
 455 	    "image/x-psd",
 456 	    "image/x-tga",
 457 	    "image/x-xcf",
 458 	    NULL
 459 	  }
 460 	},
 461 	{ N_("Illustration"),
 462 	  { "application/illustrator",
 463 	    "application/vnd.corel-draw",
 464 	    "application/vnd.stardivision.draw",
 465 	    "application/vnd.oasis.opendocument.graphics",
 466 	    "application/x-dia-diagram",
 467 	    "application/x-karbon",
 468 	    "application/x-killustrator",
 469 	    "application/x-kivio",
 470 	    "application/x-kontour",
 471 	    "application/x-wpg",
 472 	    NULL
 473 	  }
 474 	},
 475 	{ N_("Spreadsheet"),
 476 	  { "application/vnd.lotus-1-2-3",
 477 	    "application/vnd.ms-excel",
 478 	    "application/vnd.stardivision.calc",
 479 	    "application/vnd.sun.xml.calc",
 480 	    "application/vnd.oasis.opendocument.spreadsheet",
 481 	    "application/x-applix-spreadsheet",
 482 	    "application/x-gnumeric",
 483 	    "application/x-kspread",
 484 	    "application/x-kspread-crypt",
 485 	    "application/x-quattropro",
 486 	    "application/x-sc",
 487 	    "application/x-siag",
 488 	    NULL
 489 	  }
 490 	},
 491 	{ N_("Presentation"),
 492 	  { "application/vnd.ms-powerpoint",
 493 	    "application/vnd.sun.xml.impress",
 494 	    "application/vnd.oasis.opendocument.presentation",
 495 	    "application/x-magicpoint",
 496 	    "application/x-kpresenter",
 497 	    NULL
 498 	  }
 499 	},
 500 	{ N_("Pdf / Postscript"),
 501 	  { "application/pdf",
 502 	    "application/postscript",
 503 	    "application/x-dvi",
 504 	    "image/x-eps",
 505 	    NULL
 506 	  }
 507 	},
 508 	{ N_("Text File"),
 509 	  { "text/plain",
 510 	    NULL
 511 	  }
 512 	}
 513 };
 514 
 515 static void
 516 type_add_custom_type (NautilusQueryEditorRow *row,
 517 		      const char *mime_type,
 518 		      const char *description,
 519 		      GtkTreeIter *iter)
 520 {
 521 	GtkTreeModel *model;
 522 	GtkListStore *store;
 523 	
 524 	model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget));
 525 	store = GTK_LIST_STORE (model);
 526 
 527 	gtk_list_store_append (store, iter);
 528 	gtk_list_store_set (store, iter,
 529 			    0, description,
 530 			    2, mime_type,
 531 			    -1);
 532 }
 533 
 534 
 535 static void
 536 type_combo_changed (GtkComboBox *combo_box, NautilusQueryEditorRow *row)
 537 {
 538 	GtkTreeIter iter;
 539 	gboolean other;
 540 	GtkTreeModel *model;
 541 
 542 	if (!gtk_combo_box_get_active_iter  (GTK_COMBO_BOX (row->type_widget),
 543 					     &iter)) {
 544 		return;
 545 	}
 546 
 547 	model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget));
 548 	gtk_tree_model_get (model, &iter, 3, &other, -1);
 549 
 550 	if (other) {
 551 		GList *mime_infos, *l;
 552 		GtkWidget *dialog;
 553 		GtkWidget *scrolled, *treeview;
 554 		GtkListStore *store;
 555 		GtkTreeViewColumn *column;
 556 		GtkCellRenderer *renderer;
 557 		GtkWidget *toplevel;
 558 		GtkTreeSelection *selection;
 559 
 560 		mime_infos = g_content_types_get_registered ();
 561 
 562 		store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
 563 		for (l = mime_infos; l != NULL; l = l->next) {
 564 			GtkTreeIter iter;
 565 			char *mime_type = l->data;
 566 			char *description;
 567 
 568 			description = g_content_type_get_description (mime_type);
 569 			if (description == NULL) {
 570 				description = g_strdup (mime_type);
 571 			}
 572 			
 573 			gtk_list_store_append (store, &iter);
 574 			gtk_list_store_set (store, &iter,
 575 					    0, description,
 576 					    1, mime_type,
 577 					    -1);
 578 			
 579 			g_free (mime_type);
 580 			g_free (description);
 581 		}
 582 		g_list_free (mime_infos);
 583 		
 584 
 585 		
 586 		toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
 587 		dialog = gtk_dialog_new_with_buttons (_("Select type"),
 588 						      GTK_WINDOW (toplevel),
 589 						      0,
 590 						      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 591 						      _("Select"), GTK_RESPONSE_OK,
 592 						      NULL);
 593 		gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 600);
 594 			
 595 		scrolled = gtk_scrolled_window_new (NULL, NULL);
 596 		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
 597 						GTK_POLICY_AUTOMATIC,
 598 						GTK_POLICY_AUTOMATIC);
 599 		gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
 600 						     GTK_SHADOW_IN);
 601 		
 602 		gtk_widget_show (scrolled);
 603 		gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), scrolled, TRUE, TRUE, 6);
 604 
 605 		treeview = gtk_tree_view_new ();
 606 		gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
 607 					 GTK_TREE_MODEL (store));
 608 		gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), 0,
 609 						      GTK_SORT_ASCENDING);
 610 		
 611 		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
 612 		gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
 613 
 614 
 615 		renderer = gtk_cell_renderer_text_new ();
 616 		column = gtk_tree_view_column_new_with_attributes ("Name",
 617 								   renderer,
 618 								   "text",
 619 								   0,
 620 								   NULL);
 621 		gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
 622 		gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
 623 		
 624 		gtk_widget_show (treeview);
 625 		gtk_container_add (GTK_CONTAINER (scrolled), treeview);
 626 
 627 		if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
 628 			char *mimetype, *description;
 629 
 630 			gtk_tree_selection_get_selected (selection, NULL, &iter);
 631 			gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
 632 					    0, &description,
 633 					    1, &mimetype,
 634 					    -1);
 635 
 636 			type_add_custom_type (row, mimetype, description, &iter);
 637 			gtk_combo_box_set_active_iter  (GTK_COMBO_BOX (row->type_widget),
 638 							&iter);
 639 		} else {
 640 			gtk_combo_box_set_active (GTK_COMBO_BOX (row->type_widget), 0);
 641 		}
 642 
 643 		gtk_widget_destroy (dialog);
 644 	}
 645 	
 646 	nautilus_query_editor_changed (row->editor);
 647 }
 648 
 649 static GtkWidget *
 650 type_row_create_widgets (NautilusQueryEditorRow *row)
 651 {
 652 	GtkWidget *combo;
 653 	GtkCellRenderer *cell;
 654 	GtkListStore *store;
 655 	GtkTreeIter iter;
 656 	int i;
 657 
 658 	store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN);
 659 	combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
 660 	g_object_unref (store);
 661 	
 662 	cell = gtk_cell_renderer_text_new ();
 663 	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
 664 	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
 665 					"text", 0,
 666 					NULL);
 667 	gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo),
 668 					      type_separator_func,
 669 					      NULL, NULL);
 670 
 671 	gtk_list_store_append (store, &iter);
 672 	gtk_list_store_set (store, &iter, 0, _("Any"), -1);
 673 	gtk_list_store_append (store, &iter);
 674 	gtk_list_store_set (store, &iter, 0, "---",  -1);
 675 
 676 	for (i = 0; i < G_N_ELEMENTS (mime_type_groups); i++) {
 677 		gtk_list_store_append (store, &iter);
 678 		gtk_list_store_set (store, &iter,
 679 				    0, gettext (mime_type_groups[i].name),
 680 				    1, mime_type_groups[i].mimetypes,
 681 				    -1);
 682 	}
 683 
 684 	gtk_list_store_append (store, &iter);
 685 	gtk_list_store_set (store, &iter, 0, "---",  -1);
 686 	gtk_list_store_append (store, &iter);
 687 	gtk_list_store_set (store, &iter, 0, _("Other Type..."), 3, TRUE, -1);
 688 
 689 	gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
 690 	
 691 	g_signal_connect (combo, "changed",
 692 			  G_CALLBACK (type_combo_changed),
 693 			  row);
 694 
 695 	gtk_widget_show (combo);
 696 	
 697 	gtk_box_pack_start (GTK_BOX (row->hbox), combo, FALSE, FALSE, 0);
 698 	
 699 	return combo;
 700 }
 701 
 702 static void
 703 type_row_add_to_query (NautilusQueryEditorRow *row,
 704 		       NautilusQuery          *query)
 705 {
 706 	GtkTreeIter iter;
 707 	char **mimetypes;
 708 	char *mimetype;
 709 	GtkTreeModel *model;
 710 
 711 	if (!gtk_combo_box_get_active_iter  (GTK_COMBO_BOX (row->type_widget),
 712 					     &iter)) {
 713 		return;
 714 	}
 715 
 716 	model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget));
 717 	gtk_tree_model_get (model, &iter, 1, &mimetypes, 2, &mimetype, -1);
 718 
 719 	if (mimetypes != NULL) {
 720 		while (*mimetypes != NULL) {
 721 			nautilus_query_add_mime_type (query, *mimetypes);
 722 			mimetypes++;
 723 		}
 724 	}
 725 	if (mimetype) {
 726 		nautilus_query_add_mime_type (query, mimetype);
 727 		g_free (mimetype);
 728 	}
 729 }
 730 
 731 static gboolean
 732 all_group_types_in_list (char **group_types, GList *mime_types)
 733 {
 734 	GList *l;
 735 	char **group_type;
 736 	char *mime_type;
 737 	gboolean found;
 738 
 739 	group_type = group_types;
 740 	while (*group_type != NULL) {
 741 		found = FALSE;
 742 
 743 		for (l = mime_types; l != NULL; l = l->next) {
 744 			mime_type = l->data;
 745 
 746 			if (strcmp (mime_type, *group_type) == 0) {
 747 				found = TRUE;
 748 				break;
 749 			}
 750 		}
 751 		
 752 		if (!found) {
 753 			return FALSE;
 754 		}
 755 		group_type++;
 756 	}
 757 	return TRUE;
 758 }
 759 
 760 static GList *
 761 remove_group_types_from_list (char **group_types, GList *mime_types)
 762 {
 763 	GList *l, *next;
 764 	char **group_type;
 765 	char *mime_type;
 766 
 767 	group_type = group_types;
 768 	while (*group_type != NULL) {
 769 		for (l = mime_types; l != NULL; l = next) {
 770 			mime_type = l->data;
 771 			next = l->next;
 772 
 773 			if (strcmp (mime_type, *group_type) == 0) {
 774 				mime_types = g_list_remove_link (mime_types, l);
 775 				g_free (mime_type);
 776 				break;
 777 			}
 778 		}
 779 		
 780 		group_type++;
 781 	}
 782 	return mime_types;
 783 }
 784 
 785 
 786 static void
 787 type_add_rows_from_query (NautilusQueryEditor    *editor,
 788 			  NautilusQuery          *query)
 789 {
 790 	GList *mime_types;
 791 	char *mime_type;
 792 	const char *desc;
 793 	NautilusQueryEditorRow *row;
 794 	GtkTreeIter iter;
 795 	int i;
 796 	GtkTreeModel *model;
 797 	GList *l;
 798 
 799 	mime_types = nautilus_query_get_mime_types (query);
 800 
 801 	if (mime_types == NULL) {
 802 		return;
 803 	}
 804 	
 805 	for (i = 0; i < G_N_ELEMENTS (mime_type_groups); i++) {
 806 		if (all_group_types_in_list (mime_type_groups[i].mimetypes,
 807 					     mime_types)) {
 808 			mime_types = remove_group_types_from_list (mime_type_groups[i].mimetypes,
 809 								   mime_types);
 810 
 811 			row = nautilus_query_editor_add_row (editor,
 812 							     NAUTILUS_QUERY_EDITOR_ROW_TYPE);
 813 
 814 			model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget));
 815 
 816 			gtk_tree_model_iter_nth_child (model, &iter, NULL, i + 2);
 817 			gtk_combo_box_set_active_iter  (GTK_COMBO_BOX (row->type_widget),
 818 							&iter);
 819 		}
 820 	}
 821 
 822 	for (l = mime_types; l != NULL; l = l->next) {
 823 		mime_type = l->data;
 824 
 825 		desc = g_content_type_get_description (mime_type);
 826 		if (desc == NULL) {
 827 			desc = mime_type;
 828 		}
 829 
 830 		row = nautilus_query_editor_add_row (editor,
 831 						     NAUTILUS_QUERY_EDITOR_ROW_TYPE);
 832 		
 833 		type_add_custom_type (row, mime_type, desc, &iter);
 834 		gtk_combo_box_set_active_iter  (GTK_COMBO_BOX (row->type_widget),
 835 						&iter);
 836 	}
 837 
 838 	g_list_free_full (mime_types, g_free);
 839 }
 840 
 841 /* End of row types */
 842 
 843 static NautilusQueryEditorRowType
 844 get_next_free_type (NautilusQueryEditor *editor)
 845 {
 846 	NautilusQueryEditorRow *row;
 847 	NautilusQueryEditorRowType type;
 848 	gboolean found;
 849 	GList *l;
 850 
 851 	
 852 	for (type = 0; type < NAUTILUS_QUERY_EDITOR_ROW_LAST; type++) {
 853 		found = FALSE;
 854 		for (l = editor->details->rows; l != NULL; l = l->next) {
 855 			row = l->data;
 856 			if (row->type == type) {
 857 				found = TRUE;
 858 				break;
 859 			}
 860 		}
 861 		if (!found) {
 862 			return type;
 863 		}
 864 	}
 865 	return NAUTILUS_QUERY_EDITOR_ROW_TYPE;
 866 }
 867 
 868 static void
 869 remove_row_cb (GtkButton *clicked_button, NautilusQueryEditorRow *row)
 870 {
 871 	NautilusQueryEditor *editor;
 872 
 873 	editor = row->editor;
 874 	editor->details->rows = g_list_remove (editor->details->rows, row);
 875 	row_destroy (row);
 876 
 877 	nautilus_query_editor_changed (editor);
 878 }
 879 
 880 static void
 881 create_type_widgets (NautilusQueryEditorRow *row)
 882 {
 883 	row->type_widget = row_type[row->type].create_widgets (row);
 884 }
 885 
 886 static void
 887 row_type_combo_changed_cb (GtkComboBox *combo_box, NautilusQueryEditorRow *row)
 888 {
 889 	NautilusQueryEditorRowType type;
 890 
 891 	type = gtk_combo_box_get_active (combo_box);
 892 
 893 	if (type == row->type) {
 894 		return;
 895 	}
 896 
 897 	if (row->type_widget != NULL) {
 898 		gtk_widget_destroy (row->type_widget);
 899 		row->type_widget = NULL;
 900 	}
 901 
 902 	row->type = type;
 903 	
 904 	create_type_widgets (row);
 905 
 906 	nautilus_query_editor_changed (row->editor);
 907 }
 908 
 909 static NautilusQueryEditorRow *
 910 nautilus_query_editor_add_row (NautilusQueryEditor *editor,
 911 			       NautilusQueryEditorRowType type)
 912 {
 913 	GtkWidget *hbox, *button, *image, *combo;
 914 	GtkToolItem *item;
 915 	NautilusQueryEditorRow *row;
 916 	int i;
 917 
 918 	row = g_new0 (NautilusQueryEditorRow, 1);
 919 	row->editor = editor;
 920 	row->type = type;
 921 
 922 	/* create the toolbar and the box container for its contents */
 923 	row->toolbar = gtk_toolbar_new ();
 924 	gtk_box_pack_start (GTK_BOX (editor), row->toolbar, TRUE, TRUE, 0);
 925 
 926 	item = gtk_tool_item_new ();
 927 	gtk_tool_item_set_expand (item, TRUE);
 928 	gtk_toolbar_insert (GTK_TOOLBAR (row->toolbar), item, -1);
 929 
 930 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 931 	gtk_container_add (GTK_CONTAINER (item), hbox);
 932 	row->hbox = hbox;
 933 
 934 	/* create the criterion selector combobox */
 935 	combo = gtk_combo_box_text_new ();
 936 	row->combo = combo;
 937 	for (i = 0; i < NAUTILUS_QUERY_EDITOR_ROW_LAST; i++) {
 938 		gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), gettext (row_type[i].name));
 939 	}
 940 	gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
 941 
 942 	gtk_combo_box_set_active (GTK_COMBO_BOX (combo), row->type);
 943 
 944 	editor->details->rows = g_list_append (editor->details->rows, row);
 945 
 946 	g_signal_connect (combo, "changed",
 947 			  G_CALLBACK (row_type_combo_changed_cb), row);
 948 	
 949 	create_type_widgets (row);
 950 
 951 	/* create the remove row button */
 952 	button = gtk_button_new ();
 953 	gtk_style_context_add_class (gtk_widget_get_style_context (button),
 954 				     GTK_STYLE_CLASS_RAISED);
 955 	gtk_widget_set_tooltip_text (button,
 956 				     _("Remove this criterion from the search"));
 957 	gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
 958 
 959 	image = gtk_image_new_from_icon_name ("window-close-symbolic",
 960 					      GTK_ICON_SIZE_MENU);
 961 	gtk_container_add (GTK_CONTAINER (button), image);
 962 
 963 	g_signal_connect (button, "clicked",
 964 			  G_CALLBACK (remove_row_cb), row);
 965 
 966 	/* show everything */
 967 	gtk_widget_show_all (row->toolbar);
 968 
 969 	return row;
 970 }
 971 
 972 static void
 973 add_new_row_cb (GtkButton *clicked_button, NautilusQueryEditor *editor)
 974 {
 975 	nautilus_query_editor_add_row (editor, get_next_free_type (editor));
 976 	nautilus_query_editor_changed (editor);
 977 }
 978 
 979 static void
 980 nautilus_query_editor_init (NautilusQueryEditor *editor)
 981 {
 982 	editor->details = G_TYPE_INSTANCE_GET_PRIVATE (editor, NAUTILUS_TYPE_QUERY_EDITOR,
 983 						       NautilusQueryEditorDetails);
 984 
 985 	gtk_orientable_set_orientation (GTK_ORIENTABLE (editor), GTK_ORIENTATION_VERTICAL);
 986 }
 987 
 988 static void
 989 on_location_button_toggled (GtkToggleButton     *button,
 990 		       NautilusQueryEditor *editor)
 991 {
 992 	nautilus_query_editor_changed (editor);
 993 }
 994 
 995 static gboolean
 996 entry_key_press_event_cb (GtkWidget           *widget,
 997 			  GdkEventKey         *event,
 998 			  NautilusQueryEditor *editor)
 999 {
1000 	if (event->keyval == GDK_KEY_Down) {
1001 		nautilus_window_grab_focus (NAUTILUS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (widget))));
1002 	}
1003 	return FALSE;
1004 }
1005 
1006 static void
1007 setup_widgets (NautilusQueryEditor *editor)
1008 {
1009 	GtkToolItem *item;
1010 	GtkWidget *toolbar, *button_box, *hbox;
1011 	GtkWidget *button, *image;
1012 
1013 	/* create the toolbar and the box container for its contents */
1014 	toolbar = gtk_toolbar_new ();
1015 	gtk_style_context_add_class (gtk_widget_get_style_context (toolbar),
1016 				     GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
1017 	gtk_box_pack_start (GTK_BOX (editor), toolbar, TRUE, TRUE, 0);
1018 
1019 	item = gtk_tool_item_new ();
1020 	gtk_tool_item_set_expand (item, TRUE);
1021 	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
1022 
1023 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1024 	gtk_container_add (GTK_CONTAINER (item), hbox);
1025 
1026 	/* create the search entry */
1027 	editor->details->entry = gtk_search_entry_new ();
1028 	gtk_box_pack_start (GTK_BOX (hbox), editor->details->entry, TRUE, TRUE, 0);
1029 
1030 	g_signal_connect (editor->details->entry, "key-press-event",
1031 			  G_CALLBACK (entry_key_press_event_cb), editor);
1032 	g_signal_connect (editor->details->entry, "activate",
1033 			  G_CALLBACK (entry_activate_cb), editor);
1034 	g_signal_connect (editor->details->entry, "changed",
1035 			  G_CALLBACK (entry_changed_cb), editor);
1036 
1037 	/* create the Current/All Files selector */
1038 	editor->details->search_current_button = gtk_radio_button_new_with_label (NULL, _("Current"));
1039 	gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (editor->details->search_current_button), FALSE);
1040 	editor->details->search_all_button = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (editor->details->search_current_button),
1041 											  _("All Files"));
1042 	gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (editor->details->search_all_button), FALSE);
1043 
1044 	/* connect to the signal only on one of the two, since they're mutually exclusive */
1045 	g_signal_connect (editor->details->search_current_button, "toggled",
1046 			  G_CALLBACK (on_location_button_toggled), editor);
1047 
1048 	button_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1049 	gtk_box_pack_start (GTK_BOX (hbox), button_box, FALSE, FALSE, 0);
1050 	gtk_style_context_add_class (gtk_widget_get_style_context (button_box),
1051 				     GTK_STYLE_CLASS_LINKED);
1052 	gtk_style_context_add_class (gtk_widget_get_style_context (button_box),
1053 				     GTK_STYLE_CLASS_RAISED);
1054 
1055 	gtk_box_pack_start (GTK_BOX (button_box), editor->details->search_current_button, FALSE, FALSE, 0);
1056 	gtk_box_pack_start (GTK_BOX (button_box), editor->details->search_all_button, FALSE, FALSE, 0);
1057 
1058 	/* finally, create the add new row button */
1059 	button = gtk_button_new ();
1060 	gtk_style_context_add_class (gtk_widget_get_style_context (button),
1061 				     GTK_STYLE_CLASS_RAISED);
1062 	gtk_widget_set_tooltip_text (button,
1063 				     _("Add a new criterion to this search"));
1064 	gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1065 
1066 	image = gtk_image_new_from_icon_name ("list-add-symbolic",
1067 					      GTK_ICON_SIZE_MENU);
1068 	gtk_container_add (GTK_CONTAINER (button), image);
1069 
1070 	g_signal_connect (button, "clicked",
1071 			  G_CALLBACK (add_new_row_cb), editor);
1072 
1073 	/* show everything */
1074 	gtk_widget_show_all (toolbar);
1075 }
1076 
1077 static void
1078 nautilus_query_editor_changed_force (NautilusQueryEditor *editor, gboolean force_reload)
1079 {
1080 	NautilusQuery *query;
1081 
1082 	if (editor->details->change_frozen) {
1083 		return;
1084 	}
1085 
1086 	query = nautilus_query_editor_get_query (editor);
1087 	g_signal_emit (editor, signals[CHANGED], 0,
1088 		       query, force_reload);
1089 	g_object_unref (query);
1090 }
1091 
1092 static void
1093 nautilus_query_editor_changed (NautilusQueryEditor *editor)
1094 {
1095 	nautilus_query_editor_changed_force (editor, TRUE);
1096 }
1097 
1098 static void
1099 add_location_to_query (NautilusQueryEditor *editor,
1100 		       NautilusQuery       *query)
1101 {
1102 	char *uri;
1103 
1104 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (editor->details->search_all_button))) {
1105 		uri = nautilus_get_home_directory_uri ();
1106 	} else {
1107 		uri = g_strdup (editor->details->current_uri);
1108 	}
1109 
1110 	nautilus_query_set_location (query, uri);
1111 	g_free (uri);
1112 }
1113 
1114 NautilusQuery *
1115 nautilus_query_editor_get_query (NautilusQueryEditor *editor)
1116 {
1117 	const char *query_text;
1118 	NautilusQuery *query;
1119 	GList *l;
1120 	NautilusQueryEditorRow *row;
1121 
1122 	if (editor == NULL || editor->details == NULL || editor->details->entry == NULL) {
1123 		return NULL;
1124 	}
1125 
1126 	query_text = gtk_entry_get_text (GTK_ENTRY (editor->details->entry));
1127 
1128 	query = nautilus_query_new ();
1129 	nautilus_query_set_text (query, query_text);
1130 
1131 	add_location_to_query (editor, query);
1132 
1133 	for (l = editor->details->rows; l != NULL; l = l->next) {
1134 		row = l->data;
1135 		
1136 		row_type[row->type].add_to_query (row, query);
1137 	}
1138 	
1139 	return query;
1140 }
1141 
1142 GtkWidget *
1143 nautilus_query_editor_new (void)
1144 {
1145 	GtkWidget *editor;
1146 
1147 	editor = g_object_new (NAUTILUS_TYPE_QUERY_EDITOR, NULL);
1148 	setup_widgets (NAUTILUS_QUERY_EDITOR (editor));
1149 
1150 	return editor;
1151 }
1152 
1153 static void
1154 update_location (NautilusQueryEditor *editor)
1155 {
1156 	NautilusFile *file;
1157 	GtkWidget *label;
1158 
1159 	file = nautilus_file_get_by_uri (editor->details->current_uri);
1160 
1161 	if (file != NULL) {
1162 		char *name;
1163 		if (nautilus_file_is_home (file)) {
1164 			name = g_strdup (_("Home"));
1165 		} else {
1166 			char *filename;
1167 			filename = nautilus_file_get_display_name (file);
1168 			name = g_strdup_printf ("\342\200\234%s\342\200\235", filename);
1169 			g_free (filename);
1170 		}
1171 
1172 		gtk_button_set_label (GTK_BUTTON (editor->details->search_current_button), name);
1173 		g_free (name);
1174 
1175 		label = gtk_bin_get_child (GTK_BIN (editor->details->search_current_button));
1176 		gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_MIDDLE);
1177 		g_object_set (label, "max-width-chars", 30, NULL);
1178 
1179 		nautilus_file_unref (file);
1180 	}
1181 }
1182 
1183 void
1184 nautilus_query_editor_set_location (NautilusQueryEditor *editor,
1185 				    GFile               *location)
1186 {
1187 	g_free (editor->details->current_uri);
1188 	editor->details->current_uri = g_file_get_uri (location);
1189 	update_location (editor);
1190 }
1191 
1192 static void
1193 update_rows (NautilusQueryEditor *editor,
1194 	     NautilusQuery       *query)
1195 {
1196 	NautilusQueryEditorRowType type;
1197 
1198 	/* if we were just created, set the rows from query,
1199 	 * otherwise, re-use the query setting we have already.
1200 	 */
1201 	if (query != NULL && editor->details->query == NULL) {
1202 		for (type = 0; type < NAUTILUS_QUERY_EDITOR_ROW_LAST; type++) {
1203 			row_type[type].add_rows_from_query (editor, query);
1204 		}
1205 	} else if (query == NULL && editor->details->query != NULL) {
1206 		g_list_free_full (editor->details->rows, (GDestroyNotify) row_destroy);
1207 		editor->details->rows = NULL;
1208 	}
1209 }
1210 
1211 void
1212 nautilus_query_editor_set_query (NautilusQueryEditor	*editor,
1213 				 NautilusQuery		*query)
1214 {
1215 	char *text = NULL;
1216 
1217 	if (query != NULL) {
1218 		text = nautilus_query_get_text (query);
1219 	}
1220 
1221 	if (!text) {
1222 		text = g_strdup ("");
1223 	}
1224 
1225 	editor->details->change_frozen = TRUE;
1226 	gtk_entry_set_text (GTK_ENTRY (editor->details->entry), text);
1227 
1228 	g_free (editor->details->current_uri);
1229 	editor->details->current_uri = NULL;
1230 
1231 	update_rows (editor, query);
1232 	g_clear_object (&editor->details->query);
1233 
1234 	if (query != NULL) {
1235 		editor->details->query = g_object_ref (query);
1236 		editor->details->current_uri = nautilus_query_get_location (query);
1237 		update_location (editor);
1238 	}
1239 
1240 	editor->details->change_frozen = FALSE;
1241 }