On Tue, 2009-06-23 at 12:13 +0100, John-Mark Bell wrote:
> Index: desktop/textarea.c
> ===================================================================
> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
> +++ desktop/textarea.c 2009-06-23 11:58:18.000000000 +0100
> @@ -0,0 +1,1258 @@
> +/*
> + * Copyright 2006 John-Mark Bell <[email protected]>
You should add your details here. Also, change my email address to this
one.
> +#include <stdint.h>
> +#include <string.h>
> +#include "css/css.h"
> +#include "desktop/textarea.h"
> +#include "desktop/textinput.h"
> +#include "desktop/plotters.h"
> +#include "render/font.h"
> +#include "utils/log.h"
> +#include "utils/utf8.h"
> +#include "utils/utils.h"
> +
> +#define MARGIN_LEFT 2
> +#define MARGIN_RIGHT 2
> +
> +struct line_info {
> + int b_start; /**< Byte offset of line start */
> + int b_length; /**< Byte length of line */
> +};
> +
> +struct text_area {
> +
> + int x, y; /**< Coordinates of the widget
> + (top left corner) */
What are these with respect to?
> + int scroll_x, scroll_y;
> +
> + unsigned int flags; /**< Textarea flags */
> + int vis_width; /**< Visible width, in pixels */
> + int vis_height; /**< Visible height, in pixels */
Please fix the comment alignment here.
> + char *text; /**< UTF-8 text */
> + unsigned int text_alloc; /**< Size of allocated text */
> + unsigned int text_len; /**< Length of text, in bytes */
If these are unsigned, all the indices into the text should be, too.
> + struct {
> + int line; /**< Line caret is on */
> + int char_off; /**< Character index of caret */
> + } caret_pos;
> +
> + int selection_start; /**< Character index of sel start(inclusive) */
> + int selection_end; /**< Character index of sel end(exclusive) */
> +
> + struct css_style *style; /**< Text style */
> +
> + int line_count; /**< Count of lines */
> +#define LINE_CHUNK_SIZE 256
This seems rather large.
> + struct line_info *lines; /**< Line info array */
> +
> + /** Callback functions for a redraw request*/
> + textarea_start_radraw_callback redraw_start_callback;
> + textarea_start_radraw_callback redraw_end_callback;
s/radraw/redraw/
> + intptr_t data; /** < Callback data for both callback functions*/
Is there a good reason for this to be an intptr_t? What's wrong with a
plain void *?
> + int drag_start_char; /**< Character index at which the drag was
> started*/
> +};
> +
> +
> +static void textarea_insert_text(struct text_area *ta, unsigned int index,
> + const char *text);
> +static void textarea_replace_text(struct text_area *ta, unsigned int start,
> + unsigned int end, const char *text);
Style: continuation lines should be indented 2 tabs.
> +static void textarea_reflow(struct text_area *ta, unsigned int line);
> +static int textarea_get_xy_offset(struct text_area *ta, int x, int y);
> +static void textarea_set_caret_xy(struct text_area *ta, int x, int y);
> +static bool textarea_scroll_visible(struct text_area *ta);
> +static void textarea_select(struct text_area *ta, int c_start, int c_end);
> +static void textarea_normalise_text(struct text_area *ta,
> + unsigned int b_start, unsigned int b_len);
> +// static bool textarea_mouse_click(wimp_pointer *pointer);
Is this redundant? If so, remove it.
> +
> +/**
> + * Create a text area
> + *
> + * \param x X coordinate of left border
> + * \param y Y coordinate of top border
> + * \param width width of the text area
> + * \param height width of the text area
> + * \param flags Text area flags
> + * \param font_style Font style to use, or 0 for default
> + * \return Opaque handle for textarea or 0 on error
> + */
Please synchronise this comment with the implementation.
> +struct text_area *textarea_create(int x, int y, int width, int height,
> + unsigned int flags, const struct css_style *style,
> + textarea_start_radraw_callback redraw_start_callback,
> + textarea_end_radraw_callback redraw_end_callback, intptr_t data)
> +{
> + struct text_area *ret;
> +
> + if (!redraw_start_callback || !redraw_end_callback) {
> + LOG(("no callback provided"));
> + return 0;
> + }
I'd prefer to see if (foo == NULL), rather than if (!foo), please.
Also, return NULL, not 0.
> + ret = malloc(sizeof(struct text_area));
> + if (!ret) {
> + LOG(("malloc failed"));
> + return 0;
NULL.
> + }
> +
> + ret->redraw_start_callback = redraw_start_callback;
> + ret->redraw_end_callback = redraw_end_callback;
> + ret->data = data;
> + ret->x = x;
> + ret->y = y;
> + ret->vis_width = width;
> + ret->vis_height = height;
> + ret->scroll_x = 0;
> + ret->scroll_y = 0;
> + ret->drag_start_char = 0;
> +
> +
> + ret->flags = flags;
> + ret->text = malloc(64);
> + if (!ret->text) {
> + LOG(("malloc failed"));
> + free(ret);
> + return 0;
NULL.
> + }
> + ret->text[0] = '\0';
> + ret->text_alloc = 64;
> + ret->text_len = 1;
> +
> + ret->style = malloc(sizeof(struct css_style));
> + if (!ret->style) {
> + LOG(("malloc failed"));
> + free(ret->text);
> + free(ret);
> + return 0;
> + }
> + memcpy(ret->style, style, sizeof(struct css_style));
> +
> + ret->caret_pos.line = ret->caret_pos.char_off = 0;
> + ret->selection_start = -1;
> + ret->selection_end = -1;
> +
> + ret->line_count = 0;
> + ret->lines = 0;
> +
> + return (struct text_area *)ret;
This cast is redundant.
> +}
> +
> +
> +/**
> + * Destroy a text area
> + *
> + * \param ta Text area to destroy
> + */
> +void textarea_destroy(struct text_area *ta)
> +{
> + free(ta->text);
> + free(ta->style);
> + free(ta);
> +}
What about freeing the lines array?
> +/**
> + * Insert text into the text area
> + *
> + * \param ta Text area
> + * \param index 0-based character index to insert at
> + * \param text UTF-8 text to insert
> + */
> +void textarea_insert_text(struct text_area *ta, unsigned int index,
> + const char *text)
This should report memory exhaustion.
> +{
> + unsigned int b_len = strlen(text);
> + size_t b_off, c_len;
> +
> + if (ta-> flags & TEXTAREA_READONLY)
Lose the space before flags.
> +/**
> + * Replace text in a text area
> + *
> + * \param ta Text area
> + * \param start Start character index of replaced section (inclusive)
> + * \param end End character index of replaced section (exclusive)
> + * \param text UTF-8 text to insert
> + */
> +void textarea_replace_text(struct text_area *ta, unsigned int start,
> + unsigned int end, const char *text)
Again, should report memory exhaustion.
> +{
> + int b_len = strlen(text);
> + size_t b_start, b_end, c_len, diff;
> +
> + if (ta-> flags & TEXTAREA_READONLY)
> + return;
> +
> + c_len = utf8_length(ta->text);
> +
> + if (start > c_len)
> + start = c_len;
> + if (end > c_len)
> + end = c_len;
> +
> + if (start == end)
> + return textarea_insert_text(ta, start, text);
> +
> + if (start > end) {
> + int temp = end;
> + end = start;
> + start = temp;
> + }
Might it be better to just give up here and tell the client they've got
their parameters the wrong way around?
> +
> + diff = end - start;
> +
> + for (b_start = 0; start-- > 0;
> + b_start = utf8_next(ta->text, ta->text_len, b_start))
> + ; /* do nothing */
> +
> + for (b_end = b_start; diff-- > 0;
> + b_end = utf8_next(ta->text, ta->text_len, b_end))
> + ; /* do nothing */
These loops want documenting.
> +/**
> + * Set the caret's position
> + *
> + * \param ta Text area
> + * \param caret 0-based character index to place caret at
> + */
> +void textarea_set_caret(struct text_area *ta, int caret)
> +{
> + int c_len;
> + int b_off;
> + int i;
> + int index;
> + int x, y;
> + int x0, y0, x1, y1;
> + int height;
> +
> +
> + if (ta-> flags & TEXTAREA_READONLY)
> + return;
> +
> + ta->redraw_start_callback(ta->data);
> +
> + c_len = utf8_length(ta->text);
We seem to be calculating the utf8 length of the text almost everywhere.
Would it be better to cache this?
> +/**
> + * get character offset from the beginning of the text for some coordinates
> + *
> + * \param ta Text area
> + * \param x X coordinate
> + * \param y Y coordinate
> + * \return character offset
> + */
> +static int textarea_get_xy_offset(struct text_area *ta, int x, int y)
> +{
> + size_t b_off, c_off, temp;
> + int line_height;
> + int line;
> +
> + if (!ta->line_count)
> + return 0;
> +
> + line_height = css_len2px(&(ta->style->line_height.value.length),
> + ta->style);
> +
> + x = x - ta->x - MARGIN_LEFT + ta->scroll_x;
> + y = y - ta->y + ta->scroll_y;
> +
> + if (x < 0)
> + x = 0;
> +
> + line = y / line_height;
> +
> + if (line < 0)
> + line = 0;
> + if (ta->line_count - 1 < line)
> + line = ta->line_count - 1;
> +
> + nsfont.font_position_in_string(ta->style,
> + ta->text + ta->lines[line].b_start,
> + ta->lines[line].b_length, x, &b_off, &x);
> +
> +
> + for (temp = 0, c_off = 0; temp < b_off + ta->lines[line].b_start;
> + temp = utf8_next(ta->text, ta->text_len, temp))
> + c_off++;
> +
> + /* if the offset is a space at the end of the line set the caret before
> + it otherwise the caret will go on the beginning of the next line
> + */
> + if (b_off == (unsigned)ta->lines[line].b_length &&
> + ta->text[ta->lines[line].b_start +
> + ta->lines[line].b_length - 1] == ' ')
> + c_off--;
This seems odd. Can you explain why it's needed?
> +/**
> + * Set the caret's position
> + *
> + * \param ta Text area
> + * \param x X position of caret in a window
> + * \param y Y position of caret in a window
Presumably, these are relative to the top-left of the text area?
> + */
> +void textarea_set_caret_xy(struct text_area *ta, int x, int y)
> +{
> + int c_off;
> +
> + if (ta->flags & TEXTAREA_READONLY)
> + return;
> +
> + c_off = textarea_get_xy_offset(ta, x, y);
> + textarea_set_caret(ta, c_off);
> +}
> +
> +
> +/**
> + * Get the caret's position
> + *
> + * \param ta Text area
> + * \return 0-based character index of caret location, or -1 on error
> + */
> +int textarea_get_caret(struct text_area *ta)
> +{
> + int c_off = 0, b_off;
> +
> + if (ta->text_len == 1)
> + return 0;
Please document why this is needed.
> +/**
> + * Reflow a text area from the given line onwards
> + *
> + * \param ta Text area to reflow
> + * \param line Line number to begin reflow on
> + */
> +void textarea_reflow(struct text_area *ta, unsigned int line)
This should report memory exhaustion. Anything that calls this should
check for that. Please ensure the code in this function is wrapped at 80
columns.
> +/**
> + * Handle redraw requests for text areas
> + *
> + * \param redraw Redraw request block
> + */
> +void textarea_redraw(struct text_area *ta, int x0, int y0, int x1, int y1)
> +{
> + plot.clip(x0, y0, x1, y1);
> + plot.fill(x0, y0, x1, y1, (ta->flags & TEXTAREA_READONLY) ?
> + 0xD9D9D9 : 0xFFFFFF);
What are these magic numbers? Please #define them somewhere.
> + plot.rectangle(ta->x, ta->y, ta->vis_width - 1, ta->vis_height - 1, 1,
> + 0x000000, false, false);
Similarly.
> + plot.fill(x0 - ta->scroll_x, ta->y + line * line_height
> + 1 - ta->scroll_y,
> + x1 - ta->scroll_x, ta->y + (line + 1) *
> line_height - 1 - ta->scroll_y,
> + 0xFFDDDD);
Again, magic number.
> + plot.text(ta->x + MARGIN_LEFT - ta->scroll_x, y0 -
> ta->scroll_y, ta->style,
> + ta->text + ta->lines[line].b_start,
> + ta->lines[line].b_length,
> + (ta->flags & TEXTAREA_READONLY) ?
> + 0xD9D9D9 : 0xFFFFFF,
> + 0x000000);
Similarly.
> + }
> +}
> +
> +/**
> + * Key press handling for text areas.
> + *
> + * \param ta The text area which got the keypress
> + * \param key The ucs4 character codepoint
> + * \return true if the keypress is dealt with, false otherwise.
> + */
> +bool textarea_keypress(struct text_area *ta, uint32_t key)
Ensure code in this function is wrapped at 80 columns.
> + case KEY_COPY_SELECTION:
> + case KEY_TAB:
> + case KEY_SHIFT_TAB:
> + case KEY_CR:
> + case KEY_CUT_LINE:
> + case KEY_PASTE:
> + case KEY_CUT_SELECTION:
> + case KEY_ESCAPE:
> + case KEY_WORD_LEFT:
> + case KEY_WORD_RIGHT:
Presumably, these need implementing? At the very least, should they not
just be handled by the default case?
> +/**
> + * Removes any CR characters and changes newlines into spaces if it is a
> single
> + * line textarea.
> + *
> + * \param ta Text area
> + * \param b_start Byte offset to start at
> + * \param b_len Byte length to check
> + */
> +void textarea_normalise_text(struct text_area *ta,
> + unsigned int b_start, unsigned int b_len)
> +{
> + bool multi = (ta->flags & TEXTAREA_MULTILINE) ? true:false;
> + unsigned int index;
> +
> + /*remove CR characters*/
> + for (index = 0; index < b_len; index++) {
> + if (ta->text[b_start + index] == '\r') {
> + if (b_start + index + 1 <= ta->text_len &&
> + ta->text[b_start + index + 1] == '\n') {
> + ta->text_len--;
> + memmove(ta->text + b_start + index,
> + ta->text + b_start + index + 1,
> + ta->text_len - b_start - index);
This is CRLF -> LF, right?
> + }
> + else
> + ta->text[b_start + index] = '\n';
CR -> LF.
> Index: desktop/textarea.h
> ===================================================================
> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
> +++ desktop/textarea.h 2009-06-23 11:58:18.000000000 +0100
> @@ -0,0 +1,61 @@
> +/*
> + *
Who wrote this file?
> +/** \file
> + * Single/Multi-line UTF-8 text area (interface)
> + */
> +
> +#ifndef _NETSURF_DESKTOP_TEXTAREA_H_
> +#define _NETSURF_DESKTOP_TEXTAREA_H_
> +
> +#include <stdint.h>
> +#include <stdbool.h>
> +#include "css/css.h"
> +#include "desktop/browser.h"
> +
> +/* Text area flags */
> +#define TEXTAREA_MULTILINE 0x01 /**< Text area is multiline */
> +#define TEXTAREA_READONLY 0x02 /**< Text area is read only */
> +
> +struct text_area;
> +
> +/* this is temporary only*/
> +browser_mouse_state textarea_mouse_state;
> +int textarea_pressed_x;
> +int textarea_pressed_y;
If this is temporary, it should go.
> +typedef void(*textarea_start_radraw_callback)(intptr_t data);
> +typedef void(*textarea_end_radraw_callback)(intptr_t data);
s/radraw/redraw/
> Index: gtk/gtk_window.h
> ===================================================================
> --- gtk/gtk_window.h (revision 7935)
> +++ gtk/gtk_window.h (working copy)
> @@ -76,6 +76,9 @@
> int nsgtk_gui_window_update_targets(struct gui_window *g);
> void nsgtk_window_destroy_browser(struct gui_window *g);
>
> +/*TODO: find a better place for this?*/
> +uint32_t gdkkey_to_nskey(GdkEventKey *);
Yes, probably. If not, at least namespace it correctly.
> Index: gtk/gtk_plotters.c
> ===================================================================
> --- gtk/gtk_plotters.c (revision 7935)
> +++ gtk/gtk_plotters.c (working copy)
> @@ -119,7 +119,7 @@
> line_width = 1;
>
> cairo_set_line_width(current_cr, line_width);
> - cairo_rectangle(current_cr, x0, y0, width, height);
> + cairo_rectangle(current_cr, x0 + 0.5, y0 + 0.5, width, height);
This seems orthogonal to the textarea changes.
> Index: gtk/gtk_scaffolding.c
> ===================================================================
> --- gtk/gtk_scaffolding.c (revision 7935)
> +++ gtk/gtk_scaffolding.c (working copy)
The changes in this file look like test code for the textarea. They
should probably disappear.
> Index: desktop/textinput.h
> ===================================================================
> --- desktop/textinput.h (revision 7935)
> +++ desktop/textinput.h (working copy)
> @@ -28,7 +28,8 @@
>
> #include <stdbool.h>
>
> -struct browser_window;
> +#include "desktop/browser.h"
> +
Why this change?
J.