summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Kendrick <rjek@netsurf-browser.org>2008-07-16 10:19:30 +0000
committerRob Kendrick <rjek@netsurf-browser.org>2008-07-16 10:19:30 +0000
commite7850d9762528507a96ed45f1868400ea88166dd (patch)
tree4ae7223573a267a6ca665e8613909daca88eb4fc
parentcda086882a7e6cb1eb752106ecce8c15105d1985 (diff)
downloadnetsurf-e7850d9762528507a96ed45f1868400ea88166dd.tar.gz
netsurf-e7850d9762528507a96ed45f1868400ea88166dd.tar.bz2
Merged revisions 4282-4285,4288-4293,4297-4298,4307,4309-4313,4322,4324-4680 via svnmerge from
svn://svn.netsurf-browser.org/branches/mikeL/netsurf ........ r4432 | mikeL | 2008-06-24 04:00:36 +0100 (Tue, 24 Jun 2008) | 1 line Drag events are now emited from where the press originated, instead of from where they became a drag ........ r4433 | mikeL | 2008-06-24 04:25:33 +0100 (Tue, 24 Jun 2008) | 1 line Added accelerator to 'Select All' ........ r4495 | mikeL | 2008-07-02 21:36:32 +0100 (Wed, 02 Jul 2008) | 1 line Selections are now deleted and replaced when a key is typed in a text area or text box. All input box behavior while a selection is active is now implemented (ex: pressing the right arrow key moves the caret to the end of the selection). Cut now works properly in both versions. Fixed discrepancy between where the caret was placed and selection began when starting a drag-select. Fixed bug with calculation of a selections end box. ........ r4496 | mikeL | 2008-07-02 22:11:24 +0100 (Wed, 02 Jul 2008) | 1 line Added support for cut in input boxes ........ r4497 | mikeL | 2008-07-02 22:21:35 +0100 (Wed, 02 Jul 2008) | 1 line Removed unused variables (Thanks tlsa) ........ r4498 | mikeL | 2008-07-02 23:30:30 +0100 (Wed, 02 Jul 2008) | 1 line Modified selection clearing behavior to allow for drag-saves ........ r4499 | mikeL | 2008-07-03 00:51:50 +0100 (Thu, 03 Jul 2008) | 1 line Fixed regression where it would take two clicks to place caret in an input (Thanks tlsa) ........ r4509 | mikeL | 2008-07-06 07:55:09 +0100 (Sun, 06 Jul 2008) | 1 line Basic download support implemented. Only downloading of text files works ........ r4510 | mikeL | 2008-07-06 18:55:31 +0100 (Sun, 06 Jul 2008) | 1 line Downloading of non-text files is now possible. Progress bar and size downloaded are now updated ........ r4511 | mikeL | 2008-07-06 20:46:00 +0100 (Sun, 06 Jul 2008) | 1 line Added downloads glade file ........ r4512 | mikeL | 2008-07-06 20:47:39 +0100 (Sun, 06 Jul 2008) | 1 line Downloads window now spawns in the center of the parent browser window ........ r4513 | mikeL | 2008-07-06 20:56:12 +0100 (Sun, 06 Jul 2008) | 1 line Fixed bug where backspace would be ignored if the selection began at the beginning on an input ........ r4514 | mikeL | 2008-07-06 21:26:45 +0100 (Sun, 06 Jul 2008) | 1 line Fixed compiler warnings by adding casts ........ r4516 | mikeL | 2008-07-06 21:32:41 +0100 (Sun, 06 Jul 2008) | 1 line Fixed initialization of size string, added initialization of progress ........ r4518 | mikeL | 2008-07-06 21:51:08 +0100 (Sun, 06 Jul 2008) | 1 line Added an option for short units (with space) to human_friendly_bytesize ........ r4519 | mikeL | 2008-07-06 21:52:05 +0100 (Sun, 06 Jul 2008) | 1 line Removed function size_to_string ........ r4520 | mikeL | 2008-07-06 22:03:11 +0100 (Sun, 06 Jul 2008) | 1 line Fixed pedantic error (kB instead of KB). Added missing necessary parameters to human_friendly_bytesize. Fixed incorrect bool types ........ r4521 | mikeL | 2008-07-06 22:08:15 +0100 (Sun, 06 Jul 2008) | 1 line Removed unnecessary parameter and units list from human_friendly_bytesize ........ r4522 | mikeL | 2008-07-06 22:57:03 +0100 (Sun, 06 Jul 2008) | 1 line Removed unnused variable ........ r4523 | mikeL | 2008-07-06 23:03:46 +0100 (Sun, 06 Jul 2008) | 1 line Fixed url parsing by replacing url_parse_filename with url_nice. Total size and size downloaded are now in human readable form. Speed is now calculated (roughly) ........ r4524 | mikeL | 2008-07-07 01:19:01 +0100 (Mon, 07 Jul 2008) | 1 line Added file overwrite confirmation. Changed speed to a double ........ r4546 | mikeL | 2008-07-09 17:21:43 +0100 (Wed, 09 Jul 2008) | 1 line Changed parameter of selection_get_end/start to a size_t instead of int and changed all offset variables to size_t as well ........ r4547 | mikeL | 2008-07-09 17:30:47 +0100 (Wed, 09 Jul 2008) | 1 line Added action buttons to the bottom toolbar. Added ability to clear selected (and completed) downloads with a framework for other actions. ........ r4556 | jmb | 2008-07-10 00:17:24 +0100 (Thu, 10 Jul 2008) | 3 lines A large bunch of tidying and general fixes to text input handling. Make selection code treat password fields as inputs, too. ........ r4557 | mikeL | 2008-07-10 00:24:46 +0100 (Thu, 10 Jul 2008) | 1 line Added functionality to gui_empty_clipboard and gui_start_selection (Thanks jmb) ........ r4573 | mikeL | 2008-07-10 16:33:27 +0100 (Thu, 10 Jul 2008) | 1 line Removed example download. Made the list store row aware of its download and vise versa. Added new way of handling actions from the dialog (e.g. buttons) which handles all rows in the selection. Added a few memory management function calls to clean up better ........ r4577 | mikeL | 2008-07-10 21:11:51 +0100 (Thu, 10 Jul 2008) | 1 line Download write channels now close properly. Added status column to the tree store which will change the progress bar text if a download is canceled or completed. Implemented cancel button functionality. ........ r4578 | mikeL | 2008-07-10 21:17:51 +0100 (Thu, 10 Jul 2008) | 1 line Speed is now displayed as '-' when 0 or download has stopped ........ r4580 | mikeL | 2008-07-11 02:10:57 +0100 (Fri, 11 Jul 2008) | 1 line Added two download related options (Download directory & Clear completed downloads) and a Downloads tab to the preferences dialog. Also moved the option to ask when overwriting files to Downloads tab. Added another option to the pre-download dialog, Save, which downloads the file immediately to your 'Download directory' ........ r4581 | mikeL | 2008-07-11 02:26:00 +0100 (Fri, 11 Jul 2008) | 1 line Rearranged pre-download dialog buttons to conform to the HIG ........ r4616 | mikeL | 2008-07-11 19:54:12 +0100 (Fri, 11 Jul 2008) | 1 line Limited download window updates to a user-defined variable that can be set in options (default is .5). Updates are now only done if the download window is visible. This greatly reduces the cpu usage. ........ r4617 | mikeL | 2008-07-11 20:07:48 +0100 (Fri, 11 Jul 2008) | 1 line Removed unnecessary update limit option (it is now fixed at .5) ........ r4629 | mikeL | 2008-07-13 04:21:07 +0100 (Sun, 13 Jul 2008) | 1 line Reorganized button sensitivity functions. Sensitivities are now updated when the selection changes as well as when a selected download's state changes. ........ r4635 | mikeL | 2008-07-13 17:00:05 +0100 (Sun, 13 Jul 2008) | 1 line Added error handling. Added word-wrap to the "info" cell renderer so that to keep everything under control. Fixed bug where downloads would always go to you default folder (missing breaks). ........ r4642 | mikeL | 2008-07-13 21:46:09 +0100 (Sun, 13 Jul 2008) | 1 line Added time remaining column. Fixed regression where the download info would be erased upon completion/cancelation. ........ r4655 | mikeL | 2008-07-14 23:20:33 +0100 (Mon, 14 Jul 2008) | 1 line Downloads dialog is now initialized in gtk_gui.c with no parent window. The parent windows are now set when a download is created (through an extra parameter in gui_download_window_create) and when nsgtk_download_show is called. When it is closed (when NS shuts down) all incomplete downloads are canceled (and the files deleted). Added a warning dialog when netsurf tries to close with incomplete downloads. Fixed bug where default save directory would initialize to NULL. ........ r4676 | mikeL | 2008-07-15 21:01:17 +0100 (Tue, 15 Jul 2008) | 1 line Downloads dialog is now initialized in gtk_gui.c with no parent window. The parent windows are now set when a download is created (through an extra parameter in gui_download_window_create) and when nsgtk_download_show is called. (This is the second half of the patch, last commit was only partial for some reason) ........ r4678 | mikeL | 2008-07-16 01:18:52 +0100 (Wed, 16 Jul 2008) | 1 line Addresses almost all of rjek and jmb's concerns, fixes most of the sloppiness that was present earlier. Downloads without a total_size are now handled correctly (thanks tlsa). Changes to the default download directly are now saved correctly. Billions of other small bugs fixed ........ svn path=/trunk/netsurf/; revision=4681
-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! */