]> git.armaanb.net Git - chorizo.git/blob - src/downloads.c
Use GTK file chooser for saving files
[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     base = g_path_get_basename(filename);
58     t = g_strdup_printf("%s (%.0f%% of %.1f MB)", base, p, size_mb);
59     g_free(filename);
60     g_free(base);
61     gtk_tool_button_set_label(GTK_TOOL_BUTTON(tb), t);
62     g_free(t);
63 }
64
65 void
66 download_finished(WebKitDownload *download, gpointer data) {
67     if (strcmp(gtk_tool_button_get_icon_name(GTK_TOOL_BUTTON(data)),
68                "dialog-error") != 0)
69         gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(data),
70                                       "emblem-downloads");
71     downloads--;
72 }
73
74 void
75 download_start(WebKitWebView *web_view, WebKitDownload *download,
76                gpointer data) {
77     g_signal_connect(G_OBJECT(download), "decide-destination",
78                      G_CALLBACK(download_handle), data);
79 }
80
81 gboolean
82 download_handle(WebKitDownload *download, gchar *suggested_filename,
83                 gpointer data) {
84     gchar *uri;
85     GtkToolItem *tb;
86
87     GtkWidget *chooser = gtk_file_chooser_dialog_new(
88         "Choose download location", GTK_WINDOW(dm.win),
89         GTK_FILE_CHOOSER_ACTION_SAVE, "Save file", GTK_RESPONSE_ACCEPT,
90         "Cancel", GTK_RESPONSE_CANCEL, NULL);
91     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser),
92                                         (char *)data);
93     gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(chooser),
94                                       suggested_filename);
95     gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(chooser),
96                                                    TRUE);
97     gint res = gtk_dialog_run(GTK_DIALOG(chooser));
98
99     switch (res) {
100     case GTK_RESPONSE_ACCEPT:
101         uri = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(chooser));
102         webkit_download_set_destination(
103             download, gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(chooser)));
104         break;
105     case GTK_RESPONSE_CANCEL:
106         return FALSE;
107     default:
108         return FALSE;
109     }
110
111     gtk_widget_destroy(chooser);
112
113     remove(uri + 7);
114     tb = gtk_tool_button_new(NULL, NULL);
115     gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(tb), "network-receive");
116     gtk_tool_button_set_label(GTK_TOOL_BUTTON(tb), uri);
117     gtk_toolbar_insert(GTK_TOOLBAR(dm.toolbar), tb, 0);
118     gtk_widget_show_all(dm.win);
119
120     g_signal_connect(G_OBJECT(download), "notify::estimated-progress",
121                      G_CALLBACK(changed_download_progress), tb);
122
123     downloads++;
124     g_signal_connect(G_OBJECT(download), "finished",
125                      G_CALLBACK(download_finished), tb);
126
127     g_object_ref(download);
128
129     struct DownloadItem *payload = malloc(sizeof(*payload));
130     payload->tb = (GtkToolButton *)tb;
131     payload->download = download;
132     g_signal_connect(G_OBJECT(tb), "clicked", G_CALLBACK(download_click),
133                      payload);
134     g_signal_connect(G_OBJECT(tb), "failed", G_CALLBACK(download_cancel),
135                      payload);
136     g_signal_connect(G_OBJECT(tb), "destroy_event", G_CALLBACK(g_free),
137                      payload);
138
139     // Propagate -- to whom it may concern.
140     return FALSE;
141 }
142
143 void
144 download_cancel(GtkMenuItem *tb, gpointer data) {
145     struct DownloadItem *payload = data;
146     gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(payload->tb), "dialog-error");
147     webkit_download_cancel(payload->download);
148 }
149
150 void
151 download_remove(GtkMenuItem *tb, gpointer data) {
152     struct DownloadItem *payload = data;
153     g_object_unref(payload->download);
154     gtk_widget_destroy(GTK_WIDGET(payload->tb));
155 }
156
157 void
158 download_copy_url(GtkMenuItem *tb, gpointer data) {
159     struct DownloadItem *payload = data;
160     WebKitURIRequest *req = webkit_download_get_request(payload->download);
161     const gchar *uri = webkit_uri_request_get_uri(req);
162     gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), uri,
163                            strlen(uri));
164 }
165
166 void
167 download_copy_path(GtkMenuItem *tb, gpointer data) {
168     struct DownloadItem *payload = data;
169     const gchar *path = webkit_download_get_destination(payload->download);
170     gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), path + 7,
171                            strlen(path) - 7); // Offset by 7 to remove "file://"
172 }
173
174 void
175 download_xdg_open(GtkMenuItem *tb, gpointer data) {
176     struct DownloadItem *payload = data;
177     const gchar *path = webkit_download_get_destination(payload->download);
178     char *cmd = malloc(strlen(path) + 9);
179     sprintf(cmd, "xdg-open %s", path);
180     system(cmd);
181 }
182
183 void
184 download_click(GtkToolButton *tb, gpointer data) {
185     GtkWidget *pmenu = gtk_menu_new();
186     GtkWidget *option;
187
188     if (strcmp(gtk_tool_button_get_icon_name(GTK_TOOL_BUTTON(tb)),
189                "network-receive") == 0) {
190         option = gtk_menu_item_new_with_label("Cancel download");
191         g_signal_connect(G_OBJECT(option), "activate",
192                          G_CALLBACK(download_cancel), data);
193         gtk_widget_show(option);
194         gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), option);
195     } else {
196         option = gtk_menu_item_new_with_label("Remove download");
197         g_signal_connect(G_OBJECT(option), "activate",
198                          G_CALLBACK(download_remove), data);
199         gtk_widget_show(option);
200         gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), option);
201
202         option = gtk_menu_item_new_with_label("Open file with xdg-open");
203         gtk_widget_show(option);
204         gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), option);
205         g_signal_connect(G_OBJECT(option), "activate",
206                          G_CALLBACK(download_xdg_open), data);
207     }
208
209     option = gtk_menu_item_new_with_label("Copy download URL");
210     gtk_widget_show(option);
211     gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), option);
212     g_signal_connect(G_OBJECT(option), "activate",
213                      G_CALLBACK(download_copy_url), data);
214
215     option = gtk_menu_item_new_with_label("Copy local path");
216     gtk_widget_show(option);
217     gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), option);
218     g_signal_connect(G_OBJECT(option), "activate",
219                      G_CALLBACK(download_copy_path), data);
220
221     gtk_menu_popup_at_pointer(GTK_MENU(pmenu), NULL);
222 }
223
224 gboolean
225 downloadmanager_delete(GtkWidget *obj, gpointer data) {
226     if (!quit_if_nothing_active())
227         gtk_widget_hide(dm.win);
228
229     return TRUE;
230 }
231
232 void
233 downloadmanager_setup(void) {
234     dm.win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
235     gtk_window_set_type_hint(GTK_WINDOW(dm.win), GDK_WINDOW_TYPE_HINT_DIALOG);
236     gtk_window_set_default_size(GTK_WINDOW(dm.win), 500, 250);
237     gtk_window_set_title(GTK_WINDOW(dm.win), __NAME__ " - Download Manager");
238     g_signal_connect(G_OBJECT(dm.win), "delete-event",
239                      G_CALLBACK(downloadmanager_delete), NULL);
240     g_signal_connect(G_OBJECT(dm.win), "key-press-event",
241                      G_CALLBACK(key_downloadmanager), NULL);
242
243     dm.toolbar = gtk_toolbar_new();
244     gtk_orientable_set_orientation(GTK_ORIENTABLE(dm.toolbar),
245                                    GTK_ORIENTATION_VERTICAL);
246     gtk_toolbar_set_style(GTK_TOOLBAR(dm.toolbar), GTK_TOOLBAR_BOTH_HORIZ);
247     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(dm.toolbar), FALSE);
248
249     dm.scroll = gtk_scrolled_window_new(NULL, NULL);
250     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dm.scroll),
251                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
252     gtk_container_add(GTK_CONTAINER(dm.scroll), dm.toolbar);
253
254     gtk_container_add(GTK_CONTAINER(dm.win), dm.scroll);
255 }
256
257 void
258 downloadmanager_show(void) {
259     gtk_widget_show_all(dm.win);
260 }