summaryrefslogtreecommitdiff
path: root/desktop/textarea.c
diff options
context:
space:
mode:
authorMichael Drake <tlsa@netsurf-browser.org>2013-09-30 21:38:41 +0100
committerMichael Drake <tlsa@netsurf-browser.org>2013-09-30 21:38:41 +0100
commit308a24e66171190d321dfed622dc83e16bd64549 (patch)
treecdb24ffa9cf45befc921b122ba9732bff4d5bacb /desktop/textarea.c
parent97aceb5a681faf288e38b5db9e408931ff0d3e6b (diff)
downloadnetsurf-308a24e66171190d321dfed622dc83e16bd64549.tar.gz
netsurf-308a24e66171190d321dfed622dc83e16bd64549.tar.bz2
Start implementing undo/redo. For now it just records changes, the actual undo/redo handling is not yet done.
Diffstat (limited to 'desktop/textarea.c')
-rw-r--r--desktop/textarea.c193
1 files changed, 182 insertions, 11 deletions
diff --git a/desktop/textarea.c b/desktop/textarea.c
index 65f0c53e5..8f53bf79e 100644
--- a/desktop/textarea.c
+++ b/desktop/textarea.c
@@ -64,13 +64,19 @@ struct textarea_utf8 {
};
struct textarea_undo_detail {
- unsigned int offset; /**< Offset to detail's text in undo.text */
- unsigned int len; /**< Length of detail's text */
+ unsigned int b_start; /**< Offset to detail's text in undo buffer */
+ unsigned int b_end; /**< End of text (exclusive) */
+ unsigned int b_limit; /**< End of detail space (exclusive) */
+
+ unsigned int b_text_start; /**< Start of textarea text. */
+ unsigned int b_text_end; /**< End of textarea text (exclusive) */
};
struct textarea_undo {
- unsigned int next_detail;
- struct textarea_undo_detail *details;
+ unsigned int details_alloc; /**< Details allocated for */
+ unsigned int next_detail; /**< Next detail pos */
+ unsigned int last_detail; /**< Last detail used */
+ struct textarea_undo_detail *details; /**< Array of undo details */
struct textarea_utf8 text;
};
@@ -135,6 +141,8 @@ struct textarea {
int drag_start; /**< Byte offset of drag start (in ta->show) */
struct textarea_drag drag_info; /**< Drag information */
+
+ struct textarea_undo undo; /**< Undo/redo information */
};
@@ -1425,7 +1433,7 @@ static bool textarea_replace_text_internal(struct textarea *ta, size_t b_start,
/* Ensure textarea's text buffer is large enough */
if (rep_len + ta->text.len - (b_end - b_start) >= ta->text.alloc) {
char *temp = realloc(ta->text.data,
- rep_len + ta->text.len - (b_end - b_start) +
+ rep_len + ta->text.len - (b_end - b_start) +
TA_ALLOC_STEP);
if (temp == NULL) {
LOG(("realloc failed"));
@@ -1474,6 +1482,74 @@ static bool textarea_replace_text_internal(struct textarea *ta, size_t b_start,
/**
+ * Update undo buffer by adding any text to be replaced, and allocating
+ * space as appropriate.
+ *
+ * \param ta Textarea widget
+ * \param b_start Start byte index of replaced section (inclusive)
+ * \param b_end End byte index of replaced section (exclusive)
+ * \param rep_len Byte length of replacement UTF-8 text
+ * \return false on memory exhaustion, true otherwise
+ */
+static bool textarea_copy_to_undo_buffer(struct textarea *ta,
+ size_t b_start, size_t b_end, size_t rep_len)
+{
+ struct textarea_undo *undo;
+ size_t b_offset;
+ unsigned int len = b_end - b_start;
+
+ undo = &ta->undo;
+
+ if (undo->next_detail == 0)
+ b_offset = 0;
+ else
+ b_offset = undo->details[undo->next_detail - 1].b_limit;
+
+ len = len > rep_len ? len : rep_len;
+
+ if (b_offset + len >= undo->text.alloc) {
+ /* Need more memory for undo buffer */
+ char *temp = realloc(undo->text.data,
+ b_offset + len + TA_ALLOC_STEP);
+ if (temp == NULL) {
+ LOG(("realloc failed"));
+ return false;
+ }
+
+ undo->text.data = temp;
+ undo->text.alloc = b_offset + len + TA_ALLOC_STEP;
+ }
+
+ if (undo->next_detail >= undo->details_alloc) {
+ /* Need more memory for undo details */
+ struct textarea_undo_detail *temp = realloc(undo->details,
+ (undo->next_detail + 128) *
+ sizeof(struct textarea_undo_detail));
+ if (temp == NULL) {
+ LOG(("realloc failed"));
+ return false;
+ }
+
+ undo->details = temp;
+ undo->details_alloc = undo->next_detail + 128;
+ }
+
+ /* Put text into buffer */
+ memcpy(undo->text.data + b_offset, ta->text.data + b_start,
+ b_end - b_start);
+
+ /* Update next_detail */
+ undo->details[undo->next_detail].b_start = b_offset;
+ undo->details[undo->next_detail].b_end = b_offset + b_end - b_start;
+ undo->details[undo->next_detail].b_limit = len;
+
+ undo->details[undo->next_detail].b_text_start = b_start;
+
+ return true;
+}
+
+
+/**
* Replace text in a textarea, updating undo buffer.
*
* \param ta Textarea widget
@@ -1493,8 +1569,13 @@ static bool textarea_replace_text(struct textarea *ta, size_t b_start,
size_t b_end, const char *rep, size_t rep_len,
bool add_to_clipboard, int *byte_delta, struct rect *r)
{
- /* TODO: Make copy of any replaced text */
- if (b_start != b_end) {
+ if (!(b_start != b_end && rep_len == 0 && add_to_clipboard) &&
+ !(ta->flags & TEXTAREA_PASSWORD)) {
+ /* Not just copying to clipboard, and not a password field;
+ * Sort out undo buffer. */
+ if (textarea_copy_to_undo_buffer(ta, b_start, b_end,
+ rep_len) == false)
+ return false;
}
/* Replace the text in the textarea, and reflow it */
@@ -1503,12 +1584,63 @@ static bool textarea_replace_text(struct textarea *ta, size_t b_start,
return false;
}
- if (b_start == b_end && rep_len == 0) {
- /* Nothing changed at all */
- return true;
+ if (!(b_start != b_end && rep_len == 0 && add_to_clipboard) &&
+ !(ta->flags & TEXTAREA_PASSWORD)) {
+ /* Not just copying to clipboard, and not a password field;
+ * Update UNDO buffer */
+ ta->undo.details[ta->undo.next_detail].b_text_end =
+ b_end + *byte_delta;
+ ta->undo.last_detail = ta->undo.next_detail;
+ ta->undo.next_detail++;
}
- /* TODO: Update UNDO buffer */
+ return true;
+}
+
+
+/**
+ * Undo previous change.
+ *
+ * \param ta Textarea widget
+ * \param byte_delta Updated to change in byte count in textarea (ta->show)
+ * \param r Updated to area where redraw is required
+ * \return false if nothing to undo, true otherwise
+ */
+static bool textarea_undo(struct textarea *ta, int *byte_delta, struct rect *r)
+{
+ if (ta->flags & TEXTAREA_PASSWORD)
+ /* No undo for password fields */
+ return false;
+
+ if (ta->undo.next_detail == 0)
+ /* Nothing to undo */
+ return false;
+
+ /* TODO */
+
+ return true;
+}
+
+
+/**
+ * Redo previous undo.
+ *
+ * \param ta Textarea widget
+ * \param byte_delta Updated to change in byte count in textarea (ta->show)
+ * \param r Updated to area where redraw is required
+ * \return false if nothing to redo, true otherwise
+ */
+static bool textarea_redo(struct textarea *ta, int *byte_delta, struct rect *r)
+{
+ if (ta->flags & TEXTAREA_PASSWORD)
+ /* No REDO for password fields */
+ return false;
+
+ if (ta->undo.next_detail > ta->undo.last_detail)
+ /* Nothing to redo */
+ return false;
+
+ /* TODO */
return true;
}
@@ -1652,9 +1784,21 @@ struct textarea *textarea_create(const textarea_flags flags,
ret->scroll_y = 0;
ret->bar_x = NULL;
ret->bar_y = NULL;
+ ret->h_extent = setup->width;
+ ret->v_extent = setup->height;
ret->drag_start = 0;
ret->drag_info.type = TEXTAREA_DRAG_NONE;
+ ret->undo.details_alloc = 0;
+ ret->undo.next_detail = 0;
+ ret->undo.last_detail = 0;
+ ret->undo.details = NULL;
+
+ ret->undo.text.data = NULL;
+ ret->undo.text.alloc = 0;
+ ret->undo.text.len = 0;
+ ret->undo.text.utf8_len = 0;
+
ret->text.data = malloc(TA_ALLOC_STEP);
if (ret->text.data == NULL) {
@@ -1727,6 +1871,9 @@ void textarea_destroy(struct textarea *ta)
if (ta->flags & TEXTAREA_PASSWORD)
free(ta->password.data);
+ free(ta->undo.text.data);
+ free(ta->undo.details);
+
free(ta->text.data);
free(ta->lines);
free(ta);
@@ -2562,6 +2709,30 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
}
caret += byte_delta;
break;
+ case KEY_UNDO:
+ if (!textarea_undo(ta, &byte_delta, &r)) {
+ /* We consume the UNDO, even if we can't act
+ * on it. */
+ return true;
+ }
+ caret += byte_delta;
+ if (ta->sel_start != -1) {
+ textarea_clear_selection(ta);
+ }
+ redraw = true;
+ break;
+ case KEY_REDO:
+ if (!textarea_redo(ta, &byte_delta, &r)) {
+ /* We consume the REDO, even if we can't act
+ * on it. */
+ return true;
+ }
+ caret += byte_delta;
+ if (ta->sel_start != -1) {
+ textarea_clear_selection(ta);
+ }
+ redraw = true;
+ break;
default:
return false;
}