diff options
author | Tzafrir Cohen <tzafrir@cohens.org.il> | 2012-09-07 15:14:04 +0300 |
---|---|---|
committer | Tzafrir Cohen <tzafrir@cohens.org.il> | 2012-09-07 15:14:04 +0300 |
commit | d4d16198c2924b1085258c0b6562b562c7df3c29 (patch) | |
tree | d160aabd972fee403621b84cf1fc7848e98d45b9 /editbox.h |
geresh 0.6.3geresh-0.6.3upstream
Diffstat (limited to 'editbox.h')
-rw-r--r-- | editbox.h | 826 |
1 files changed, 826 insertions, 0 deletions
diff --git a/editbox.h b/editbox.h new file mode 100644 index 0000000..062c82f --- /dev/null +++ b/editbox.h @@ -0,0 +1,826 @@ +// Copyright (C) 2003 Mooffie <mooffie@typo.co.il> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + +#ifndef BDE_EDITBOX_H +#define BDE_EDITBOX_H + +#include <vector> +#include "directvect.h" + +#include "widget.h" +#include "bidi.h" +#include "transtbl.h" +#include "undo.h" +#include "point.h" + + +// End-of-paragraph type +enum eop_t { eopNone, eopUnix, eopDOS, eopMac, eopUnicode }; + +// A "Paragraph" is a fundamental text element in our editor widget, +// EditBox. EditBox stores the text in paragraphs. Paragraphs correspond +// to "lines" in traditional unix text editors, but we reserve the term +// "lines" to screen lines, that is, a single row on the screen. + +class Paragraph { + +public: + + typedef DirectVector<idx_t> IdxArray; + + unistring str; // the text itself + + // The text is wrapped into several lines to fit the screen width. + // Since this wrapping process is costly (calculating widths, etc), we + // store the resulting line breaks in "line_breaks" and re-apply the + // wrapping process only upon text modification. + + IdxArray line_breaks; + + // The end-of-paragraph character (CR, LF, etc) is not stored directly + // in "str", but we keep a record of it in "eop". + + eop_t eop; + + // "individual_base_dir" holds the directionality of the paragraph. It + // is updated when the text is modified. + + direction_t individual_base_dir; + + // "contextual_base_dir" holds the directionality of the paragraph + // taking into account its surrounding paragraphs (that is, its + // context). "individual_base_dir", too, is used in calculating it. + // EditBox uses this variable, mainly through is_rtl(), to reorder and + // render the text. + + direction_t contextual_base_dir; + +public: + + int breaks_count() const { return line_breaks.size(); } + direction_t base_dir() const { return contextual_base_dir; } + bool is_rtl() const { return contextual_base_dir == dirRTL; } + + Paragraph() { + line_breaks.push_back(0); + individual_base_dir = contextual_base_dir = dirN; + eop = eopNone; + } + + void determine_base_dir(diralgo_t dir_algo) + { + individual_base_dir = BiDi::determine_base_dir(str.begin(), + str.len(), dir_algo); + // If we're not asked to use the contextual algorithm, then + // calculating "contextual_base_dir", which is what EditBox looks + // at, is a simple assignment. + if (dir_algo != algoContextStrong && dir_algo != algoContextRTL) + contextual_base_dir = individual_base_dir; + } +}; + +#define MIN(x,y) ((x)<(y)?(x):(y)) +#define MAX(x,y) ((x)>(y)?(x):(y)) + +// EditBox is one widget that doesn't visually notify the user about +// changes in its state. Such chores are the responsibility of a +// higher-level class, like the status line or the editor. In order to let +// other objects know about state changes and various errors, EditBox let's +// them register themselves as "listeners". This concept is similar to +// Java's listeners and adapters. + +class EditBoxStatusListener { +public: + virtual void on_wrap_change() {} + virtual void on_modification_change() {} + virtual void on_selection_change() {} + virtual void on_position_change() {} + virtual void on_auto_indent_change() {} + virtual void on_auto_justify_change() {} + virtual void on_dir_algo_change() {} + virtual void on_alt_kbd_change() {} + virtual void on_formatting_marks_change() {} + virtual void on_read_only_change() {} + virtual void on_smart_typing_change() {} + virtual void on_rtl_nsm_change() {} + virtual void on_maqaf_change() {} + virtual void on_translation_mode_change() {} +}; + +class EditBoxErrorListener { +public: + virtual void on_read_only_error(unichar ch = 0) {} + virtual void on_no_selection_error() {} + virtual void on_no_alt_kbd_error() {} + virtual void on_no_translation_table_error() {} + virtual void on_cant_display_nsm_error() {} +}; + +// A CombinedLine object addresses a line. Remember that a "line" in our +// terminology is a screen line. Therefore CombineLine is a combination of +// a paragraph number (para) and a line number inside this paragraph +// (inner_line). + +class CombinedLine { + +public: + + int para; + int inner_line; + +public: + + CombinedLine() { + para = 0; + inner_line = 0; + } + CombinedLine(int para, int inner_line) { + this->para = para; + this->inner_line = inner_line; + } + CombinedLine(const CombinedLine &other) { + para = other.para; + inner_line = other.inner_line; + } + bool operator< (const CombinedLine &other) const { + return para < other.para + || (para == other.para && inner_line < other.inner_line); + } + bool operator== (const CombinedLine &other) const { + return para == other.para + && inner_line == other.inner_line; + } + bool operator> (const CombinedLine &other) const { + return !(*this < other + || *this == other); + } + void swap(CombinedLine &other) { + CombinedLine tmp = other; + other = *this; + *this = tmp; + } + + // Some operations, like calculating the difference between two lines, + // were meant be used on lines that are close together. the is_near() + // method helps us avoid this problem by telling us that the lines are + // far apart. (the exact number is unimportant, but it should be of + // the same magnitude as the window height.) + + bool is_near(const CombinedLine &other) const { + int diff = para - other.para; + return diff < 40 && diff > -40; + } +}; + +// EditBox is the central widget in our editor. It stores the text and does +// the actual editing. +// +// The text is stored as a vector of pointers to Paragraphs. Professional +// editors are not built that way. What I did here is not an effective +// data-structure for an editor. +// +// It's too late to "fix" this as it would require major modifications. +// This editor started as a tiny project and from the start I intended it +// for simple editing tasks, like composing mail messages and nothing more. +// +// Data Transfer: +// +// EditBox does not handle loading and saving files. This is the task of an +// I/O module. To allow other objects to access EditBox's textual data, we +// provide a data transfer interface: +// +// start_data_transfer(direction); +// transfer_data(buffer); +// end_data_transfer(); + +class Scrollbar; + +class EditBox : public Widget { + +public: + + // the following typedef's are shorthands for STL's types. There're + // all used by the reordering algorithm (BiDi related). + + typedef DirectVector<level_t> LevelsArray; + typedef Paragraph::IdxArray IdxArray; + typedef DirectVector<attribute_t> AttributeArray; + typedef DirectVector<int> IntArray; + + // EditBox supports two types of wrap (in addition to no-wrap): + // wrpAnywhere, that breaks lines even inside words; and + // wrpAtWhiteSpace, that doesn't break words (unless they're longer + // than the window width). + + enum WrapType { wrpOff, wrpAnywhere, wrpAtWhiteSpace }; + + // How to display the Hebrew maqaf? + // 1. mqfAsis ("as is") is used when the user has Unicode fonts -- they + // usually have maqaf. + // 2. 8-bit fonts don't have it, that's why we may have to convert it + // to ASCII's dash (mqfTransliterate). + // 3. mqfHighlighed makes the maqaf very visible, in case the user + // wants to know whether the document is dominated by maqafs. + + enum maqaf_display_t { mqfAsis, mqfTransliterated, mqfHighlighted }; + + // How to display RTL NSMs? + // 1. rtlnsmOff -- don't print them. + // 2. rtlnsmTransliterated -- use ASCII characters to represent them. + // 3. rtlnsmAsis -- if the user is lucky to have a system (terminal + + // font) that is capable of displaying Hebrew points, print them as-is. + + enum rtl_nsm_display_t { rtlnsmOff, rtlnsmTransliterated, rtlnsmAsis }; + + enum syn_hlt_t { synhltOff, synhltHTML, synhltEmail }; + + // In the update() method we draw only the region that needs to be + // redrawn. + // rgnCursor -- only position the cursor; rgnCurrent -- draw the + // current paragraph and position the cursor; rgnRange -- draw a range + // of paragraphs; argnAll -- draw all visible paragraphs. + + enum region { rgnNone = 0, rgnCursor = 1, rgnCurrent = 2, + rgnRange = 4, rgnAll = 8 }; + + enum data_transfer_direction { dataTransferIn, dataTransferOut }; + +protected: + + // for translating the next char typed. + static TranslationTable transtbl; + + // ASCII representations of various non-displayable characters and + // concepts, like line continuation. + static TranslationTable reprtbl; + + // Alternate keyboard -- usually the user's national language. + static TranslationTable altkbdtbl; + + EditBoxStatusListener *status_listener; + EditBoxErrorListener *error_listener; + +#define NOTIFY_CHANGE(event) \ + do { \ + if (status_listener) \ + status_listener->on_ ## event ## _change(); \ + } while (0) + +#define NOTIFY_ERROR(event) \ + do { \ + if (error_listener) \ + error_listener->on_ ## event ## _error(); \ + } while (0) + +#define NOTIFY_ERROR_ARG(event, arg) \ + do { \ + if (error_listener) \ + error_listener->on_ ## event ## _error(arg); \ + } while (0) + + // The text itself: a vector of pointers to Paragraphs + std::vector<Paragraph *> paragraphs; + + // The cursor position + Point cursor; + + UndoStack undo_stack; + unistring clipboard; + + // Margins make it easier for us to implement features such as line + // numbering. Currently EditBox only uses "margin_after" to reserve + // one column for the '$' character (the indicator that, in no-wrap + // mode, tells the user that only part of the line is shown), and for + // the cursor, when it stands past the last character in the line. + // + // Other editors call these "left_margin" and "right_margin", but since + // this is a BiDi editor, where paragraphs may be RTL oriented, I use + // "before" and "after" to mean "left" and "right", respectively, for + // LTR paragraphs, and "right" and "left", respectively, for RTL + // paragraphs. + + int margin_before; + int margin_after; + + // top_line refers to the first visible line in our window. + CombinedLine top_line; + + // The "selection" (shown highlighted when active) stretches from the + // curser position till this primary_mark. + Point primary_mark; + + // When the user moves the cursor vertically, the editor tries to + // reposition the cursor on the same visual column. This is not always + // possible, e.g. when that column would be inside a TAB segment, or + // when the new line is not long enough to contain this column. + // optinal_vis_colum holds the column on which the cursor _should_ + // stand. + int optimal_vis_column; + + // Has the buffer been modified? + bool modified; + + // justification_column holds the maximum line length to use when + // justifying paragraphs. + idx_t justification_column; + + // when rfc2646_trailing_space is true, we keep a trailing space on + // each line when justifying paragraphs. + bool rfc2646_trailing_space; + + // When the user moves the cursor past the top or the bottom of the window, + // EditBox scrolls scroll_step lines up or down. + int scroll_step; + + // update_region tells the update() method which part of the window to + // paint. it's a bitmask of 'enum region'. + int update_region; + // when drawing a range of paragraphs (rgnRange), update_from and + // update_to denote the region. + int update_from; + int update_to; + + // Modes + bool alt_kbd; // is the alternative keyboard active? (cf. altkbdtbl) + bool auto_justify; // is auto-justify mode active? + bool auto_indent; // whether to auto-indent lines. + bool translation_mode; // translate next char? (cf. transtbl) + bool read_only; // is the user allowed to make changes to the buffer? + bool smart_typing; // replace simple ASCII chars with fancy ones. + + // Display + WrapType wrap_type; + diralgo_t dir_algo; + int tab_width; + bool show_explicits; // display BiDi formatting codes? + bool show_paragraph_endings; // display paragraph endings? + bool show_tabs; + rtl_nsm_display_t rtl_nsm_display; + maqaf_display_t maqaf_display; + + syn_hlt_t syn_hlt; + bool underline_hlt; + + int non_interactive_text_width; + // on resize event, compare new width to old_width + // and wrap lines only if they're different. + int old_width; + + bool bidi_enabled; + + bool visual_cursor_movement; + + // Where the last edit operation took place? + Point last_modification; + + // We emulate some emacs commands (e.g. C-k) with these + // flags. When prev_command_type is cmdtpKill, we know + // we need to append to the clipboard. + enum CommandType { cmdtpUnknown, cmdtpKill }; + CommandType prev_command_type, current_command_type; + +private: + + struct _t_data_transfer { + bool in_transfer; + data_transfer_direction dir; + Point cursor_origin; + bool skip_undo; + bool clear_modified_flag; + bool at_eof; + bool prev_is_cr; + int ntransferred_out; + int ntransferred_out_max; + } data_transfer; + +public: + + void set_read_only(bool value); + bool is_read_only() const { return read_only; } + INTERACTIVE void toggle_read_only() { set_read_only(!is_read_only()); } + + void set_cursor_position(const Point &point); + void get_cursor_position(Point& point) const { point = cursor; } + + void set_maqaf_display(maqaf_display_t disp); + maqaf_display_t get_maqaf_display() const { return maqaf_display; } + INTERACTIVE void toggle_maqaf(); + INTERACTIVE void set_maqaf_display_transliterated() { + set_maqaf_display(mqfTransliterated); + } + INTERACTIVE void set_maqaf_display_highlighted() { + set_maqaf_display(mqfHighlighted); + } + INTERACTIVE void set_maqaf_display_asis() { + set_maqaf_display(mqfAsis); + } + + void set_smart_typing(bool val); + bool is_smart_typing() const { return smart_typing; } + INTERACTIVE void toggle_smart_typing() + { set_smart_typing(!is_smart_typing()); } + + bool set_rtl_nsm_display(rtl_nsm_display_t disp); + rtl_nsm_display_t get_rtl_nsm_display() const + { return rtl_nsm_display; } + INTERACTIVE void toggle_rtl_nsm(); + INTERACTIVE void set_rtl_nsm_off() { + set_rtl_nsm_display(rtlnsmOff); + } + INTERACTIVE void set_rtl_nsm_asis() { + set_rtl_nsm_display(rtlnsmAsis); + } + INTERACTIVE void set_rtl_nsm_transliterated() { + set_rtl_nsm_display(rtlnsmTransliterated); + } + + void set_formatting_marks(bool value); + bool has_formatting_marks() const { return show_paragraph_endings; } + INTERACTIVE void toggle_formatting_marks(); + + void set_tab_width(int value); + int get_tab_width() const { return tab_width; } + + void set_modified(bool value); + bool is_modified() const { return modified; } + + void set_auto_indent(bool value); + bool is_auto_indent() const { return auto_indent; } + INTERACTIVE void toggle_auto_indent() + { set_auto_indent(!is_auto_indent()); } + + void set_auto_justify(bool value); + bool is_auto_justify() const { return auto_justify; } + INTERACTIVE void toggle_auto_justify() + { set_auto_justify(!is_auto_justify()); } + + void set_justification_column(int value); + int get_justification_column() const { return justification_column; } + void set_rfc2646_trailing_space(bool value) { rfc2646_trailing_space = value; } + + void set_translation_table(const TranslationTable &tbl) { transtbl = tbl; } + void set_translation_mode(bool value); + bool in_translation_mode() const { return translation_mode; } + INTERACTIVE void set_translate_next_char() { set_translation_mode(true); } + + void set_scroll_step(int value); + int get_scroll_step() const { return scroll_step; } + + void set_wrap_type(WrapType value); + WrapType get_wrap_type() const { return wrap_type; } + INTERACTIVE void toggle_wrap(); + INTERACTIVE void set_wrap_type_at_white_space() { + set_wrap_type(wrpAtWhiteSpace); + } + INTERACTIVE void set_wrap_type_anywhere() { + set_wrap_type(wrpAnywhere); + } + INTERACTIVE void set_wrap_type_off() { + set_wrap_type(wrpOff); + } + void set_non_interactive_text_width(int cols) + { non_interactive_text_width = cols; } + + void set_dir_algo(diralgo_t value); + diralgo_t get_dir_algo() const { return dir_algo; } + INTERACTIVE void toggle_dir_algo(); + INTERACTIVE void set_dir_algo_unicode() { set_dir_algo(algoUnicode); } + INTERACTIVE void set_dir_algo_force_ltr() { set_dir_algo(algoForceLTR); } + INTERACTIVE void set_dir_algo_force_rtl() { set_dir_algo(algoForceRTL); } + INTERACTIVE void set_dir_algo_context_strong() { set_dir_algo(algoContextStrong); } + INTERACTIVE void set_dir_algo_context_rtl() { set_dir_algo(algoContextRTL); } + + void set_alt_kbd_table(const TranslationTable &tbl) { altkbdtbl = tbl; } + void set_alt_kbd(bool val); + bool get_alt_kbd() const { return alt_kbd; } + INTERACTIVE void toggle_alt_kbd(); + + void set_primary_mark(const Point &point); + void set_primary_mark() { set_primary_mark(cursor); } + void unset_primary_mark(); + bool is_primary_mark_set() const { return primary_mark.para != -1; } + bool has_selected_text() const { return is_primary_mark_set(); } + INTERACTIVE void toggle_primary_mark(); + + void set_repr_table(const TranslationTable &tbl) + { reprtbl = tbl; } + void set_status_listener(EditBoxStatusListener *obj) + { status_listener = obj; } + void set_error_listener(EditBoxErrorListener *obj) + { error_listener = obj; } + void set_undo_size_limit(size_t limit) + { undo_stack.set_size_limit(limit); } + void set_key_for_key_undo(bool value) + { undo_stack.set_merge(!value); } + bool is_key_for_key_undo() const + { return !undo_stack.is_merge(); } + INTERACTIVE void toggle_key_for_key_undo(); + void sync_scrollbar(Scrollbar *scrollbar); + + void enable_bidi(bool value); + bool is_bidi_enabled() const { return bidi_enabled; } + INTERACTIVE void toggle_bidi(); + + void set_syn_hlt(syn_hlt_t sh); + syn_hlt_t get_syn_hlt() const + { return syn_hlt; } + void detect_syntax(); + + INTERACTIVE void menu_set_syn_hlt_none(); + INTERACTIVE void menu_set_syn_hlt_html(); + INTERACTIVE void menu_set_syn_hlt_email(); + + void set_underline(bool v); + bool get_underline() const + { return underline_hlt; } + INTERACTIVE void toggle_underline(); + + void set_visual_cursor_movement(bool v); + bool get_visual_cursor_movement() const { return visual_cursor_movement; } + INTERACTIVE void toggle_visual_cursor_movement(); + +protected: + + int parags_count() const { return paragraphs.size(); } + + inline Paragraph *curr_para() { return paragraphs[cursor.para]; }; + +public: + + HAS_ACTIONS_MAP(EditBox, Widget); + HAS_BINDINGS_MAP(EditBox, Widget); + + /////////////////////////////////////////////////////////////////////// + // Initialization + /////////////////////////////////////////////////////////////////////// + + EditBox(); + virtual ~EditBox(); + void new_document(); + + /////////////////////////////////////////////////////////////////////// + // Data Transfer + /////////////////////////////////////////////////////////////////////// + +public: + + void start_data_transfer(data_transfer_direction dir, + bool new_document = false, + bool selection_only = false); + int transfer_data(unichar *buf, int len); + void end_data_transfer(); + +protected: + + int transfer_data_in(unichar *data, int len); + int transfer_data_out(unichar *buf, int len); + bool is_in_data_transfer() const; + + /////////////////////////////////////////////////////////////////////// + // Movement + /////////////////////////////////////////////////////////////////////// + +public: + + INTERACTIVE void move_forward_char(); + INTERACTIVE void move_backward_char(); + INTERACTIVE void move_next_line(); + INTERACTIVE void move_previous_line(); + INTERACTIVE void move_forward_page(); + INTERACTIVE void move_backward_page(); + INTERACTIVE void move_beginning_of_line(); + INTERACTIVE void move_end_of_line(); + INTERACTIVE void move_beginning_of_buffer(); + INTERACTIVE void move_end_of_buffer(); + INTERACTIVE void move_backward_word(); + INTERACTIVE void move_forward_word(); + INTERACTIVE void move_last_modification(); + INTERACTIVE void key_left(); + INTERACTIVE void key_right(); + INTERACTIVE void key_home(); + INTERACTIVE void center_line(); + void move_first_char(unichar ch); + bool search_forward(unistring str); + unichar get_current_char(); + void move_absolute_line(int line); + + // Visual cursor movement: + INTERACTIVE void move_forward_visual_char(); + INTERACTIVE void move_backward_visual_char(); + INTERACTIVE void move_beginning_of_visual_line(); + +protected: + + void post_horizontal_movement(); + void post_vertical_movement(); + bool is_at_end_of_buffer(); + bool is_at_beginning_of_buffer(); + bool is_at_beginning_of_word(); + void move_relative_line(int diff); + void add_rows_to_line(CombinedLine &line, int rows); + int lines_diff(CombinedLine L1, CombinedLine L2); + void scroll_to_cursor_line(); + int calc_inner_line(); + int calc_vis_column(); + void move_to_vis_column(int column); + void invalidate_optimal_vis_column() + { optimal_vis_column = -1; } + bool valid_optimal_vis_column() const + { return optimal_vis_column != -1; } + int get_effective_scroll_step() const + { return MAX(1, MIN(window_height() / 2 + 0 , scroll_step)); } + + // Visual cursor movement related: + bool is_at_end_of_screen_line(); + bool is_at_first_screen_line(); + bool is_at_last_screen_line(); + + /////////////////////////////////////////////////////////////////////// + // Modification + /////////////////////////////////////////////////////////////////////// + +public: + + INTERACTIVE void delete_paragraph(); + INTERACTIVE void cut_end_of_paragraph(); + INTERACTIVE void delete_forward_word(); + INTERACTIVE void delete_backward_word(); + INTERACTIVE void delete_forward_char(); + INTERACTIVE void delete_backward_char(); + void delete_text(int len, bool skip_undo = false, + unistring *alt_deleted = NULL); + void insert_char(unichar ch); + void insert_text(const unichar *str, int len, bool skip_undo = false); + void insert_text(const unistring &str, bool skip_undo = false) + { insert_text(str.begin(), str.len(), skip_undo); } + void replace_text(const unichar *str, int len, int delete_len, + bool skip_undo = false); + void replace_text(const unistring &str, int delete_len, + bool skip_undo = false) + { replace_text(str.begin(), str.len(), delete_len, skip_undo); } + INTERACTIVE void copy(); + INTERACTIVE void cut(); + INTERACTIVE void paste(); + INTERACTIVE void undo(); + INTERACTIVE void redo(); + INTERACTIVE void justify(); + INTERACTIVE void insert_maqaf(); + INTERACTIVE void toggle_eops(); + void set_eops(eop_t new_eop); + eop_t get_dominant_eop() { return paragraphs[0]->eop; } + INTERACTIVE void set_eops_unix() { set_eops(eopUnix); } + INTERACTIVE void set_eops_dos() { set_eops(eopDOS); } + INTERACTIVE void set_eops_mac() { set_eops(eopMac); } + INTERACTIVE void set_eops_unicode() { set_eops(eopUnicode); } + void log2vis(const char *options); + void key_enter(); + void key_dash(); + +protected: + + void post_modification(); + void post_para_modification(Paragraph &p); + void undo_op(UndoOp *opp); + void redo_op(UndoOp *opp); + void calc_contextual_dirs(int min_para, int max_para, bool update_display); + inline void set_contextual_dir(Paragraph &p, direction_t dir, + bool update_display); + int calc_distance(Point p1, Point p2); + void copy_text(int len, bool append = false); + void cut_or_copy(bool just_copy); + inline unichar get_eop_char(eop_t eop); + inline unichar get_curr_eop_char(); + + //////////////////////////////////////////////////////////////////////////// + // Rendering + //////////////////////////////////////////////////////////////////////////// +public: + + virtual void update(); + virtual void update_cursor() { request_update(rgnCursor); update(); } + virtual bool is_dirty() const { return update_region != rgnNone; } + virtual void invalidate_view() { request_update(rgnAll); } + void reformat(); + +protected: + + // get_char_width() returns the width of a characetr, but since + // this character may be part of a LAM-ALEF ligature, we need + // to keep state information between calls - wdstate. + struct wdstate { + bool may_start_lam_alef; + void clear() { + may_start_lam_alef = false; + } + wdstate() { + clear(); + } + }; + + int get_text_width() const; + // :TODO: try to make this inline. FreeBSD's compiler complains... + /*inline*/ + int get_char_width(unichar ch, int pos, + wdstate *stt = NULL, bool visual = false); + int get_str_width(const unichar *str, idx_t len, bool visual = false); + int get_rev_str_width(const unichar *str, idx_t len); + void wrap_para(Paragraph ¶); + void rewrap_all(); + void request_update(region rgn); + void request_update(int lo, int hi); + + struct _t_cache { + int owner_para; + LevelsArray levels; + IdxArray position_L_to_V; + IdxArray position_V_to_L; + AttributeArray attributes; + unistring vis; + + void invalidate() { + owner_para = -1; + } + bool owned_by(int para) { + return owner_para == para; + } + void set_owner(int para) { + owner_para = para; + } + } cache; + + virtual void redraw_paragraph( + Paragraph &p, + int window_start_line, + bool only_cursor, + int para_num + ); + + void EditBox::redraw_unwrapped_paragraph( + Paragraph &p, + int window_start_line, + bool only_cursor, + int para_num, + LevelsArray &levels, + IdxArray& position_L_to_V, + IdxArray& position_V_to_L, + AttributeArray& attributes, + bool eop_is_selected + ); + + void EditBox::redraw_wrapped_paragraph( + Paragraph &p, + int window_start_line, + bool only_cursor, + int para_num, + LevelsArray &levels, + IdxArray& position_L_to_V, + IdxArray& position_V_to_L, + AttributeArray& attributes, + bool eop_is_selected + ); + + void draw_unistr(const unichar *str, idx_t len, + attribute_t *attributes = NULL, int *tab_widths = NULL); + void calc_tab_widths(const unichar *str, idx_t len, + bool rev, IntArray &tab_widths); + void draw_rev_unistr(const unichar *str, idx_t len, + attribute_t *attributes = NULL); + unichar get_char_repr(unichar ch); + void put_unichar_attr_at(int line, int col, unichar ch, int attr); + void draw_eop(int y, int x, Paragraph &p, bool selected); + virtual void do_syntax_highlight(const unistring &str, + AttributeArray &attributes, int para_num); + + /////////////////////////////////////////////////////////////////////// + // Misc + /////////////////////////////////////////////////////////////////////// + +public: + + virtual bool handle_event(const Event &evt); + virtual void resize(int lines, int columns, int y, int x); + +public: + + int get_number_of_paragraphs() const + { return parags_count(); } + const unistring &get_paragraph_text(int i) { + return paragraphs[i]->str; + } +}; + +#endif + |