Revert "Fix to the new internal copiousoutput handling"
[elinks.git] / src / dialogs / download.c
blob825e0b53d82555aaa88410d3b8194cd504dff603
1 /* Download dialogs */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
11 #include "elinks.h"
13 #include "bfu/dialog.h"
14 #include "bfu/hierbox.h"
15 #include "dialogs/download.h"
16 #include "dialogs/menu.h"
17 #include "dialogs/progress.h"
18 #include "dialogs/status.h"
19 #include "intl/gettext/libintl.h"
20 #include "main/object.h"
21 #include "main/select.h"
22 #include "network/connection.h"
23 #include "network/progress.h"
24 #include "protocol/bittorrent/dialogs.h"
25 #include "protocol/protocol.h"
26 #include "protocol/uri.h"
27 #include "session/download.h"
28 #include "session/session.h"
29 #include "terminal/draw.h"
30 #include "terminal/terminal.h"
31 #include "util/color.h"
32 #include "util/conv.h"
33 #include "util/error.h"
34 #include "util/memlist.h"
35 #include "util/memory.h"
36 #include "util/string.h"
37 #include "util/time.h"
40 static void
41 undisplay_download(struct file_download *file_download)
43 /* We are maybe called from bottom halve so check consistency */
44 if (is_in_downloads_list(file_download) && file_download->dlg_data)
45 cancel_dialog(file_download->dlg_data, NULL);
48 static void
49 do_abort_download(struct file_download *file_download)
51 /* We are maybe called from bottom halve so check consistency */
52 if (is_in_downloads_list(file_download)) {
53 file_download->stop = 1;
54 abort_download(file_download);
58 static widget_handler_status_T
59 dlg_set_notify(struct dialog_data *dlg_data, struct widget_data *widget_data)
61 struct file_download *file_download = dlg_data->dlg->udata;
63 file_download->notify = 1;
64 #if CONFIG_BITTORRENT
65 if (file_download->uri->protocol == PROTOCOL_BITTORRENT)
66 set_bittorrent_notify_on_completion(&file_download->download,
67 file_download->term);
68 #endif
69 undisplay_download(file_download);
70 return EVENT_PROCESSED;
73 static widget_handler_status_T
74 dlg_abort_download(struct dialog_data *dlg_data, struct widget_data *widget_data)
76 struct file_download *file_download = dlg_data->dlg->udata;
78 object_unlock(file_download);
79 register_bottom_half(do_abort_download, file_download);
80 return EVENT_PROCESSED;
83 static widget_handler_status_T
84 push_delete_button(struct dialog_data *dlg_data, struct widget_data *widget_data)
86 struct file_download *file_download = dlg_data->dlg->udata;
88 file_download->delete = 1;
89 #if CONFIG_BITTORRENT
90 if (file_download->uri->protocol == PROTOCOL_BITTORRENT)
91 set_bittorrent_files_for_deletion(&file_download->download);
92 #endif
93 object_unlock(file_download);
94 register_bottom_half(do_abort_download, file_download);
95 return EVENT_PROCESSED;
98 static widget_handler_status_T
99 dlg_undisplay_download(struct dialog_data *dlg_data, struct widget_data *widget_data)
101 struct file_download *file_download = dlg_data->dlg->udata;
103 object_unlock(file_download);
104 register_bottom_half(undisplay_download, file_download);
105 return EVENT_PROCESSED;
109 static void
110 download_abort_function(struct dialog_data *dlg_data)
112 struct file_download *file_download = dlg_data->dlg->udata;
114 file_download->dlg_data = NULL;
118 static void
119 download_dialog_layouter(struct dialog_data *dlg_data)
121 struct file_download *file_download = dlg_data->dlg->udata;
122 struct terminal *term = dlg_data->win->term;
123 int w = dialog_max_width(term);
124 int rw = w;
125 int x, y = 0;
126 int url_len;
127 unsigned char *url;
128 struct download *download = &file_download->download;
129 struct color_pair *dialog_text_color = get_bfu_color(term, "dialog.text");
130 unsigned char *msg = get_download_msg(download, term, 1, 1, "\n");
131 int show_meter = (download_is_progressing(download)
132 && download->progress->size >= 0);
133 #if CONFIG_BITTORRENT
134 int bittorrent = (file_download->uri->protocol == PROTOCOL_BITTORRENT
135 && (show_meter || download->state == S_RESUME));
136 #endif
138 redraw_below_window(dlg_data->win);
139 file_download->dlg_data = dlg_data;
141 if (!msg) return;
143 url = get_uri_string(file_download->uri, URI_PUBLIC);
144 if (!url) {
145 mem_free(msg);
146 return;
148 #ifdef CONFIG_UTF8
149 if (term->utf8_cp)
150 decode_uri(url);
151 else
152 #endif /* CONFIG_UTF8 */
153 decode_uri_for_display(url);
154 url_len = strlen(url);
156 if (show_meter) {
157 int_lower_bound(&w, DOWN_DLG_MIN);
160 dlg_format_text_do(term, url, 0, &y, w, &rw,
161 dialog_text_color, ALIGN_LEFT, 1);
163 y++;
164 if (show_meter) y += 2;
166 #if CONFIG_BITTORRENT
167 if (bittorrent) y += 2;
168 #endif
169 dlg_format_text_do(term, msg, 0, &y, w, &rw,
170 dialog_text_color, ALIGN_LEFT, 1);
172 y++;
173 dlg_format_buttons(term, dlg_data->widgets_data,
174 dlg_data->number_of_widgets, 0, &y, w,
175 &rw, ALIGN_CENTER, 1);
177 draw_dialog(dlg_data, w, y);
179 w = rw;
180 if (url_len > w) {
181 /* Truncate too long urls */
182 url_len = w;
183 url[url_len] = '\0';
184 if (url_len > 4) {
185 url[--url_len] = '.';
186 url[--url_len] = '.';
187 url[--url_len] = '.';
191 y = dlg_data->box.y + DIALOG_TB + 1;
192 x = dlg_data->box.x + DIALOG_LB;
193 dlg_format_text_do(term, url, x, &y, w, NULL,
194 dialog_text_color, ALIGN_LEFT, 0);
196 if (show_meter) {
197 y++;
198 draw_progress_bar(download->progress, term, x, y, w, NULL, NULL);
199 y++;
202 #if CONFIG_BITTORRENT
203 if (bittorrent) {
204 y++;
205 draw_bittorrent_piece_progress(download, term, x, y, w, NULL, NULL);
206 y++;
208 #endif
209 y++;
210 dlg_format_text_do(term, msg, x, &y, w, NULL,
211 dialog_text_color, ALIGN_LEFT, 0);
213 y++;
214 dlg_format_buttons(term, dlg_data->widgets_data,
215 dlg_data->number_of_widgets, x, &y, w,
216 NULL, ALIGN_CENTER, 0);
218 mem_free(url);
219 mem_free(msg);
222 void
223 display_download(struct terminal *term, struct file_download *file_download,
224 struct session *ses)
226 /* [gettext_accelerator_context(display_download)] */
227 struct dialog *dlg;
229 if (!is_in_downloads_list(file_download))
230 return;
232 #if CONFIG_BITTORRENT
233 #define DOWNLOAD_WIDGETS_COUNT 5
234 #else
235 #define DOWNLOAD_WIDGETS_COUNT 4
236 #endif
238 dlg = calloc_dialog(DOWNLOAD_WIDGETS_COUNT, 0);
239 if (!dlg) return;
241 undisplay_download(file_download);
242 file_download->ses = ses;
243 dlg->title = _("Download", term);
244 dlg->layouter = download_dialog_layouter;
245 dlg->abort = download_abort_function;
246 dlg->udata = file_download;
248 object_lock(file_download);
250 add_dlg_button(dlg, _("~Background", term), B_ENTER | B_ESC, dlg_undisplay_download, NULL);
251 add_dlg_button(dlg, _("Background with ~notify", term), B_ENTER | B_ESC, dlg_set_notify, NULL);
253 #if CONFIG_BITTORRENT
254 if (file_download->uri->protocol == PROTOCOL_BITTORRENT)
255 add_dlg_button(dlg, _("~Info", term), B_ENTER | B_ESC, dlg_show_bittorrent_info, NULL);
256 #endif
258 add_dlg_button(dlg, _("~Abort", term), 0, dlg_abort_download, NULL);
260 /* Downloads scheduled to be opened by external handlers are always
261 * deleted. */
262 if (!file_download->external_handler) {
263 add_dlg_button(dlg, _("Abort and ~delete file", term), 0, push_delete_button, NULL);
266 #if CONFIG_BITTORRENT
267 add_dlg_end(dlg, DOWNLOAD_WIDGETS_COUNT - !!file_download->external_handler
268 - (file_download->uri->protocol != PROTOCOL_BITTORRENT));
269 #else
270 add_dlg_end(dlg, DOWNLOAD_WIDGETS_COUNT - !!file_download->external_handler);
271 #endif
273 do_dialog(term, dlg, getml(dlg, (void *) NULL));
277 /* The download manager */
279 static void
280 lock_file_download(struct listbox_item *item)
282 object_lock((struct file_download *) item->udata);
285 static void
286 unlock_file_download(struct listbox_item *item)
288 object_unlock((struct file_download *) item->udata);
291 static int
292 is_file_download_used(struct listbox_item *item)
294 return is_object_used((struct file_download *) item->udata);
297 static unsigned char *
298 get_file_download_text(struct listbox_item *item, struct terminal *term)
300 struct file_download *file_download = item->udata;
301 unsigned char *uristring;
303 uristring = get_uri_string(file_download->uri, URI_PUBLIC);
304 if (uristring) {
305 #ifdef CONFIG_UTF8
306 if (term->utf8_cp)
307 decode_uri(uristring);
308 else
309 #endif /* CONFIG_UTF8 */
310 decode_uri_for_display(uristring);
313 return uristring;
316 static unsigned char *
317 get_file_download_info(struct listbox_item *item, struct terminal *term)
319 return NULL;
322 static struct uri *
323 get_file_download_uri(struct listbox_item *item)
325 struct file_download *file_download = item->udata;
327 return get_uri_reference(file_download->uri);
330 static struct listbox_item *
331 get_file_download_root(struct listbox_item *item)
333 return NULL;
336 static int
337 can_delete_file_download(struct listbox_item *item)
339 return 1;
342 static void
343 delete_file_download(struct listbox_item *item, int last)
345 struct file_download *file_download = item->udata;
347 assert(!is_object_used(file_download));
348 register_bottom_half(do_abort_download, file_download);
351 static enum dlg_refresh_code
352 refresh_file_download(struct dialog_data *dlg_data, void *data)
354 /* Always refresh (until we keep finished downloads) */
355 return are_there_downloads() ? REFRESH_DIALOG : REFRESH_STOP;
358 /* TODO: Make it configurable */
359 #define DOWNLOAD_METER_WIDTH 15
360 #define DOWNLOAD_URI_PERCENTAGE 50
362 static void
363 draw_file_download(struct listbox_item *item, struct listbox_context *context,
364 int x, int y, int width)
366 struct file_download *file_download = item->udata;
367 struct download *download = &file_download->download;
368 unsigned char *stylename;
369 struct color_pair *color;
370 unsigned char *text;
371 int length;
372 int trimmedlen;
373 int meter = DOWNLOAD_METER_WIDTH;
375 /* We have nothing to work with */
376 if (width < 4) return;
378 stylename = (item == context->box->sel) ? "menu.selected"
379 : ((item->marked) ? "menu.marked"
380 : "menu.normal");
382 color = get_bfu_color(context->term, stylename);
384 text = get_file_download_text(item, context->term);
385 if (!text) return;
387 length = strlen(text);
388 /* Show atleast the required percentage of the URI */
389 if (length * DOWNLOAD_URI_PERCENTAGE / 100 < width - meter - 4) {
390 trimmedlen = int_min(length, width - meter - 4);
391 } else {
392 trimmedlen = int_min(length, width - 3);
395 draw_text(context->term, x, y, text, trimmedlen, 0, color);
396 if (trimmedlen < length) {
397 draw_text(context->term, x + trimmedlen, y, "...", 3, 0, color);
398 trimmedlen += 3;
401 mem_free(text);
403 if (!download->progress
404 || download->progress->size < 0
405 || download->state != S_TRANS
406 || !has_progress(download->progress)) {
407 /* TODO: Show trimmed error message. */
408 return;
411 if (!dialog_has_refresh(context->dlg_data))
412 refresh_dialog(context->dlg_data, refresh_file_download, NULL);
414 if (trimmedlen + meter >= width) return;
416 x += width - meter;
418 draw_progress_bar(download->progress, context->term, x, y, meter, NULL, NULL);
421 static struct listbox_ops_messages download_messages = {
422 /* cant_delete_item */
423 N_("Sorry, but download \"%s\" cannot be interrupted."),
424 /* cant_delete_used_item */
425 N_("Sorry, but download \"%s\" is being used by something else."),
426 /* cant_delete_folder */
427 NULL,
428 /* cant_delete_used_folder */
429 NULL,
430 /* delete_marked_items_title */
431 N_("Interrupt marked downloads"),
432 /* delete_marked_items */
433 N_("Interrupt marked downloads?"),
434 /* delete_folder_title */
435 NULL,
436 /* delete_folder */
437 NULL,
438 /* delete_item_title */
439 N_("Interrupt download"),
440 /* delete_item; xgettext:c-format */
441 N_("Interrupt this download?"),
442 /* clear_all_items_title */
443 N_("Interrupt all downloads"),
444 /* clear_all_items_title */
445 N_("Do you really want to interrupt all downloads?"),
448 static const struct listbox_ops downloads_listbox_ops = {
449 lock_file_download,
450 unlock_file_download,
451 is_file_download_used,
452 get_file_download_text,
453 get_file_download_info,
454 get_file_download_uri,
455 get_file_download_root,
456 NULL,
457 can_delete_file_download,
458 delete_file_download,
459 draw_file_download,
460 &download_messages,
464 static widget_handler_status_T
465 push_info_button(struct dialog_data *dlg_data, struct widget_data *button)
467 struct listbox_data *box = get_dlg_listbox_data(dlg_data);
468 struct terminal *term = dlg_data->win->term;
469 struct session *ses = dlg_data->dlg->udata;
470 struct file_download *file_download = box->sel ? box->sel->udata : NULL;
472 assert(ses);
474 if (!file_download) return EVENT_PROCESSED;
476 /* Don't layer on top of the download manager */
477 delete_window(dlg_data->win);
479 display_download(term, file_download, ses);
480 return EVENT_PROCESSED;
484 /* TODO: Ideas for buttons .. should be pretty trivial most of it
486 * - Resume or something that will use some goto like handler
487 * - Open button that can be used to set file_download->prog.
488 * - Toggle notify button
490 static const struct hierbox_browser_button download_buttons[] = {
491 /* [gettext_accelerator_context(.download_buttons)] */
492 { N_("~Info"), push_info_button },
493 { N_("~Abort"), push_hierbox_delete_button },
494 #if 0
495 /* This requires more work to make locking work and query the user */
496 { N_("Abort and delete file"), push_delete_button },
497 #endif
498 { N_("C~lear"), push_hierbox_clear_button },
501 static struct_hierbox_browser(
502 download_browser,
503 N_("Download manager"),
504 download_buttons,
505 &downloads_listbox_ops
508 void
509 download_manager(struct session *ses)
511 hierbox_browser(&download_browser, ses);
513 /* FIXME: It's workaround for bug 397. Real fix is needed. */
514 download_browser.do_not_save_state = 1;
517 void
518 init_download_display(struct file_download *file_download)
520 file_download->box_item = add_listbox_leaf(&download_browser, NULL,
521 file_download);
524 void
525 done_download_display(struct file_download *file_download)
527 if (file_download->box_item) {
528 done_listbox_item(&download_browser, file_download->box_item);
529 file_download->box_item = NULL;