]> git.armaanb.net Git - chorizo.git/blob - src/downloads.c
52a8eb00b89c6f9d89e9647721762f7531fe8860
[chorizo.git] / src / downloads.c
1 #include <webkit2/webkit2.h>
2
3 #include "downloads.h"
4
5 gboolean download_handle(WebKitDownload *, gchar *, gpointer);
6 void download_click(GtkToolButton *, gpointer);
7 void download_cancel(GtkMenuItem *, gpointer);
8 gboolean downloadmanager_delete(GtkWidget *, gpointer);
9
10 struct DownloadManager {
11     GtkWidget *scroll;
12     GtkWidget *toolbar;
13     GtkWidget *win;
14 } dm;
15
16 gint downloads = 0;
17
18 struct DownloadItem {
19     GtkToolButton *tb;
20     WebKitDownload *download;
21 };
22
23 gboolean
24 key_downloadmanager(GtkWidget *widget, GdkEvent *event, gpointer data) {
25     if (event->type == GDK_KEY_PRESS) {
26         if (((GdkEventKey *)event)->state & GDK_CONTROL_MASK) {
27             int key = ((GdkEventKey *)event)->keyval;
28             if ((def_key("close_tab", GDK_KEY_q) == key) ||
29                 (def_key("download_manager", GDK_KEY_y) == key)) {
30                 downloadmanager_delete(dm.win, NULL);
31                 return TRUE;
32             }
33         }
34     }
35
36     return FALSE;
37 }
38
39 void
40 changed_download_progress(GObject *obj, GParamSpec *pspec, gpointer data) {
41     WebKitDownload *download = WEBKIT_DOWNLOAD(obj);
42     WebKitURIResponse *resp;
43     GtkToolItem *tb = GTK_TOOL_ITEM(data);
44     gdouble p, size_mb;
45     const gchar *uri;
46     gchar *t, *filename, *base;
47
48     p = webkit_download_get_estimated_progress(download);
49     p = p > 1 ? 1 : p;
50     p = p < 0 ? 0 : p;
51     p *= 100;
52     resp = webkit_download_get_response(download);
53     size_mb = webkit_uri_response_get_content_length(resp) / 1e6;
54
55     uri = webkit_download_get_destination(download);
56     filename = g_filename_from_uri(uri, NULL, NULL);
57     if (filename == NULL) {
58         /* This really should not happen because WebKit uses that URI
59          * to write to a file... */
60         fprintf(stderr, __NAME__ ": Could not construct file name from URI!\n");
61         t = g_strdup_printf("%s (%.0f%% of %.1f MB)",
62                             webkit_uri_response_get_uri(resp), p, size_mb);
63     } else {
64         base = g_path_get_basename(filename);
65         t = g_strdup_printf("%s (%.0f%% of %.1f MB)", base, p, size_mb);
66         g_free(filename);
67         g_free(base);
68     }
69     gtk_tool_button_set_label(GTK_TOOL_BUTTON(tb), t);
70     g_free(t);
71 }
72
73 void
74 download_finished(WebKitDownload *download, gpointer data) {
75     if (strcmp(gtk_tool_button_get_icon_name(GTK_TOOL_BUTTON(data)),
76                "dialog-error") != 0)
77         gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(data),
78                                       "emblem-downloads");
79     downloads--;
80 }
81
82 void
83 download_start(WebKitWebView *web_view, WebKitDownload *download,
84                gpointer data) {
85     g_signal_connect(G_OBJECT(download), "decide-destination",
86                      G_CALLBACK(download_handle), data);
87 }
88
89 gboolean
90 download_handle(WebKitDownload *download, gchar *suggested_filename,
91                 gpointer data) {
92     gchar *sug_clean, *path, *path2 = NULL, *uri;
93     GtkToolItem *tb;
94     int suffix = 1;
95     size_t i;
96
97     sug_clean = g_strdup(suggested_filename);
98     for (i = 0; i < strlen(sug_clean); i++)
99         if (sug_clean[i] == G_DIR_SEPARATOR)
100             sug_clean[i] = '_';
101
102     path = g_build_filename((char *)data, sug_clean, NULL);
103     path2 = g_strdup(path);
104     while (g_file_test(path2, G_FILE_TEST_EXISTS) && suffix < 1000) {
105         g_free(path2);
106
107         path2 = g_strdup_printf("%s.%d", path, suffix);
108         suffix++;
109     }
110
111     if (suffix == 1000) {
112         fprintf(stderr, __NAME__ ": Suffix reached limit for download.\n");
113         webkit_download_cancel(download);
114     } else {
115         uri = g_filename_to_uri(path2, NULL, NULL);
116         webkit_download_set_destination(download, uri);
117         g_free(uri);
118
119         tb = gtk_tool_button_new(NULL, NULL);
120         gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(tb), "network-receive");
121         gtk_tool_button_set_label(GTK_TOOL_BUTTON(tb), sug_clean);
122         gtk_toolbar_insert(GTK_TOOLBAR(dm.toolbar), tb, 0);
123         gtk_widget_show_all(dm.win);
124
125         g_signal_connect(G_OBJECT(download), "notify::estimated-progress",
126                          G_CALLBACK(changed_download_progress), tb);
127
128         downloads++;
129         g_signal_connect(G_OBJECT(download), "finished",
130                          G_CALLBACK(download_finished), tb);
131
132         g_object_ref(download);
133
134         struct DownloadItem *payload = malloc(sizeof(*payload));
135         payload->tb = (GtkToolButton *)tb;
136         payload->download = download;
137         g_signal_connect(G_OBJECT(tb), "clicked", G_CALLBACK(download_click),
138                          payload);
139         g_signal_connect(G_OBJECT(tb), "failed", G_CALLBACK(download_cancel),
140                          payload);
141         g_signal_connect(G_OBJECT(tb), "destroy_event", G_CALLBACK(g_free),
142                          payload);
143     }
144
145     g_free(sug_clean);
146     g_free(path);
147     g_free(path2);
148
149     // Propagate -- to whom it may concern.
150     return FALSE;
151 }
152
153 void
154 download_cancel(GtkMenuItem *tb, gpointer data) {
155     struct DownloadItem *payload = data;
156     gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(payload->tb), "dialog-error");
157     webkit_download_cancel(payload->download);
158 }
159
160 void
161 download_remove(GtkMenuItem *tb, gpointer data) {
162     struct DownloadItem *payload = data;
163     g_object_unref(payload->download);
164     gtk_widget_destroy(GTK_WIDGET(payload->tb));
165 }
166
167 void
168 download_copy_url(GtkMenuItem *tb, gpointer data) {
169     struct DownloadItem *payload = data;
170     WebKitURIRequest *req = webkit_download_get_request(payload->download);
171     const gchar *uri = webkit_uri_request_get_uri(req);
172     gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), uri,
173                            strlen(uri));
174 }
175
176 void
177 download_copy_path(GtkMenuItem *tb, gpointer data) {
178     struct DownloadItem *payload = data;
179     const gchar *path = webkit_download_get_destination(payload->download);
180     gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), path + 7,
181                            strlen(path) - 7); // Offset by 7 to remove "file://"
182 }
183
184 void
185 download_xdg_open(GtkMenuItem *tb, gpointer data) {
186     struct DownloadItem *payload = data;
187     const gchar *path = webkit_download_get_destination(payload->download);
188     char *cmd = malloc(strlen(path) + 9);
189     sprintf(cmd, "xdg-open %s", path);
190     system(cmd);
191 }
192
193 void
194 download_click(GtkToolButton *tb, gpointer data) {
195     GtkWidget *pmenu = gtk_menu_new();
196     GtkWidget *option;
197
198     if (strcmp(gtk_tool_button_get_icon_name(GTK_TOOL_BUTTON(tb)),
199                "network-receive") == 0) {
200         option = gtk_menu_item_new_with_label("Cancel download");
201         g_signal_connect(G_OBJECT(option), "activate",
202                          G_CALLBACK(download_cancel), data);
203         gtk_widget_show(option);
204         gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), option);
205     } else {
206         option = gtk_menu_item_new_with_label("Remove download");
207         g_signal_connect(G_OBJECT(option), "activate",
208                          G_CALLBACK(download_remove), data);
209         gtk_widget_show(option);
210         gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), option);
211
212         option = gtk_menu_item_new_with_label("Open file with xdg-open");
213         gtk_widget_show(option);
214         gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), option);
215         g_signal_connect(G_OBJECT(option), "activate",
216                          G_CALLBACK(download_xdg_open), data);
217     }
218
219     option = gtk_menu_item_new_with_label("Copy download URL");
220     gtk_widget_show(option);
221     gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), option);
222     g_signal_connect(G_OBJECT(option), "activate",
223                      G_CALLBACK(download_copy_url), data);
224
225     option = gtk_menu_item_new_with_label("Copy local path");
226     gtk_widget_show(option);
227     gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), option);
228     g_signal_connect(G_OBJECT(option), "activate",
229                      G_CALLBACK(download_copy_path), data);
230
231     gtk_menu_popup_at_pointer(GTK_MENU(pmenu), NULL);
232 }
233
234 gboolean
235 downloadmanager_delete(GtkWidget *obj, gpointer data) {
236     if (!quit_if_nothing_active())
237         gtk_widget_hide(dm.win);
238
239     return TRUE;
240 }
241
242 void
243 downloadmanager_setup(void) {
244     dm.win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
245     gtk_window_set_type_hint(GTK_WINDOW(dm.win), GDK_WINDOW_TYPE_HINT_DIALOG);
246     gtk_window_set_default_size(GTK_WINDOW(dm.win), 500, 250);
247     gtk_window_set_title(GTK_WINDOW(dm.win), __NAME__ " - Download Manager");
248     g_signal_connect(G_OBJECT(dm.win), "delete-event",
249                      G_CALLBACK(downloadmanager_delete), NULL);
250     g_signal_connect(G_OBJECT(dm.win), "key-press-event",
251                      G_CALLBACK(key_downloadmanager), NULL);
252
253     dm.toolbar = gtk_toolbar_new();
254     gtk_orientable_set_orientation(GTK_ORIENTABLE(dm.toolbar),
255                                    GTK_ORIENTATION_VERTICAL);
256     gtk_toolbar_set_style(GTK_TOOLBAR(dm.toolbar), GTK_TOOLBAR_BOTH_HORIZ);
257     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(dm.toolbar), FALSE);
258
259     dm.scroll = gtk_scrolled_window_new(NULL, NULL);
260     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dm.scroll),
261                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
262     gtk_container_add(GTK_CONTAINER(dm.scroll), dm.toolbar);
263
264     gtk_container_add(GTK_CONTAINER(dm.win), dm.scroll);
265 }
266
267 void
268 downloadmanager_show(void) {
269     gtk_widget_show_all(dm.win);
270 }