/* Copyright (C) 2008 Christian Dywan This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. See the file COPYING for the full license text. */ #include "midori-searchaction.h" #include "gtkiconentry.h" #include "marshal.h" #include "sokoke.h" #include #include #include struct _MidoriSearchAction { GtkAction parent_instance; KatzeArray* search_engines; KatzeItem* current_item; KatzeItem* default_item; gchar* text; GtkWidget* last_proxy; GtkWidget* dialog; GtkWidget* treeview; GtkWidget* edit_button; GtkWidget* remove_button; GtkWidget* default_button; }; struct _MidoriSearchActionClass { GtkActionClass parent_class; }; G_DEFINE_TYPE (MidoriSearchAction, midori_search_action, GTK_TYPE_ACTION) enum { PROP_0, PROP_SEARCH_ENGINES, PROP_CURRENT_ITEM, PROP_DEFAULT_ITEM, PROP_TEXT, PROP_DIALOG }; enum { SUBMIT, FOCUS_OUT, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; static void midori_search_action_finalize (GObject* object); static void midori_search_action_set_property (GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec); static void midori_search_action_get_property (GObject* object, guint prop_id, GValue* value, GParamSpec* pspec); static void midori_search_action_activate (GtkAction* object); static GtkWidget* midori_search_action_create_tool_item (GtkAction* action); static void midori_search_action_connect_proxy (GtkAction* action, GtkWidget* proxy); static void midori_search_action_disconnect_proxy (GtkAction* action, GtkWidget* proxy); static void midori_search_action_class_init (MidoriSearchActionClass* class) { GObjectClass* gobject_class; GtkActionClass* action_class; signals[SUBMIT] = g_signal_new ("submit", G_TYPE_FROM_CLASS (class), (GSignalFlags) (G_SIGNAL_RUN_LAST), 0, 0, NULL, midori_cclosure_marshal_VOID__STRING_BOOLEAN, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_BOOLEAN); signals[FOCUS_OUT] = g_signal_new ("focus-out", G_TYPE_FROM_CLASS (class), (GSignalFlags) (G_SIGNAL_RUN_LAST), 0, 0, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); gobject_class = G_OBJECT_CLASS (class); gobject_class->finalize = midori_search_action_finalize; gobject_class->set_property = midori_search_action_set_property; gobject_class->get_property = midori_search_action_get_property; action_class = GTK_ACTION_CLASS (class); action_class->activate = midori_search_action_activate; action_class->create_tool_item = midori_search_action_create_tool_item; action_class->connect_proxy = midori_search_action_connect_proxy; action_class->disconnect_proxy = midori_search_action_disconnect_proxy; g_object_class_install_property (gobject_class, PROP_SEARCH_ENGINES, g_param_spec_object ( "search-engines", "Search Engines", "The list of search engines", KATZE_TYPE_ARRAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_CURRENT_ITEM, g_param_spec_object ( "current-item", "Current Item", "The currently selected item", KATZE_TYPE_ITEM, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * MidoriSearchAction:default-item: * * The default search engine. * * Since: 0.1.6 */ g_object_class_install_property (gobject_class, PROP_DEFAULT_ITEM, g_param_spec_object ( "default-item", "Default Item", "The default search engine", KATZE_TYPE_ITEM, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_TEXT, g_param_spec_string ( "text", "Text", "The current text typed in the entry", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_DIALOG, g_param_spec_object ( "dialog", "Dialog", "A dialog to manage search engines", GTK_TYPE_DIALOG, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } static void midori_search_action_init (MidoriSearchAction* search_action) { search_action->search_engines = NULL; search_action->current_item = NULL; search_action->default_item = NULL; search_action->text = NULL; search_action->last_proxy = NULL; search_action->dialog = NULL; search_action->treeview = NULL; search_action->edit_button = NULL; search_action->remove_button = NULL; search_action->default_button = NULL; } static void midori_search_action_finalize (GObject* object) { MidoriSearchAction* search_action = MIDORI_SEARCH_ACTION (object); katze_assign (search_action->text, NULL); G_OBJECT_CLASS (midori_search_action_parent_class)->finalize (object); } static void midori_search_action_set_property (GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec) { MidoriSearchAction* search_action = MIDORI_SEARCH_ACTION (object); switch (prop_id) { case PROP_SEARCH_ENGINES: midori_search_action_set_search_engines (search_action, g_value_get_object (value)); break; case PROP_CURRENT_ITEM: midori_search_action_set_current_item (search_action, g_value_get_object (value)); break; case PROP_DEFAULT_ITEM: midori_search_action_set_default_item (search_action, g_value_get_object (value)); break; case PROP_TEXT: midori_search_action_set_text (search_action, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void midori_search_action_get_property (GObject* object, guint prop_id, GValue* value, GParamSpec* pspec) { MidoriSearchAction* search_action = MIDORI_SEARCH_ACTION (object); switch (prop_id) { case PROP_SEARCH_ENGINES: g_value_set_object (value, search_action->search_engines); break; case PROP_CURRENT_ITEM: g_value_set_object (value, search_action->current_item); break; case PROP_DEFAULT_ITEM: g_value_set_object (value, search_action->default_item); break; case PROP_TEXT: g_value_set_string (value, search_action->text); break; case PROP_DIALOG: g_value_set_object (value, midori_search_action_get_dialog (search_action)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void midori_search_action_activate (GtkAction* action) { GSList* proxies; GtkWidget* alignment; GtkWidget* entry; proxies = gtk_action_get_proxies (action); if (!proxies) return; do if (GTK_IS_TOOL_ITEM (proxies->data)) { alignment = gtk_bin_get_child (GTK_BIN (proxies->data)); entry = gtk_bin_get_child (GTK_BIN (alignment)); /* Obviously only one widget can end up with the focus. Yet we can't predict which one that is, can we? */ gtk_widget_grab_focus (entry); MIDORI_SEARCH_ACTION (action)->last_proxy = proxies->data; } while ((proxies = g_slist_next (proxies))); if (GTK_ACTION_CLASS (midori_search_action_parent_class)->activate) GTK_ACTION_CLASS (midori_search_action_parent_class)->activate (action); } static GtkWidget* midori_search_action_create_tool_item (GtkAction* action) { GtkWidget* toolitem; GtkWidget* entry; GtkWidget* alignment; toolitem = GTK_WIDGET (gtk_tool_item_new ()); entry = gtk_icon_entry_new (); gtk_icon_entry_set_icon_highlight (GTK_ICON_ENTRY (entry), GTK_ICON_ENTRY_PRIMARY, TRUE); alignment = gtk_alignment_new (0, 0.5, 1, 0.1); gtk_container_add (GTK_CONTAINER (alignment), entry); gtk_widget_show (entry); gtk_container_add (GTK_CONTAINER (toolitem), alignment); gtk_widget_show (alignment); MIDORI_SEARCH_ACTION (action)->last_proxy = GTK_WIDGET (toolitem); return toolitem; } static void _midori_search_action_move_index (MidoriSearchAction* search_action, guint n) { gint i; KatzeItem* item; i = katze_array_get_item_index (search_action->search_engines, search_action->current_item); item = katze_array_get_nth_item (search_action->search_engines, i + n); if (item) midori_search_action_set_current_item (search_action, item); } static gboolean midori_search_action_key_press_event_cb (GtkWidget* entry, GdkEventKey* event, MidoriSearchAction* search_action) { const gchar* text; switch (event->keyval) { case GDK_ISO_Enter: case GDK_KP_Enter: case GDK_Return: text = gtk_entry_get_text (GTK_ENTRY (entry)); g_signal_emit (search_action, signals[SUBMIT], 0, text, MIDORI_MOD_NEW_TAB (event->state)); search_action->last_proxy = entry; return TRUE; case GDK_Up: if (MIDORI_MOD_SCROLL (event->state)) _midori_search_action_move_index (search_action, - 1); return TRUE; case GDK_Down: if (MIDORI_MOD_SCROLL (event->state)) _midori_search_action_move_index (search_action, + 1); return TRUE; } return FALSE; } static gboolean midori_search_action_focus_out_event_cb (GtkWidget* widget, GdkEventKey* event, GtkAction* action) { g_signal_emit (action, signals[FOCUS_OUT], 0); return FALSE; } static void midori_search_action_engine_activate_cb (GtkWidget* menuitem, MidoriSearchAction* search_action) { KatzeItem* item; item = (KatzeItem*)g_object_get_data (G_OBJECT (menuitem), "engine"); midori_search_action_set_current_item (search_action, item); } static void midori_search_action_manage_activate_cb (GtkWidget* menuitem, MidoriSearchAction* search_action) { GtkWidget* dialog; dialog = midori_search_action_get_dialog (search_action); if (gtk_widget_get_visible (dialog)) gtk_window_present (GTK_WINDOW (dialog)); else gtk_widget_show (dialog); } /* Private function, used by MidoriView */ /* static */ GdkPixbuf* midori_search_action_get_icon (KatzeItem* item, GtkWidget* widget, const gchar** icon_name) { const gchar* icon; if ((icon = katze_item_get_uri (item)) && (g_strstr_len (icon, 8, "://"))) return katze_load_cached_icon (icon, widget); if ((icon = katze_item_get_icon (item)) && *icon) { GdkScreen* screen; GtkIconTheme* icon_theme; screen = gtk_widget_get_screen (widget); icon_theme = gtk_icon_theme_get_for_screen (screen); if (gtk_icon_theme_has_icon (icon_theme, icon)) { *icon_name = icon; return NULL; } } *icon_name = GTK_STOCK_FILE; return NULL; } static void midori_search_action_icon_released_cb (GtkWidget* entry, GtkIconEntryPosition icon_pos, gint button, GtkAction* action) { KatzeArray* search_engines; GtkWidget* menu; GtkWidget* menuitem; KatzeItem* item; GdkPixbuf* icon; GtkWidget* image; if (icon_pos == GTK_ICON_ENTRY_SECONDARY) return; search_engines = MIDORI_SEARCH_ACTION (action)->search_engines; menu = gtk_menu_new (); if (!katze_array_is_empty (search_engines)) { KATZE_ARRAY_FOREACH_ITEM (item, search_engines) { const gchar* icon_name; menuitem = gtk_image_menu_item_new_with_label ( katze_item_get_name (item)); image = gtk_image_new (); icon = midori_search_action_get_icon (item, entry, &icon_name); if (icon) { gtk_image_set_from_pixbuf (GTK_IMAGE (image), icon); g_object_unref (icon); } else gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name, GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image); #if GTK_CHECK_VERSION (2, 16, 0) gtk_image_menu_item_set_always_show_image ( GTK_IMAGE_MENU_ITEM (menuitem), TRUE); #endif gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); g_object_set_data (G_OBJECT (menuitem), "engine", item); g_signal_connect (menuitem, "activate", G_CALLBACK (midori_search_action_engine_activate_cb), action); gtk_widget_show (menuitem); } } else { menuitem = gtk_image_menu_item_new_with_label (_("Empty")); gtk_widget_set_sensitive (menuitem, FALSE); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); gtk_widget_show (menuitem); } menuitem = gtk_separator_menu_item_new (); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); gtk_widget_show (menuitem); menuitem = gtk_image_menu_item_new_with_mnemonic (_("_Manage Search Engines")); image = gtk_image_new_from_stock (GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); g_signal_connect (menuitem, "activate", G_CALLBACK (midori_search_action_manage_activate_cb), action); gtk_widget_show (menuitem); katze_widget_popup (entry, GTK_MENU (menu), NULL, KATZE_MENU_POSITION_LEFT); } static gboolean midori_search_action_scroll_event_cb (GtkWidget* entry, GdkEventScroll* event, MidoriSearchAction* search_action) { if (event->direction == GDK_SCROLL_DOWN) _midori_search_action_move_index (search_action, + 1); else if (event->direction == GDK_SCROLL_UP) _midori_search_action_move_index (search_action, - 1); return FALSE; } static void midori_search_action_set_entry_icon (MidoriSearchAction* search_action, GtkWidget* entry) { GdkPixbuf* icon; if (search_action->current_item) { const gchar* icon_name; icon = midori_search_action_get_icon (search_action->current_item, entry, &icon_name); if (icon) { gtk_icon_entry_set_icon_from_pixbuf (GTK_ICON_ENTRY (entry), GTK_ICON_ENTRY_PRIMARY, icon); g_object_unref (icon); } else gtk_icon_entry_set_icon_from_icon_name (GTK_ICON_ENTRY (entry), GTK_ICON_ENTRY_PRIMARY, icon_name); sokoke_entry_set_default_text (GTK_ENTRY (entry), katze_item_get_name (search_action->current_item)); } else { gtk_icon_entry_set_icon_from_stock (GTK_ICON_ENTRY (entry), GTK_ICON_ENTRY_PRIMARY, GTK_STOCK_FIND); sokoke_entry_set_default_text (GTK_ENTRY (entry), ""); } } static void midori_search_action_connect_proxy (GtkAction* action, GtkWidget* proxy) { GtkWidget* alignment; GtkWidget* entry; GTK_ACTION_CLASS (midori_search_action_parent_class)->connect_proxy ( action, proxy); if (GTK_IS_TOOL_ITEM (proxy)) { alignment = gtk_bin_get_child (GTK_BIN (proxy)); entry = gtk_bin_get_child (GTK_BIN (alignment)); midori_search_action_set_entry_icon (MIDORI_SEARCH_ACTION (action), entry); g_object_connect (entry, "signal::key-press-event", midori_search_action_key_press_event_cb, action, "signal::focus-out-event", midori_search_action_focus_out_event_cb, action, "signal::icon-release", midori_search_action_icon_released_cb, action, "signal::scroll-event", midori_search_action_scroll_event_cb, action, NULL); } MIDORI_SEARCH_ACTION (action)->last_proxy = proxy; } static void midori_search_action_disconnect_proxy (GtkAction* action, GtkWidget* proxy) { GSList* proxies; /* FIXME: This is wrong */ g_signal_handlers_disconnect_by_func (proxy, G_CALLBACK (gtk_action_activate), action); GTK_ACTION_CLASS (midori_search_action_parent_class)->disconnect_proxy (action, proxy); if (MIDORI_SEARCH_ACTION (action)->last_proxy == proxy) { proxies = gtk_action_get_proxies (action); if (proxies) MIDORI_SEARCH_ACTION (action)->last_proxy = proxies->data; } } const gchar* midori_search_action_get_text (MidoriSearchAction* search_action) { g_return_val_if_fail (MIDORI_IS_SEARCH_ACTION (search_action), NULL); return search_action->text; } void midori_search_action_set_text (MidoriSearchAction* search_action, const gchar* text) { GSList* proxies; GtkWidget* alignment; GtkWidget* entry; g_return_if_fail (MIDORI_IS_SEARCH_ACTION (search_action)); katze_assign (search_action->text, g_strdup (text)); g_object_notify (G_OBJECT (search_action), "text"); proxies = gtk_action_get_proxies (GTK_ACTION (search_action)); if (!proxies) return; do if (GTK_IS_TOOL_ITEM (proxies->data)) { alignment = gtk_bin_get_child (GTK_BIN (proxies->data)); entry = gtk_bin_get_child (GTK_BIN (alignment)); gtk_entry_set_text (GTK_ENTRY (entry), text ? text : ""); search_action->last_proxy = proxies->data; } while ((proxies = g_slist_next (proxies))); } KatzeArray* midori_search_action_get_search_engines (MidoriSearchAction* search_action) { g_return_val_if_fail (MIDORI_IS_SEARCH_ACTION (search_action), NULL); return search_action->search_engines; } static void midori_search_action_engines_add_item_cb (KatzeArray* list, KatzeItem* item, MidoriSearchAction* search_action) { if (!search_action->current_item) midori_search_action_set_current_item (search_action, item); } static void midori_search_action_engines_remove_item_cb (KatzeArray* list, KatzeItem* item, MidoriSearchAction* search_action) { KatzeItem* found_item; if (search_action->current_item == item) { found_item = katze_array_get_nth_item (list, 0); midori_search_action_set_current_item (search_action, found_item); } } void midori_search_action_set_search_engines (MidoriSearchAction* search_action, KatzeArray* search_engines) { GSList* proxies; GtkWidget* alignment; GtkWidget* entry; g_return_if_fail (MIDORI_IS_SEARCH_ACTION (search_action)); g_return_if_fail (!search_engines || katze_array_is_a (search_engines, KATZE_TYPE_ITEM)); /* FIXME: Disconnect old search engines */ /* FIXME: Disconnect and reconnect dialog signals */ if (search_engines) g_object_ref (search_engines); katze_object_assign (search_action->search_engines, search_engines); if (!search_engines) return; g_object_connect (search_engines, "signal-after::add-item", midori_search_action_engines_add_item_cb, search_action, "signal-after::remove-item", midori_search_action_engines_remove_item_cb, search_action, NULL); g_object_notify (G_OBJECT (search_action), "search-engines"); proxies = gtk_action_get_proxies (GTK_ACTION (search_action)); if (!proxies) return; do if (GTK_IS_TOOL_ITEM (proxies->data)) { alignment = gtk_bin_get_child (GTK_BIN (proxies->data)); entry = gtk_bin_get_child (GTK_BIN (alignment)); /* FIXME: Unset the current item if it isn't in the list */ } while ((proxies = g_slist_next (proxies))); } KatzeItem* midori_search_action_get_current_item (MidoriSearchAction* search_action) { g_return_val_if_fail (MIDORI_IS_SEARCH_ACTION (search_action), NULL); return search_action->current_item; } void midori_search_action_set_current_item (MidoriSearchAction* search_action, KatzeItem* item) { GSList* proxies; GtkWidget* alignment; GtkWidget* entry; g_return_if_fail (MIDORI_IS_SEARCH_ACTION (search_action)); g_return_if_fail (!item || KATZE_IS_ITEM (item)); if (item) g_object_ref (item); katze_object_assign (search_action->current_item, item); g_object_notify (G_OBJECT (search_action), "current-item"); proxies = gtk_action_get_proxies (GTK_ACTION (search_action)); if (!proxies) return; do if (GTK_IS_TOOL_ITEM (proxies->data)) { alignment = gtk_bin_get_child (GTK_BIN (proxies->data)); entry = gtk_bin_get_child (GTK_BIN (alignment)); midori_search_action_set_entry_icon (search_action, entry); } while ((proxies = g_slist_next (proxies))); } /** * midori_search_action_get_default_item: * @search_action: a #MidoriSearchAction * * Retrieves the default search engine. * * Since 0.1.6 * * Return value: a #KatzeItem **/ KatzeItem* midori_search_action_get_default_item (MidoriSearchAction* search_action) { g_return_val_if_fail (MIDORI_IS_SEARCH_ACTION (search_action), NULL); return search_action->default_item; } /** * midori_search_action_set_default_item: * @search_action: a #MidoriSearchAction * @item: a #KatzeItem * * Sets the default search engine. * * Since 0.1.6 **/ void midori_search_action_set_default_item (MidoriSearchAction* search_action, KatzeItem* item) { g_return_if_fail (MIDORI_IS_SEARCH_ACTION (search_action)); g_return_if_fail (!item || KATZE_IS_ITEM (item)); if (item) g_object_ref (item); katze_object_assign (search_action->default_item, item); g_object_notify (G_OBJECT (search_action), "default-item"); } static void midori_search_action_dialog_render_tick_cb (GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter, GtkWidget* treeview) { KatzeItem* item; MidoriSearchAction* search_action; gint width; gtk_tree_model_get (model, iter, 0, &item, -1); search_action = g_object_get_data (G_OBJECT (treeview), "search-action"); gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (treeview), GTK_ICON_SIZE_MENU, &width, NULL); g_object_set (renderer, "stock-id", search_action->default_item == item ? GTK_STOCK_YES : NULL, "width", width + 4, NULL); g_object_unref (item); } static void midori_search_action_dialog_render_icon_cb (GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter, GtkWidget* treeview) { KatzeItem* item; MidoriSearchAction* search_action; GdkPixbuf* icon; const gchar* icon_name; gtk_tree_model_get (model, iter, 0, &item, -1); search_action = g_object_get_data (G_OBJECT (treeview), "search-action"); if ((icon = midori_search_action_get_icon (item, treeview, &icon_name))) { g_object_set (renderer, "pixbuf", icon, "yalign", 0.25, NULL); g_object_unref (icon); } else g_object_set (renderer, "icon-name", icon_name, "yalign", 0.25, NULL); g_object_unref (item); } static void midori_search_action_dialog_render_text (GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter, GtkWidget* treeview) { KatzeItem* item; const gchar* name; const gchar* text; gchar* markup; gtk_tree_model_get (model, iter, 0, &item, -1); name = katze_item_get_name (item); text = katze_item_get_text (item); markup = g_markup_printf_escaped ("%s\n%s", name, text ? text : ""); g_object_set (renderer, "markup", markup, NULL); g_free (markup); g_object_unref (item); } static void midori_search_action_dialog_render_token (GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter, GtkWidget* treeview) { KatzeItem* item; const gchar* token; gchar* markup; gtk_tree_model_get (model, iter, 0, &item, -1); token = katze_item_get_token (item); markup = g_markup_printf_escaped ("%s", token ? token : ""); g_object_set (renderer, "markup", markup, "yalign", 0.0, NULL); g_free (markup); g_object_unref (item); } static void midori_search_action_editor_name_changed_cb (GtkWidget* entry, GtkWidget* dialog) { const gchar* text = gtk_entry_get_text (GTK_ENTRY (entry)); gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT, text && *text); } static inline const gchar* STR_NON_NULL (const gchar* string) { return string ? string : ""; } static void midori_search_action_get_editor (MidoriSearchAction* search_action, gboolean new_engine) { GtkWidget* toplevel; GtkWidget* dialog; GtkSizeGroup* sizegroup; KatzeItem* item; GtkWidget* hbox; GtkWidget* label; GtkTreeModel* liststore; GtkTreeIter iter; GtkTreeSelection* selection; GtkWidget* entry_name; GtkWidget* entry_description; GtkWidget* entry_uri; GtkWidget* entry_icon; GtkWidget* entry_token; toplevel = gtk_widget_get_toplevel (search_action->treeview); dialog = gtk_dialog_new_with_buttons ( new_engine ? _("Add search engine") : _("Edit search engine"), toplevel ? GTK_WINDOW (toplevel) : NULL, GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, new_engine ? GTK_STOCK_ADD : GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); gtk_window_set_icon_name (GTK_WINDOW (dialog), new_engine ? GTK_STOCK_ADD : GTK_STOCK_REMOVE); gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), 5); sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); if (new_engine) { item = katze_item_new (); gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT, FALSE); } else { selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW (search_action->treeview)); gtk_tree_selection_get_selected (selection, &liststore, &iter); gtk_tree_model_get (liststore, &iter, 0, &item, -1); } hbox = gtk_hbox_new (FALSE, 8); gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); label = gtk_label_new_with_mnemonic (_("_Name:")); gtk_size_group_add_widget (sizegroup, label); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); entry_name = gtk_entry_new (); g_signal_connect (entry_name, "changed", G_CALLBACK (midori_search_action_editor_name_changed_cb), dialog); gtk_entry_set_activates_default (GTK_ENTRY (entry_name), TRUE); if (!new_engine) gtk_entry_set_text (GTK_ENTRY (entry_name), STR_NON_NULL (katze_item_get_name (item))); gtk_box_pack_start (GTK_BOX (hbox), entry_name, TRUE, TRUE, 0); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hbox); gtk_widget_show_all (hbox); hbox = gtk_hbox_new (FALSE, 8); gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); label = gtk_label_new_with_mnemonic (_("_Description:")); gtk_size_group_add_widget (sizegroup, label); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); entry_description = gtk_entry_new (); gtk_entry_set_activates_default (GTK_ENTRY (entry_description), TRUE); if (!new_engine) gtk_entry_set_text (GTK_ENTRY (entry_description) , STR_NON_NULL (katze_item_get_text (item))); gtk_box_pack_start (GTK_BOX (hbox), entry_description, TRUE, TRUE, 0); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hbox); gtk_widget_show_all (hbox); hbox = gtk_hbox_new (FALSE, 8); gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); label = gtk_label_new_with_mnemonic (_("_Address:")); gtk_size_group_add_widget (sizegroup, label); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); entry_uri = gtk_entry_new (); gtk_entry_set_activates_default (GTK_ENTRY (entry_uri), TRUE); if (!new_engine) gtk_entry_set_text (GTK_ENTRY (entry_uri) , STR_NON_NULL (katze_item_get_uri (item))); gtk_box_pack_start (GTK_BOX (hbox), entry_uri, TRUE, TRUE, 0); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hbox); gtk_widget_show_all (hbox); hbox = gtk_hbox_new (FALSE, 8); gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); label = gtk_label_new_with_mnemonic (_("_Icon:")); gtk_size_group_add_widget (sizegroup, label); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); entry_icon = gtk_entry_new (); gtk_entry_set_activates_default (GTK_ENTRY (entry_icon), TRUE); if (!new_engine) gtk_entry_set_text (GTK_ENTRY (entry_icon) , STR_NON_NULL (katze_item_get_icon (item))); gtk_box_pack_start (GTK_BOX (hbox), entry_icon, TRUE, TRUE, 0); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hbox); gtk_widget_show_all (hbox); hbox = gtk_hbox_new (FALSE, 8); gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); label = gtk_label_new_with_mnemonic (_("_Token:")); gtk_size_group_add_widget (sizegroup, label); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); entry_token = gtk_entry_new (); gtk_entry_set_activates_default (GTK_ENTRY (entry_token), TRUE); if (!new_engine) gtk_entry_set_text (GTK_ENTRY (entry_token) , STR_NON_NULL (katze_item_get_token (item))); gtk_box_pack_start (GTK_BOX (hbox), entry_token, TRUE, TRUE, 0); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hbox); gtk_widget_show_all (hbox); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { g_object_set (item, "name", gtk_entry_get_text (GTK_ENTRY (entry_name)), "text", gtk_entry_get_text (GTK_ENTRY (entry_description)), "uri", gtk_entry_get_text (GTK_ENTRY (entry_uri)), "icon", gtk_entry_get_text (GTK_ENTRY (entry_icon)), "token", gtk_entry_get_text (GTK_ENTRY (entry_token)), NULL); if (new_engine) katze_array_add_item (search_action->search_engines, item); /* If it isn't a new search engine but the old default one, we need to update the default search engine after editing it. */ else if (item == midori_search_action_get_default_item (search_action)) midori_search_action_set_default_item (search_action, item); } gtk_widget_destroy (dialog); } static void midori_search_action_activate_edit_cb (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, MidoriSearchAction* search_action) { GtkTreeSelection* selection; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); if (gtk_tree_selection_get_selected (selection, NULL, NULL)) midori_search_action_get_editor (search_action, FALSE); } static void midori_search_action_dialog_add_cb (GtkWidget* widget, MidoriSearchAction* search_action) { midori_search_action_get_editor (search_action, TRUE); } static void midori_search_action_dialog_edit_cb (GtkWidget* widget, MidoriSearchAction* search_action) { GtkWidget* treeview; GtkTreeSelection* selection; treeview = search_action->treeview; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); if (gtk_tree_selection_get_selected (selection, NULL, NULL)) midori_search_action_get_editor (search_action, FALSE); } static void midori_search_action_dialog_remove_cb (GtkWidget* widget, MidoriSearchAction* search_action) { KatzeArray* search_engines; GtkWidget* treeview; GtkTreeSelection* selection; GtkTreeModel* liststore; GtkTreeIter iter; KatzeItem* item; search_engines = search_action->search_engines; treeview = search_action->treeview; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); if (gtk_tree_selection_get_selected (selection, &liststore, &iter)) { gtk_tree_model_get (liststore, &iter, 0, &item, -1); katze_array_remove_item (search_engines, item); g_object_unref (item); /* FIXME: we want to allow undo of some kind */ } } static void midori_search_action_dialog_default_cb (GtkWidget* widget, MidoriSearchAction* search_action) { KatzeArray* search_engines; GtkWidget* treeview; GtkTreeSelection* selection; GtkTreeModel* liststore; GtkTreeIter iter; KatzeItem* item; search_engines = search_action->search_engines; treeview = search_action->treeview; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); if (gtk_tree_selection_get_selected (selection, &liststore, &iter)) { gtk_tree_model_get (liststore, &iter, 0, &item, -1); midori_search_action_set_default_item (search_action, item); g_object_unref (item); gtk_widget_queue_draw (treeview); } } static void midori_search_action_treeview_selection_cb (GtkTreeSelection* selection, MidoriSearchAction* search_action) { gboolean selected; selected = gtk_tree_selection_get_selected (selection, NULL, NULL); gtk_widget_set_sensitive (search_action->edit_button, selected); gtk_widget_set_sensitive (search_action->remove_button, selected); gtk_widget_set_sensitive (search_action->default_button, selected); } static void midori_search_action_dialog_engines_add_item_cb (KatzeArray* list, KatzeItem* item, GtkAction* action) { MidoriSearchAction* search_action; GtkTreeModel* liststore; GtkTreeIter iter; search_action = MIDORI_SEARCH_ACTION (action); liststore = gtk_tree_view_get_model (GTK_TREE_VIEW (search_action->treeview)); gtk_list_store_append (GTK_LIST_STORE (liststore), &iter); gtk_list_store_set (GTK_LIST_STORE (liststore), &iter, 0, item, -1); } static void midori_search_action_dialog_engines_remove_item_cb (KatzeArray* list, KatzeItem* item, GtkAction* action) { MidoriSearchAction* search_action; GtkTreeModel* liststore; GtkTreeIter iter; gboolean valid; KatzeItem* found_item; search_action = MIDORI_SEARCH_ACTION (action); liststore = gtk_tree_view_get_model (GTK_TREE_VIEW (search_action->treeview)); valid = gtk_tree_model_get_iter_first (liststore, &iter); while (valid) { gtk_tree_model_get (liststore, &iter, 0, &found_item, -1); if (found_item == item) { gtk_list_store_remove (GTK_LIST_STORE (liststore), &iter); valid = FALSE; } else valid = gtk_tree_model_iter_next (liststore, &iter); } } static void midori_search_action_treeview_destroy_cb (GtkWidget* treeview, MidoriSearchAction* search_action) { if (search_action->search_engines) { g_signal_handlers_disconnect_by_func ( search_action->search_engines, midori_search_action_dialog_engines_add_item_cb, search_action); g_signal_handlers_disconnect_by_func ( search_action->search_engines, midori_search_action_dialog_engines_remove_item_cb, search_action); } } static void midori_search_action_dialog_respnse_cb (GtkWidget* dialog, gint response, gpointer data) { gtk_widget_destroy (dialog); } /** * midori_search_action_get_dialog: * @search_action: a #MidoriSearchAction * * Obtains a dialog that provides an interface for managing * the list of search engines. * * The dialog is created once and this function will return * the very same dialog until it is destroyed, in which case * a new dialog is created. * * Return value: a #GtkDialog **/ GtkWidget* midori_search_action_get_dialog (MidoriSearchAction* search_action) { const gchar* dialog_title; GtkWidget* toplevel; GtkWidget* dialog; gint width, height; GtkWidget* xfce_heading; GtkWidget* hbox; GtkTreeViewColumn* column; GtkCellRenderer* renderer_text; GtkCellRenderer* renderer_pixbuf; GtkListStore* liststore; GtkWidget* treeview; GtkWidget* scrolled; guint i; KatzeItem* item; GtkWidget* vbox; GtkWidget* button; GtkWidget* image; g_return_val_if_fail (MIDORI_IS_SEARCH_ACTION (search_action), NULL); /* If there is a dialog, use that. We want only one. */ if (search_action->dialog) return search_action->dialog; dialog_title = _("Manage Search Engines"); toplevel = search_action->last_proxy ? gtk_widget_get_toplevel (search_action->last_proxy) : NULL; dialog = gtk_dialog_new_with_buttons (dialog_title, toplevel ? GTK_WINDOW (toplevel) : NULL, GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR, #if !HAVE_OSX #if !HAVE_HILDON GTK_STOCK_HELP, GTK_RESPONSE_HELP, #endif GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, #endif NULL); g_signal_connect (dialog, "destroy", G_CALLBACK (gtk_widget_destroyed), &search_action->dialog); gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_PROPERTIES); /* TODO: Implement some kind of help function */ gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_HELP, FALSE); sokoke_widget_get_text_size (dialog, "M", &width, &height); gtk_window_set_default_size (GTK_WINDOW (dialog), width * 52, -1); g_signal_connect (dialog, "response", G_CALLBACK (midori_search_action_dialog_respnse_cb), NULL); /* TODO: Do we want tooltips for explainations or can we omit that? We need mnemonics */ if ((xfce_heading = sokoke_xfce_header_new ( gtk_window_get_icon_name (GTK_WINDOW (dialog)), dialog_title))) gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), xfce_heading, FALSE, FALSE, 0); hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, TRUE, TRUE, 12); liststore = gtk_list_store_new (1, KATZE_TYPE_ITEM); treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (liststore)); search_action->treeview = treeview; g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), "changed", G_CALLBACK (midori_search_action_treeview_selection_cb), search_action); g_signal_connect (treeview, "row-activated", G_CALLBACK (midori_search_action_activate_edit_cb), search_action); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); g_object_set_data (G_OBJECT (treeview), "search-action", search_action); column = gtk_tree_view_column_new (); renderer_pixbuf = gtk_cell_renderer_pixbuf_new (); gtk_tree_view_column_pack_start (column, renderer_pixbuf, FALSE); gtk_tree_view_column_set_cell_data_func (column, renderer_pixbuf, (GtkTreeCellDataFunc)midori_search_action_dialog_render_tick_cb, treeview, NULL); renderer_pixbuf = gtk_cell_renderer_pixbuf_new (); gtk_tree_view_column_pack_start (column, renderer_pixbuf, FALSE); gtk_tree_view_column_set_cell_data_func (column, renderer_pixbuf, (GtkTreeCellDataFunc)midori_search_action_dialog_render_icon_cb, treeview, NULL); renderer_text = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (column, renderer_text, TRUE); gtk_tree_view_column_set_cell_data_func (column, renderer_text, (GtkTreeCellDataFunc)midori_search_action_dialog_render_text, treeview, NULL); renderer_text = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (column, renderer_text, TRUE); gtk_tree_view_column_set_cell_data_func (column, renderer_text, (GtkTreeCellDataFunc)midori_search_action_dialog_render_token, treeview, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); scrolled = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add (GTK_CONTAINER (scrolled), treeview); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX (hbox), scrolled, TRUE, TRUE, 5); i = 0; if (search_action->search_engines) KATZE_ARRAY_FOREACH_ITEM (item, search_action->search_engines) gtk_list_store_insert_with_values (GTK_LIST_STORE (liststore), NULL, i++, 0, item, -1); g_object_unref (liststore); g_signal_connect (treeview, "destroy", G_CALLBACK (midori_search_action_treeview_destroy_cb), search_action); vbox = gtk_vbox_new (FALSE, 4); gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 4); button = gtk_button_new_from_stock (GTK_STOCK_ADD); g_signal_connect (button, "clicked", G_CALLBACK (midori_search_action_dialog_add_cb), search_action); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); button = gtk_button_new_from_stock (GTK_STOCK_EDIT); search_action->edit_button = button; g_signal_connect (button, "clicked", G_CALLBACK (midori_search_action_dialog_edit_cb), search_action); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); if (!i) gtk_widget_set_sensitive (button, FALSE); button = gtk_button_new_from_stock (GTK_STOCK_REMOVE); search_action->remove_button = button; g_signal_connect (button, "clicked", G_CALLBACK (midori_search_action_dialog_remove_cb), search_action); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); if (!i) gtk_widget_set_sensitive (button, FALSE); button = gtk_label_new (""); /* This is an invisible separator */ gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 8); button = gtk_button_new_with_mnemonic (_("Use as _default")); image = gtk_image_new_from_stock (GTK_STOCK_YES, GTK_ICON_SIZE_BUTTON); gtk_button_set_image (GTK_BUTTON (button), image); search_action->default_button = button; g_signal_connect (button, "clicked", G_CALLBACK (midori_search_action_dialog_default_cb), search_action); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); if (!i) gtk_widget_set_sensitive (button, FALSE); button = gtk_label_new (""); /* This is an invisible separator */ gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 12); button = gtk_button_new_from_stock (GTK_STOCK_GO_DOWN); gtk_widget_set_sensitive (button, FALSE); gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0); button = gtk_button_new_from_stock (GTK_STOCK_GO_UP); gtk_widget_set_sensitive (button, FALSE); gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0); #if HAVE_OSX GtkWidget* icon; hbox = gtk_hbox_new (FALSE, 0); button = gtk_button_new (); icon = gtk_image_new_from_stock (GTK_STOCK_HELP, GTK_ICON_SIZE_BUTTON); gtk_button_set_image (GTK_BUTTON (button), icon); /* TODO: Implement some kind of help function */ gtk_widget_set_sensitive (button, FALSE); /* g_signal_connect (button, "clicked", G_CALLBACK (midori_search_action_dialog_help_clicked_cb), dialog); */ gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 4); gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0); #endif gtk_widget_show_all (GTK_DIALOG (dialog)->vbox); if (search_action->search_engines) g_object_connect (search_action->search_engines, "signal-after::add-item", midori_search_action_dialog_engines_add_item_cb, search_action, "signal-after::remove-item", midori_search_action_dialog_engines_remove_item_cb, search_action, NULL); search_action->dialog = dialog; return dialog; }