summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--!NetSurf/Resources/en/Messages29
-rw-r--r--desktop/browser.c25
-rw-r--r--desktop/gui.h2
-rw-r--r--desktop/selection.c61
-rw-r--r--desktop/selection.h8
-rw-r--r--desktop/textinput.c922
-rw-r--r--gtk/dialogs/gtk_options.c24
-rw-r--r--gtk/gtk_download.c638
-rw-r--r--gtk/gtk_download.h56
-rw-r--r--gtk/gtk_gui.c34
-rw-r--r--gtk/gtk_scaffolding.c28
-rw-r--r--gtk/gtk_scaffolding.h2
-rw-r--r--gtk/gtk_selection.c27
-rw-r--r--gtk/gtk_window.c14
-rw-r--r--gtk/options.h9
-rw-r--r--gtk/res/downloads.glade118
-rw-r--r--gtk/res/netsurf.glade183
-rw-r--r--gtk/res/options.glade132
-rw-r--r--riscos/download.c2
-rw-r--r--riscos/window.c2
20 files changed, 1654 insertions, 662 deletions
diff --git a/!NetSurf/Resources/en/Messages b/!NetSurf/Resources/en/Messages
index 7c73f50b1..3e218b985 100644
--- a/!NetSurf/Resources/en/Messages
+++ b/!NetSurf/Resources/en/Messages
@@ -298,6 +298,35 @@ DownloadU:%s of unknown • %s/s • %s total
Downloaded:%s complete • average %s/s • %s total
Unwritten:Writing data to file failed.
+# GTK download window tokens
+#
+# This section contains tokens which are used in the gtk
+# download window.
+#
+
+# Column Headers
+#
+gtkProgress:Progress
+gtkDetails:Details
+gtkSpeed:Speed
+gtkRemaining:Remaining
+
+# Status
+#
+gtkError:Error
+gtkComplete:Complete
+gtkCanceled:Canceled
+gtkWorking:Working
+
+# Dialogs
+#
+gtkStartDownload:Download file?
+gtkFailed:Download failed
+gtkFileError:File error: %s
+gtkInfo:%s from %s is %s in size
+gtkSave:Save file as...
+gtkUnknownHost:an unknown host
+gtkUnknownFile:
# Printing user interface tokens
# ==============================
diff --git a/desktop/browser.c b/desktop/browser.c
index 55cfcb56b..155febc50 100644
--- a/desktop/browser.c
+++ b/desktop/browser.c
@@ -629,7 +629,8 @@ void browser_window_convert_to_download(struct browser_window *bw)
if (fetch) {
/* create download window */
download_window = gui_download_window_create(c->url,
- c->mime_type, fetch, c->total_size);
+ c->mime_type, fetch, c->total_size,
+ bw->window);
if (download_window) {
/* extract fetch from content */
@@ -1230,8 +1231,6 @@ void browser_window_mouse_click(struct browser_window *bw,
if (!c)
return;
- browser_window_remove_caret(bw);
-
switch (c->type) {
case CONTENT_HTML:
browser_window_mouse_action_html(bw, mouse, x, y);
@@ -1426,7 +1425,7 @@ void browser_window_mouse_action_html(struct browser_window *bw,
status = messages_get("FormTextarea");
pointer = GUI_POINTER_CARET;
- if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)) {
+ if (mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2)) {
if (text_box && selection_root(bw->sel) != gadget_box)
selection_init(bw->sel, gadget_box);
@@ -1446,7 +1445,7 @@ void browser_window_mouse_action_html(struct browser_window *bw,
nsfont_position_in_string(text_box->style,
text_box->text,
text_box->length,
- x - text_box_x,
+ x - gadget_box_x - text_box->x,
&idx,
&pixel_offset);
@@ -1459,14 +1458,14 @@ void browser_window_mouse_action_html(struct browser_window *bw,
} else
status = c->status_message;
}
- else if (mouse & BROWSER_MOUSE_CLICK_1)
+ else if (mouse & BROWSER_MOUSE_PRESS_1)
selection_clear(bw->sel, true);
break;
case GADGET_TEXTBOX:
case GADGET_PASSWORD:
status = messages_get("FormTextbox");
pointer = GUI_POINTER_CARET;
- if ((mouse & BROWSER_MOUSE_CLICK_1) &&
+ if ((mouse & BROWSER_MOUSE_PRESS_1) &&
!(mouse & (BROWSER_MOUSE_MOD_1 |
BROWSER_MOUSE_MOD_2))) {
browser_window_input_click(bw,
@@ -1487,7 +1486,7 @@ void browser_window_mouse_action_html(struct browser_window *bw,
nsfont_position_in_string(text_box->style,
text_box->text,
text_box->length,
- x - text_box_x,
+ x - gadget_box_x - text_box->x,
&idx,
&pixel_offset);
@@ -1497,7 +1496,7 @@ void browser_window_mouse_action_html(struct browser_window *bw,
if (selection_dragging(bw->sel))
bw->drag_type = DRAGGING_SELECTION;
}
- else if (mouse & BROWSER_MOUSE_CLICK_1)
+ else if (mouse & BROWSER_MOUSE_PRESS_1)
selection_clear(bw->sel, true);
break;
case GADGET_HIDDEN:
@@ -1586,7 +1585,6 @@ void browser_window_mouse_action_html(struct browser_window *bw,
/* key presses must be directed at the
* main browser window, paste text
* operations ignored */
- browser_window_remove_caret(bw);
if (selection_dragging(bw->sel)) {
bw->drag_type =
@@ -1599,7 +1597,7 @@ void browser_window_mouse_action_html(struct browser_window *bw,
done = true;
}
}
- else if (mouse & BROWSER_MOUSE_CLICK_1)
+ else if (mouse & BROWSER_MOUSE_PRESS_1)
selection_clear(bw->sel, true);
}
@@ -1838,9 +1836,10 @@ void browser_window_mouse_track_html(struct browser_window *bw,
if (box) {
int pixel_offset;
size_t idx;
+
nsfont_position_in_string(box->style,
box->text, box->length,
- dx, &idx, &pixel_offset);
+ dx, &idx, &pixel_offset);
selection_track(bw->sel, mouse,
box->byte_offset + idx);
@@ -2194,7 +2193,7 @@ void browser_window_redraw_rect(struct browser_window *bw, int x, int y,
void browser_redraw_box(struct content *c, struct box *box)
{
int x, y;
- union content_msg_data data;
+ union content_msg_data data;
box_coords(box, &x, &y);
diff --git a/desktop/gui.h b/desktop/gui.h
index 6ed0243d7..77fd861c3 100644
--- a/desktop/gui.h
+++ b/desktop/gui.h
@@ -99,7 +99,7 @@ void gui_window_set_scale(struct gui_window *g, float scale);
struct gui_download_window *gui_download_window_create(const char *url,
const char *mime_type, struct fetch *fetch,
- unsigned int total_size);
+ unsigned int total_size, struct gui_window *gui);
void gui_download_window_data(struct gui_download_window *dw, const char *data,
unsigned int size);
void gui_download_window_error(struct gui_download_window *dw,
diff --git a/desktop/selection.c b/desktop/selection.c
index 74ae3165f..2be6853a2 100644
--- a/desktop/selection.c
+++ b/desktop/selection.c
@@ -50,7 +50,9 @@
*/
#define IS_INPUT(box) ((box) && (box)->gadget && \
- ((box)->gadget->type == GADGET_TEXTAREA || (box)->gadget->type == GADGET_TEXTBOX))
+ ((box)->gadget->type == GADGET_TEXTAREA || \
+ (box)->gadget->type == GADGET_TEXTBOX || \
+ (box)->gadget->type == GADGET_PASSWORD))
/** check whether the given text box is in the same number space as the
current selection; number spaces are identified by their uppermost nybble */
@@ -69,7 +71,6 @@ static bool redraw_handler(const char *text, size_t length, struct box *box,
size_t whitespace_length);
static void selection_redraw(struct selection *s, unsigned start_idx,
unsigned end_idx);
-static unsigned selection_label_subtree(struct box *box, unsigned idx);
static bool save_handler(const char *text, size_t length, struct box *box,
void *handle, const char *whitespace_text,
size_t whitespace_length);
@@ -79,8 +80,7 @@ static bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
unsigned int num_space, seln_traverse_handler handler,
void *handle, save_text_whitespace *before, bool *first,
bool do_marker);
-static struct box *get_box(struct box *b, unsigned offset, int *pidx);
-
+static struct box *get_box(struct box *b, unsigned offset, size_t *pidx);
/**
* Creates a new selection object associated with a browser window.
@@ -261,11 +261,14 @@ bool selection_click(struct selection *s, browser_mouse_state mouse,
gui_drag_save_selection(s, s->bw->window);
}
else if (!modkeys) {
- if (mouse & BROWSER_MOUSE_DRAG_1) {
-
+ if (pos && (mouse & BROWSER_MOUSE_PRESS_1))
+ /* Clear the selection if mouse is pressed outside the selection,
+ * Otherwise clear on release (to allow for drags) */
+ selection_clear(s, true);
+ else if (mouse & BROWSER_MOUSE_DRAG_1) {
/* start new selection drag */
selection_clear(s, true);
-
+
selection_set_start(s, idx);
selection_set_end(s, idx);
@@ -291,12 +294,13 @@ bool selection_click(struct selection *s, browser_mouse_state mouse,
}
gui_start_selection(s->bw->window);
}
- else if (pos && (mouse & BROWSER_MOUSE_CLICK_1)) {
-
- /* clear selection */
- selection_clear(s, true);
- s->drag_state = DRAG_NONE;
- }
+ /* Selection should be cleared when button is released but in
+ * the RO interface click is the same as press */
+// else if (!pos && (mouse & BROWSER_MOUSE_CLICK_1)) {
+// /* clear selection */
+// selection_clear(s, true);
+// s->drag_state = DRAG_NONE;
+// }
else if (mouse & BROWSER_MOUSE_CLICK_2) {
/* ignore Adjust clicks when there's no selection */
@@ -699,15 +703,12 @@ void selection_select_all(struct selection *s)
old_end = s->end_idx;
s->defined = true;
- s->start_idx = 0;
- s->end_idx = s->max_idx;
-
- if (was_defined) {
- selection_redraw(s, 0, old_start);
- selection_redraw(s, old_end, s->end_idx);
- }
+
+ if (IS_INPUT(s->root))
+ selection_set_start(s, s->root->children->children->byte_offset);
else
- selection_redraw(s, 0, s->max_idx);
+ selection_set_start(s, 0);
+ selection_set_end(s, s->max_idx);
}
@@ -722,9 +723,17 @@ void selection_set_start(struct selection *s, unsigned offset)
{
bool was_defined = selection_defined(s);
unsigned old_start = s->start_idx;
-
+
s->start_idx = offset;
s->defined = (s->start_idx < s->end_idx);
+
+ if (s->root->gadget && s->defined) {
+ /* update the caret text_box and offset so that it stays at the
+ * beginning of the selection */
+ s->root->gadget->caret_text_box = selection_get_start(s,
+ &s->root->gadget->caret_box_offset);
+ assert(s->root->gadget->caret_text_box != NULL);
+ }
if (was_defined) {
if (offset < old_start)
@@ -773,14 +782,14 @@ void selection_set_end(struct selection *s, unsigned offset)
* \return ptr to box, or NULL if no selection defined
*/
-struct box *get_box(struct box *b, unsigned offset, int *pidx)
+struct box *get_box(struct box *b, unsigned offset, size_t *pidx)
{
struct box *child = b->children;
if (b->text) {
if (offset >= b->byte_offset &&
- offset < b->byte_offset + b->length + b->space) {
+ offset <= b->byte_offset + b->length + b->space) {
/* it's in this box */
*pidx = offset - b->byte_offset;
@@ -810,7 +819,7 @@ struct box *get_box(struct box *b, unsigned offset, int *pidx)
* \return ptr to box, or NULL if no selection defined
*/
-struct box *selection_get_start(struct selection *s, int *pidx)
+struct box *selection_get_start(struct selection *s, size_t *pidx)
{
return (s->defined ? get_box(s->root, s->start_idx, pidx) : NULL);
}
@@ -824,7 +833,7 @@ struct box *selection_get_start(struct selection *s, int *pidx)
* \return ptr to box, or NULL if no selection defined.
*/
-struct box *selection_get_end(struct selection *s, int *pidx)
+struct box *selection_get_end(struct selection *s, size_t *pidx)
{
return (s->defined ? get_box(s->root, s->end_idx, pidx) : NULL);
}
diff --git a/desktop/selection.h b/desktop/selection.h
index c04e503f3..1fca4c383 100644
--- a/desktop/selection.h
+++ b/desktop/selection.h
@@ -84,8 +84,8 @@ void selection_select_all(struct selection *s);
void selection_set_start(struct selection *s, unsigned idx);
void selection_set_end(struct selection *s, unsigned idx);
-struct box *selection_get_start(struct selection *s, int *pidx);
-struct box *selection_get_end(struct selection *s, int *pidx);
+struct box *selection_get_start(struct selection *s, size_t *pidx);
+struct box *selection_get_end(struct selection *s, size_t *pidx);
bool selection_click(struct selection *s, browser_mouse_state mouse,
unsigned idx);
@@ -105,6 +105,8 @@ bool selection_highlighted(struct selection *s, unsigned start, unsigned end,
bool selection_save_text(struct selection *s, const char *path);
void selection_update(struct selection *s, size_t byte_offset, int change,
- bool redraw);
+ bool redraw);
+
+unsigned selection_label_subtree(struct box *box, unsigned idx);
#endif
diff --git a/desktop/textinput.c b/desktop/textinput.c
index c2678d021..6d9f7d5d2 100644
--- a/desktop/textinput.c
+++ b/desktop/textinput.c
@@ -62,11 +62,13 @@ static bool browser_window_textarea_paste_text(struct browser_window *bw,
const char *utf8, unsigned utf8_len, bool last, void *handle);
static bool browser_window_input_paste_text(struct browser_window *bw,
const char *utf8, unsigned utf8_len, bool last, void *handle);
-static void browser_window_textarea_move_caret(struct browser_window *bw, void *p);
+static void browser_window_textarea_move_caret(struct browser_window *bw,
+ void *p);
static void browser_window_input_move_caret(struct browser_window *bw, void *p);
static void input_update_display(struct browser_window *bw, struct box *input,
- unsigned form_offset, unsigned box_offset, bool to_textarea,
- bool redraw);
+ unsigned box_offset, bool to_textarea, bool redraw);
+static size_t get_form_offset(struct box* input, struct box* text_box,
+ size_t char_offset);
static bool textbox_insert(struct browser_window *bw, struct box *text_box,
unsigned char_offset, const char *utf8, unsigned utf8_len);
static bool textbox_delete(struct browser_window *bw, struct box *text_box,
@@ -75,6 +77,7 @@ static struct box *textarea_insert_break(struct browser_window *bw,
struct box *text_box, size_t char_offset);
static bool delete_handler(struct browser_window *bw, struct box *b,
int offset, size_t length);
+static void delete_selection(struct selection *s);
static struct box *line_start(struct box *text_box);
static struct box *line_end(struct box *text_box);
static struct box *line_above(struct box *text_box);
@@ -102,7 +105,8 @@ void caret_remove(struct caret *c)
int w = (c->height + 7) / 8;
int xc = c->x;
c->defined = false;
- browser_window_redraw_rect(c->bw, xc - w, c->y, 2 * w, c->height);
+ browser_window_redraw_rect(c->bw,
+ xc - w, c->y, 2 * w, c->height);
}
}
@@ -114,7 +118,7 @@ void caret_remove(struct caret *c)
*
* \param c structure describing text caret
* \param bw browser window containing caret
- * \param box INLINE box containing caret
+ * \param box TEXT box containing caret
* \param char_offset byte offset within UTF-8 representation
* \param pixel_offset from left side of box
*/
@@ -145,26 +149,26 @@ void caret_set_position(struct caret *c, struct browser_window *bw,
/**
* Given the x,y co-ordinates of a point within a textarea, return the
- * INLINE box pointer, and the character and pixel offsets within that
+ * TEXT box pointer, and the character and pixel offsets within that
* box at which the caret should be positioned. (eg. for mouse clicks,
* drag-and-drop insertions etc)
*
* \param textarea the textarea being considered
* \param x x ordinate of point
* \param y y ordinate of point
- * \param pchar_offset receives the char offset within the INLINE box
- * \param ppixel_offset receives the pixel offset within the INLINE box
- * \return pointer to INLINE box
+ * \param pchar_offset receives the char offset within the TEXT box
+ * \param ppixel_offset receives the pixel offset within the TEXT box
+ * \return pointer to TEXT box
*/
struct box *textarea_get_position(struct box *textarea, int x, int y,
int *pchar_offset, int *ppixel_offset)
{
/* A textarea is an INLINE_BLOCK containing a single
- * INLINE_CONTAINER, which contains the text as runs of INLINE
- * separated by BR. There is at least one INLINE. The first and
- * last boxes are INLINE. Consecutive BR may not be present. These
- * constraints are satisfied by using a 0-length INLINE for blank
+ * INLINE_CONTAINER, which contains the text as runs of TEXT
+ * separated by BR. There is at least one TEXT. The first and
+ * last boxes are TEXT. Consecutive BR may not be present. These
+ * constraints are satisfied by using a 0-length TEXT for blank
* lines. */
struct box *inline_container, *text_box;
@@ -251,10 +255,10 @@ void browser_window_textarea_click(struct browser_window *bw,
int x, int y)
{
/* A textarea is an INLINE_BLOCK containing a single
- * INLINE_CONTAINER, which contains the text as runs of INLINE
- * separated by BR. There is at least one INLINE. The first and
- * last boxes are INLINE. Consecutive BR may not be present. These
- * constraints are satisfied by using a 0-length INLINE for blank
+ * INLINE_CONTAINER, which contains the text as runs of TEXT
+ * separated by BR. There is at least one TEXT. The first and
+ * last boxes are TEXT. Consecutive BR may not be present. These
+ * constraints are satisfied by using a 0-length TEXT for blank
* lines. */
int char_offset = 0, pixel_offset = 0;
@@ -275,7 +279,7 @@ void browser_window_textarea_click(struct browser_window *bw,
scrolled = ensure_caret_visible(textarea);
box_x -= textarea->scroll_x;
box_y -= textarea->scroll_y;
-
+
browser_window_place_caret(bw,
box_x + inline_container->x + text_box->x +
pixel_offset,
@@ -315,6 +319,7 @@ bool browser_window_textarea_callback(struct browser_window *bw,
char utf8[6];
unsigned int utf8_len;
bool scrolled,reflow = false;
+ bool selection_exists = bw->sel->defined;
/* box_dump(textarea, 0); */
LOG(("key %i at %i in '%.*s'", key, char_offset,
@@ -325,7 +330,7 @@ bool browser_window_textarea_callback(struct browser_window *bw,
box_y -= textarea->scroll_y;
if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) {
- /* normal character insertion */
+ /* normal character insertion */
utf8_len = utf8_from_ucs4(key, utf8);
if (!textbox_insert(bw, text_box, char_offset, utf8, utf8_len))
@@ -336,15 +341,20 @@ bool browser_window_textarea_callback(struct browser_window *bw,
} else switch (key) {
case KEY_DELETE_LEFT:
- if (char_offset == 0) {
+ if (selection_exists) {
+ /* Have a selection; delete it */
+ textbox_delete(bw, text_box, 0, 0);
+ } else if (char_offset == 0) {
/* at the start of a text box */
struct box *prev;
- while (text_box->prev && text_box->prev->type == BOX_BR) {
+ if (text_box->prev && text_box->prev->type == BOX_BR) {
/* previous box is BR: remove it */
box_unlink_and_free(text_box->prev);
}
+ /* This needs to be after the BR removal, as that may
+ * result in no previous box existing */
if (!text_box->prev)
/* at very beginning of text area: ignore */
return true;
@@ -357,7 +367,7 @@ bool browser_window_textarea_callback(struct browser_window *bw,
char_offset = prev->length; /* caret at join */
if (!textbox_insert(bw, prev, prev->length,
- text_box->text, text_box->length))
+ text_box->text, text_box->length))
return true;
box_unlink_and_free(text_box);
@@ -367,17 +377,40 @@ bool browser_window_textarea_callback(struct browser_window *bw,
} else {
/* delete a character */
- int prev_offset = char_offset;
- char_offset = utf8_prev(text_box->text, char_offset);
+ size_t prev_offset = char_offset;
+ size_t new_offset =
+ utf8_prev(text_box->text, char_offset);
- textbox_delete(bw, text_box, char_offset,
- prev_offset - char_offset);
+ if (textbox_delete(bw, text_box, new_offset,
+ prev_offset - new_offset))
+ char_offset = new_offset;
}
reflow = true;
break;
- case KEY_DELETE_LINE_END: {
+ case KEY_DELETE_LINE_START:
+ {
+ struct box *start_box = line_start(text_box);
+
+ /* Clear the selection, if one exists */
+ if (selection_exists)
+ selection_clear(bw->sel, false);
+
+ textarea_cut(bw, start_box, 0, text_box, char_offset);
+ text_box = start_box;
+ char_offset = 0;
+ reflow = true;
+ }
+ break;
+
+ case KEY_DELETE_LINE_END:
+ {
struct box *end_box = line_end(text_box);
+
+ /* Clear the selection, if one exists */
+ if (selection_exists)
+ selection_clear(bw->sel, false);
+
if (end_box != text_box ||
char_offset < text_box->length + text_box->space) {
/* there's something at the end of the line to delete */
@@ -389,15 +422,20 @@ bool browser_window_textarea_callback(struct browser_window *bw,
}
/* no break */
case KEY_DELETE_RIGHT: /* delete to right */
- if (char_offset >= text_box->length) {
+ if (selection_exists) {
+ /* Delete selection */
+ textbox_delete(bw, text_box, 0, 0);
+ } else if (char_offset >= text_box->length) {
/* at the end of a text box */
struct box *next;
- while (text_box->next && text_box->next->type == BOX_BR) {
+ if (text_box->next && text_box->next->type == BOX_BR) {
/* next box is a BR: remove it */
box_unlink_and_free(text_box->next);
}
+ /* This test is after the BR removal, as that may
+ * result in no subsequent box being present */
if (!text_box->next)
/* at very end of text area: ignore */
return true;
@@ -409,7 +447,7 @@ bool browser_window_textarea_callback(struct browser_window *bw,
assert(next->text);
if (!textbox_insert(bw, text_box, text_box->length,
- next->text, next->length))
+ next->text, next->length))
return true;
box_unlink_and_free(next);
@@ -419,16 +457,23 @@ bool browser_window_textarea_callback(struct browser_window *bw,
}
else {
/* delete a character */
- int next_offset = utf8_next(text_box->text, text_box->length,
- char_offset);
+ size_t next_offset = utf8_next(text_box->text,
+ text_box->length, char_offset);
+
textbox_delete(bw, text_box, char_offset,
- next_offset - char_offset);
+ next_offset - char_offset);
}
reflow = true;
break;
case 10:
case 13: /* paragraph break */
+ if (selection_exists) {
+ /* If we have a selection, then delete it,
+ * so it's replaced by the break */
+ textbox_delete(bw, text_box, 0, 0);
+ }
+
new_text = textarea_insert_break(bw, text_box, char_offset);
if (!new_text)
return true;
@@ -440,46 +485,60 @@ bool browser_window_textarea_callback(struct browser_window *bw,
reflow = true;
break;
- case 21: { /* Ctrl + U */
+ case 21: /* Ctrl + U */
+ {
struct box *start_box = line_start(text_box);
struct box *end_box = line_end(text_box);
+ /* Clear the selection, if one exists */
+ if (selection_exists)
+ selection_clear(bw->sel, false);
+
textarea_cut(bw, start_box, 0, end_box, end_box->length);
text_box = start_box;
char_offset = 0;
reflow = true;
}
- break;
+ break;
case 22: /* Ctrl + V */
gui_paste_from_clipboard(bw->window,
- box_x + inline_container->x + text_box->x + pixel_offset,
+ box_x + inline_container->x +
+ text_box->x + pixel_offset,
box_y + inline_container->y + text_box->y);
/* screen updated and caret repositioned already */
return true;
- case 24: { /* Ctrl + X */
- int start_idx, end_idx;
- struct box *start_box = selection_get_start(bw->sel, &start_idx);
+ case 24: /* Ctrl + X */
+ {
+ size_t start_idx, end_idx;
+ struct box *start_box =
+ selection_get_start(bw->sel, &start_idx);
struct box *end_box = selection_get_end(bw->sel, &end_idx);
+
if (start_box && end_box) {
selection_clear(bw->sel, false);
- textarea_cut(bw, start_box, start_idx, end_box, end_idx);
-
+ textarea_cut(bw, start_box, start_idx,
+ end_box, end_idx);
text_box = start_box;
char_offset = start_idx;
reflow = true;
}
}
- break;
+ break;
case KEY_RIGHT:
- if ((unsigned int) char_offset < text_box->length) {
+ if (selection_exists) {
+ /* In selection, move caret to end */
+ text_box = selection_get_end(bw->sel, &char_offset);
+ } else if (char_offset < text_box->length) {
+ /* Within-box movement */
char_offset = utf8_next(text_box->text,
text_box->length, char_offset);
} else {
+ /* Between-box movement */
if (!text_box->next)
/* at end of text area: ignore */
return true;
@@ -492,9 +551,14 @@ bool browser_window_textarea_callback(struct browser_window *bw,
break;
case KEY_LEFT:
- if (char_offset != 0) {
+ if (selection_exists) {
+ /* In selection, move caret to start */
+ text_box = selection_get_start(bw->sel, &char_offset);
+ } else if (char_offset > 0) {
+ /* Within-box movement */
char_offset = utf8_prev(text_box->text, char_offset);
} else {
+ /* Between-box movement */
if (!text_box->prev)
/* at start of text area: ignore */
return true;
@@ -507,6 +571,7 @@ bool browser_window_textarea_callback(struct browser_window *bw,
break;
case KEY_UP:
+ selection_clear(bw->sel, true);
browser_window_textarea_click(bw, BROWSER_MOUSE_CLICK_1,
textarea,
box_x, box_y,
@@ -515,6 +580,7 @@ bool browser_window_textarea_callback(struct browser_window *bw,
return true;
case KEY_DOWN:
+ selection_clear(bw->sel, true);
browser_window_textarea_click(bw, BROWSER_MOUSE_CLICK_1,
textarea,
box_x, box_y,
@@ -549,7 +615,12 @@ bool browser_window_textarea_callback(struct browser_window *bw,
char_offset = text_box->length;
break;
- case KEY_WORD_LEFT: {
+ case KEY_WORD_LEFT:
+ {
+ /* if there is a selection, caret should stay at beginning */
+ if (selection_exists)
+ break;
+
bool start_of_word = (char_offset <= 0 ||
isspace(text_box->text[char_offset - 1]));
@@ -561,7 +632,7 @@ bool browser_window_textarea_callback(struct browser_window *bw,
if (start_of_word) {
/* find the preceding non-BR box */
prev = text_box->prev;
- while (prev && prev->type == BOX_BR)
+ if (prev && prev->type == BOX_BR)
prev = prev->prev;
}
@@ -570,13 +641,22 @@ bool browser_window_textarea_callback(struct browser_window *bw,
break;
}
+ assert(prev->type == BOX_TEXT);
+
text_box = prev;
char_offset = prev->length;
}
}
- break;
+ break;
+
+ case KEY_WORD_RIGHT:
+ {
+ /* if there is a selection, caret should move to the end */
+ if (selection_exists) {
+ text_box = selection_get_end(bw->sel, &char_offset);
+ break;
+ }
- case KEY_WORD_RIGHT: {
bool in_word = (char_offset < text_box->length &&
!isspace(text_box->text[char_offset]));
@@ -585,7 +665,7 @@ bool browser_window_textarea_callback(struct browser_window *bw,
struct box *next = text_box->next;
/* find the next non-BR box */
- while (next && next->type == BOX_BR)
+ if (next && next->type == BOX_BR)
next = next->next;
if (!next) {
@@ -594,20 +674,22 @@ bool browser_window_textarea_callback(struct browser_window *bw,
break;
}
+ assert(next->type == BOX_TEXT);
+
text_box = next;
char_offset = 0;
- if (in_word &&
- text_box->length > 0 &&
- !isspace(text_box->text[0])) {
+ if (in_word && text_box->length > 0 &&
+ !isspace(text_box->text[0])) {
/* just stay at the start of this box */
break;
}
}
}
- break;
+ break;
- case KEY_PAGE_UP: {
+ case KEY_PAGE_UP:
+ {
int nlines = (textarea->height / text_box->height) - 1;
while (nlines-- > 0)
@@ -616,30 +698,22 @@ bool browser_window_textarea_callback(struct browser_window *bw,
if (char_offset > text_box->length)
char_offset = text_box->length;
}
- break;
+ break;
- case KEY_PAGE_DOWN: {
+ case KEY_PAGE_DOWN:
+ {
int nlines = (textarea->height / text_box->height) - 1;
+
while (nlines-- > 0)
text_box = line_below(text_box);
- /* vague attempt to keep the caret at the same horizontal position,
- given that the code currently cannot support it being beyond the
- end of a line */
-
+ /* vague attempt to keep the caret at the same horizontal
+ * position, given that the code currently cannot support it
+ * being beyond the end of a line */
if (char_offset > text_box->length)
char_offset = text_box->length;
}
- break;
-
- case KEY_DELETE_LINE_START: {
- struct box *start_box = line_start(text_box);
- textarea_cut(bw, start_box, 0, text_box, char_offset);
- text_box = start_box;
- char_offset = 0;
- reflow = true;
- }
- break;
+ break;
default:
return false;
@@ -685,9 +759,21 @@ bool browser_window_textarea_callback(struct browser_window *bw,
char_offset = text_box->length + text_box->space;
}
}
-
+
nsfont_width(text_box->style, text_box->text,
char_offset, &pixel_offset);
+
+ selection_clear(bw->sel, true);
+
+ browser_window_place_caret(bw,
+ box_x + inline_container->x + text_box->x +
+ pixel_offset,
+ box_y + inline_container->y + text_box->y,
+ text_box->height,
+ browser_window_textarea_callback,
+ browser_window_textarea_paste_text,
+ browser_window_textarea_move_caret,
+ textarea);
textarea->gadget->caret_inline_container = inline_container;
textarea->gadget->caret_text_box = text_box;
@@ -700,16 +786,6 @@ bool browser_window_textarea_callback(struct browser_window *bw,
box_x -= textarea->scroll_x;
box_y -= textarea->scroll_y;
- browser_window_place_caret(bw,
- box_x + inline_container->x + text_box->x +
- pixel_offset,
- box_y + inline_container->y + text_box->y,
- text_box->height,
- browser_window_textarea_callback,
- browser_window_textarea_paste_text,
- browser_window_textarea_move_caret,
- textarea);
-
if (scrolled || reflow)
browser_redraw_box(bw->current_content, textarea);
@@ -735,54 +811,31 @@ void browser_window_input_click(struct browser_window* bw,
size_t char_offset = 0;
int pixel_offset = 0, dx = 0;
struct box *text_box = input->children->children;
- int uchars;
- unsigned int offset;
nsfont_position_in_string(text_box->style, text_box->text,
text_box->length, x - text_box->x,
&char_offset, &pixel_offset);
assert(char_offset <= text_box->length);
+ /* Shift the text box horizontally to ensure that the
+ * caret position is visible, and ideally centred */
text_box->x = 0;
if ((input->width < text_box->width) &&
(input->width / 2 < pixel_offset)) {
dx = text_box->x;
+ /* Move left so caret is centred */
text_box->x = input->width / 2 - pixel_offset;
+ /* Clamp, so text box's right hand edge coincides
+ * with the input's right hand edge */
if (text_box->x < input->width - text_box->width)
text_box->x = input->width - text_box->width;
dx -= text_box->x;
}
input->gadget->caret_box_offset = char_offset;
- /* Update caret_form_offset */
- for (uchars = 0, offset = 0; offset < char_offset; uchars++) {
- if ((text_box->text[offset] & 0x80) == 0x00) {
- offset++;
- continue;
- }
- assert((text_box->text[offset] & 0xC0) == 0xC0);
- for (++offset; offset < char_offset &&
- (text_box->text[offset] & 0xC0) == 0x80;
- offset++)
- /* do nothing */;
- }
- /* uchars is the number of real Unicode characters at the left
- * side of the caret.
- */
- for (offset = 0; uchars > 0 && offset < input->gadget->length;
- uchars--) {
- if ((input->gadget->value[offset] & 0x80) == 0x00) {
- offset++;
- continue;
- }
- assert((input->gadget->value[offset] & 0xC0) == 0xC0);
- for (++offset; offset < input->gadget->length &&
- (input->gadget->value[offset] & 0xC0) == 0x80;
- offset++)
- /* do nothing */;
- }
- assert(uchars == 0);
- input->gadget->caret_form_offset = offset;
+ input->gadget->caret_form_offset =
+ get_form_offset(input, text_box, char_offset);
input->gadget->caret_pixel_offset = pixel_offset;
+
browser_window_place_caret(bw,
box_x + input->children->x +
text_box->x + pixel_offset,
@@ -797,7 +850,6 @@ void browser_window_input_click(struct browser_window* bw,
browser_redraw_box(bw->current_content, input);
}
-
/**
* Key press callback for text or password input boxes.
*
@@ -813,8 +865,10 @@ bool browser_window_input_callback(struct browser_window *bw,
{
struct box *input = (struct box *)p;
struct box *text_box = input->children->children;
- unsigned int box_offset = input->gadget->caret_box_offset;
- size_t form_offset = input->gadget->caret_form_offset;
+ size_t box_offset = input->gadget->caret_box_offset;
+ size_t form_offset = input->gadget->caret_form_offset =
+ get_form_offset(input, text_box, box_offset);
+ size_t end_offset;
int pixel_offset = input->gadget->caret_pixel_offset;
int box_x, box_y;
struct form* form = input->gadget->form;
@@ -822,168 +876,137 @@ bool browser_window_input_callback(struct browser_window *bw,
char utf8[6];
unsigned int utf8_len;
bool to_textarea = false;
+ bool selection_exists = bw->sel->defined;
+
+ selection_get_end(bw->sel, &end_offset);
box_coords(input, &box_x, &box_y);
-
+
+ /* normal character insertion */
if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) {
- char *value;
-
/* have we exceeded max length of input? */
utf8_len = utf8_length(input->gadget->value);
if (utf8_len >= input->gadget->maxlength)
return true;
-
- /* normal character insertion */
-
- /* Insert key in gadget */
+
utf8_len = utf8_from_ucs4(key, utf8);
-
- value = realloc(input->gadget->value,
- input->gadget->length + utf8_len + 1);
- if (!value) {
- warn_user("NoMemory", 0);
- return true;
- }
- input->gadget->value = value;
-
- memmove(input->gadget->value + form_offset + utf8_len,
- input->gadget->value + form_offset,
- input->gadget->length - form_offset);
- memcpy(input->gadget->value + form_offset, utf8, utf8_len);
- input->gadget->length += utf8_len;
- input->gadget->value[input->gadget->length] = 0;
- form_offset += utf8_len;
-
- /* Insert key in text box */
- /* Convert space into NBSP */
- utf8_len = utf8_from_ucs4(
- (input->gadget->type == GADGET_PASSWORD) ?
- '*' : (key == ' ') ? 160 : key,
- utf8);
-
+
if (!textbox_insert(bw, text_box, box_offset, utf8, utf8_len))
return true;
-
+
box_offset += utf8_len;
+
changed = true;
} else switch (key) {
- case KEY_DELETE_LEFT: {
- int prev_offset;
+ case KEY_DELETE_LEFT:
+ {
+ int prev_offset, new_offset;
- if (box_offset <= 0)
+ if (selection_exists) {
+ textbox_delete(bw, text_box, 0, 0);
+ } else {
+ /* Can't delete left from text box start */
+ if (box_offset == 0)
return true;
-
- /* Gadget */
- prev_offset = form_offset;
- /* Go to the previous valid UTF-8 character */
- form_offset = utf8_prev(input->gadget->value,
- form_offset);
-
- memmove(input->gadget->value + form_offset,
- input->gadget->value + prev_offset,
- input->gadget->length - prev_offset);
- input->gadget->length -= prev_offset - form_offset;
- input->gadget->value[input->gadget->length] = 0;
-
- /* Text box */
+
prev_offset = box_offset;
- /* Go to the previous valid UTF-8 character */
- box_offset = utf8_prev(text_box->text, box_offset);
+ new_offset = utf8_prev(text_box->text, box_offset);
- textbox_delete(bw, text_box, box_offset,
- prev_offset - box_offset);
- changed = true;
+ if (textbox_delete(bw, text_box, new_offset,
+ prev_offset - new_offset))
+ box_offset = new_offset;
}
+
+ changed = true;
+ }
break;
- case KEY_DELETE_RIGHT: {
- unsigned next_offset;
+ case KEY_DELETE_RIGHT:
+ {
+ unsigned next_offset;
+ if (selection_exists) {
+ textbox_delete(bw, text_box, 0, 0);
+ } else {
+ /* Can't delete right from text box end */
if (box_offset >= text_box->length)
return true;
-
- /* Gadget */
+
/* Go to the next valid UTF-8 character */
- next_offset = utf8_next(input->gadget->value,
- input->gadget->length,
- form_offset);
-
- memmove(input->gadget->value + form_offset,
- input->gadget->value + next_offset,
- input->gadget->length - next_offset);
- input->gadget->length -= next_offset - form_offset;
- input->gadget->value[input->gadget->length] = 0;
-
- /* Text box */
- /* Go to the next valid UTF-8 character */
- next_offset = utf8_next(text_box->text, text_box->length,
- box_offset);
+ next_offset = utf8_next(text_box->text,
+ text_box->length, box_offset);
textbox_delete(bw, text_box, box_offset,
next_offset - box_offset);
- changed = true;
}
+
+ changed = true;
+ }
break;
- case 9: { /* Tab */
- struct form_control *next_input;
- /* Find next text entry field that is actually
- * displayed (i.e. has an associated box) */
- for (next_input = input->gadget->next;
- next_input &&
- ((next_input->type != GADGET_TEXTBOX &&
- next_input->type != GADGET_TEXTAREA &&
- next_input->type != GADGET_PASSWORD) ||
- !next_input->box);
- next_input = next_input->next)
- ;
- if (!next_input)
- return true;
+ case 9: /* Tab */
+ {
+ struct form_control *next_input;
+ /* Find next text entry field that is actually
+ * displayed (i.e. has an associated box) */
+ for (next_input = input->gadget->next;
+ next_input &&
+ ((next_input->type != GADGET_TEXTBOX &&
+ next_input->type != GADGET_TEXTAREA &&
+ next_input->type != GADGET_PASSWORD) ||
+ !next_input->box);
+ next_input = next_input->next)
+ ;
+ if (!next_input)
+ return true;
- input = next_input->box;
- text_box = input->children->children;
- form_offset = box_offset = 0;
- to_textarea = next_input->type == GADGET_TEXTAREA;
- }
+ input = next_input->box;
+ text_box = input->children->children;
+ box_offset = 0;
+ to_textarea = next_input->type == GADGET_TEXTAREA;
+ }
break;
case 10:
case 13: /* Return/Enter hit */
+ selection_clear(bw->sel, true);
+
if (form)
browser_form_submit(bw, bw, form, 0);
return true;
- case 11: { /* Shift + Tab */
- struct form_control *prev_input;
- /* Find previous text entry field that is actually
- * displayed (i.e. has an associated box) */
- for (prev_input = input->gadget->prev;
- prev_input &&
- ((prev_input->type != GADGET_TEXTBOX &&
- prev_input->type != GADGET_TEXTAREA &&
- prev_input->type != GADGET_PASSWORD) ||
- !prev_input->box);
- prev_input = prev_input->prev)
- ;
- if (!prev_input)
- return true;
+ case 11: /* Shift + Tab */
+ {
+ struct form_control *prev_input;
+ /* Find previous text entry field that is actually
+ * displayed (i.e. has an associated box) */
+ for (prev_input = input->gadget->prev;
+ prev_input &&
+ ((prev_input->type != GADGET_TEXTBOX &&
+ prev_input->type != GADGET_TEXTAREA &&
+ prev_input->type != GADGET_PASSWORD) ||
+ !prev_input->box);
+ prev_input = prev_input->prev)
+ ;
+ if (!prev_input)
+ return true;
- input = prev_input->box;
- text_box = input->children->children;
- form_offset = box_offset = 0;
- to_textarea = prev_input->type == GADGET_TEXTAREA;
- }
+ input = prev_input->box;
+ text_box = input->children->children;
+ box_offset = 0;
+ to_textarea = prev_input->type == GADGET_TEXTAREA;
+ }
break;
case 21: /* Ctrl + U */
- text_box->text[0] = 0;
- text_box->length = 0;
+ /* Clear the selection, if one exists */
+ if (selection_exists)
+ selection_clear(bw->sel, false);
+
+ textarea_cut(bw, text_box, 0, text_box, text_box->length);
box_offset = 0;
- input->gadget->value[0] = 0;
- input->gadget->length = 0;
- form_offset = 0;
changed = true;
break;
@@ -995,93 +1018,98 @@ bool browser_window_input_callback(struct browser_window *bw,
/* screen updated and caret repositioned already */
return true;
+ case 24: /* Ctrl + X */
+ {
+ size_t start_idx, end_idx;
+ struct box *start_box =
+ selection_get_start(bw->sel, &start_idx);
+ struct box *end_box = selection_get_end(bw->sel, &end_idx);
+
+ if (start_box && end_box) {
+ selection_clear(bw->sel, false);
+ textarea_cut(bw, start_box, start_idx,
+ end_box, end_idx);
+
+ text_box = start_box;
+ box_offset = start_idx;
+ changed = true;
+ }
+ }
+ break;
case KEY_RIGHT:
- /* Text box */
+ if (selection_exists) {
+ box_offset = end_offset;
+ break;
+ }
+
/* Go to the next valid UTF-8 character */
box_offset = utf8_next(text_box->text, text_box->length,
box_offset);
- /* Gadget */
- /* Go to the next valid UTF-8 character */
- form_offset = utf8_next(input->gadget->value,
- input->gadget->length, form_offset);
break;
case KEY_LEFT:
- /* Text box */
+ /* If there is a selection, caret should remain at start */
+ if (selection_exists)
+ break;
+
/* Go to the previous valid UTF-8 character */
box_offset = utf8_prev(text_box->text, box_offset);
- /* Gadget */
- /* Go to the previous valid UTF-8 character */
- form_offset = utf8_prev(input->gadget->value, form_offset);
break;
case KEY_LINE_START:
- box_offset = form_offset = 0;
+ box_offset = 0;
break;
case KEY_LINE_END:
box_offset = text_box->length;
- form_offset = input->gadget->length;
break;
- case KEY_WORD_LEFT: {
- size_t nchars;
- /* Text box */
- if (word_left(input->gadget->value, &form_offset, &nchars)) {
- /* Gadget */
- while (box_offset > 0 && nchars-- > 0)
- box_offset = utf8_prev(text_box->text, box_offset);
- } else {
+ case KEY_WORD_LEFT:
+ /* If there is a selection, caret should remain at start */
+ if (selection_exists)
+ break;
+
+ if (!word_left(text_box->text, &box_offset, NULL))
box_offset = 0;
- form_offset = 0;
+
+ break;
+
+ case KEY_WORD_RIGHT:
+ if (selection_exists) {
+ box_offset = end_offset;
+ break;
}
- }
- break;
-
- case KEY_WORD_RIGHT: {
- size_t nchars;
- /* Text box */
- if (word_right(input->gadget->value, input->gadget->length,
- &form_offset, &nchars)) {
- /* Gadget */
- const char *text = text_box->text;
- unsigned len = text_box->length;
- while (box_offset < len && nchars-- > 0)
- box_offset = utf8_next(text, len, box_offset);
- } else {
+
+ if (!word_right(text_box->text, text_box->length,
+ &box_offset, NULL))
box_offset = text_box->length;
- form_offset = input->gadget->length;
- }
- }
- break;
+
+ break;
case KEY_DELETE_LINE_START:
- if (box_offset <= 0)
+ if (selection_exists)
+ selection_clear(bw->sel, true);
+
+ if (box_offset == 0)
return true;
- /* Text box */
- textbox_delete(bw, text_box, 0, box_offset);
+ textarea_cut(bw, text_box, 0, text_box, box_offset);
box_offset = 0;
- /* Gadget */
- memmove(input->gadget->value,
- input->gadget->value + form_offset,
- (input->gadget->length - form_offset) + 1); /* inc NUL */
- input->gadget->length -= form_offset;
- form_offset = 0;
changed = true;
break;
case KEY_DELETE_LINE_END:
+ if (selection_exists)
+ selection_clear(bw->sel, true);
+
if (box_offset >= text_box->length)
return true;
- /* Text box */
- textbox_delete(bw, text_box, box_offset, text_box->length - box_offset);
- /* Gadget */
- input->gadget->length = form_offset;
- input->gadget->value[form_offset] = 0;
+ textarea_cut(bw, text_box, box_offset,
+ text_box, text_box->length);
+
changed = true;
break;
@@ -1089,10 +1117,8 @@ bool browser_window_input_callback(struct browser_window *bw,
return false;
}
- input->gadget->caret_form_offset = form_offset;
-
- input_update_display(bw, input, form_offset, box_offset,
- to_textarea, changed);
+ selection_clear(bw->sel, true);
+ input_update_display(bw, input, box_offset, to_textarea, changed);
return true;
}
@@ -1137,8 +1163,54 @@ void browser_window_remove_caret(struct browser_window *bw)
bw->paste_callback = NULL;
bw->move_callback = NULL;
bw->caret_p = NULL;
+
+ selection_clear(bw->sel, true);
}
+/**
+ * Calculates the form_offset from the box_offset
+ *
+ * \param input The root box containing both the textbox and gadget
+ * \param text_box The textbox containing the caret
+ * \param char_offset The caret offset within text_box
+ * \return the translated form_offset
+ */
+
+size_t get_form_offset(struct box* input, struct box* text_box,
+ size_t char_offset)
+{
+ int uchars;
+ unsigned int offset;
+
+ for (uchars = 0, offset = 0; offset < char_offset; uchars++) {
+ if ((text_box->text[offset] & 0x80) == 0x00) {
+ offset++;
+ continue;
+ }
+ assert((text_box->text[offset] & 0xC0) == 0xC0);
+ for (++offset; offset < char_offset &&
+ (text_box->text[offset] & 0xC0) == 0x80;
+ offset++)
+ /* do nothing */;
+ }
+ /* uchars is the number of real Unicode characters at the left
+ * side of the caret.
+ */
+ for (offset = 0; uchars > 0 && offset < input->gadget->length;
+ uchars--) {
+ if ((input->gadget->value[offset] & 0x80) == 0x00) {
+ offset++;
+ continue;
+ }
+ assert((input->gadget->value[offset] & 0xC0) == 0xC0);
+ for (++offset; offset < input->gadget->length &&
+ (input->gadget->value[offset] & 0xC0) == 0x80;
+ offset++)
+ /* do nothing */;
+ }
+ assert(uchars == 0);
+ return offset;
+}
/**
* Handle key presses in a browser window.
@@ -1168,13 +1240,15 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key)
selection_clear(bw->sel, true);
return true;
}
- /* if there's no selection, leave Escape for the caller */
+ /* if there's no selection,
+ * leave Escape for the caller */
return false;
}
/* pass on to the appropriate field */
if (!bw->caret_callback)
return false;
+
return bw->caret_callback(bw, key, bw->caret_p);
}
@@ -1194,6 +1268,7 @@ bool browser_window_paste_text(struct browser_window *bw, const char *utf8,
{
if (!bw->paste_callback)
return false;
+
return bw->paste_callback(bw, utf8, utf8_len, last, bw->caret_p);
}
@@ -1254,7 +1329,8 @@ bool browser_window_textarea_paste_text(struct browser_window *bw,
char_offset = 0;
/* handle CR/LF and LF/CR terminations */
- if ((*p == '\n' && p[1] == '\r') || (*p == '\r' && p[1] == '\n'))
+ if ((*p == '\n' && p[1] == '\r') ||
+ (*p == '\r' && p[1] == '\n'))
p++;
utf8 = ++p;
}
@@ -1269,26 +1345,33 @@ bool browser_window_textarea_paste_text(struct browser_window *bw,
/* reflow textarea preserving width and height */
textarea_reflow(bw, textarea, inline_container);
/* reflowing may have broken our caret offset
- this bit should hopefully continue to work if textarea_reflow
- is fixed to update the caret itself */
+ * this bit should hopefully continue to work if
+ * textarea_reflow is fixed to update the caret itself */
char_offset = textarea->gadget->caret_box_offset;
text_box = textarea->gadget->caret_text_box;
- while((char_offset > text_box->length+text_box->space) && (text_box->next) && (text_box->next->type == BOX_TEXT))
- {
- LOG(("Caret out of range: Was %d in boxlen %d space %d",char_offset,text_box->length,text_box->space));
- char_offset -= text_box->length+text_box->space;
+
+ while ((char_offset > text_box->length + text_box->space) &&
+ (text_box->next) &&
+ (text_box->next->type == BOX_TEXT)) {
+ LOG(("Caret out of range: Was %d in boxlen %d "
+ "space %d", char_offset,
+ text_box->length, text_box->space));
+ char_offset -= text_box->length + text_box->space;
text_box = text_box->next;
}
- /* not sure if this will happen or not... but won't stick an assert here as we can recover from it */
- if(char_offset > text_box->length)
- {
- LOG(("Caret moved beyond end of line: Was %d in boxlen %d",char_offset,text_box->length));
+
+ /* not sure if this will happen or not...
+ * but won't stick an assert here as we can recover from it */
+ if (char_offset > text_box->length) {
+ LOG(("Caret moved beyond end of line: "
+ "Was %d in boxlen %d", char_offset,
+ text_box->length));
char_offset = text_box->length;
}
+
textarea->gadget->caret_text_box = text_box;
textarea->gadget->caret_box_offset = char_offset;
-
nsfont_width(text_box->style, text_box->text,
char_offset, &pixel_offset);
@@ -1312,7 +1395,6 @@ bool browser_window_textarea_paste_text(struct browser_window *bw,
textarea);
browser_redraw_box(bw->current_content, textarea);
-
}
return success;
@@ -1335,8 +1417,7 @@ bool browser_window_input_paste_text(struct browser_window *bw,
{
struct box *input = handle;
struct box *text_box = input->children->children;
- unsigned int box_offset = input->gadget->caret_box_offset;
- unsigned int form_offset = input->gadget->caret_form_offset;
+ size_t box_offset = input->gadget->caret_box_offset;
unsigned int nchars = utf8_length(input->gadget->value);
const char *ep = utf8 + utf8_len;
const char *p = utf8;
@@ -1351,7 +1432,6 @@ bool browser_window_input_paste_text(struct browser_window *bw,
char buf[80 + 6];
int nbytes = 0;
unsigned utf8_len;
- char *value;
/* how many more chars can we insert in one go? */
while (p < ep && nbytes < 80 &&
@@ -1373,26 +1453,7 @@ bool browser_window_input_paste_text(struct browser_window *bw,
}
utf8_len = p - utf8;
-
- value = realloc(input->gadget->value,
- input->gadget->length + utf8_len + 1);
- if (!value) {
- /* we still need to update the screen */
- warn_user("NoMemory", 0);
- update = true;
- success = false;
- break;
- }
- input->gadget->value = value;
-
- memmove(input->gadget->value + form_offset + utf8_len,
- input->gadget->value + form_offset,
- input->gadget->length - form_offset);
- memcpy(input->gadget->value + form_offset, utf8, utf8_len);
- input->gadget->length += utf8_len;
- input->gadget->value[input->gadget->length] = 0;
- form_offset += utf8_len;
-
+
if (!textbox_insert(bw, text_box, box_offset, buf, nbytes)) {
/* we still need to update the screen */
update = true;
@@ -1400,6 +1461,9 @@ bool browser_window_input_paste_text(struct browser_window *bw,
break;
}
box_offset += nbytes;
+ /* Keep caret_form_offset in sync -- textbox_insert uses this
+ * to determine where to insert into the gadget's value */
+ input->gadget->caret_form_offset += nbytes;
/* handle CR/LF and LF/CR terminations */
if (*p == '\n') {
@@ -1414,7 +1478,7 @@ bool browser_window_input_paste_text(struct browser_window *bw,
}
if (update)
- input_update_display(bw, input, form_offset, box_offset, false, true);
+ input_update_display(bw, input, box_offset, false, true);
return success;
}
@@ -1501,8 +1565,7 @@ void browser_window_input_move_caret(struct browser_window *bw, void *p)
*/
void input_update_display(struct browser_window *bw, struct box *input,
- unsigned form_offset, unsigned box_offset, bool to_textarea,
- bool redraw)
+ unsigned box_offset, bool to_textarea, bool redraw)
{
struct box *text_box = input->children->children;
unsigned pixel_offset;
@@ -1517,11 +1580,15 @@ void input_update_display(struct browser_window *bw, struct box *input,
nsfont_width(text_box->style, text_box->text, box_offset,
(int *) &pixel_offset);
+
+ /* Shift text box horizontally, so caret is visible */
dx = text_box->x;
text_box->x = 0;
if (input->width < text_box->width &&
- input->width / 2 < (int)pixel_offset) {
+ input->width / 2 < (int) pixel_offset) {
+ /* Make caret appear in centre of text input */
text_box->x = input->width / 2 - pixel_offset;
+ /* Clamp if we've shifted too far left */
if (text_box->x < input->width - text_box->width)
text_box->x = input->width - text_box->width;
}
@@ -1535,7 +1602,6 @@ void input_update_display(struct browser_window *bw, struct box *input,
}
input->gadget->caret_box_offset = box_offset;
- input->gadget->caret_form_offset = form_offset;
browser_window_place_caret(bw,
box_x + input->children->x +
@@ -1561,7 +1627,7 @@ void input_update_display(struct browser_window *bw, struct box *input,
*
* \param bw browser window
* \param text_box text box
- * \param char_offset offsets (bytes) at which to insert text
+ * \param char_offset offset (bytes) at which to insert text
* \param utf8 UTF-8 text to insert
* \param utf8_len length (bytes) of UTF-8 text to insert
* \return true iff successful
@@ -1571,7 +1637,33 @@ bool textbox_insert(struct browser_window *bw, struct box *text_box,
unsigned char_offset, const char *utf8, unsigned utf8_len)
{
char *text;
+ struct box *input = text_box->parent->parent;
+
+ if (bw->sel->defined)
+ delete_selection(bw->sel);
+
+ /* insert into form gadget (text and password inputs only) */
+ if (input->gadget && (input->gadget->type == GADGET_TEXTBOX ||
+ input->gadget->type == GADGET_PASSWORD) &&
+ input->gadget->value) {
+ size_t form_offset = input->gadget->caret_form_offset;
+ char *value = realloc(input->gadget->value,
+ input->gadget->length + utf8_len + 1);
+ if (!value) {
+ warn_user("NoMemory", 0);
+ return true;
+ }
+ input->gadget->value = value;
+ memmove(input->gadget->value + form_offset + utf8_len,
+ input->gadget->value + form_offset,
+ input->gadget->length - form_offset);
+ memcpy(input->gadget->value + form_offset, utf8, utf8_len);
+ input->gadget->length += utf8_len;
+ input->gadget->value[input->gadget->length] = 0;
+ }
+
+ /* insert in text box */
text = talloc_realloc(bw->current_content, text_box->text,
char,
text_box->length + text_box->space + utf8_len + 1);
@@ -1597,15 +1689,15 @@ bool textbox_insert(struct browser_window *bw, struct box *text_box,
text_box->length - char_offset);
}
- memcpy(text_box->text + char_offset, utf8, utf8_len);
+ memcpy(text_box->text + char_offset,
+ input->gadget->type == GADGET_PASSWORD ? "*": utf8,
+ utf8_len);
text_box->length += utf8_len;
- /* nothing should assume that the text is terminated, but just in case */
+ /* nothing should assume that the text is terminated,
+ * but just in case */
text_box->text[text_box->length] = 0;
- selection_update(bw->sel, text_box->byte_offset + char_offset,
- utf8_len, false);
-
text_box->width = UNKNOWN_WIDTH;
return true;
@@ -1619,12 +1711,40 @@ bool textbox_insert(struct browser_window *bw, struct box *text_box,
* \param text_box text box
* \param char_offset offset within text box (bytes) of first char to delete
* \param utf8_len length (bytes) of chars to be deleted
+ * \return true on success, false otherwise
+ *
+ * ::char_offset and ::utf8_len are only considered when there is no selection.
+ * If there is a selection, the entire selected area is deleted.
*/
bool textbox_delete(struct browser_window *bw, struct box *text_box,
unsigned char_offset, unsigned utf8_len)
{
unsigned next_offset = char_offset + utf8_len;
+ struct box *form = text_box->parent->parent;
+
+ if (bw->sel->defined) {
+ delete_selection(bw->sel);
+ return true;
+ }
+
+ /* delete from form gadget (text and password inputs only) */
+ if (form->gadget && (form->gadget->type == GADGET_TEXTBOX ||
+ form->gadget->type == GADGET_PASSWORD) &&
+ form->gadget->value) {
+ size_t form_offset = get_form_offset(form, text_box,
+ char_offset);
+ size_t next_offset = get_form_offset(form, text_box,
+ char_offset + utf8_len);
+
+ memmove(form->gadget->value + form_offset,
+ form->gadget->value + next_offset,
+ form->gadget->length - next_offset);
+ form->gadget->length -= utf8_len;
+ form->gadget->value[form->gadget->length] = 0;
+ }
+
+ /* delete from visible textbox */
if (next_offset <= text_box->length + text_box->space) {
/* handle removal of trailing space */
if (text_box->space && next_offset > text_box->length) {
@@ -1635,27 +1755,27 @@ bool textbox_delete(struct browser_window *bw, struct box *text_box,
char_offset = tmp;
else
text_box->space = false;
- }
- else
+ } else {
text_box->space = false;
+ }
+
text_box->length = char_offset;
- }
- else {
+ } else {
memmove(text_box->text + char_offset,
text_box->text + next_offset,
text_box->length - next_offset);
text_box->length -= utf8_len;
}
- /* nothing should assume that the text is terminated, but just in case */
+ /* nothing should assume that the text is terminated,
+ * but just in case */
text_box->text[text_box->length] = 0;
- selection_update(bw->sel, text_box->byte_offset + char_offset,
- -(int)utf8_len, false);
-
text_box->width = UNKNOWN_WIDTH;
+
return true;
}
+
return false;
}
@@ -1673,22 +1793,53 @@ bool textbox_delete(struct browser_window *bw, struct box *text_box,
bool delete_handler(struct browser_window *bw, struct box *b,
int offset, size_t length)
{
- if (offset <= 0 && length >= b->length + b->space) {
- selection_update(bw->sel, b->byte_offset,
- -(b->length + b->space), false);
+ size_t text_length = b->length + b->space;
+ /* only remove if its not the first box */
+ if (offset <= 0 && length >= text_length && b->prev != NULL) {
/* remove the entire box */
box_unlink_and_free(b);
return true;
- }
- else {
- return textbox_delete(bw, b, offset, length);
- }
+ } else
+ return textbox_delete(bw, b, offset,
+ min(length, text_length - offset));
}
/**
+ * Remove the selected text from a text box and gadget (if applicable)
+ *
+ * \param s The selection to be removed
+ */
+
+void delete_selection(struct selection *s)
+{
+ assert(s->defined);
+ size_t start_offset, end_offset;
+ struct box *text_box = selection_get_start(s, &start_offset);
+ struct box *end_box = selection_get_end(s, &end_offset);
+ struct box *next;
+ size_t sel_len = s->end_idx - s->start_idx;
+ int beginning = 0;
+
+ /* Clear selection so that deletion from textboxes proceeds */
+ selection_clear(s, true);
+
+ /* handle first box */
+ delete_handler(s->bw, text_box, start_offset, sel_len);
+ if (text_box == end_box)
+ return;
+
+ for (text_box = text_box->next; text_box != end_box; text_box = next) {
+ next = text_box->next;
+ box_unlink_and_free(text_box);
+ }
+
+ delete_handler(s->bw, end_box, beginning, end_offset);
+}
+
+/**
* Locate the first inline box at the start of this line
*
* \param text_box text box from which to start searching
@@ -1760,8 +1911,8 @@ struct box *line_below(struct box *text_box)
* \param char_offset offset (in bytes) at which text box is to be split
*/
-struct box *textarea_insert_break(struct browser_window *bw, struct box *text_box,
- size_t char_offset)
+struct box *textarea_insert_break(struct browser_window *bw,
+ struct box *text_box, size_t char_offset)
{
struct box *new_br, *new_text;
char *text = talloc_array(bw->current_content, char,
@@ -1804,6 +1955,7 @@ struct box *textarea_insert_break(struct browser_window *bw, struct box *text_bo
* \param start_idx index (bytes) within start box
* \param end_box text box at end of range
* \param end_idx index (bytes) within end box
+ * \return true iff successful
*/
bool textarea_cut(struct browser_window *bw,
@@ -1827,8 +1979,7 @@ bool textarea_cut(struct browser_window *bw,
return false;
}
box_unlink_and_free(box);
- }
- else {
+ } else {
/* append box text to clipboard and then delete it */
if (!gui_add_to_clipboard(box->text + start_idx,
box->length - start_idx, box->space)) {
@@ -1842,10 +1993,10 @@ bool textarea_cut(struct browser_window *bw,
gui_commit_clipboard();
return false;
}
- }
- else
+ } else {
textbox_delete(bw, box, start_idx,
- (box->length + box->space) - start_idx);
+ (box->length + box->space) - start_idx);
+ }
}
del = true;
@@ -1855,16 +2006,19 @@ bool textarea_cut(struct browser_window *bw,
/* and the last box */
if (box) {
- if (gui_add_to_clipboard(box->text + start_idx, end_idx - start_idx, box->space)) {
+ if (gui_add_to_clipboard(box->text + start_idx,
+ end_idx - start_idx, end_idx > box->length)) {
if (del) {
- if (!delete_handler(bw, box, start_idx, end_idx - start_idx))
+ if (!delete_handler(bw, box, start_idx,
+ end_idx - start_idx))
success = false;
+ } else {
+ textbox_delete(bw, box, start_idx,
+ end_idx - start_idx);
}
- else
- textbox_delete(bw, box, start_idx, end_idx - start_idx);
- }
- else
+ } else {
success = false;
+ }
}
return gui_commit_clipboard() ? success : false;
@@ -1910,12 +2064,14 @@ bool word_left(const char *text, size_t *poffset, size_t *pchars)
bool success = false;
size_t nchars = 0;
+ /* Skip any spaces immediately prior to the offset */
while (offset > 0) {
offset = utf8_prev(text, offset);
nchars++;
if (!isspace(text[offset])) break;
}
+ /* Now skip all non-space characters */
while (offset > 0) {
size_t prev = utf8_prev(text, offset);
success = true;
@@ -1948,12 +2104,14 @@ bool word_right(const char *text, size_t len, size_t *poffset, size_t *pchars)
bool success = false;
size_t nchars = 0;
+ /* Skip all non-space characters after the offset */
while (offset < len) {
if (isspace(text[offset])) break;
offset = utf8_next(text, len, offset);
nchars++;
}
+ /* Now skip all space characters */
while (offset < len) {
offset = utf8_next(text, len, offset);
nchars++;
@@ -1977,30 +2135,44 @@ bool word_right(const char *text, size_t len, size_t *poffset, size_t *pchars)
bool ensure_caret_visible(struct box *textarea)
{
- int cx,cy;
- int scrollx,scrolly;
+ int cx, cy;
+ int scrollx, scrolly;
+
+ assert(textarea->gadget);
+
scrollx = textarea->scroll_x;
scrolly = textarea->scroll_y;
- assert(textarea->gadget);
+
/* Calculate the caret coordinates */
- cx = textarea->gadget->caret_pixel_offset+textarea->gadget->caret_text_box->x;
+ cx = textarea->gadget->caret_pixel_offset +
+ textarea->gadget->caret_text_box->x;
cy = textarea->gadget->caret_text_box->y;
+
/* Ensure they are visible */
- if (!box_hscrollbar_present(textarea))
+ if (!box_hscrollbar_present(textarea)) {
scrollx = 0;
- else if (cx-textarea->scroll_x < 0)
+ } else if (cx-textarea->scroll_x < 0) {
scrollx = cx;
- else if (cx > textarea->scroll_x+textarea->width)
- scrollx = cx-textarea->width;
- if (!box_vscrollbar_present(textarea))
+ } else if (cx > textarea->scroll_x + textarea->width) {
+ scrollx = cx - textarea->width;
+ }
+
+ if (!box_vscrollbar_present(textarea)) {
scrolly = 0;
- else if (cy-textarea->scroll_y < 0)
+ } else if (cy - textarea->scroll_y < 0) {
scrolly = cy;
- else if (cy+textarea->gadget->caret_text_box->height > textarea->scroll_y+textarea->height)
- scrolly = (cy+textarea->gadget->caret_text_box->height)-textarea->height;
+ } else if (cy + textarea->gadget->caret_text_box->height >
+ textarea->scroll_y + textarea->height) {
+ scrolly = (cy + textarea->gadget->caret_text_box->height) -
+ textarea->height;
+ }
+
if ((scrollx == textarea->scroll_x) && (scrolly == textarea->scroll_y))
return false;
+
textarea->scroll_x = scrollx;
textarea->scroll_y = scrolly;
+
return true;
}
+
diff --git a/gtk/dialogs/gtk_options.c b/gtk/dialogs/gtk_options.c
index e7ed37eec..a40e49e2d 100644
--- a/gtk/dialogs/gtk_options.c
+++ b/gtk/dialogs/gtk_options.c
@@ -52,7 +52,6 @@ DECLARE(checkDisablePopups);
DECLARE(checkDisablePlugins);
DECLARE(spinHistoryAge);
DECLARE(checkHoverURLs);
-DECLARE(checkRequestOverwrite);
DECLARE(checkDisplayRecentURLs);
DECLARE(checkSendReferer);
@@ -82,6 +81,10 @@ DECLARE(fontPreview);
DECLARE(spinMemoryCacheSize);
DECLARE(spinDiscCacheAge);
+DECLARE(checkClearDownloads);
+DECLARE(checkRequestOverwrite);
+DECLARE(fileChooserDownloads);
+
/* Used when the feature is not implemented yet */
#define FIND_WIDGET(x) (x) = glade_xml_get_widget(gladeFile, #x); \
if ((x) == NULL) LOG(("Unable to find widget '%s'!", #x))
@@ -112,7 +115,6 @@ GtkDialog* nsgtk_options_init(struct browser_window *bw, GtkWindow *parent) {
//CONNECT(checkDisablePlugins, "toggled");
//CONNECT(spinHistoryAge, "focus-out-event");
//CONNECT(checkHoverURLs, "toggled");
- //CONNECT(checkRequestOverwrite, "toggled");
CONNECT(checkDisplayRecentURLs, "toggled");
CONNECT(checkSendReferer, "toggled");
@@ -142,6 +144,10 @@ GtkDialog* nsgtk_options_init(struct browser_window *bw, GtkWindow *parent) {
CONNECT(spinMemoryCacheSize, "value-changed");
CONNECT(spinDiscCacheAge, "value-changed");
+ CONNECT(checkClearDownloads, "toggled");
+ CONNECT(checkRequestOverwrite, "toggled");
+ CONNECT(fileChooserDownloads, "current-folder-changed");
+
g_signal_connect(G_OBJECT(wndPreferences), "response",
G_CALLBACK (dialog_response_handler), NULL);
@@ -166,8 +172,11 @@ GtkDialog* nsgtk_options_init(struct browser_window *bw, GtkWindow *parent) {
gtk_combo_box_set_active(GTK_COMBO_BOX((x)), (y))
#define SET_FONT(x, y) (x) = glade_xml_get_widget(gladeFile, #x); \
gtk_font_button_set_font_name(GTK_FONT_BUTTON((x)), (y))
+#define SET_FILE_CHOOSER(x, y) (x) = glade_xml_get_widget(gladeFile, #x); \
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER((x)), (y))
#define SET_BUTTON(x) (x) = glade_xml_get_widget(gladeFile, #x);
+
void nsgtk_options_load(void) {
char b[20];
int proxytype = 0;
@@ -219,6 +228,10 @@ void nsgtk_options_load(void) {
SET_SPIN(spinMemoryCacheSize, option_memory_cache_size >> 20);
SET_SPIN(spinDiscCacheAge, option_disc_cache_age);
+
+ SET_CHECK(checkClearDownloads, option_downloads_clear);
+ SET_CHECK(checkRequestOverwrite, option_request_overwrite);
+ SET_FILE_CHOOSER(fileChooserDownloads, option_downloads_directory);
}
static void dialog_response_handler (GtkDialog *dlg, gint res_id){
@@ -261,6 +274,9 @@ static gboolean on_dialog_close (GtkDialog *dlg, gboolean stay_alive){
LOG(("Signal emitted on '%s'", #x)); \
if ((y)) free((y)); \
(y) = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON((x))));
+#define FILE_CHOOSER_CHANGED(x, y) gboolean on_##x##_changed(GtkWidget *widget, gpointer data) { \
+ LOG(("Signal emitted on '%s'", #x)); \
+ (y) = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER((x)));
#define BUTTON_CLICKED(x) gboolean on_##x##_changed(GtkWidget *widget, gpointer data) { \
LOG(("Signal emitted on '%s'", #x));
@@ -362,3 +378,7 @@ BUTTON_CLICKED(fontPreview)
SPIN_CHANGED(spinMemoryCacheSize, option_memory_cache_size)
option_memory_cache_size <<= 20;}
SPIN_CHANGED(spinDiscCacheAge, option_disc_cache_age)}
+
+CHECK_CHANGED(checkClearDownloads, option_downloads_clear)}
+CHECK_CHANGED(checkRequestOverwrite, option_request_overwrite)}
+FILE_CHOOSER_CHANGED(fileChooserDownloads, option_downloads_directory)}
diff --git a/gtk/gtk_download.c b/gtk/gtk_download.c
index ab16d6786..e84b6d2fd 100644
--- a/gtk/gtk_download.c
+++ b/gtk/gtk_download.c
@@ -17,113 +17,587 @@
*/
#include <gtk/gtk.h>
+#include <glib/gstdio.h>
#include "utils/log.h"
-
+#include "utils/utils.h"
+#include "utils/url.h"
+#include "utils/messages.h"
+#include "content/fetch.h"
+#include "desktop/gui.h"
#include "gtk/gtk_gui.h"
+#include "gtk/gtk_scaffolding.h"
+#include "gtk/options.h"
#include "gtk/gtk_download.h"
-static GtkWindow *wndDownload;
-static GtkTreeView *treeDownloads;
-static GtkListStore *list_downloads;
+#define GLADE_NAME "downloads.glade"
-enum {
- COLUMN_URL = 0,
- COLUMN_DESTINATION,
- COLUMN_PERCENTAGE,
- COLUMN_DESCRIPTION,
+static GtkWindow *nsgtk_download_window, *nsgtk_download_parent;
+static GtkWidget *nsgtk_download_progressBar;
- N_COLUMNS
-};
+static GtkTreeView *nsgtk_download_tree;
+static GtkListStore *nsgtk_download_store;
+static GtkTreeSelection *nsgtk_download_selection;
+static GtkTreeIter nsgtk_download_iter;
+static GList *nsgtk_download_buttons;
+static gint nsgtk_downloads;
+gchar* status_messages[] = { "gtkWorking", "gtkError", "gtkComplete",
+ "gtkCanceled" };
-void nsgtk_downloadPause_clicked(GtkToolButton *button, gpointer data);
+static gboolean nsgtk_download_hide (GtkWidget *window);
+
+static GtkTreeView *nsgtk_download_tree_view_new(GladeXML *gladeFile);
+static void nsgtk_download_tree_view_row_activated(GtkTreeView *tree,
+ GtkTreePath *path, GtkTreeViewColumn *column, gpointer data);
+
+static void nsgtk_download_store_update_item(struct gui_download_window *dl);
+static void nsgtk_download_store_create_item (struct gui_download_window *dl);
+static void nsgtk_download_store_clear_item (struct gui_download_window *dl);
+static void nsgtk_download_store_cancel_item (struct gui_download_window *dl);
+
+static void nsgtk_download_selection_do(GtkWidget *button,
+ selection_action action);
+
+static void nsgtk_download_sensitivity_set(struct gui_download_window *dl,
+ nsgtk_download_actions sensitivity);
+static void nsgtk_download_sensitivity_selection_changed(
+ GtkTreeSelection *selection);
+static void nsgtk_download_sensitivity_update_buttons(
+ nsgtk_download_actions sensitivity);
+
+static gchar* nsgtk_download_dialog_show (gchar *filename, gchar *domain,
+ gchar *size);
+static gchar* nsgtk_download_info_to_string (struct gui_download_window *dl);
+static gchar* nsgtk_download_time_to_string (gint seconds);
+static gboolean nsgtk_download_handle_error (GError *error);
-static void nsgtk_download_add(const char *url, const char *dest, int prog, const char *desc)
-{
- GtkTreeIter iter;
- gtk_list_store_append(list_downloads, &iter);
- gtk_list_store_set(list_downloads, &iter,
- COLUMN_URL, url,
- COLUMN_DESTINATION, dest,
- COLUMN_PERCENTAGE, prog,
- COLUMN_DESCRIPTION, desc,
- -1);
-}
-void nsgtk_download_initialise(void)
+void nsgtk_download_init()
{
- wndDownload = GTK_WINDOW(glade_xml_get_widget(gladeWindows,
- "wndDownloads"));
- treeDownloads = GTK_TREE_VIEW(glade_xml_get_widget(gladeWindows,
- "treeDownloads"));
+ gchar *glade_location = g_strconcat(res_dir_location, GLADE_NAME, NULL);
+ GladeXML *gladeFile = glade_xml_new(glade_location, NULL, NULL);
+ g_free(glade_location);
+
+ nsgtk_download_buttons =
+ glade_xml_get_widget_prefix(gladeFile, "button");
+ nsgtk_download_progressBar =
+ glade_xml_get_widget(gladeFile, "progressBar");
+ nsgtk_download_window = GTK_WINDOW(glade_xml_get_widget(gladeFile,
+ "wndDownloads"));
+ nsgtk_download_parent = NULL;
+
+ gtk_window_set_transient_for(GTK_WINDOW(nsgtk_download_window),
+ nsgtk_download_parent);
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(nsgtk_download_window),
+ FALSE);
- gtk_tool_item_set_expand(GTK_TOOL_ITEM(
- glade_xml_get_widget(gladeWindows, "toolProgress")), TRUE);
+ nsgtk_download_tree = nsgtk_download_tree_view_new(gladeFile);
- list_downloads = gtk_list_store_new(N_COLUMNS,
- G_TYPE_STRING, /* URL */
- G_TYPE_STRING, /* Destination */
+ nsgtk_download_store = gtk_list_store_new(NSGTK_DOWNLOAD_N_COLUMNS,
G_TYPE_INT, /* % complete */
- G_TYPE_STRING /* time left */
+ G_TYPE_STRING, /* Description */
+ G_TYPE_STRING, /* Time remaining */
+ G_TYPE_STRING, /* Speed */
+ G_TYPE_STRING, /* Status */
+ G_TYPE_POINTER /* Download structure */
);
- gtk_tree_view_set_model(treeDownloads, GTK_TREE_MODEL(list_downloads));
-
- gtk_tree_view_insert_column_with_attributes(treeDownloads, -1,
- "URL",
- gtk_cell_renderer_text_new(),
- "ellipsize", PANGO_ELLIPSIZE_START,
- "text",
- COLUMN_URL,
- NULL);
-
-/* gtk_tree_view_insert_column_with_attributes(treeDownloads, -1,
- "Destination",
- gtk_cell_renderer_text_new(),
- "text",
- COLUMN_DESTINATION,
- NULL);*/
-
- gtk_tree_view_insert_column_with_attributes(treeDownloads, -1,
- "Progress",
- gtk_cell_renderer_progress_new(),
- "value",
- COLUMN_PERCENTAGE,
- NULL);
-
- gtk_tree_view_insert_column_with_attributes(treeDownloads, -1,
- "Details",
- gtk_cell_renderer_text_new(),
- "text",
- COLUMN_DESCRIPTION,
- NULL);
-
- /* add some fake entries to play about with */
- nsgtk_download_add("http://www.netsurf-browser.org/downloads/netsurf-1.0.zip",
- "/home/rjek/Downloads/netsurf-1.0.zip",
- 23,
- "500kB of 2MB, 120kB/sec, 12 seconds");
+ gtk_tree_view_set_model(nsgtk_download_tree,
+ GTK_TREE_MODEL(nsgtk_download_store));
+ g_object_unref(nsgtk_download_store);
+
+ nsgtk_download_selection =
+ gtk_tree_view_get_selection(nsgtk_download_tree);
+ gtk_tree_selection_set_mode(nsgtk_download_selection,
+ GTK_SELECTION_MULTIPLE);
+
+ g_signal_connect(G_OBJECT(nsgtk_download_selection), "changed",
+ G_CALLBACK(nsgtk_download_sensitivity_selection_changed),
+ NULL);
+ g_signal_connect(G_OBJECT(nsgtk_download_window), "delete-event",
+ G_CALLBACK(nsgtk_download_hide), NULL);
+ g_signal_connect(glade_xml_get_widget(gladeFile, "buttonClear"),
+ "clicked", G_CALLBACK(nsgtk_download_selection_do),
+ nsgtk_download_store_clear_item);
+ g_signal_connect(glade_xml_get_widget(gladeFile, "buttonCancel"),
+ "clicked", G_CALLBACK(nsgtk_download_selection_do),
+ nsgtk_download_store_cancel_item);
+ g_signal_connect(nsgtk_download_tree, "row-activated",
+ G_CALLBACK(nsgtk_download_tree_view_row_activated),
+ NULL);
+}
+
+void nsgtk_download_destroy ()
+{
+ gtk_tree_selection_select_all(nsgtk_download_selection);
+ nsgtk_download_selection_do(NULL, nsgtk_download_store_cancel_item);
+}
+
+bool nsgtk_check_for_downloads (GtkWindow *parent)
+{
+ if (nsgtk_downloads != 0) {
+ GtkWidget *dialog;
+ dialog = gtk_message_dialog_new_with_markup(parent,
+ GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_NONE,
+ "<big><b>Quit NetSurf?</b></big>\n\n"
+ "<small>There are still downloads running, "
+ "if you quit now these will be canceled and the"
+ " files deleted</small>");
+ gtk_dialog_add_buttons(GTK_DIALOG(dialog), "gtk-cancel",
+ GTK_RESPONSE_CANCEL, "gtk-quit",
+ GTK_RESPONSE_CLOSE, NULL);
+
+ gint response = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+
+ if (response == GTK_RESPONSE_CANCEL)
+ return true;
+ }
+
+ return false;
+}
+
+void nsgtk_download_show(GtkWindow *parent)
+{
+ gtk_window_set_transient_for(nsgtk_download_window,
+ nsgtk_download_parent);
+ gtk_window_present(nsgtk_download_window);
+}
+
+gboolean nsgtk_download_hide (GtkWidget *window)
+{
+ gtk_widget_hide(window);
+ return TRUE;
+}
+
+struct gui_download_window *gui_download_window_create(const char *url,
+ const char *mime_type, struct fetch *fetch,
+ unsigned int total_size, struct gui_window *gui)
+{
+ nsgtk_download_parent = nsgtk_scaffolding_get_window(gui);
+ struct gui_download_window *download;
+ gchar *domain;
+ gchar *filename;
+ gchar *destination;
+ gchar *size = (total_size == 0 ?
+ "Unknown" : human_friendly_bytesize(total_size));
+
+ if (url_nice(url, &filename, false) != URL_FUNC_OK)
+ strcpy(filename, messages_get("gtkUnknownFile"));
+ if (url_host(url, &domain) != URL_FUNC_OK)
+ strcpy(domain, messages_get("gtkUnknownHost"));
+
+ destination = nsgtk_download_dialog_show(filename, domain, size);
+ if (destination == NULL)
+ return NULL;
+
+ download = malloc(sizeof *download);
+ download->fetch = fetch;
+ download->name = g_string_new(filename);
+ download->time_left = g_string_new("");
+ download->size_total = total_size;
+ download->size_downloaded = 0;
+ download->speed = 0;
+ download->time_remaining = -1;
+ download->filename = destination;
+ download->status = (total_size == 0 ? NSGTK_DOWNLOAD_WORKING :
+ NSGTK_DOWNLOAD_NONE);
+ download->progress = (total_size == 0 ? 100 : 0);
+ download->timer = g_timer_new();
+ download->error = NULL;
+ download->write = g_io_channel_new_file(destination, "w",
+ &download->error);
+ if (nsgtk_download_handle_error(download->error)) {
+ free(download);
+ return NULL;
+ }
+
+ if (g_str_has_prefix(mime_type, "text") == FALSE)
+ g_io_channel_set_encoding(download->write, NULL,
+ &download->error);
+
+ /* Add the new row and store the reference to it (which keeps track of
+ * the tree changes) */
+ gtk_list_store_append(nsgtk_download_store, &nsgtk_download_iter);
+ download->row = gtk_tree_row_reference_new(
+ GTK_TREE_MODEL(nsgtk_download_store),
+ gtk_tree_model_get_path(GTK_TREE_MODEL
+ (nsgtk_download_store), &nsgtk_download_iter));
+
+ nsgtk_download_sensitivity_set(download, NSGTK_DOWNLOAD_CANCEL);
+
+ nsgtk_download_store_create_item(download);
+ nsgtk_download_show(nsgtk_download_parent);
+
+ nsgtk_downloads++;
+ return download;
+}
+
+
+void gui_download_window_data(struct gui_download_window *dw, const char *data,
+ unsigned int size)
+{
+ g_io_channel_write_chars(dw->write, data, size, NULL, &dw->error);
+ if (dw->error != NULL) {
+ dw->status = NSGTK_DOWNLOAD_ERROR;
+ dw->speed = 0;
+ dw->time_remaining = -1;
+ dw->sensitivity = NSGTK_DOWNLOAD_CLEAR;
+ nsgtk_download_store_update_item(dw);
+ fetch_abort(dw->fetch);
+
+ nsgtk_downloads--;
+ gtk_window_present(nsgtk_download_window);
+ return;
+ }
+
+ dw->size_downloaded += size;
+ gfloat elapsed = g_timer_elapsed(dw->timer, NULL);
+
+ /* If enough time has gone by, update the window */
+ if (elapsed - dw->last_update > .5 &&
+ GTK_WIDGET_VISIBLE(nsgtk_download_window)) {
+ dw->speed = dw->size_downloaded / elapsed;
+ dw->time_remaining = (dw->size_total - dw->size_downloaded)/
+ dw->speed;
+
+ if (dw->size_total)
+ dw->progress = (gfloat)(dw->size_downloaded)/
+ dw->size_total*100;
- nsgtk_download_add("http://www.rjek.com/gniggle.zip",
- "/home/rjek/Downloads/gniggle.zip",
- 68,
- "20kB of 90kB, 63kB/sec, 2 seconds");
+ nsgtk_download_store_update_item(dw);
+ dw->last_update = elapsed;
+ }
+}
+
+
+void gui_download_window_error(struct gui_download_window *dw,
+ const char *error_msg)
+{
+}
+
+
+void gui_download_window_done(struct gui_download_window *dw)
+{
+ g_io_channel_shutdown(dw->write, TRUE, &dw->error);
+ g_io_channel_unref(dw->write);
+
+ dw->speed = 0;
+ dw->progress = 100;
+ nsgtk_download_sensitivity_set(dw, NSGTK_DOWNLOAD_CLEAR);
+ dw->status = NSGTK_DOWNLOAD_COMPLETE;
+
+ if (option_downloads_clear)
+ nsgtk_download_store_clear_item(dw);
+ else
+ nsgtk_download_store_update_item(dw);
+
+ nsgtk_downloads--;
+}
+
+
+GtkTreeView* nsgtk_download_tree_view_new(GladeXML *gladeFile)
+{
+ GtkTreeView *treeview = GTK_TREE_VIEW(glade_xml_get_widget(gladeFile,
+ "treeDownloads"));
+ GtkCellRenderer *renderer;
+ gchar *progress, *information, *speed, *remaining;
+
+ /* Progress column */
+ renderer = gtk_cell_renderer_progress_new();
+ gtk_tree_view_insert_column_with_attributes (treeview, -1,
+ messages_get("gtkProgress"), renderer, "value",
+ NSGTK_DOWNLOAD_PROGRESS, "text",
+ NSGTK_DOWNLOAD_STATUS, NULL);
+
+ /* Information column */
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(renderer), "wrap-mode", PANGO_WRAP_WORD_CHAR,
+ "wrap-width", 300, NULL);
+ gtk_tree_view_insert_column_with_attributes (treeview, -1,
+ messages_get("gtkDetails"), renderer, "text",
+ NSGTK_DOWNLOAD_INFO, NULL);
+ gtk_tree_view_column_set_expand(gtk_tree_view_get_column(treeview,
+ NSGTK_DOWNLOAD_INFO), TRUE);
+
+ /* Time remaining column */
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes (treeview, -1,
+ messages_get("gtkRemaining"), renderer, "text",
+ NSGTK_DOWNLOAD_REMAINING, NULL);
+
+ /* Speed column */
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes (treeview, -1,
+ messages_get("gtkSpeed"), renderer, "text",
+ NSGTK_DOWNLOAD_SPEED, NULL);
+
+ return treeview;
+}
+
+void nsgtk_download_tree_view_row_activated(GtkTreeView *tree,
+ GtkTreePath *path, GtkTreeViewColumn *column, gpointer data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ model = gtk_tree_view_get_model(tree);
+
+ if (gtk_tree_model_get_iter(model, &iter, path)) {
+ /* TODO: This will be a context action (pause, start, clear) */
+ nsgtk_download_selection_do(NULL,
+ nsgtk_download_store_clear_item);
+ }
+}
+
+void nsgtk_download_store_update_item (struct gui_download_window *dl)
+{
+ gchar *info = nsgtk_download_info_to_string(dl);
+ gchar *speed = g_strconcat(human_friendly_bytesize(dl->speed), "/s",
+ NULL);
+ gchar *time = nsgtk_download_time_to_string(dl->time_remaining);
+
+ /* Updates iter (which is needed to set and get data) with the dl row */
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(nsgtk_download_store),
+ &nsgtk_download_iter,
+ gtk_tree_row_reference_get_path(dl->row));
+
+ gtk_list_store_set (nsgtk_download_store, &nsgtk_download_iter,
+ NSGTK_DOWNLOAD_PROGRESS, dl->progress,
+ NSGTK_DOWNLOAD_INFO, info,
+ NSGTK_DOWNLOAD_SPEED, dl->speed == 0 ? "-" : speed,
+ NSGTK_DOWNLOAD_REMAINING, time,
+ -1);
+ if (dl->status != NSGTK_DOWNLOAD_NONE)
+ gtk_list_store_set(nsgtk_download_store, &nsgtk_download_iter,
+ NSGTK_DOWNLOAD_STATUS,
+ messages_get(status_messages[dl->status]));
+
+ g_free(info);
+ g_free(speed);
+ g_free(time);
+}
+
+void nsgtk_download_store_create_item (struct gui_download_window *dl)
+{
+ nsgtk_download_store_update_item(dl);
+ /* The iter has already been updated to this row */
+ gtk_list_store_set (nsgtk_download_store, &nsgtk_download_iter,
+ NSGTK_DOWNLOAD, dl, -1);
+}
+
+void nsgtk_download_store_clear_item (struct gui_download_window *dl)
+{
+ if (dl->sensitivity & NSGTK_DOWNLOAD_CLEAR) {
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(nsgtk_download_store),
+ &nsgtk_download_iter,
+ gtk_tree_row_reference_get_path(dl->row));
+ gtk_list_store_remove(nsgtk_download_store,
+ &nsgtk_download_iter);
+ g_free(dl);
+ }
+}
+
+void nsgtk_download_store_cancel_item (struct gui_download_window *dl)
+{
+ if (dl->sensitivity & NSGTK_DOWNLOAD_CANCEL) {
+ dl->status = NSGTK_DOWNLOAD_CANCELED;
+ dl->speed = 0;
+ dl->progress = 0;
+ dl->time_remaining = -1;
+ nsgtk_download_sensitivity_set(dl, NSGTK_DOWNLOAD_CLEAR);
+
+ if (dl->fetch)
+ fetch_abort(dl->fetch);
+
+ g_unlink(dl->filename);
+
+ nsgtk_download_store_update_item(dl);
+
+ nsgtk_downloads--;
+ }
+}
+
+void nsgtk_download_selection_do(GtkWidget *button, selection_action action)
+{
+ GList *rows, *dls = NULL;
+ GtkTreeModel *model = GTK_TREE_MODEL(nsgtk_download_store);
+
+ rows = gtk_tree_selection_get_selected_rows(nsgtk_download_selection,
+ &model);
+ while (rows != NULL) {
+ struct gui_download_window *dl;
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(nsgtk_download_store),
+ &nsgtk_download_iter, (GtkTreePath*)rows->data);
+ gtk_tree_model_get(GTK_TREE_MODEL(nsgtk_download_store),
+ &nsgtk_download_iter, NSGTK_DOWNLOAD,
+ &dl, -1);
+ dls = g_list_prepend(dls, dl);
+
+ rows = rows->next;
+ }
+ g_list_foreach(dls, (GFunc)action, NULL);
- nsgtk_download_add("http://www.whopper.com/biggy.iso",
- "/home/rjek/Downlaods/biggy.iso",
- 2,
- "2MB of 1923MB, 3kB/sec, 20 hours");
+ g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL);
+ g_list_foreach(rows, (GFunc)g_free, NULL);
+ g_list_free(rows);
+ g_list_free(dls);
+}
+void nsgtk_download_sensitivity_set(struct gui_download_window *dl,
+ nsgtk_download_actions sensitivity)
+{
+ dl->sensitivity = sensitivity;
+ if (gtk_tree_selection_path_is_selected(nsgtk_download_selection,
+ gtk_tree_row_reference_get_path(dl->row)))
+ nsgtk_download_sensitivity_update_buttons(sensitivity);
+
}
-void nsgtk_download_show(void)
+void nsgtk_download_sensitivity_selection_changed (GtkTreeSelection *selection)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL(nsgtk_download_store);
+ GtkTreeIter iter;
+ GList *rows;
+ nsgtk_download_actions sensitivity = 0;
+
+ rows = gtk_tree_selection_get_selected_rows(selection, &model);
+ while (rows != NULL) {
+ struct gui_download_window *dl;
+ gtk_tree_model_get_iter(model, &iter, (GtkTreePath*)rows->data);
+ gtk_tree_model_get(model, &iter, NSGTK_DOWNLOAD, &dl, -1);
+ sensitivity |= dl->sensitivity;
+ rows = rows->next;
+ }
+
+ nsgtk_download_sensitivity_update_buttons(sensitivity);
+}
+
+void nsgtk_download_sensitivity_update_buttons(
+ nsgtk_download_actions sensitivity)
+{
+ /* Glade seems to pack the buttons in an arbitrary order */
+ enum { PAUSE_BUTTON, CLEAR_BUTTON, CANCEL_BUTTON, RESUME_BUTTON };
+
+ gtk_widget_set_sensitive(g_list_nth_data(nsgtk_download_buttons,
+ PAUSE_BUTTON), sensitivity & NSGTK_DOWNLOAD_PAUSE);
+ gtk_widget_set_sensitive(g_list_nth_data(nsgtk_download_buttons,
+ CLEAR_BUTTON), sensitivity & NSGTK_DOWNLOAD_CLEAR);
+ gtk_widget_set_sensitive(g_list_nth_data(nsgtk_download_buttons,
+ CANCEL_BUTTON), sensitivity & NSGTK_DOWNLOAD_CANCEL);
+ gtk_widget_set_sensitive(g_list_nth_data(nsgtk_download_buttons,
+ RESUME_BUTTON), sensitivity & NSGTK_DOWNLOAD_RESUME);
+}
+
+gchar* nsgtk_download_dialog_show (gchar *filename, gchar *domain, gchar *size)
{
- gtk_widget_show_all(GTK_WIDGET(wndDownload));
- gdk_window_raise(GTK_WIDGET(wndDownload)->window);
+ enum { GTK_RESPONSE_DOWNLOAD, GTK_RESPONSE_SAVE_AS };
+ GtkWidget *dialog;
+ gchar *destination = NULL;
+ gchar *info = g_strdup_printf(messages_get("gtkInfo"), filename,
+ domain, size);
+
+ dialog = gtk_message_dialog_new_with_markup(nsgtk_download_parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
+ "<span size=\"x-large\" weight=\"ultrabold\">%s</span>"
+ "\n\n<small>%s</small>",
+ messages_get("gtkStartDownload"), info);
+
+ gtk_dialog_add_buttons(GTK_DIALOG(dialog), GTK_STOCK_SAVE,
+ GTK_RESPONSE_DOWNLOAD, GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE_AS,
+ GTK_RESPONSE_SAVE_AS, NULL);
+
+ gint result = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ g_free(info);
+
+ switch (result) {
+ case GTK_RESPONSE_SAVE_AS: {
+ dialog = gtk_file_chooser_dialog_new
+ (messages_get("gtkSave"),
+ nsgtk_download_parent,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+ gtk_file_chooser_set_current_name
+ (GTK_FILE_CHOOSER(dialog), filename);
+ gtk_file_chooser_set_do_overwrite_confirmation
+ (GTK_FILE_CHOOSER(dialog),
+ option_request_overwrite);
+
+ gint result = gtk_dialog_run(GTK_DIALOG(dialog));
+ if (result == GTK_RESPONSE_ACCEPT)
+ destination = gtk_file_chooser_get_filename
+ (GTK_FILE_CHOOSER(dialog));
+ gtk_widget_destroy(dialog);
+ break;
+ }
+ case GTK_RESPONSE_DOWNLOAD: {
+ destination = g_strconcat(option_downloads_directory,
+ "/", filename, NULL);
+ break;
+ }
+ }
+ return destination;
}
-void nsgtk_downloadPause_clicked(GtkToolButton *button, gpointer data)
+gchar* nsgtk_download_info_to_string (struct gui_download_window *dl)
+{
+ if (dl->status != NSGTK_DOWNLOAD_ERROR)
+ return g_strdup_printf("%s\n%s of %s completed",
+ dl->name->str,
+ human_friendly_bytesize(dl->size_downloaded),
+ dl->size_total == 0 ? "Unknown" :
+ human_friendly_bytesize(dl->size_total));
+ else
+ return g_strdup_printf("%s\n%s", dl->name->str,
+ dl->error->message);
+}
+
+gchar* nsgtk_download_time_to_string (gint seconds)
{
+ gint hours, minutes;
+ if (seconds < 0)
+ return g_strdup("-");
+
+ hours = seconds / 3600;
+ seconds -= hours * 3600;
+ minutes = seconds / 60;
+ seconds -= minutes * 60;
+
+ if (hours > 0)
+ return g_strdup_printf("%u:%02u:%02u", hours, minutes,
+ seconds);
+ else
+ return g_strdup_printf("%u:%02u", minutes, seconds);
}
+
+gboolean nsgtk_download_handle_error (GError *error)
+{
+ if (error != NULL) {
+ GtkWidget*dialog;
+ gchar *message = g_strdup_printf(messages_get("gtkFileError"),
+ error->message);
+
+ dialog = gtk_message_dialog_new_with_markup
+ (nsgtk_download_parent,
+ GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ "<big><b>%s</b></big>\n\n"
+ "<small>%s</small>", messages_get("gtkFailed"),
+ message);
+
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
diff --git a/gtk/gtk_download.h b/gtk/gtk_download.h
index cb4761f14..b7a6bd699 100644
--- a/gtk/gtk_download.h
+++ b/gtk/gtk_download.h
@@ -21,7 +21,59 @@
#include <gtk/gtk.h>
-void nsgtk_download_initialise(void);
-void nsgtk_download_show(void);
+enum {
+ NSGTK_DOWNLOAD_PROGRESS,
+ NSGTK_DOWNLOAD_INFO,
+ NSGTK_DOWNLOAD_REMAINING,
+ NSGTK_DOWNLOAD_SPEED,
+ NSGTK_DOWNLOAD_STATUS,
+ NSGTK_DOWNLOAD,
+
+ NSGTK_DOWNLOAD_N_COLUMNS
+};
+
+typedef enum {
+ NSGTK_DOWNLOAD_WORKING,
+ NSGTK_DOWNLOAD_ERROR,
+ NSGTK_DOWNLOAD_COMPLETE,
+ NSGTK_DOWNLOAD_CANCELED,
+ NSGTK_DOWNLOAD_NONE
+} nsgtk_download_status;
+
+typedef enum {
+ NSGTK_DOWNLOAD_PAUSE = 1 << 0,
+ NSGTK_DOWNLOAD_RESUME = 1 << 1,
+ NSGTK_DOWNLOAD_CANCEL = 1 << 2,
+ NSGTK_DOWNLOAD_CLEAR = 1 << 3
+} nsgtk_download_actions;
+
+struct gui_download_window {
+ struct fetch *fetch;
+ nsgtk_download_actions sensitivity;
+ nsgtk_download_status status;
+
+ GString *name;
+ GString *time_left;
+ gint size_total;
+ gint size_downloaded;
+ gint progress;
+ gfloat last_update;
+ gfloat time_remaining;
+ gfloat speed;
+ gchar *filename;
+
+ GtkTreeRowReference *row;
+ GTimer *timer;
+ GIOChannel *write;
+ GError *error;
+};
+
+typedef void (*selection_action)(struct gui_download_window *dl);
+
+void nsgtk_download_init();
+void nsgtk_download_destroy (void);
+bool nsgtk_check_for_downloads(GtkWindow *parent);
+void nsgtk_download_show(GtkWindow *parent);
+void nsgtk_download_add(gchar *url, gchar *destination);
#endif
diff --git a/gtk/gtk_gui.c b/gtk/gtk_gui.c
index 61696a673..2f59fb75b 100644
--- a/gtk/gtk_gui.c
+++ b/gtk/gtk_gui.c
@@ -267,6 +267,12 @@ void gui_init(int argc, char** argv)
LOG(("Using '%s' as certificate path", buf));
option_ca_path = strdup(buf);
}
+
+ if (!option_downloads_directory) {
+ char *home = getenv("HOME");
+ LOG(("Using '%s' as download directory", home));
+ option_downloads_directory = home;
+ }
find_resource(buf, "messages", "./gtk/res/messages");
LOG(("Using '%s' as Messages file", buf));
@@ -301,7 +307,7 @@ void gui_init(int argc, char** argv)
wndWarning = GTK_WINDOW(glade_xml_get_widget(gladeWindows, "wndWarning"));
nsgtk_history_init();
- nsgtk_download_initialise();
+ nsgtk_download_init();
}
@@ -391,6 +397,7 @@ void gui_multitask(void)
void gui_quit(void)
{
+ nsgtk_download_destroy();
urldb_save_cookies(option_cookie_jar);
urldb_save(option_url_file);
free(default_stylesheet_url);
@@ -401,31 +408,6 @@ void gui_quit(void)
}
-
-struct gui_download_window *gui_download_window_create(const char *url,
- const char *mime_type, struct fetch *fetch,
- unsigned int total_size)
-{
- return 0;
-}
-
-
-void gui_download_window_data(struct gui_download_window *dw, const char *data,
- unsigned int size)
-{
-}
-
-
-void gui_download_window_error(struct gui_download_window *dw,
- const char *error_msg)
-{
-}
-
-
-void gui_download_window_done(struct gui_download_window *dw)
-{
-}
-
static void nsgtk_select_menu_clicked(GtkCheckMenuItem *checkmenuitem,
gpointer user_data)
{
diff --git a/gtk/gtk_scaffolding.c b/gtk/gtk_scaffolding.c
index 66eefc837..c13acbad5 100644
--- a/gtk/gtk_scaffolding.c
+++ b/gtk/gtk_scaffolding.c
@@ -102,6 +102,7 @@ struct menu_events {
static int open_windows = 0; /**< current number of open browsers */
static struct gtk_scaffolding *current_model; /**< current window for model dialogue use */
+static gboolean nsgtk_window_delete_event(GtkWidget *, gpointer);
static void nsgtk_window_destroy_event(GtkWidget *, gpointer);
static void nsgtk_window_update_back_forward(struct gtk_scaffolding *);
@@ -233,6 +234,14 @@ void nsgtk_attach_menu_handlers(GladeXML *xml, gpointer g)
/* event handlers and support functions for them */
+gboolean nsgtk_window_delete_event(GtkWidget *widget, gpointer data)
+{
+ if (open_windows == 1 && nsgtk_check_for_downloads(GTK_WINDOW(widget)))
+ return TRUE;
+ else
+ return FALSE;
+}
+
void nsgtk_window_destroy_event(GtkWidget *widget, gpointer data)
{
struct gtk_scaffolding *g = data;
@@ -469,20 +478,24 @@ MENUHANDLER(close_window)
MENUHANDLER(quit)
{
- netsurf_quit = true;
+ struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
+
+ if (!nsgtk_check_for_downloads(gw->window))
+ netsurf_quit = true;
return TRUE;
}
MENUHANDLER(cut)
{
struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
+ struct browser_window *bw = nsgtk_get_browser_for_gui(gw->top_level);
GtkWidget *focused = gtk_window_get_focus(gw->window);
/* If the url bar has focus, let gtk handle it */
if (GTK_IS_EDITABLE (focused))
gtk_editable_cut_clipboard (GTK_EDITABLE(gw->url_bar));
else
- /* TODO: Implement Cut functionality */;
+ browser_window_key_press(bw, 24);
}
MENUHANDLER(copy)
@@ -630,7 +643,8 @@ MENUHANDLER(status_bar)
MENUHANDLER(downloads)
{
- nsgtk_download_show();
+ struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
+ nsgtk_download_show(gw->window);
return TRUE;
}
@@ -876,7 +890,7 @@ nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel)
g->status_pane = GTK_PANED(GET_WIDGET("hpaned1"));
g->preferences_dialog = NULL;
-
+
/* set this window's size and position to what's in the options, or
* or some sensible default if they're not set yet.
*/
@@ -964,6 +978,7 @@ nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel)
gtk_widget_hide_on_delete, NULL);
/* connect signals to handlers. */
+ CONNECT(g->window, "delete-event", nsgtk_window_delete_event, NULL);
CONNECT(g->window, "destroy", nsgtk_window_destroy_event, g);
/* toolbar, URL bar, and menu bar signal handlers */
@@ -1101,6 +1116,11 @@ gboolean nsgtk_scaffolding_is_busy(struct gtk_scaffolding *scaffold)
return GTK_WIDGET_SENSITIVE((GTK_WIDGET(scaffold->stop_button)));
}
+GtkWindow* nsgtk_scaffolding_get_window (struct gui_window *g)
+{
+ return g->scaffold->window;
+}
+
void nsgtk_scaffolding_popup_menu(struct gtk_scaffolding *g, guint button)
{
nsgtk_scaffolding_update_edit_actions_sensitivity(g, g->popup_xml, TRUE);
diff --git a/gtk/gtk_scaffolding.h b/gtk/gtk_scaffolding.h
index 8e3084316..5f886b68a 100644
--- a/gtk/gtk_scaffolding.h
+++ b/gtk/gtk_scaffolding.h
@@ -29,6 +29,8 @@ nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel);
gboolean nsgtk_scaffolding_is_busy(nsgtk_scaffolding *scaffold);
+GtkWindow* nsgtk_scaffolding_get_window (struct gui_window *g);
+
void nsgtk_attach_toplevel_viewport(nsgtk_scaffolding *g, GtkViewport *vp);
void nsgtk_scaffolding_destroy(nsgtk_scaffolding *scaffold);
diff --git a/gtk/gtk_selection.c b/gtk/gtk_selection.c
index 0e5b397fb..c3631dfc6 100644
--- a/gtk/gtk_selection.c
+++ b/gtk/gtk_selection.c
@@ -25,9 +25,10 @@
#include "desktop/browser.h"
#include "gtk/gtk_selection.h"
#include "gtk/gtk_window.h"
+#include "utils/utf8.h"
-GString *current_selection;
-GtkClipboard *clipboard;
+static GString *current_selection = NULL;
+static GtkClipboard *clipboard;
static bool copy_handler(const char *text, size_t length, struct box *box,
void *handle, const char *whitespace_text,
@@ -63,15 +64,19 @@ bool copy_handler(const char *text, size_t length, struct box *box,
bool gui_copy_to_clipboard(struct selection *s)
{
- current_selection = g_string_new(NULL);
clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
if (s->defined && selection_traverse(s, copy_handler, NULL))
- gtk_clipboard_set_text (clipboard, current_selection->str, -1);
+ gui_commit_clipboard();
return TRUE;
}
void gui_start_selection(struct gui_window *g)
{
+ if (current_selection == NULL)
+ current_selection = g_string_new(NULL);
+ else
+ g_string_set_size(current_selection, 0);
+
gtk_widget_grab_focus(GTK_WIDGET(g->drawing_area));
}
@@ -80,18 +85,26 @@ void gui_paste_from_clipboard(struct gui_window *g, int x, int y)
char *text;
clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
text = gtk_clipboard_wait_for_text (clipboard);
- if (text)
- browser_window_paste_text(g->bw, text,
- strlen(text), true);
+ /* clipboard_wait... converts the string to utf8 for us */
+ if (text != NULL)
+ browser_window_paste_text(g->bw, text, strlen(text), true);
+ free(text);
}
bool gui_empty_clipboard(void)
{
+ if (!current_selection)
+ current_selection = g_string_new(NULL);
+ else
+ g_string_set_size(current_selection, 0);
+
return true;
}
bool gui_commit_clipboard(void)
{
+ clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_text (clipboard, current_selection->str, -1);
return true;
}
diff --git a/gtk/gtk_window.c b/gtk/gtk_window.c
index e7363c3f1..a63341ff5 100644
--- a/gtk/gtk_window.c
+++ b/gtk/gtk_window.c
@@ -331,8 +331,7 @@ gboolean nsgtk_window_motion_notify_event(GtkWidget *widget,
if (g->mouse->state & BROWSER_MOUSE_PRESS_1){
/* Start button 1 drag */
browser_window_mouse_click(g->bw, BROWSER_MOUSE_DRAG_1,
- event->x / g->bw->scale,
- event->y / g->bw->scale);
+ g->mouse->pressed_x, g->mouse->pressed_y);
/* Replace PRESS with HOLDING and declare drag in progress */
g->mouse->state ^= (BROWSER_MOUSE_PRESS_1 |
BROWSER_MOUSE_HOLDING_1);
@@ -341,8 +340,7 @@ gboolean nsgtk_window_motion_notify_event(GtkWidget *widget,
else if (g->mouse->state & BROWSER_MOUSE_PRESS_2){
/* Start button 2 drag */
browser_window_mouse_click(g->bw, BROWSER_MOUSE_DRAG_2,
- event->x / g->bw->scale,
- event->y / g->bw->scale);
+ g->mouse->pressed_x, g->mouse->pressed_y);
/* Replace PRESS with HOLDING and declare drag in progress */
g->mouse->state ^= (BROWSER_MOUSE_PRESS_2 |
BROWSER_MOUSE_HOLDING_2);
@@ -382,9 +380,12 @@ gboolean nsgtk_window_button_press_event(GtkWidget *widget,
g->mouse->state |= BROWSER_MOUSE_MOD_1;
if (event->state & GDK_CONTROL_MASK)
g->mouse->state |= BROWSER_MOUSE_MOD_2;
+
+ g->mouse->pressed_x = event->x / g->bw->scale;
+ g->mouse->pressed_y = event->y / g->bw->scale;
- browser_window_mouse_click(g->bw, g->mouse->state, event->x / g->bw->scale,
- event->y / g->bw->scale);
+ browser_window_mouse_click(g->bw, g->mouse->state, g->mouse->pressed_x,
+ g->mouse->pressed_y);
}
gboolean nsgtk_window_button_release_event(GtkWidget *widget,
@@ -462,7 +463,6 @@ gboolean nsgtk_window_keypress_event(GtkWidget *widget, GdkEventKey *event,
{
struct gui_window *g = data;
uint32_t nskey = gdkkey_to_nskey(event);
-
if (browser_window_key_press(g->bw, nskey))
return TRUE;
diff --git a/gtk/options.h b/gtk/options.h
index 80d72c0d5..1c7f4f544 100644
--- a/gtk/options.h
+++ b/gtk/options.h
@@ -22,13 +22,22 @@
#include "desktop/options.h"
extern bool option_render_resample;
+extern bool option_downloads_clear;
+extern bool option_request_overwrite;
+extern char *option_downloads_directory;
extern char *option_url_file;
#define EXTRA_OPTION_DEFINE \
bool option_render_resample = false; \
+bool option_downloads_clear = false; \
+bool option_request_overwrite = true; \
+char *option_downloads_directory = 0; \
char *option_url_file = 0;
#define EXTRA_OPTION_TABLE \
{ "render_resample", OPTION_BOOL, &option_render_resample }, \
+{ "downloads_clear", OPTION_BOOL, &option_downloads_clear }, \
+{ "request_overwrite", OPTION_BOOL, &option_request_overwrite }, \
+{ "downloads_directory",OPTION_STRING, &option_downloads_directory }, \
{ "url_file", OPTION_STRING, &option_url_file },
#endif
diff --git a/gtk/res/downloads.glade b/gtk/res/downloads.glade
new file mode 100644
index 000000000..ab12ad8c4
--- /dev/null
+++ b/gtk/res/downloads.glade
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.4 on Mon Jul 14 18:30:15 2008 -->
+<glade-interface>
+ <widget class="GtkWindow" id="wndDownloads">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="title" translatable="yes">NetSurf Downloads</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="default_width">500</property>
+ <property name="default_height">300</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkTreeView" id="treeDownloads">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_clickable">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="border_width">2</property>
+ <child>
+ <widget class="GtkProgressBar" id="progressBar">
+ <property name="visible">True</property>
+ <property name="fraction">0.20999999344348907</property>
+ <property name="text" translatable="yes">21% of 3 files</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToolbar" id="toolbar2">
+ <property name="visible">True</property>
+ <property name="toolbar_style">GTK_TOOLBAR_BOTH_HORIZ</property>
+ <property name="show_arrow">False</property>
+ <property name="icon_size">GTK_ICON_SIZE_MENU</property>
+ <property name="icon_size_set">True</property>
+ <child>
+ <widget class="GtkToolButton" id="buttonPause">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="is_important">True</property>
+ <property name="label"> Pause</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-media-pause</property>
+ <signal name="clicked" handler="button_clear_clicked" object="NULL"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToolButton" id="buttonResume">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="is_important">True</property>
+ <property name="label"> Resume</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-media-play</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToolButton" id="buttonCancel">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="is_important">True</property>
+ <property name="label"> Cancel</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-cancel</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToolButton" id="buttonClear">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Remove completed or cancelled downloads from the list</property>
+ <property name="visible_vertical">False</property>
+ <property name="is_important">True</property>
+ <property name="label"> Clear</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-clear</property>
+ <signal name="clicked" handler="button_clear_clicked"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
diff --git a/gtk/res/netsurf.glade b/gtk/res/netsurf.glade
index d1b1e15f4..09af6fea9 100644
--- a/gtk/res/netsurf.glade
+++ b/gtk/res/netsurf.glade
@@ -232,6 +232,7 @@
<property name="label">gtk-select-all</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
+ <accelerator key="a" modifiers="GDK_CONTROL_MASK" signal="activate"/>
</widget>
</child>
<child>
@@ -329,9 +330,9 @@
<property name="visible">True</property>
<property name="label" translatable="yes">Zoom _in</property>
<property name="use_underline">True</property>
- <accelerator key="plus" modifiers="GDK_CONTROL_MASK" signal="activate"/>
- <accelerator key="KP_Add" modifiers="GDK_CONTROL_MASK" signal="activate"/>
<accelerator key="equal" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ <accelerator key="KP_Add" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ <accelerator key="plus" modifiers="GDK_CONTROL_MASK" signal="activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image564">
<property name="visible">True</property>
@@ -346,8 +347,8 @@
<property name="visible">True</property>
<property name="label" translatable="yes">_Normal size</property>
<property name="use_underline">True</property>
- <accelerator key="0" modifiers="GDK_CONTROL_MASK" signal="activate"/>
<accelerator key="KP_0" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ <accelerator key="0" modifiers="GDK_CONTROL_MASK" signal="activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image565">
<property name="visible">True</property>
@@ -362,8 +363,8 @@
<property name="visible">True</property>
<property name="label" translatable="yes">Zoom _out</property>
<property name="use_underline">True</property>
- <accelerator key="minus" modifiers="GDK_CONTROL_MASK" signal="activate"/>
<accelerator key="KP_Subtract" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ <accelerator key="minus" modifiers="GDK_CONTROL_MASK" signal="activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image566">
<property name="visible">True</property>
@@ -801,25 +802,17 @@
<placeholder/>
</child>
<child>
- <widget class="GtkHSeparator" id="hseparator3">
- <property name="visible">True</property>
- </widget>
- <packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="y_options">GTK_FILL</property>
- </packing>
- </child>
- <child>
- <widget class="GtkVScrollbar" id="coreScrollVertical">
+ <widget class="GtkStatusbar" id="statusbar1">
+ <property name="height_request">1</property>
<property name="visible">True</property>
- <property name="adjustment">0 0 100 1 10 0</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="bottom_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_SHRINK | GTK_FILL</property>
</packing>
</child>
<child>
@@ -858,17 +851,25 @@
</packing>
</child>
<child>
- <widget class="GtkStatusbar" id="statusbar1">
- <property name="height_request">1</property>
+ <widget class="GtkVScrollbar" id="coreScrollVertical">
<property name="visible">True</property>
+ <property name="adjustment">0 0 100 1 10 0</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator3">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_FILL</property>
</packing>
</child>
</widget>
@@ -912,106 +913,106 @@
<property name="column_spacing">11</property>
<property name="row_spacing">10</property>
<child>
- <widget class="GtkEntry" id="entryLoginUser">
+ <widget class="GtkLabel" id="labelLoginHost">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="has_focus">True</property>
- <property name="text" translatable="yes">sesame</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">moo.yoo.com</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
- <property name="y_options"></property>
+ <property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkEntry" id="entryLoginPass">
+ <widget class="GtkLabel" id="label57">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="visibility">False</property>
- <property name="activates_default">True</property>
- <property name="text" translatable="yes">opensesame</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Password</property>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
- <property name="y_options"></property>
+ <property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="labelLoginRealm">
+ <widget class="GtkLabel" id="label56">
<property name="visible">True</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">my sekr3t area</property>
+ <property name="label" translatable="yes">Username</property>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label55">
+ <widget class="GtkLabel" id="label54">
<property name="visible">True</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">Realm</property>
+ <property name="label" translatable="yes">Host</property>
</widget>
<packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label54">
+ <widget class="GtkLabel" id="label55">
<property name="visible">True</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">Host</property>
+ <property name="label" translatable="yes">Realm</property>
</widget>
<packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label56">
+ <widget class="GtkLabel" id="labelLoginRealm">
<property name="visible">True</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">Username</property>
+ <property name="label" translatable="yes">my sekr3t area</property>
</widget>
<packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label57">
+ <widget class="GtkEntry" id="entryLoginPass">
<property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Password</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="activates_default">True</property>
+ <property name="text" translatable="yes">opensesame</property>
</widget>
<packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
- <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="labelLoginHost">
+ <widget class="GtkEntry" id="entryLoginUser">
<property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">moo.yoo.com</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="text" translatable="yes">sesame</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="x_options">GTK_FILL</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
</packing>
</child>
</widget>
@@ -1344,82 +1345,82 @@
<property name="n_columns">2</property>
<property name="column_spacing">4</property>
<child>
- <widget class="GtkLabel" id="label117">
+ <widget class="GtkLabel" id="labelHistoryAddress">
<property name="visible">True</property>
- <property name="xalign">1</property>
- <property name="label" translatable="yes">Address</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">http://netsurf.sf.net/</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_MIDDLE</property>
</widget>
<packing>
- <property name="x_options">GTK_FILL</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label118">
+ <widget class="GtkLabel" id="labelHistoryLastVisit">
<property name="visible">True</property>
- <property name="xalign">1</property>
- <property name="label" translatable="yes">Last visited</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Fri Aug 09 00:00:00 2006</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
</widget>
<packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
- <property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label119">
+ <widget class="GtkLabel" id="labelHistoryVisits">
<property name="visible">True</property>
- <property name="xalign">1</property>
- <property name="label" translatable="yes">Number of visits</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">2</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
</widget>
<packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
- <property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="labelHistoryVisits">
+ <widget class="GtkLabel" id="label119">
<property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">2</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">Number of visits</property>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="labelHistoryLastVisit">
+ <widget class="GtkLabel" id="label118">
<property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Fri Aug 09 00:00:00 2006</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">Last visited</property>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="labelHistoryAddress">
+ <widget class="GtkLabel" id="label117">
<property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">http://netsurf.sf.net/</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_MIDDLE</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">Address</property>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
diff --git a/gtk/res/options.glade b/gtk/res/options.glade
index 9e0c08650..d3f5eaca8 100644
--- a/gtk/res/options.glade
+++ b/gtk/res/options.glade
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.4.4 on Mon Jun 9 14:16:52 2008 -->
+<!--Generated with glade3 3.4.4 on Tue Jul 15 19:19:26 2008 -->
<glade-interface>
<widget class="GtkDialog" id="dlgPreferences">
<property name="border_width">5</property>
@@ -311,22 +311,6 @@
<property name="visible">True</property>
<property name="homogeneous">True</property>
<child>
- <widget class="GtkCheckButton" id="checkRequestOverwrite">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="can_focus">True</property>
- <property name="tooltip" translatable="yes">Ask before overwriting files when downloading.</property>
- <property name="label" translatable="yes">Request confirmation before overwriting files</property>
- <property name="use_underline">True</property>
- <property name="response_id">0</property>
- <property name="draw_indicator">True</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- <child>
<widget class="GtkCheckButton" id="checkDisplayRecentURLs">
<property name="visible">True</property>
<property name="can_focus">True</property>
@@ -339,7 +323,6 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">1</property>
</packing>
</child>
<child>
@@ -356,7 +339,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">2</property>
+ <property name="position">1</property>
</packing>
</child>
</widget>
@@ -716,7 +699,7 @@ NTLM authentication</property>
<widget class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<child>
- <widget class="GtkCheckButton" id="checkResampleImages1">
+ <widget class="GtkCheckButton" id="checkResampleImages">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip" translatable="yes">Resample images when not at natural size</property>
@@ -1474,6 +1457,114 @@ Fantasy</property>
<property name="tab_fill">False</property>
</packing>
</child>
+ <child>
+ <widget class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="left_padding">12</property>
+ <child>
+ <widget class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkCheckButton" id="checkClearDownloads">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Erase the download from the list as soon as it completes.</property>
+ <property name="label" translatable="yes">Automatically clear downloads when completed</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="checkRequestOverwrite">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Ask before overwriting files when downloading.</property>
+ <property name="label" translatable="yes">Request confirmation before overwriting files</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Download directory: </property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkFileChooserButton" id="fileChooserDownloads">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">The default directory where files are saved</property>
+ <property name="action">GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER</property>
+ <property name="width_chars">25</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Downloads&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label109">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Downloads</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ <property name="position">5</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
</widget>
<packing>
<property name="position">1</property>
@@ -1511,7 +1602,6 @@ Fantasy</property>
<packing>
<property name="expand">False</property>
<property name="pack_type">GTK_PACK_END</property>
- <property name="position">1</property>
</packing>
</child>
</widget>
diff --git a/riscos/download.c b/riscos/download.c
index 95da4f814..d8d86b75f 100644
--- a/riscos/download.c
+++ b/riscos/download.c
@@ -211,7 +211,7 @@ const char *ro_gui_download_temp_name(struct gui_download_window *dw)
struct gui_download_window *gui_download_window_create(const char *url,
const char *mime_type, struct fetch *fetch,
- unsigned int total_size)
+ unsigned int total_size, struct gui_window *gui)
{
const char *temp_name;
char *nice, *scheme = NULL;
diff --git a/riscos/window.c b/riscos/window.c
index 29cc64e08..045cabe56 100644
--- a/riscos/window.c
+++ b/riscos/window.c
@@ -2856,7 +2856,7 @@ bool ro_gui_window_dataload(struct gui_window *g, wimp_message *message)
const char *filename = message->data.data_xfer.file_name;
- browser_window_mouse_click(g->bw, BROWSER_MOUSE_CLICK_1, pos.x, pos.y);
+ browser_window_mouse_click(g->bw, BROWSER_MOUSE_PRESS_1, pos.x, pos.y);
if (!ro_gui_window_import_text(g, filename, false))
return true; /* it was for us, it just didn't work! */