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"
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
);
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 /* The user of this terminal wants to be notified about the
65 * download. Make this also the terminal where the
66 * notification appears. However, keep the original terminal
67 * for external handlers, because the handler may have been
68 * chosen based on the environment variables (usually TERM or
69 * DISPLAY) of the ELinks process in that terminal. */
70 if (!file_download
->external_handler
)
71 file_download
->term
= dlg_data
->win
->term
;
74 if (file_download
->uri
->protocol
== PROTOCOL_BITTORRENT
)
75 set_bittorrent_notify_on_completion(&file_download
->download
,
78 undisplay_download(file_download
);
79 return EVENT_PROCESSED
;
82 static widget_handler_status_T
83 dlg_abort_download(struct dialog_data
*dlg_data
, struct widget_data
*widget_data
)
85 struct file_download
*file_download
= dlg_data
->dlg
->udata
;
87 object_unlock(file_download
);
88 register_bottom_half(do_abort_download
, file_download
);
89 return EVENT_PROCESSED
;
92 static widget_handler_status_T
93 push_delete_button(struct dialog_data
*dlg_data
, struct widget_data
*widget_data
)
95 struct file_download
*file_download
= dlg_data
->dlg
->udata
;
97 file_download
->delete = 1;
99 if (file_download
->uri
->protocol
== PROTOCOL_BITTORRENT
)
100 set_bittorrent_files_for_deletion(&file_download
->download
);
102 object_unlock(file_download
);
103 register_bottom_half(do_abort_download
, file_download
);
104 return EVENT_PROCESSED
;
107 static widget_handler_status_T
108 dlg_undisplay_download(struct dialog_data
*dlg_data
, struct widget_data
*widget_data
)
110 struct file_download
*file_download
= dlg_data
->dlg
->udata
;
112 object_unlock(file_download
);
113 register_bottom_half(undisplay_download
, file_download
);
114 return EVENT_PROCESSED
;
119 download_abort_function(struct dialog_data
*dlg_data
)
121 struct file_download
*file_download
= dlg_data
->dlg
->udata
;
123 file_download
->dlg_data
= NULL
;
128 download_dialog_layouter(struct dialog_data
*dlg_data
)
130 struct file_download
*file_download
= dlg_data
->dlg
->udata
;
131 struct terminal
*term
= dlg_data
->win
->term
;
132 int w
= dialog_max_width(term
);
137 struct download
*download
= &file_download
->download
;
138 struct color_pair
*dialog_text_color
= get_bfu_color(term
, "dialog.text");
139 unsigned char *msg
= get_download_msg(download
, term
, 1, 1, "\n");
140 int show_meter
= (download_is_progressing(download
)
141 && download
->progress
->size
>= 0);
142 #if CONFIG_BITTORRENT
143 int bittorrent
= (file_download
->uri
->protocol
== PROTOCOL_BITTORRENT
144 && (show_meter
|| is_in_state(download
->state
, S_RESUME
)));
147 redraw_windows(REDRAW_BEHIND_WINDOW
, dlg_data
->win
);
148 file_download
->dlg_data
= dlg_data
;
152 url
= get_uri_string(file_download
->uri
, URI_PUBLIC
);
161 #endif /* CONFIG_UTF8 */
162 decode_uri_for_display(url
);
163 url_len
= strlen(url
);
166 int_lower_bound(&w
, DOWN_DLG_MIN
);
169 dlg_format_text_do(dlg_data
, url
, 0, &y
, w
, &rw
,
170 dialog_text_color
, ALIGN_LEFT
, 1);
173 if (show_meter
) y
+= 2;
175 #if CONFIG_BITTORRENT
176 if (bittorrent
) y
+= 2;
178 dlg_format_text_do(dlg_data
, msg
, 0, &y
, w
, &rw
,
179 dialog_text_color
, ALIGN_LEFT
, 1);
182 dlg_format_buttons(dlg_data
, dlg_data
->widgets_data
,
183 dlg_data
->number_of_widgets
, 0, &y
, w
,
184 &rw
, ALIGN_CENTER
, 1);
186 draw_dialog(dlg_data
, w
, y
);
190 /* Truncate too long urls */
194 url
[--url_len
] = '.';
195 url
[--url_len
] = '.';
196 url
[--url_len
] = '.';
200 y
= dlg_data
->box
.y
+ DIALOG_TB
+ 1;
201 x
= dlg_data
->box
.x
+ DIALOG_LB
;
202 dlg_format_text_do(dlg_data
, url
, x
, &y
, w
, NULL
,
203 dialog_text_color
, ALIGN_LEFT
, 0);
207 draw_progress_bar(download
->progress
, term
, x
, y
, w
, NULL
, NULL
);
211 #if CONFIG_BITTORRENT
214 draw_bittorrent_piece_progress(download
, term
, x
, y
, w
, NULL
, NULL
);
219 dlg_format_text_do(dlg_data
, msg
, x
, &y
, w
, NULL
,
220 dialog_text_color
, ALIGN_LEFT
, 0);
223 dlg_format_buttons(dlg_data
, dlg_data
->widgets_data
,
224 dlg_data
->number_of_widgets
, x
, &y
, w
,
225 NULL
, ALIGN_CENTER
, 0);
232 display_download(struct terminal
*term
, struct file_download
*file_download
,
235 /* [gettext_accelerator_context(display_download)] */
238 if (!is_in_downloads_list(file_download
))
241 #if CONFIG_BITTORRENT
242 #define DOWNLOAD_WIDGETS_COUNT 5
244 #define DOWNLOAD_WIDGETS_COUNT 4
247 dlg
= calloc_dialog(DOWNLOAD_WIDGETS_COUNT
, 0);
250 undisplay_download(file_download
);
251 file_download
->ses
= ses
;
252 dlg
->title
= _("Download", term
);
253 dlg
->layouter
= download_dialog_layouter
;
254 dlg
->abort
= download_abort_function
;
255 dlg
->udata
= file_download
;
257 object_lock(file_download
);
259 add_dlg_button(dlg
, _("~Background", term
), B_ENTER
| B_ESC
, dlg_undisplay_download
, NULL
);
260 add_dlg_button(dlg
, _("Background with ~notify", term
), B_ENTER
| B_ESC
, dlg_set_notify
, NULL
);
262 #if CONFIG_BITTORRENT
263 if (file_download
->uri
->protocol
== PROTOCOL_BITTORRENT
)
264 add_dlg_button(dlg
, _("~Info", term
), B_ENTER
| B_ESC
, dlg_show_bittorrent_info
, NULL
);
267 add_dlg_button(dlg
, _("~Abort", term
), 0, dlg_abort_download
, NULL
);
269 /* Downloads scheduled to be opened by external handlers are always
271 if (!file_download
->external_handler
) {
272 add_dlg_button(dlg
, _("Abort and ~delete file", term
), 0, push_delete_button
, NULL
);
275 #if CONFIG_BITTORRENT
276 add_dlg_end(dlg
, DOWNLOAD_WIDGETS_COUNT
- !!file_download
->external_handler
277 - (file_download
->uri
->protocol
!= PROTOCOL_BITTORRENT
));
279 add_dlg_end(dlg
, DOWNLOAD_WIDGETS_COUNT
- !!file_download
->external_handler
);
282 do_dialog(term
, dlg
, getml(dlg
, (void *) NULL
));
286 /* The download manager */
289 lock_file_download(struct listbox_item
*item
)
291 object_lock((struct file_download
*) item
->udata
);
295 unlock_file_download(struct listbox_item
*item
)
297 object_unlock((struct file_download
*) item
->udata
);
301 is_file_download_used(struct listbox_item
*item
)
303 return is_object_used((struct file_download
*) item
->udata
);
306 static unsigned char *
307 get_file_download_text(struct listbox_item
*item
, struct terminal
*term
)
309 struct file_download
*file_download
= item
->udata
;
310 unsigned char *uristring
;
312 uristring
= get_uri_string(file_download
->uri
, URI_PUBLIC
);
316 decode_uri(uristring
);
318 #endif /* CONFIG_UTF8 */
319 decode_uri_for_display(uristring
);
325 static unsigned char *
326 get_file_download_info(struct listbox_item
*item
, struct terminal
*term
)
332 get_file_download_uri(struct listbox_item
*item
)
334 struct file_download
*file_download
= item
->udata
;
336 return get_uri_reference(file_download
->uri
);
339 static struct listbox_item
*
340 get_file_download_root(struct listbox_item
*item
)
346 can_delete_file_download(struct listbox_item
*item
)
352 delete_file_download(struct listbox_item
*item
, int last
)
354 struct file_download
*file_download
= item
->udata
;
356 assert(!is_object_used(file_download
));
357 register_bottom_half(do_abort_download
, file_download
);
360 static enum dlg_refresh_code
361 refresh_file_download(struct dialog_data
*dlg_data
, void *data
)
363 /* Always refresh (until we keep finished downloads) */
364 return are_there_downloads() ? REFRESH_DIALOG
: REFRESH_STOP
;
367 /* TODO: Make it configurable */
368 #define DOWNLOAD_METER_WIDTH 15
369 #define DOWNLOAD_URI_PERCENTAGE 50
372 draw_file_download(struct listbox_item
*item
, struct listbox_context
*context
,
373 int x
, int y
, int width
)
375 struct file_download
*file_download
= item
->udata
;
376 struct download
*download
= &file_download
->download
;
377 unsigned char *stylename
;
378 struct color_pair
*color
;
382 int meter
= DOWNLOAD_METER_WIDTH
;
384 /* We have nothing to work with */
385 if (width
< 4) return;
387 stylename
= (item
== context
->box
->sel
) ? "menu.selected"
388 : ((item
->marked
) ? "menu.marked"
391 color
= get_bfu_color(context
->term
, stylename
);
393 text
= get_file_download_text(item
, context
->term
);
396 length
= strlen(text
);
397 /* Show atleast the required percentage of the URI */
398 if (length
* DOWNLOAD_URI_PERCENTAGE
/ 100 < width
- meter
- 4) {
399 trimmedlen
= int_min(length
, width
- meter
- 4);
401 trimmedlen
= int_min(length
, width
- 3);
404 draw_text(context
->term
, x
, y
, text
, trimmedlen
, 0, color
);
405 if (trimmedlen
< length
) {
406 draw_text(context
->term
, x
+ trimmedlen
, y
, "...", 3, 0, color
);
412 if (!download
->progress
413 || download
->progress
->size
< 0
414 || !is_in_state(download
->state
, S_TRANS
)
415 || !has_progress(download
->progress
)) {
416 /* TODO: Show trimmed error message. */
420 if (!dialog_has_refresh(context
->dlg_data
))
421 refresh_dialog(context
->dlg_data
, refresh_file_download
, NULL
);
423 if (trimmedlen
+ meter
>= width
) return;
427 draw_progress_bar(download
->progress
, context
->term
, x
, y
, meter
, NULL
, NULL
);
430 static struct listbox_ops_messages download_messages
= {
431 /* cant_delete_item */
432 N_("Sorry, but download \"%s\" cannot be interrupted."),
433 /* cant_delete_used_item */
434 N_("Sorry, but download \"%s\" is being used by something else."),
435 /* cant_delete_folder */
437 /* cant_delete_used_folder */
439 /* delete_marked_items_title */
440 N_("Interrupt marked downloads"),
441 /* delete_marked_items */
442 N_("Interrupt marked downloads?"),
443 /* delete_folder_title */
447 /* delete_item_title */
448 N_("Interrupt download"),
449 /* delete_item; xgettext:c-format */
450 N_("Interrupt this download?"),
451 /* clear_all_items_title */
452 N_("Interrupt all downloads"),
453 /* clear_all_items_title */
454 N_("Do you really want to interrupt all downloads?"),
457 static const struct listbox_ops downloads_listbox_ops
= {
459 unlock_file_download
,
460 is_file_download_used
,
461 get_file_download_text
,
462 get_file_download_info
,
463 get_file_download_uri
,
464 get_file_download_root
,
466 can_delete_file_download
,
467 delete_file_download
,
473 static widget_handler_status_T
474 push_info_button(struct dialog_data
*dlg_data
, struct widget_data
*button
)
476 struct listbox_data
*box
= get_dlg_listbox_data(dlg_data
);
477 struct terminal
*term
= dlg_data
->win
->term
;
478 struct session
*ses
= dlg_data
->dlg
->udata
;
479 struct file_download
*file_download
= box
->sel
? box
->sel
->udata
: NULL
;
483 if (!file_download
) return EVENT_PROCESSED
;
485 /* Don't layer on top of the download manager */
486 delete_window(dlg_data
->win
);
488 display_download(term
, file_download
, ses
);
489 return EVENT_PROCESSED
;
493 /* TODO: Ideas for buttons .. should be pretty trivial most of it
495 * - Resume or something that will use some goto like handler
496 * - Open button that can be used to set file_download->prog.
497 * - Toggle notify button
499 static const struct hierbox_browser_button download_buttons
[] = {
500 /* [gettext_accelerator_context(.download_buttons)] */
501 { N_("~Info"), push_info_button
},
502 { N_("~Abort"), push_hierbox_delete_button
},
504 /* This requires more work to make locking work and query the user */
505 { N_("Abort and delete file"), push_delete_button
},
507 { N_("C~lear"), push_hierbox_clear_button
},
510 static struct_hierbox_browser(
512 N_("Download manager"),
514 &downloads_listbox_ops
518 download_manager(struct session
*ses
)
520 hierbox_browser(&download_browser
, ses
);
522 /* FIXME: It's workaround for bug 397. Real fix is needed. */
523 download_browser
.do_not_save_state
= 1;
527 init_download_display(struct file_download
*file_download
)
529 file_download
->box_item
= add_listbox_leaf(&download_browser
, NULL
,
534 done_download_display(struct file_download
*file_download
)
536 if (file_download
->box_item
) {
537 done_listbox_item(&download_browser
, file_download
->box_item
);
538 file_download
->box_item
= NULL
;