/* * Revealable find toolbar * * Copyright (C) 2017 Patrick McDermott * * This file is part of Marquee. * * Marquee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Marquee is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Marquee. If not, see . */ #include "find-toolbar.h" #include #include #include #include "../i18n.h" #include "../web-view.h" struct _MqFindToolbar { GtkRevealer parent_instance; GtkWidget *search_entry; GtkSpinner *spinner; GtkWidget *matches_label; gboolean match_case; WebKitFindController *find_controller; }; enum { PROP_WEB_VIEW = 1, N_PROPERTIES }; static GParamSpec *obj_properties[N_PROPERTIES] = {NULL,}; struct _MqFindToolbarClass { GtkRevealerClass parent_class; }; G_DEFINE_TYPE(MqFindToolbar, mq_find_toolbar, GTK_TYPE_REVEALER) static void search(MqFindToolbar *find_toolbar, gboolean forward, gboolean wrap_around) { guint32 find_options; find_options = WEBKIT_FIND_OPTIONS_NONE; if (!find_toolbar->match_case) { find_options |= WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE; } if (!forward) { find_options |= WEBKIT_FIND_OPTIONS_BACKWARDS; } if (wrap_around) { find_options |= WEBKIT_FIND_OPTIONS_WRAP_AROUND; } webkit_find_controller_search(find_toolbar->find_controller, gtk_entry_get_text(GTK_ENTRY(find_toolbar->search_entry)), find_options, G_MAXUINT); gtk_spinner_start(find_toolbar->spinner); } static void search_finished(MqFindToolbar *find_toolbar) { webkit_find_controller_search_finish(find_toolbar->find_controller); } static void hide(MqFindToolbar *find_toolbar) { gtk_revealer_set_reveal_child(GTK_REVEALER(find_toolbar), FALSE); gtk_label_set_text(GTK_LABEL(find_toolbar->matches_label), NULL); search_finished(find_toolbar); } static void search_changed_cb(GtkSearchEntry G_GNUC_UNUSED *entry, MqFindToolbar *find_toolbar) { search(find_toolbar, TRUE, FALSE); } static gboolean search_key_press_event_cb(GtkSearchEntry G_GNUC_UNUSED *entry, GdkEventKey *event, MqFindToolbar *find_toolbar) { switch (event->keyval) { case GDK_KEY_Escape: hide(find_toolbar); return TRUE; case GDK_KEY_Return: case GDK_KEY_KP_Enter: case GDK_KEY_ISO_Enter: search(find_toolbar, !(event->state & GDK_SHIFT_MASK), FALSE); return TRUE; default: return FALSE; } } static void prev_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqFindToolbar *find_toolbar) { search(find_toolbar, FALSE, FALSE); } static void next_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqFindToolbar *find_toolbar) { search(find_toolbar, TRUE, FALSE); } static void match_case_toggled_cb(GtkToggleButton *toggle_button, MqFindToolbar *find_toolbar) { find_toolbar->match_case = gtk_toggle_button_get_active(toggle_button); search(find_toolbar, TRUE, FALSE); } static void close_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqFindToolbar *find_toolbar) { hide(find_toolbar); } static void found_text_cb(WebKitFindController G_GNUC_UNUSED *find_controller, guint match_count, MqFindToolbar *find_toolbar) { gchar *text; guint32 find_options; gtk_spinner_stop(find_toolbar->spinner); find_options = webkit_find_controller_get_options( find_toolbar->find_controller); if (find_options & WEBKIT_FIND_OPTIONS_WRAP_AROUND) { if (find_options & WEBKIT_FIND_OPTIONS_BACKWARDS) { if (match_count == 1) { text = g_strdup("1 match, " "wrapped from top to bottom"); } else { text = g_strdup_printf("%u matches, " "wrapped from top to bottom", match_count); } } else { if (match_count == 1) { text = g_strdup("1 match, " "wrapped from bottom to top"); } else { text = g_strdup_printf("%u matches, " "wrapped from bottom to top", match_count); } } } else { if (match_count == 1) { text = g_strdup("1 match"); } else { text = g_strdup_printf("%u matches", match_count); } } gtk_label_set_text(GTK_LABEL(find_toolbar->matches_label), text); g_free(text); } static void failed_to_find_text_cb(WebKitFindController G_GNUC_UNUSED *find_controller, MqFindToolbar *find_toolbar) { const gchar *search_text; guint32 find_options; search_text = webkit_find_controller_get_search_text( find_toolbar->find_controller); if (!search_text[0]) { /* Search entry cleared. */ gtk_spinner_stop(find_toolbar->spinner); gtk_label_set_text(GTK_LABEL(find_toolbar->matches_label), ""); return; } find_options = webkit_find_controller_get_options( find_toolbar->find_controller); if (find_options & WEBKIT_FIND_OPTIONS_WRAP_AROUND) { gtk_spinner_stop(find_toolbar->spinner); gtk_label_set_text(GTK_LABEL(find_toolbar->matches_label), _("No matches")); } else { search(find_toolbar, !(find_options & WEBKIT_FIND_OPTIONS_BACKWARDS), TRUE); } } static void set_web_view(MqFindToolbar *find_toolbar, MqWebView *web_view) { find_toolbar->find_controller = webkit_web_view_get_find_controller( WEBKIT_WEB_VIEW(web_view)); g_signal_connect(find_toolbar->find_controller, "found-text", G_CALLBACK(found_text_cb), find_toolbar); g_signal_connect(find_toolbar->find_controller, "failed-to-find-text", G_CALLBACK(failed_to_find_text_cb), find_toolbar); } static void get_property(GObject *object, guint property_id, GValue *value, GParamSpec *param_spec) { switch (property_id) { case PROP_WEB_VIEW: g_value_set_object(value, NULL); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, param_spec); break; } } static void set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *param_spec) { MqFindToolbar *find_toolbar; find_toolbar = MQ_FIND_TOOLBAR(object); switch (property_id) { case PROP_WEB_VIEW: set_web_view(find_toolbar, g_value_get_object(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, param_spec); break; } } static void mq_find_toolbar_class_init(MqFindToolbarClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->get_property = get_property; object_class->set_property = set_property; obj_properties[PROP_WEB_VIEW] = g_param_spec_object( "web-view", P_("MqWebView"), P_("The associated MqWebView instance"), MQ_TYPE_WEB_VIEW, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_properties(object_class, N_PROPERTIES, obj_properties); } static void mq_find_toolbar_init(MqFindToolbar *find_toolbar) { GtkWidget *close_button; GtkWidget *prev_button; GtkWidget *next_button; GtkWidget *match_case_button; GtkWidget *box; /* Search entry */ find_toolbar->search_entry = gtk_search_entry_new(); g_signal_connect(find_toolbar->search_entry, "search-changed", G_CALLBACK(search_changed_cb), find_toolbar); g_signal_connect(find_toolbar->search_entry, "key-press-event", G_CALLBACK(search_key_press_event_cb), find_toolbar); /* Previous button */ prev_button = gtk_button_new_from_icon_name("go-up", GTK_ICON_SIZE_BUTTON); gtk_button_set_relief(GTK_BUTTON(prev_button), GTK_RELIEF_NONE); gtk_widget_set_tooltip_text(prev_button, _("Find previous occurrence")); gtk_widget_set_can_focus(prev_button, FALSE); g_signal_connect(prev_button, "clicked", G_CALLBACK(prev_clicked_cb), find_toolbar); /* Next button */ next_button = gtk_button_new_from_icon_name("go-down", GTK_ICON_SIZE_BUTTON); gtk_button_set_relief(GTK_BUTTON(next_button), GTK_RELIEF_NONE); gtk_widget_set_tooltip_text(next_button, _("Find next occurrence")); gtk_widget_set_can_focus(next_button, FALSE); g_signal_connect(next_button, "clicked", G_CALLBACK(next_clicked_cb), find_toolbar); /* Case sensitivity button */ match_case_button = gtk_toggle_button_new_with_mnemonic( _("Mat_ch Case")); gtk_button_set_relief(GTK_BUTTON(match_case_button), GTK_RELIEF_NONE); gtk_widget_set_tooltip_text(match_case_button, _("Search with case sensitivity")); gtk_widget_set_can_focus(match_case_button, FALSE); g_signal_connect(match_case_button, "toggled", G_CALLBACK(match_case_toggled_cb), find_toolbar); /* Spinner */ find_toolbar->spinner = GTK_SPINNER(gtk_spinner_new()); /* Matches label */ find_toolbar->matches_label = gtk_label_new(NULL); /* Close button */ close_button = gtk_button_new_from_icon_name("window-close", GTK_ICON_SIZE_BUTTON); gtk_button_set_relief(GTK_BUTTON(close_button), GTK_RELIEF_NONE); gtk_widget_set_tooltip_text(close_button, _("Close find bar")); gtk_widget_set_can_focus(close_button, FALSE); g_signal_connect(close_button, "clicked", G_CALLBACK(close_clicked_cb), find_toolbar); /* Box */ box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_start(GTK_BOX(box), find_toolbar->search_entry, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(box), prev_button, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(box), next_button, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(box), match_case_button, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(find_toolbar->spinner), FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(box), find_toolbar->matches_label, FALSE, FALSE, 0); gtk_box_pack_end(GTK_BOX(box), close_button, FALSE, FALSE, 0); /* Revealer */ gtk_revealer_set_transition_type(GTK_REVEALER(find_toolbar), GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN); gtk_container_add(GTK_CONTAINER(find_toolbar), box); find_toolbar->match_case = FALSE; } GtkWidget * mq_find_toolbar_new(MqWebView *web_view) { return g_object_new(MQ_TYPE_FIND_TOOLBAR, "web-view", web_view, NULL); } void mq_find_toolbar_reveal(MqFindToolbar *find_toolbar) { gtk_revealer_set_reveal_child(GTK_REVEALER(find_toolbar), TRUE); gtk_widget_grab_focus(find_toolbar->search_entry); }