BrowserDownloadsBar.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /*
  2. * Copyright (C) 2011 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. #include "BrowserDownloadsBar.h"
  26. #include <glib/gi18n.h>
  27. #define BROWSER_TYPE_DOWNLOAD (browser_download_get_type())
  28. #define BROWSER_DOWNLOAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BROWSER_TYPE_DOWNLOAD, BrowserDownload))
  29. typedef struct _BrowserDownload BrowserDownload;
  30. typedef struct _BrowserDownloadClass BrowserDownloadClass;
  31. static GType browser_download_get_type();
  32. struct _BrowserDownloadsBar {
  33. GtkInfoBar parent;
  34. };
  35. struct _BrowserDownloadsBarClass {
  36. GtkInfoBarClass parentClass;
  37. };
  38. G_DEFINE_TYPE(BrowserDownloadsBar, browser_downloads_bar, GTK_TYPE_INFO_BAR)
  39. static void
  40. browserDownloadsBarChildRemoved(GtkContainer *infoBar, GtkWidget *widget, BrowserDownloadsBar *downloadsBar)
  41. {
  42. GList *children = gtk_container_get_children(infoBar);
  43. if (g_list_length(children) == 1)
  44. gtk_info_bar_response(GTK_INFO_BAR(downloadsBar), GTK_RESPONSE_CLOSE);
  45. g_list_free(children);
  46. }
  47. static void browserDownloadsBarResponse(GtkInfoBar *infoBar, gint responseId)
  48. {
  49. gtk_widget_destroy(GTK_WIDGET(infoBar));
  50. }
  51. static void browser_downloads_bar_init(BrowserDownloadsBar *downloadsBar)
  52. {
  53. GtkWidget *contentBox = gtk_info_bar_get_content_area(GTK_INFO_BAR(downloadsBar));
  54. g_signal_connect_after(contentBox, "remove", G_CALLBACK(browserDownloadsBarChildRemoved), downloadsBar);
  55. gtk_orientable_set_orientation(GTK_ORIENTABLE(contentBox), GTK_ORIENTATION_VERTICAL);
  56. GtkWidget *title = gtk_label_new(NULL);
  57. gtk_label_set_markup(GTK_LABEL(title), "<span size='xx-large' weight='bold'>Downloads</span>");
  58. gtk_misc_set_alignment(GTK_MISC(title), 0., 0.5);
  59. gtk_box_pack_start(GTK_BOX(contentBox), title, FALSE, FALSE, 12);
  60. gtk_widget_show(title);
  61. }
  62. static void browser_downloads_bar_class_init(BrowserDownloadsBarClass *klass)
  63. {
  64. GtkInfoBarClass *infoBarClass = GTK_INFO_BAR_CLASS(klass);
  65. infoBarClass->response = browserDownloadsBarResponse;
  66. }
  67. GtkWidget *browser_downloads_bar_new()
  68. {
  69. GtkInfoBar *downloadsBar = GTK_INFO_BAR(g_object_new(BROWSER_TYPE_DOWNLOADS_BAR, NULL));
  70. gtk_info_bar_add_buttons(downloadsBar, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
  71. return GTK_WIDGET(downloadsBar);
  72. }
  73. struct _BrowserDownload {
  74. GtkBox parent;
  75. WebKitDownload *download;
  76. guint64 contentLength;
  77. guint64 downloadedSize;
  78. gboolean finished;
  79. GtkWidget *statusLabel;
  80. GtkWidget *remainingLabel;
  81. GtkWidget *progressBar;
  82. GtkWidget *actionButton;
  83. };
  84. struct _BrowserDownloadClass {
  85. GtkBoxClass parentClass;
  86. };
  87. G_DEFINE_TYPE(BrowserDownload, browser_download, GTK_TYPE_BOX)
  88. static void actionButtonClicked(GtkButton *button, BrowserDownload *browserDownload)
  89. {
  90. if (!browserDownload->finished) {
  91. webkit_download_cancel(browserDownload->download);
  92. return;
  93. }
  94. gtk_show_uri(gtk_widget_get_screen(GTK_WIDGET(browserDownload)),
  95. webkit_download_get_destination(browserDownload->download),
  96. gtk_get_current_event_time(), NULL);
  97. gtk_widget_destroy(GTK_WIDGET(browserDownload));
  98. }
  99. static void browserDownloadFinalize(GObject *object)
  100. {
  101. BrowserDownload *browserDownload = BROWSER_DOWNLOAD(object);
  102. if (browserDownload->download) {
  103. g_object_unref(browserDownload->download);
  104. browserDownload->download = NULL;
  105. }
  106. G_OBJECT_CLASS(browser_download_parent_class)->finalize(object);
  107. }
  108. static void browser_download_init(BrowserDownload *download)
  109. {
  110. GtkWidget *mainBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
  111. gtk_box_pack_start(GTK_BOX(download), mainBox, FALSE, FALSE, 0);
  112. gtk_widget_show(mainBox);
  113. GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
  114. gtk_box_pack_start(GTK_BOX(mainBox), vbox, TRUE, TRUE, 0);
  115. gtk_widget_show(vbox);
  116. GtkWidget *statusBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
  117. gtk_box_pack_start(GTK_BOX(vbox), statusBox, TRUE, TRUE, 0);
  118. gtk_widget_show(statusBox);
  119. download->statusLabel = gtk_label_new("Starting Download");
  120. gtk_label_set_ellipsize(GTK_LABEL(download->statusLabel), PANGO_ELLIPSIZE_END);
  121. gtk_misc_set_alignment(GTK_MISC(download->statusLabel), 0., 0.5);
  122. gtk_box_pack_start(GTK_BOX(statusBox), download->statusLabel, TRUE, TRUE, 0);
  123. gtk_widget_show(download->statusLabel);
  124. download->remainingLabel = gtk_label_new(NULL);
  125. gtk_misc_set_alignment(GTK_MISC(download->remainingLabel), 1., 0.5);
  126. gtk_box_pack_end(GTK_BOX(statusBox), download->remainingLabel, TRUE, TRUE, 0);
  127. gtk_widget_show(download->remainingLabel);
  128. download->progressBar = gtk_progress_bar_new();
  129. gtk_box_pack_start(GTK_BOX(vbox), download->progressBar, FALSE, FALSE, 0);
  130. gtk_widget_show(download->progressBar);
  131. download->actionButton = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
  132. g_signal_connect(download->actionButton, "clicked", G_CALLBACK(actionButtonClicked), download);
  133. gtk_box_pack_end(GTK_BOX(mainBox), download->actionButton, FALSE, FALSE, 0);
  134. gtk_widget_show(download->actionButton);
  135. }
  136. static void browser_download_class_init(BrowserDownloadClass *klass)
  137. {
  138. GObjectClass *objectClass = G_OBJECT_CLASS(klass);
  139. objectClass->finalize = browserDownloadFinalize;
  140. }
  141. static void downloadReceivedResponse(WebKitDownload *download, GParamSpec *paramSpec, BrowserDownload *browserDownload)
  142. {
  143. WebKitURIResponse *response = webkit_download_get_response(download);
  144. browserDownload->contentLength = webkit_uri_response_get_content_length(response);
  145. char *text = g_strdup_printf("Downloading %s", webkit_uri_response_get_uri(response));
  146. gtk_label_set_text(GTK_LABEL(browserDownload->statusLabel), text);
  147. g_free(text);
  148. }
  149. static gchar *remainingTime(BrowserDownload *browserDownload)
  150. {
  151. guint64 total = browserDownload->contentLength;
  152. guint64 current = browserDownload->downloadedSize;
  153. gdouble elapsedTime = webkit_download_get_elapsed_time(browserDownload->download);
  154. if (current <= 0)
  155. return NULL;
  156. gdouble perByteTime = elapsedTime / current;
  157. gdouble interval = perByteTime * (total - current);
  158. int hours = (int) (interval / 3600);
  159. interval -= hours * 3600;
  160. int mins = (int) (interval / 60);
  161. interval -= mins * 60;
  162. int secs = (int) interval;
  163. if (hours > 0) {
  164. if (mins > 0)
  165. return g_strdup_printf (ngettext ("%u:%02u hour left", "%u:%02u hours left", hours), hours, mins);
  166. return g_strdup_printf (ngettext ("%u hour left", "%u hours left", hours), hours);
  167. }
  168. if (mins > 0)
  169. return g_strdup_printf (ngettext ("%u:%02u minute left", "%u:%02u minutes left", mins), mins, secs);
  170. return g_strdup_printf (ngettext ("%u second left", "%u seconds left", secs), secs);
  171. }
  172. static void downloadProgress(WebKitDownload *download, GParamSpec *paramSpec, BrowserDownload *browserDownload)
  173. {
  174. gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(browserDownload->progressBar),
  175. webkit_download_get_estimated_progress(download));
  176. char *remaining = remainingTime(browserDownload);
  177. gtk_label_set_text(GTK_LABEL(browserDownload->remainingLabel), remaining);
  178. g_free(remaining);
  179. }
  180. static void downloadReceivedData(WebKitDownload *download, guint64 dataLength, BrowserDownload *browserDownload)
  181. {
  182. browserDownload->downloadedSize += dataLength;
  183. }
  184. static void downloadFinished(WebKitDownload *download, BrowserDownload *browserDownload)
  185. {
  186. gchar *text = g_strdup_printf("Download completed: %s", webkit_download_get_destination(download));
  187. gtk_label_set_text(GTK_LABEL(browserDownload->statusLabel), text);
  188. g_free(text);
  189. gtk_label_set_text(GTK_LABEL(browserDownload->remainingLabel), NULL);
  190. gtk_button_set_image(GTK_BUTTON(browserDownload->actionButton),
  191. gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON));
  192. gtk_button_set_label(GTK_BUTTON(browserDownload->actionButton), "Open ...");
  193. browserDownload->finished = TRUE;
  194. }
  195. static void downloadFailed(WebKitDownload *download, GError *error, BrowserDownload *browserDownload)
  196. {
  197. g_signal_handlers_disconnect_by_func(browserDownload->download, downloadFinished, browserDownload);
  198. if (g_error_matches(error, WEBKIT_DOWNLOAD_ERROR, WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER)) {
  199. gtk_widget_destroy(GTK_WIDGET(browserDownload));
  200. return;
  201. }
  202. char *errorMessage = g_strdup_printf("Download failed: %s", error->message);
  203. gtk_label_set_text(GTK_LABEL(browserDownload->statusLabel), errorMessage);
  204. g_free(errorMessage);
  205. gtk_label_set_text(GTK_LABEL(browserDownload->remainingLabel), NULL);
  206. gtk_widget_set_sensitive(browserDownload->actionButton, FALSE);
  207. }
  208. GtkWidget *browserDownloadNew(WebKitDownload *download)
  209. {
  210. BrowserDownload *browserDownload = BROWSER_DOWNLOAD(g_object_new(BROWSER_TYPE_DOWNLOAD,
  211. "orientation", GTK_ORIENTATION_VERTICAL,
  212. NULL));
  213. browserDownload->download = g_object_ref(download);
  214. g_signal_connect(browserDownload->download, "notify::response", G_CALLBACK(downloadReceivedResponse), browserDownload);
  215. g_signal_connect(browserDownload->download, "notify::estimated-progress", G_CALLBACK(downloadProgress), browserDownload);
  216. g_signal_connect(browserDownload->download, "received-data", G_CALLBACK(downloadReceivedData), browserDownload);
  217. g_signal_connect(browserDownload->download, "finished", G_CALLBACK(downloadFinished), browserDownload);
  218. g_signal_connect(browserDownload->download, "failed", G_CALLBACK(downloadFailed), browserDownload);
  219. return GTK_WIDGET(browserDownload);
  220. }
  221. void browser_downloads_bar_add_download(BrowserDownloadsBar *downloadsBar, WebKitDownload *download)
  222. {
  223. GtkWidget *browserDownload = browserDownloadNew(download);
  224. GtkWidget *contentBox = gtk_info_bar_get_content_area(GTK_INFO_BAR(downloadsBar));
  225. gtk_box_pack_start(GTK_BOX(contentBox), browserDownload, FALSE, TRUE, 0);
  226. gtk_widget_show(browserDownload);
  227. }