[GTK] Show url of history items in a status bar in MiniBrowser
[WebKit-https.git] / Tools / MiniBrowser / gtk / BrowserWindow.c
1 /*
2  * Copyright (C) 2006, 2007 Apple Inc.
3  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4  * Copyright (C) 2011 Igalia S.L.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
19  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
25  * THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "BrowserWindow.h"
29
30 enum {
31     PROP_0,
32
33     PROP_VIEW
34 };
35
36 struct _BrowserWindow {
37     GtkWindow parent;
38
39     GtkWidget *mainBox;
40     GtkWidget *uriEntry;
41     GtkWidget *backItem;
42     GtkWidget *forwardItem;
43     GtkWidget *statusLabel;
44     WebKitWebView *webView;
45
46 };
47
48 struct _BrowserWindowClass {
49     GtkWindowClass parent;
50 };
51
52 static gint windowCount = 0;
53
54 G_DEFINE_TYPE(BrowserWindow, browser_window, GTK_TYPE_WINDOW)
55
56 static void browserWindowSetStatusText(BrowserWindow *window, const char *text)
57 {
58 #if GTK_CHECK_VERSION(3, 2, 0)
59     gtk_label_set_text(GTK_LABEL(window->statusLabel), text);
60     gtk_widget_set_visible(window->statusLabel, !!text);
61 #endif
62 }
63
64 static void resetStatusText(GtkWidget *widget, BrowserWindow *window)
65 {
66     browserWindowSetStatusText(window, NULL);
67 }
68
69 static void activateUriEntryCallback(BrowserWindow *window)
70 {
71     webkit_web_view_load_uri(window->webView, gtk_entry_get_text(GTK_ENTRY(window->uriEntry)));
72 }
73
74 static void goBackCallback(BrowserWindow *window)
75 {
76     webkit_web_view_go_back(window->webView);
77 }
78
79 static void goForwardCallback(BrowserWindow *window)
80 {
81     webkit_web_view_go_forward(window->webView);
82 }
83
84 static void webViewURIChanged(WebKitWebView *webView,  GParamSpec *pspec, BrowserWindow *window)
85 {
86     gtk_entry_set_text(GTK_ENTRY(window->uriEntry), webkit_web_view_get_uri(webView));
87 }
88
89 static gboolean resetEntryProgress(GtkEntry *entry)
90 {
91     gtk_entry_set_progress_fraction(entry, 0);
92     return FALSE;
93 }
94
95 static void webViewLoadProgressChanged(WebKitWebView *webView, GParamSpec *pspec, BrowserWindow *window)
96 {
97     gdouble progress = webkit_web_view_get_estimated_load_progress(webView);
98     gtk_entry_set_progress_fraction(GTK_ENTRY(window->uriEntry), progress);
99     if (progress == 1.0)
100         g_timeout_add(500, (GSourceFunc)resetEntryProgress, window->uriEntry);
101 }
102
103 static void browserWindowHistoryItemSelected(BrowserWindow *window, GtkMenuItem *item)
104 {
105     GtkAction *action = gtk_activatable_get_related_action(GTK_ACTIVATABLE(item));
106     browserWindowSetStatusText(window, action ? gtk_action_get_name(action) : NULL);
107 }
108
109 static void browserWindowHistoryItemActivated(BrowserWindow *window, GtkAction *action)
110 {
111     WebKitBackForwardListItem *item = g_object_get_data(G_OBJECT(action), "back-forward-list-item");
112     if (!item)
113         return;
114
115     webkit_web_view_go_to_back_forward_list_item(window->webView, item);
116 }
117
118 static GtkWidget *browserWindowCreateBackForwardMenu(BrowserWindow *window, GList *list)
119 {
120     if (!list)
121         return NULL;
122
123     GtkWidget *menu = gtk_menu_new();
124     GList *listItem;
125     for (listItem = list; listItem; listItem = g_list_next(listItem)) {
126         WebKitBackForwardListItem *item = (WebKitBackForwardListItem *)listItem->data;
127         const char *uri = webkit_back_forward_list_item_get_uri(item);
128         const char *title = webkit_back_forward_list_item_get_title(item);
129
130         GtkAction *action = gtk_action_new(uri, title, NULL, NULL);
131         g_object_set_data_full(G_OBJECT(action), "back-forward-list-item", g_object_ref(item), g_object_unref);
132         g_signal_connect_swapped(action, "activate", G_CALLBACK(browserWindowHistoryItemActivated), window);
133
134         GtkWidget *menuItem = gtk_action_create_menu_item(action);
135         g_signal_connect_swapped(menuItem, "select", G_CALLBACK(browserWindowHistoryItemSelected), window);
136         g_object_unref(action);
137
138         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuItem);
139         gtk_widget_show(menuItem);
140     }
141
142     /* FIXME: This shoulnd't be necessary when didMouseMoveOverElement
143      * is implemented in WebKit2 GTK+ API.
144      */
145     g_signal_connect(menu, "hide", G_CALLBACK(resetStatusText), window);
146
147     return menu;
148 }
149
150 static void browserWindowUpdateNavigationActions(BrowserWindow *window, WebKitBackForwardList *backForwadlist)
151 {
152     gtk_widget_set_sensitive(window->backItem, webkit_web_view_can_go_back(window->webView));
153     gtk_widget_set_sensitive(window->forwardItem, webkit_web_view_can_go_forward(window->webView));
154
155     GList *list = webkit_back_forward_list_get_back_list_with_limit(backForwadlist, 10);
156     gtk_menu_tool_button_set_menu(GTK_MENU_TOOL_BUTTON(window->backItem),
157                                   browserWindowCreateBackForwardMenu(window, list));
158     g_list_free(list);
159
160     list = webkit_back_forward_list_get_forward_list_with_limit(backForwadlist, 10);
161     gtk_menu_tool_button_set_menu(GTK_MENU_TOOL_BUTTON(window->forwardItem),
162                                   browserWindowCreateBackForwardMenu(window, list));
163     g_list_free(list);
164 }
165
166 static void backForwadlistChanged(WebKitBackForwardList *backForwadlist, WebKitBackForwardListItem *itemAdded, GList *itemsRemoved, BrowserWindow *window)
167 {
168     browserWindowUpdateNavigationActions(window, backForwadlist);
169 }
170
171 static void browserWindowFinalize(GObject *gObject)
172 {
173     G_OBJECT_CLASS(browser_window_parent_class)->finalize(gObject);
174
175     if (g_atomic_int_dec_and_test(&windowCount))
176         gtk_main_quit();
177 }
178
179 static void browserWindowGetProperty(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
180 {
181     BrowserWindow *window = BROWSER_WINDOW(object);
182
183     switch (propId) {
184     case PROP_VIEW:
185         g_value_set_object(value, browser_window_get_view(window));
186         break;
187     default:
188         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
189     }
190 }
191
192 static void browserWindowSetProperty(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
193 {
194     BrowserWindow* window = BROWSER_WINDOW(object);
195
196     switch (propId) {
197     case PROP_VIEW:
198         window->webView = g_value_get_object(value);
199         break;
200     default:
201         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
202     }
203 }
204
205 static void browser_window_init(BrowserWindow *window)
206 {
207     g_atomic_int_inc(&windowCount);
208
209     gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
210
211     window->uriEntry = gtk_entry_new();
212     g_signal_connect_swapped(window->uriEntry, "activate", G_CALLBACK(activateUriEntryCallback), (gpointer)window);
213
214     GtkWidget *toolbar = gtk_toolbar_new();
215     gtk_orientable_set_orientation(GTK_ORIENTABLE(toolbar), GTK_ORIENTATION_HORIZONTAL);
216     gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH_HORIZ);
217
218     GtkToolItem *item = gtk_menu_tool_button_new_from_stock(GTK_STOCK_GO_BACK);
219     window->backItem = GTK_WIDGET(item);
220     gtk_menu_tool_button_set_menu(GTK_MENU_TOOL_BUTTON(item), 0);
221     g_signal_connect_swapped(item, "clicked", G_CALLBACK(goBackCallback), (gpointer)window);
222     gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
223     gtk_widget_show(GTK_WIDGET(item));
224
225     item = gtk_menu_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD);
226     window->forwardItem = GTK_WIDGET(item);
227     gtk_menu_tool_button_set_menu(GTK_MENU_TOOL_BUTTON(item), 0);
228     g_signal_connect_swapped(G_OBJECT(item), "clicked", G_CALLBACK(goForwardCallback), (gpointer)window);
229     gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
230     gtk_widget_show(GTK_WIDGET(item));
231
232     item = gtk_tool_item_new();
233     gtk_tool_item_set_expand(item, TRUE);
234     gtk_container_add(GTK_CONTAINER(item), window->uriEntry);
235     gtk_widget_show(window->uriEntry);
236     gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
237     gtk_widget_show(GTK_WIDGET(item));
238
239     item = gtk_tool_button_new_from_stock(GTK_STOCK_OK);
240     g_signal_connect_swapped(item, "clicked", G_CALLBACK(activateUriEntryCallback), (gpointer)window);
241     gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
242     gtk_widget_show(GTK_WIDGET(item));
243
244     GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
245     window->mainBox = vbox;
246     gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
247     gtk_widget_show(toolbar);
248
249     gtk_container_add(GTK_CONTAINER(window), vbox);
250     gtk_widget_show(vbox);
251 }
252
253 static void browserWindowConstructed(GObject *gObject)
254 {
255     BrowserWindow *window = BROWSER_WINDOW(gObject);
256
257     g_signal_connect(window->webView, "notify::uri", G_CALLBACK(webViewURIChanged), window);
258     g_signal_connect(window->webView, "notify::estimated-load-progress", G_CALLBACK(webViewLoadProgressChanged), window);
259
260     WebKitBackForwardList *backForwadlist = webkit_web_view_get_back_forward_list(window->webView);
261     g_signal_connect(backForwadlist, "changed", G_CALLBACK(backForwadlistChanged), window);
262
263 #if GTK_CHECK_VERSION(3, 2, 0)
264     GtkWidget *overlay = gtk_overlay_new();
265     gtk_box_pack_start(GTK_BOX(window->mainBox), overlay, TRUE, TRUE, 0);
266     gtk_widget_show(overlay);
267
268     window->statusLabel = gtk_label_new(NULL);
269     gtk_widget_set_halign(window->statusLabel, GTK_ALIGN_START);
270     gtk_widget_set_valign(window->statusLabel, GTK_ALIGN_END);
271     gtk_widget_set_margin_left(window->statusLabel, 1);
272     gtk_widget_set_margin_right(window->statusLabel, 1);
273     gtk_widget_set_margin_top(window->statusLabel, 1);
274     gtk_widget_set_margin_bottom(window->statusLabel, 1);
275     gtk_overlay_add_overlay(GTK_OVERLAY(overlay), window->statusLabel);
276
277     gtk_container_add(GTK_CONTAINER(overlay), GTK_WIDGET(window->webView));
278 #else
279     gtk_box_pack_start(GTK_BOX(window->mainBox), GTK_WIDGET(window->webView), TRUE, TRUE, 0);
280 #endif
281     gtk_widget_show(GTK_WIDGET(window->webView));
282 }
283
284 static void browser_window_class_init(BrowserWindowClass *klass)
285 {
286     GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
287
288     gobjectClass->constructed = browserWindowConstructed;
289     gobjectClass->get_property = browserWindowGetProperty;
290     gobjectClass->set_property = browserWindowSetProperty;
291     gobjectClass->finalize = browserWindowFinalize;
292
293     g_object_class_install_property(gobjectClass,
294                                     PROP_VIEW,
295                                     g_param_spec_object("view",
296                                                         "View",
297                                                         "The web view of this window",
298                                                         WEBKIT_TYPE_WEB_VIEW,
299                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
300 }
301
302 // Public API.
303 GtkWidget *browser_window_new(WebKitWebView *view)
304 {
305     g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(view), 0);
306
307     return GTK_WIDGET(g_object_new(BROWSER_TYPE_WINDOW,
308                                    "type", GTK_WINDOW_TOPLEVEL,
309                                    "view", view, NULL));
310 }
311
312 WebKitWebView *browser_window_get_view(BrowserWindow *window)
313 {
314     g_return_val_if_fail(BROWSER_IS_WINDOW(window), 0);
315
316     return window->webView;
317 }