[GTK] MiniBrowser's browser_window_get_or_create_web_view_for_automation never create...
[WebKit-https.git] / Tools / MiniBrowser / gtk / BrowserSearchBar.c
1 /*
2  * Copyright (C) 2013 Igalia S.L.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "cmakeconfig.h"
27 #include "BrowserSearchBar.h"
28
29 static const char *searchEntryFailedStyle = "GtkEntry#searchEntry {background-color: #ff6666;}";
30
31 struct _BrowserSearchBar {
32     GtkToolbar parent;
33
34     WebKitWebView *webView;
35     GtkWidget *entry;
36     GtkCssProvider *cssProvider;
37     GtkWidget *prevButton;
38     GtkWidget *nextButton;
39     GtkWidget *optionsMenu;
40     GtkWidget *caseCheckButton;
41     GtkWidget *begginigWordCheckButton;
42     GtkWidget *capitalAsBegginigWordCheckButton;
43 };
44
45 G_DEFINE_TYPE(BrowserSearchBar, browser_search_bar, GTK_TYPE_TOOLBAR)
46
47 static void setFailedStyleForEntry(BrowserSearchBar *searchBar, gboolean failedSearch)
48 {
49     gtk_css_provider_load_from_data(searchBar->cssProvider, failedSearch ? searchEntryFailedStyle : "", -1, NULL);
50 }
51
52 static void doSearch(BrowserSearchBar *searchBar)
53 {
54     GtkEntry *entry = GTK_ENTRY(searchBar->entry);
55
56     if (!gtk_entry_get_text_length(entry)) {
57         webkit_find_controller_search_finish(webkit_web_view_get_find_controller(searchBar->webView));
58         gtk_entry_set_icon_from_stock(entry, GTK_ENTRY_ICON_SECONDARY, NULL);
59         setFailedStyleForEntry(searchBar, FALSE);
60         return;
61     }
62
63     if (!gtk_entry_get_icon_stock(entry, GTK_ENTRY_ICON_SECONDARY))
64         gtk_entry_set_icon_from_stock(entry, GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
65
66     WebKitFindOptions options = WEBKIT_FIND_OPTIONS_WRAP_AROUND;
67     if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(searchBar->caseCheckButton)))
68         options |= WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE;
69     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(searchBar->begginigWordCheckButton)))
70         options |= WEBKIT_FIND_OPTIONS_AT_WORD_STARTS;
71     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(searchBar->capitalAsBegginigWordCheckButton)))
72         options |= WEBKIT_FIND_OPTIONS_TREAT_MEDIAL_CAPITAL_AS_WORD_START;
73
74     const gchar *text = gtk_entry_get_text(entry);
75     webkit_find_controller_search(webkit_web_view_get_find_controller(searchBar->webView), text, options, G_MAXUINT);
76 }
77
78 static void searchNext(BrowserSearchBar *searchBar)
79 {
80     webkit_find_controller_search_next(webkit_web_view_get_find_controller(searchBar->webView));
81 }
82
83 static void searchPrevious(BrowserSearchBar *searchBar)
84 {
85     webkit_find_controller_search_previous(webkit_web_view_get_find_controller(searchBar->webView));
86 }
87
88 static void searchCloseButtonClickedCallback(BrowserSearchBar *searchBar)
89 {
90     browser_search_bar_close(searchBar);
91 }
92
93 static void searchEntryMenuIconPressedCallback(BrowserSearchBar *searchBar, GtkEntryIconPosition iconPosition, GdkEvent *event)
94 {
95     if (iconPosition == GTK_ENTRY_ICON_PRIMARY) {
96 #if GTK_CHECK_VERSION(3, 22, 0)
97         gtk_menu_popup_at_pointer(GTK_MENU(searchBar->optionsMenu), event);
98 #else
99         GdkEventButton *eventButton = (GdkEventButton *)event;
100         gtk_menu_popup(GTK_MENU(searchBar->optionsMenu), NULL, NULL, NULL, NULL, eventButton->button, eventButton->time);
101 #endif
102     }
103 }
104
105 static void searchEntryClearIconReleasedCallback(BrowserSearchBar *searchBar, GtkEntryIconPosition iconPosition)
106 {
107     if (iconPosition == GTK_ENTRY_ICON_SECONDARY)
108         gtk_entry_set_text(GTK_ENTRY(searchBar->entry), "");
109 }
110
111 static void searchEntryChangedCallback(GtkEntry *entry, BrowserSearchBar *searchBar)
112 {
113     doSearch(searchBar);
114 }
115
116 static void searchEntryActivatedCallback(BrowserSearchBar *searchBar)
117 {
118     searchNext(searchBar);
119 }
120
121 static void searchPrevButtonClickedCallback(BrowserSearchBar *searchBar)
122 {
123     searchPrevious(searchBar);
124 }
125
126 static void searchNextButtonClickedCallback(BrowserSearchBar *searchBar)
127 {
128     searchNext(searchBar);
129 }
130
131 static void searchMenuCheckButtonToggledCallback(BrowserSearchBar *searchBar)
132 {
133     doSearch(searchBar);
134 }
135
136 static void findControllerFailedToFindTextCallback(BrowserSearchBar *searchBar)
137 {
138     setFailedStyleForEntry(searchBar, TRUE);
139 }
140
141 static void findControllerFoundTextCallback(BrowserSearchBar *searchBar)
142 {
143     setFailedStyleForEntry(searchBar, FALSE);
144 }
145
146 static void browser_search_bar_init(BrowserSearchBar *searchBar)
147 {
148     gtk_widget_set_hexpand(GTK_WIDGET(searchBar), TRUE);
149
150     GtkToolItem *toolItem = gtk_tool_item_new();
151     gtk_tool_item_set_expand(toolItem, TRUE);
152     gtk_toolbar_insert(GTK_TOOLBAR(searchBar), toolItem, 0);
153
154     GtkBox *hBox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5));
155     gtk_box_set_homogeneous(hBox, TRUE);
156     gtk_container_add(GTK_CONTAINER(toolItem), GTK_WIDGET(hBox));
157
158     gtk_box_pack_start(hBox, gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0), TRUE, TRUE, 0);
159
160     searchBar->entry = gtk_entry_new();
161     gtk_widget_set_name(searchBar->entry, "searchEntry");
162     gtk_entry_set_placeholder_text(GTK_ENTRY(searchBar->entry), "Search");
163     gtk_entry_set_icon_from_stock(GTK_ENTRY(searchBar->entry), GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
164     gtk_box_pack_start(hBox, searchBar->entry, TRUE, TRUE, 0);
165
166     searchBar->cssProvider = gtk_css_provider_new();
167     gtk_style_context_add_provider(gtk_widget_get_style_context(searchBar->entry), GTK_STYLE_PROVIDER(searchBar->cssProvider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
168
169     GtkBox *hBoxButtons = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
170     gtk_box_pack_start(hBox, GTK_WIDGET(hBoxButtons), TRUE, TRUE, 0);
171
172     searchBar->prevButton = gtk_button_new();
173     GtkButton *button = GTK_BUTTON(searchBar->prevButton);
174     GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_GO_UP, GTK_ICON_SIZE_SMALL_TOOLBAR);
175     gtk_button_set_image(button, image);
176     gtk_button_set_relief(button, GTK_RELIEF_NONE);
177     gtk_button_set_focus_on_click(button, FALSE);
178     gtk_box_pack_start(hBoxButtons, searchBar->prevButton, FALSE, FALSE, 0);
179
180     searchBar->nextButton = gtk_button_new();
181     button = GTK_BUTTON(searchBar->nextButton);
182     image = gtk_image_new_from_stock(GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_SMALL_TOOLBAR);
183     gtk_button_set_image(button, image);
184     gtk_button_set_relief(button, GTK_RELIEF_NONE);
185     gtk_button_set_focus_on_click(button, FALSE);
186     gtk_box_pack_start(hBoxButtons, searchBar->nextButton, FALSE, FALSE, 0);
187
188     GtkWidget *closeButton = gtk_button_new();
189     button = GTK_BUTTON(closeButton);
190     image = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_SMALL_TOOLBAR);
191     gtk_button_set_image(button, image);
192     gtk_button_set_relief(button, GTK_RELIEF_NONE);
193     gtk_button_set_focus_on_click(button, FALSE);
194     gtk_box_pack_end(hBoxButtons, closeButton, FALSE, FALSE, 0);
195
196     searchBar->optionsMenu = g_object_ref_sink(gtk_menu_new());
197
198     searchBar->caseCheckButton = gtk_check_menu_item_new_with_mnemonic("Ca_se sensitive");
199     gtk_container_add(GTK_CONTAINER(searchBar->optionsMenu), searchBar->caseCheckButton);
200
201     searchBar->begginigWordCheckButton = gtk_check_menu_item_new_with_mnemonic("Only at the _beginning of words");
202     gtk_container_add(GTK_CONTAINER(searchBar->optionsMenu), searchBar->begginigWordCheckButton);
203
204     searchBar->capitalAsBegginigWordCheckButton = gtk_check_menu_item_new_with_mnemonic("Capital _always as beginning of word");
205     gtk_container_add(GTK_CONTAINER(searchBar->optionsMenu), searchBar->capitalAsBegginigWordCheckButton);
206
207     g_signal_connect_swapped(closeButton, "clicked", G_CALLBACK(searchCloseButtonClickedCallback), searchBar);
208     g_signal_connect_swapped(searchBar->entry, "icon-press", G_CALLBACK(searchEntryMenuIconPressedCallback), searchBar);
209     g_signal_connect_swapped(searchBar->entry, "icon-release", G_CALLBACK(searchEntryClearIconReleasedCallback), searchBar);
210     g_signal_connect_after(searchBar->entry, "changed", G_CALLBACK(searchEntryChangedCallback), searchBar);
211     g_signal_connect_swapped(searchBar->entry, "activate", G_CALLBACK(searchEntryActivatedCallback), searchBar);
212     g_signal_connect_swapped(searchBar->nextButton, "clicked", G_CALLBACK(searchNextButtonClickedCallback), searchBar);
213     g_signal_connect_swapped(searchBar->prevButton, "clicked", G_CALLBACK(searchPrevButtonClickedCallback), searchBar);
214     g_signal_connect_swapped(searchBar->caseCheckButton, "toggled", G_CALLBACK(searchMenuCheckButtonToggledCallback), searchBar);
215     g_signal_connect_swapped(searchBar->begginigWordCheckButton, "toggled", G_CALLBACK(searchMenuCheckButtonToggledCallback), searchBar);
216     g_signal_connect_swapped(searchBar->capitalAsBegginigWordCheckButton, "toggled", G_CALLBACK(searchMenuCheckButtonToggledCallback), searchBar);
217
218     gtk_widget_show_all(GTK_WIDGET(toolItem));
219     gtk_widget_show_all(searchBar->optionsMenu);
220 }
221
222 static void browserSearchBarFinalize(GObject *gObject)
223 {
224     BrowserSearchBar *searchBar = BROWSER_SEARCH_BAR(gObject);
225
226     if (searchBar->webView) {
227         g_object_unref(searchBar->webView);
228         searchBar->webView = NULL;
229     }
230
231     if (searchBar->cssProvider) {
232         g_object_unref(searchBar->cssProvider);
233         searchBar->cssProvider = NULL;
234     }
235
236     if (searchBar->optionsMenu) {
237         g_object_unref(searchBar->optionsMenu);
238         searchBar->optionsMenu = NULL;
239     }
240
241     G_OBJECT_CLASS(browser_search_bar_parent_class)->finalize(gObject);
242 }
243
244 static void browser_search_bar_class_init(BrowserSearchBarClass *klass)
245 {
246     GObjectClass *gObjectClass = G_OBJECT_CLASS(klass);
247
248     gObjectClass->finalize = browserSearchBarFinalize;
249 }
250
251 GtkWidget *browser_search_bar_new(WebKitWebView *webView)
252 {
253     g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(webView), NULL);
254
255     GtkWidget *searchBar = GTK_WIDGET(g_object_new(BROWSER_TYPE_SEARCH_BAR, NULL));
256     BROWSER_SEARCH_BAR(searchBar)->webView = g_object_ref(webView);
257
258     WebKitFindController *controller = webkit_web_view_get_find_controller(webView);
259     g_signal_connect_swapped(controller, "failed-to-find-text", G_CALLBACK(findControllerFailedToFindTextCallback), searchBar);
260     g_signal_connect_swapped(controller, "found-text", G_CALLBACK(findControllerFoundTextCallback), searchBar);
261
262     return searchBar;
263 }
264
265 void browser_search_bar_add_accelerators(BrowserSearchBar *searchBar, GtkAccelGroup *accelGroup)
266 {
267     g_return_if_fail(BROWSER_IS_SEARCH_BAR(searchBar));
268     g_return_if_fail(GTK_IS_ACCEL_GROUP(accelGroup));
269
270     gtk_widget_add_accelerator(searchBar->nextButton, "clicked", accelGroup, GDK_KEY_F3, 0, GTK_ACCEL_VISIBLE);
271     gtk_widget_add_accelerator(searchBar->nextButton, "clicked", accelGroup, GDK_KEY_G, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
272
273     gtk_widget_add_accelerator(searchBar->prevButton, "clicked", accelGroup, GDK_KEY_F3, GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
274     gtk_widget_add_accelerator(searchBar->prevButton, "clicked", accelGroup, GDK_KEY_G, GDK_SHIFT_MASK | GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
275 }
276
277 void browser_search_bar_open(BrowserSearchBar *searchBar)
278 {
279     g_return_if_fail(BROWSER_IS_SEARCH_BAR(searchBar));
280
281     GtkEntry *entry = GTK_ENTRY(searchBar->entry);
282
283     gtk_widget_show(GTK_WIDGET(searchBar));
284     gtk_widget_grab_focus(GTK_WIDGET(entry));
285     gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
286     if (gtk_entry_get_text_length(entry))
287         doSearch(searchBar);
288 }
289
290 void browser_search_bar_close(BrowserSearchBar *searchBar)
291 {
292     g_return_if_fail(BROWSER_IS_SEARCH_BAR(searchBar));
293
294     gtk_widget_hide(GTK_WIDGET(searchBar));
295     WebKitFindController *controller = webkit_web_view_get_find_controller(searchBar->webView);
296     webkit_find_controller_search_finish(controller);
297 }