note description: "Objects that ..." legal: "See notice at end of class." status: "See notice at end of class." author: "" date: "$Date$" revision: "$Revision$" class EV_RICH_TEXT_IMP inherit EV_RICH_TEXT_I undefine text_length, selected_text redefine interface, next_change_of_character end EV_TEXT_IMP redefine interface, make, on_key_event, initialize_buffer_events end create make feature {NONE} -- Initialization make -- Set up `Current' do create temp_start_iter.make create temp_end_iter.make create tab_positions Precursor {EV_TEXT_IMP} tab_positions.internal_add_actions.extend (agent update_tab_positions) tab_positions.internal_remove_actions.extend (agent update_tab_positions) pango_tab_array := {GTK2}.pango_tab_array_new (1, True) {GTK2}.gtk_text_view_set_tabs (text_view, pango_tab_array) set_tab_width (96 // 2) end initialize_buffer_events -- Initialize `text_buffer' events do Precursor {EV_TEXT_IMP} real_signal_connect (text_buffer, "mark_set", agent (app_implementation.gtk_marshal).text_buffer_mark_set_intermediary (object_id, ?, ?), Void) end feature {NONE} -- Implementation next_change_of_character (current_pos: INTEGER; a_text_length: INTEGER): INTEGER local character_change: INTEGER_REF range_info: EV_CHARACTER_FORMAT_RANGE_INFORMATION do character_change := 0 range_info := internal_character_format_range_information (current_pos, a_text_length + 1, True, character_change) Result := character_change end initialize_for_saving -- Initialize `Current' for save operations, by performing -- optimizations that prevent the control from slowing down due to -- unecessary optimizations. do -- Do nothing end complete_saving -- Restore `Current' back to its default state before last call -- to `initialize_for_saving'. do -- Do nothing end initialize_for_loading -- Initialize `Current' for load operations, by performing -- optimizations that prevent the control from slowing down due to -- unecessary optimizations. do -- Do nothing end complete_loading -- Restore `Current' back to its default state before last call -- to `initialize_for_loading'. do -- Do nothing end font_char_set (a_font: EV_FONT): INTEGER -- `Result' is char set of font `a_font'. do Result := 0 end feature -- Status Report format_paragraph (start_line, end_line: INTEGER; format: EV_PARAGRAPH_FORMAT) -- Apply paragraph formatting `format' to lines `start_line', `end_line' inclusive. local format_imp: detachable EV_PARAGRAPH_FORMAT_IMP do format_imp ?= format.implementation check format_imp /= Void then end modify_paragraph_internal (start_line, end_line, format_imp, format_imp.dummy_paragraph_format_range_information) end character_format_range_information (start_index, end_index: INTEGER): EV_CHARACTER_FORMAT_RANGE_INFORMATION -- Formatting range information from caret position `start_index' to `end_index'. -- All attributes in `Result' are set to `True' if they remain consitent from `start_index' to --`end_index' and `False' otherwise. -- `Result' is a snapshot of `Current', and does not remain consistent as the contents -- are subsequently changed. do Result := internal_character_format_range_information (start_index, end_index, False, Void) end internal_character_format_range_information (start_index, end_index: INTEGER; abort_on_change: BOOLEAN; change_index: detachable INTEGER_REF): EV_CHARACTER_FORMAT_RANGE_INFORMATION -- Formatting range information from caret position `start_index' to `end_index'. -- All attributes in `Result' are set to `True' if they remain consistent from `start_index' to --`end_index' and `False' otherwise. -- `Result' is a snapshot of `Current', and does not remain consistent as the contents -- are subsequently changed. local a_character_index: INTEGER a_text_iter: POINTER a_text_attributes: POINTER previous_text_attributes: POINTER previous_font_family, font_family: STRING_32 non_contiguous_range_information: INTEGER font_family_contiguous: BOOLEAN a_change: BOOLEAN exit_loop: BOOLEAN do from font_family_contiguous := True a_character_index := start_index + 1 a_text_iter := a_text_iter.memory_alloc ({EV_GTK_TEXT_ITER_STRUCT}.structure_size) {GTK2}.gtk_text_buffer_get_iter_at_offset (text_buffer, a_text_iter.item, start_index - 1) previous_text_attributes := gtk_text_view_get_default_attributes (text_view) a_change := gtk_text_iter_get_attributes (a_text_iter.item, previous_text_attributes) a_text_attributes := gtk_text_view_get_default_attributes (text_view) create previous_font_family.make_from_c ({GTK2}.pango_font_description_get_family (gtk_text_attributes_struct_font_description (previous_text_attributes))) until exit_loop or else a_character_index = end_index loop {GTK2}.gtk_text_buffer_get_iter_at_offset (text_buffer, a_text_iter.item, a_character_index - 1) a_text_attributes := gtk_text_attributes_copy (previous_text_attributes) a_change := gtk_text_iter_get_attributes (a_text_iter.item, a_text_attributes) if font_family_contiguous then create font_family.make_from_c ({GTK2}.pango_font_description_get_family (gtk_text_attributes_struct_font_description (a_text_attributes))) if font_family.hash_code /= previous_font_family.hash_code then non_contiguous_range_information := non_contiguous_range_information.bit_or ({EV_CHARACTER_FORMAT_CONSTANTS}.font_family) font_family_contiguous := False end previous_font_family := font_family end if {GTK2}.pango_font_description_get_style (gtk_text_attributes_struct_font_description (a_text_attributes)) /= {GTK2}.pango_font_description_get_style (gtk_text_attributes_struct_font_description (previous_text_attributes)) then non_contiguous_range_information := non_contiguous_range_information.bit_or ({EV_CHARACTER_FORMAT_CONSTANTS}.font_shape) end if {GTK2}.pango_font_description_get_weight (gtk_text_attributes_struct_font_description (a_text_attributes)) /= {GTK2}.pango_font_description_get_weight (gtk_text_attributes_struct_font_description (previous_text_attributes)) then non_contiguous_range_information := non_contiguous_range_information.bit_or ({EV_CHARACTER_FORMAT_CONSTANTS}.font_weight) end if {GTK2}.pango_font_description_get_size (gtk_text_attributes_struct_font_description (a_text_attributes)) /= {GTK2}.pango_font_description_get_size (gtk_text_attributes_struct_font_description (previous_text_attributes)) then non_contiguous_range_information := non_contiguous_range_information.bit_or ({EV_CHARACTER_FORMAT_CONSTANTS}.font_height) end if {GTK}.gdk_color_struct_red (gtk_text_appearance_struct_fg_color (gtk_text_attributes_struct_text_appearance (a_text_attributes))) /= {GTK}.gdk_color_struct_red (gtk_text_appearance_struct_fg_color (gtk_text_attributes_struct_text_appearance (previous_text_attributes))) or else {GTK}.gdk_color_struct_green (gtk_text_appearance_struct_fg_color (gtk_text_attributes_struct_text_appearance (a_text_attributes))) /= {GTK}.gdk_color_struct_green (gtk_text_appearance_struct_fg_color (gtk_text_attributes_struct_text_appearance (previous_text_attributes))) or else {GTK}.gdk_color_struct_blue (gtk_text_appearance_struct_fg_color (gtk_text_attributes_struct_text_appearance (a_text_attributes))) /= {GTK}.gdk_color_struct_blue (gtk_text_appearance_struct_fg_color (gtk_text_attributes_struct_text_appearance (previous_text_attributes))) then non_contiguous_range_information := non_contiguous_range_information.bit_or ({EV_CHARACTER_FORMAT_CONSTANTS}.color) end if {GTK}.gdk_color_struct_red (gtk_text_appearance_struct_bg_color (gtk_text_attributes_struct_text_appearance (a_text_attributes))) /= {GTK}.gdk_color_struct_red (gtk_text_appearance_struct_bg_color (gtk_text_attributes_struct_text_appearance (previous_text_attributes))) or else {GTK}.gdk_color_struct_green (gtk_text_appearance_struct_bg_color (gtk_text_attributes_struct_text_appearance (a_text_attributes))) /= {GTK}.gdk_color_struct_green (gtk_text_appearance_struct_bg_color (gtk_text_attributes_struct_text_appearance (previous_text_attributes))) or else {GTK}.gdk_color_struct_blue (gtk_text_appearance_struct_bg_color (gtk_text_attributes_struct_text_appearance (a_text_attributes))) /= {GTK}.gdk_color_struct_blue (gtk_text_appearance_struct_bg_color (gtk_text_attributes_struct_text_appearance (previous_text_attributes))) then non_contiguous_range_information := non_contiguous_range_information.bit_or ({EV_CHARACTER_FORMAT_CONSTANTS}.background_color) end if gtk_text_appearance_struct_strikethrough (gtk_text_attributes_struct_text_appearance (a_text_attributes)) /= gtk_text_appearance_struct_strikethrough (gtk_text_attributes_struct_text_appearance (previous_text_attributes)) then non_contiguous_range_information := non_contiguous_range_information.bit_or ({EV_CHARACTER_FORMAT_CONSTANTS}.effects_striked_out) end if gtk_text_appearance_struct_underline (gtk_text_attributes_struct_text_appearance (a_text_attributes)) /= gtk_text_appearance_struct_underline (gtk_text_attributes_struct_text_appearance (previous_text_attributes)) then non_contiguous_range_information := non_contiguous_range_information.bit_or ({EV_CHARACTER_FORMAT_CONSTANTS}.effects_underlined) end if gtk_text_appearance_struct_rise (gtk_text_attributes_struct_text_appearance (a_text_attributes)) /= gtk_text_appearance_struct_rise (gtk_text_attributes_struct_text_appearance (a_text_attributes)) then non_contiguous_range_information := non_contiguous_range_information.bit_or ({EV_CHARACTER_FORMAT_CONSTANTS}.effects_vertical_offset) end gtk_text_attributes_free (previous_text_attributes) previous_text_attributes := a_text_attributes a_character_index := a_character_index + 1 if abort_on_change and then non_contiguous_range_information > 0 then non_contiguous_range_information := non_contiguous_range_information.bit_or ({EV_CHARACTER_FORMAT_CONSTANTS}.font_family) exit_loop := True end end gtk_text_attributes_free (previous_text_attributes) a_text_iter.memory_free create Result.make_with_flags ((511).bit_xor (non_contiguous_range_information)) -- 511 is the mask value for character format constants if change_index /= Void then change_index.set_item (a_character_index - 1) -- We take off one as the change occurs before `character_index' is incremented at end of loop end end paragraph_format_range_information (start_index, end_index: INTEGER): EV_PARAGRAPH_FORMAT_RANGE_INFORMATION -- Formatting range information from caret position `start_index' to `end_index'. -- All attributes in `Result' are set to `True' if they remain consitent from `start_index' to --`end_index' and `False' otherwise. -- `Result' is a snapshot of `Current', and does not remain consistent as the contents -- are subsequently changed. local a_character_index: INTEGER a_text_iter: EV_GTK_TEXT_ITER_STRUCT a_text_attributes: POINTER previous_text_attributes: POINTER non_contiguous_range_information: INTEGER font_family_contiguous: BOOLEAN a_change: BOOLEAN do from font_family_contiguous := True a_character_index := start_index + 1 create a_text_iter.make {GTK2}.gtk_text_buffer_get_iter_at_offset (text_buffer, a_text_iter.item, start_index - 1) previous_text_attributes := gtk_text_view_get_default_attributes (text_view) a_change := gtk_text_iter_get_attributes (a_text_iter.item, previous_text_attributes) until a_character_index > end_index loop create a_text_iter.make {GTK2}.gtk_text_buffer_get_iter_at_offset (text_buffer, a_text_iter.item, a_character_index - 2) a_text_attributes := gtk_text_view_get_default_attributes (text_view) a_change := gtk_text_iter_get_attributes (a_text_iter.item, a_text_attributes) if gtk_text_attributes_struct_justification (a_text_attributes) /= gtk_text_attributes_struct_justification (previous_text_attributes) then non_contiguous_range_information := non_contiguous_range_information.bit_or ({EV_PARAGRAPH_CONSTANTS}.alignment) end if gtk_text_attributes_struct_left_margin (a_text_attributes) /= gtk_text_attributes_struct_left_margin (previous_text_attributes) then non_contiguous_range_information := non_contiguous_range_information.bit_or ({EV_PARAGRAPH_CONSTANTS}.left_margin) end if gtk_text_attributes_struct_right_margin (a_text_attributes) /= gtk_text_attributes_struct_right_margin (previous_text_attributes) then non_contiguous_range_information := non_contiguous_range_information.bit_or ({EV_PARAGRAPH_CONSTANTS}.right_margin) end if gtk_text_attributes_struct_pixels_above_lines (a_text_attributes) /= gtk_text_attributes_struct_pixels_above_lines (previous_text_attributes) then non_contiguous_range_information := non_contiguous_range_information.bit_or ({EV_PARAGRAPH_CONSTANTS}.top_spacing) end if gtk_text_attributes_struct_pixels_below_lines (a_text_attributes) /= gtk_text_attributes_struct_pixels_below_lines (previous_text_attributes) then non_contiguous_range_information := non_contiguous_range_information.bit_or ({EV_PARAGRAPH_CONSTANTS}.bottom_spacing) end gtk_text_attributes_free (previous_text_attributes) previous_text_attributes := a_text_attributes a_character_index := a_character_index + 1 end gtk_text_attributes_free (previous_text_attributes) create Result.make_with_flags ((31).bit_xor (non_contiguous_range_information)) -- 31 is the mask value for paragraph format constants end paragraph_format_contiguous, internal_paragraph_format_contiguous (start_position, end_position: INTEGER): BOOLEAN -- Is paragraph formatting from line `start_position' to `end_position' contiguous? local range_info: EV_PARAGRAPH_FORMAT_RANGE_INFORMATION do range_info := paragraph_format_range_information (start_position, end_position) Result := range_info.alignment and then range_info.left_margin and then range_info.right_margin and then range_info.top_spacing and then range_info.bottom_spacing end character_format_contiguous (start_index, end_index: INTEGER): BOOLEAN -- Is formatting from caret position `start_index' to `end_index' contiguous? -- Internal version which permits optimizations as caret position and selection -- does not need to be restored. do Result := internal_character_format_contiguous (start_index, end_index) end internal_character_format_contiguous (start_index, end_index: INTEGER): BOOLEAN -- Is formatting from caret position `start_index' to `end_index' contiguous? local a_range_info: EV_CHARACTER_FORMAT_RANGE_INFORMATION do a_range_info := internal_character_format_range_information (start_index, end_index, True, Void) Result := a_range_info.font_family end selected_paragraph_format: EV_PARAGRAPH_FORMAT -- `Result' is paragraph format of current selection. -- If more than one format is contained in the selection, `Result' -- is the first of these formats. do Result := paragraph_format (start_selection) end modify_region (start_position, end_position: INTEGER; format: EV_CHARACTER_FORMAT; applicable_attributes: EV_CHARACTER_FORMAT_RANGE_INFORMATION) -- Modify formatting from `start_position' to `end_position' applying all attributes of `format' that are set to -- `True' within `applicable_attributes', ignoring others. local a_format_imp: detachable EV_CHARACTER_FORMAT_IMP do a_format_imp ?= format.implementation check a_format_imp /= Void then end modify_region_internal (text_buffer, start_position, end_position, a_format_imp, applicable_attributes) end modify_paragraph (start_position, end_position: INTEGER; format: EV_PARAGRAPH_FORMAT; applicable_attributes: EV_PARAGRAPH_FORMAT_RANGE_INFORMATION) -- Modify paragraph formatting from caret positions `start_position' to `end_position' applying all attributes of `format' that are set to -- `True' within `applicable_attributes', ignoring others. local format_imp: detachable EV_PARAGRAPH_FORMAT_IMP do format_imp ?= format.implementation check format_imp /= Void then end modify_paragraph_internal (start_position, end_position, format_imp, applicable_attributes) end paragraph_format, internal_paragraph_format (caret_index: INTEGER): EV_PARAGRAPH_FORMAT -- `Result' is paragraph_format at caret position `caret_index'. local a_text_iter: EV_GTK_TEXT_ITER_STRUCT a_text_attributes: POINTER a_justification: INTEGER a_change: BOOLEAN do create Result create a_text_iter.make {GTK2}.gtk_text_buffer_get_iter_at_offset (text_buffer, a_text_iter.item, caret_index - 1) a_text_attributes := gtk_text_view_get_default_attributes (text_view) a_change := gtk_text_iter_get_attributes (a_text_iter.item, a_text_attributes) Result.set_bottom_spacing (gtk_text_attributes_struct_pixels_below_lines (a_text_attributes)) Result.set_top_spacing (gtk_text_attributes_struct_pixels_above_lines (a_text_attributes)) Result.set_left_margin (gtk_text_attributes_struct_left_margin (a_text_attributes)) Result.set_right_margin (gtk_text_attributes_struct_right_margin (a_text_attributes)) a_justification := gtk_text_attributes_struct_justification (a_text_attributes) if a_justification = {GTK}.gtk_justify_left_enum then Result.enable_left_alignment elseif a_justification = {GTK}.gtk_justify_center_enum then Result.enable_center_alignment elseif a_justification = {GTK}.gtk_justify_right_enum then Result.enable_right_alignment elseif a_justification = {GTK}.gtk_justify_fill_enum then Result.enable_justification end gtk_text_attributes_free (a_text_attributes) end selected_character_format: EV_CHARACTER_FORMAT -- Format of the character which starts the selection do Result := character_format (start_selection) end index_from_position (an_x_position, a_y_position: INTEGER): INTEGER -- Index of character closest to position `x_position', `y_position'. local a_buf_x, a_buf_y: INTEGER a_text_iter: EV_GTK_TEXT_ITER_STRUCT text_count: INTEGER do gtk_text_view_window_to_buffer_coords (text_view, an_x_position, a_y_position, $a_buf_x, $a_buf_y) create a_text_iter.make gtk_text_view_get_iter_at_location (text_view, a_text_iter.item, a_buf_x, a_buf_y) text_count := {GTK2}.gtk_text_buffer_get_char_count (text_buffer) Result := {GTK2}.gtk_text_iter_get_offset (a_text_iter.item) + 1 Result := Result.min (text_count).max (1) end position_from_index (an_index: INTEGER): EV_COORDINATE -- Position of character at index `an_index'. local a_text_iter: EV_GTK_TEXT_ITER_STRUCT a_x, a_y, a_x2, a_y2: INTEGER a_rectangle: MANAGED_POINTER do create a_text_iter.make create a_rectangle.make ({GTK}.c_gdk_rectangle_struct_size) {GTK2}.gtk_text_buffer_get_iter_at_offset (text_buffer, a_text_iter.item, an_index - 1) {GTK2}.gtk_text_view_get_iter_location (text_view, a_text_iter.item, a_rectangle.item) a_x := {GTK}.gdk_rectangle_struct_x (a_rectangle.item) a_y := {GTK}.gdk_rectangle_struct_y (a_rectangle.item) gtk_text_view_buffer_to_window_coords (text_view, a_x, a_y, $a_x2, $a_y2) create Result.set (a_x2, a_y2) end character_displayed (an_index: INTEGER): BOOLEAN -- Is character `an_index' currently visible in `Current'? local a_text_iter: EV_GTK_TEXT_ITER_STRUCT a_x, a_y, a_char_x, a_char_y, a_char_width, a_char_height: INTEGER a_rectangle: MANAGED_POINTER do create a_text_iter.make create a_rectangle.make ({GTK}.c_gdk_rectangle_struct_size) {GTK2}.gtk_text_buffer_get_iter_at_offset (text_buffer, a_text_iter.item, an_index - 1) {GTK2}.gtk_text_view_get_iter_location (text_view, a_text_iter.item, a_rectangle.item) a_x := {GTK}.gdk_rectangle_struct_x (a_rectangle.item) a_y := {GTK}.gdk_rectangle_struct_y (a_rectangle.item) a_char_width := {GTK}.gdk_rectangle_struct_width (a_rectangle.item) a_char_height := {GTK}.gdk_rectangle_struct_height (a_rectangle.item) gtk_text_view_buffer_to_window_coords (text_view, a_x, a_y, $a_char_x, $a_char_y) Result := (a_char_x >= 0 and a_char_x < width) and then (a_char_y >= 0 and a_char_y < height) end feature -- Status report character_format (pos: INTEGER): EV_CHARACTER_FORMAT -- `Result' is character format at position `pos'. On some platforms -- this may be optimized to take the selected character format and therefore -- should only be used by `next_change_of_character'. do Result := internal_character_format (pos).attached_interface end internal_character_format (character_index: INTEGER): EV_CHARACTER_FORMAT_IMP -- `Result' is character format of character `character_index'. local a_text_iter: EV_GTK_TEXT_ITER_STRUCT a_text_attributes, a_text_appearance: POINTER a_font_description: POINTER a_color: POINTER font_size, font_weight, font_style: INTEGER a_family: EV_GTK_C_STRING a_change: BOOLEAN l_result: detachable EV_CHARACTER_FORMAT_IMP do l_result ?= (create {EV_CHARACTER_FORMAT}).implementation check l_result /= Void then end create a_text_iter.make {GTK2}.gtk_text_buffer_get_iter_at_offset (text_buffer, a_text_iter.item, character_index - 2) a_text_attributes := gtk_text_view_get_default_attributes (text_view) a_change := gtk_text_iter_get_attributes (a_text_iter.item, a_text_attributes) a_text_appearance := gtk_text_attributes_struct_text_appearance (a_text_attributes) a_font_description := gtk_text_attributes_struct_font_description (a_text_attributes) create a_family.share_from_pointer ({GTK2}.pango_font_description_get_family (a_font_description)) font_style := {GTK2}.pango_font_description_get_style (a_font_description) font_weight := {GTK2}.pango_font_description_get_weight (a_font_description) if font_weight <= {EV_FONT_IMP}.pango_weight_ultra_light then font_weight := {EV_FONT_CONSTANTS}.weight_thin elseif font_weight <= {EV_FONT_IMP}.pango_weight_normal then font_weight := {EV_FONT_CONSTANTS}.weight_regular elseif font_weight <= {EV_FONT_IMP}.pango_weight_bold then font_weight := {EV_FONT_CONSTANTS}.weight_bold else font_weight := {EV_FONT_CONSTANTS}.weight_black end font_size := {GTK2}.pango_font_description_get_size (a_font_description) // {GTK2}.pango_scale if {GTK2}.pango_font_description_get_style (a_font_description) > 0 then font_style := {EV_FONT_CONSTANTS}.shape_italic else font_style := {EV_FONT_CONSTANTS}.shape_regular end l_result.set_font_attributes (a_family.string, {EV_FONT_CONSTANTS}.family_sans, font_size, font_weight, font_style, 0) a_color := gtk_text_appearance_struct_fg_color (a_text_appearance) l_result.set_fcolor ( {GTK}.gdk_color_struct_red (a_color) // 256, {GTK}.gdk_color_struct_green (a_color) // 256, {GTK}.gdk_color_struct_blue (a_color) // 256 ) a_color := gtk_text_appearance_struct_bg_color (a_text_appearance) l_result.set_bcolor ( {GTK}.gdk_color_struct_red (a_color) // 256, {GTK}.gdk_color_struct_green (a_color) // 256, {GTK}.gdk_color_struct_blue (a_color) // 256 ) l_result.set_effects_internal (gtk_text_appearance_struct_underline (a_text_appearance).to_boolean, gtk_text_appearance_struct_strikethrough (a_text_appearance).to_boolean, gtk_text_appearance_struct_rise (a_text_appearance)) gtk_text_attributes_free (a_text_attributes) Result := l_result end feature -- Status setting set_current_format (format: EV_CHARACTER_FORMAT) -- apply `format' to current caret position, applicable -- to next typed characters. do current_format := format end current_format: detachable EV_CHARACTER_FORMAT -- Format to be applied to next typed characters format_region (start_position, end_position: INTEGER; format: EV_CHARACTER_FORMAT) -- Apply `format' to all characters between the caret positions `start_position' and `end_position'. -- Formatting is applied immediately. May or may not change the cursor position. local a_format_imp: detachable EV_CHARACTER_FORMAT_IMP do if not is_destroyed then a_format_imp ?= format.implementation check a_format_imp /= Void then end modify_region_internal (text_buffer, start_position, end_position, a_format_imp, a_format_imp.dummy_character_format_range_information) end end buffered_format (start_position, end_position: INTEGER; format: EV_CHARACTER_FORMAT) -- Apply a character format `format' from caret positions `start_position' to `end_position' to -- format buffer. Call `flush_format_buffer' to apply buffered contents to `Current'. local a_format_imp: detachable EV_CHARACTER_FORMAT_IMP do if not buffer_locked_in_format_mode then buffer_locked_in_format_mode := True -- Temporarily remove text buffer to avoid redraw and event firing append_buffer := text_buffer {GTK2}.object_ref (append_buffer) {GTK2}.gtk_text_view_set_buffer (text_view, {GTK2}.gtk_text_buffer_new (default_pointer)) end a_format_imp ?= format.implementation check a_format_imp /= Void then end modify_region_internal (append_buffer, start_position, end_position, a_format_imp, a_format_imp.dummy_character_format_range_information) end buffered_append (a_text: READABLE_STRING_GENERAL; format: EV_CHARACTER_FORMAT) -- Apply `a_text' with format `format' to append buffer. -- To apply buffer contents to `Current', call `flush_append_buffer' or -- `flush_append_buffer_to'. local text_tag_table: POINTER buffer_length: INTEGER a_format_imp: detachable EV_CHARACTER_FORMAT_IMP l_count: INTEGER l_char_code: NATURAL_32 do l_count := a_text.count if l_count >= 1 then if not buffer_locked_in_append_mode then text_tag_table := {GTK2}.gtk_text_buffer_get_tag_table (text_buffer) append_buffer := {GTK2}.gtk_text_buffer_new (text_tag_table) buffer_locked_in_append_mode := True end l_char_code := a_text.code (1) if l_count = 1 and (l_char_code = ('%N').natural_32_code or l_char_code = ('%T').natural_32_code) then append_text_internal (append_buffer, a_text) else buffer_length := {GTK2}.gtk_text_buffer_get_char_count (append_buffer) + 1 append_text_internal (append_buffer, a_text) a_format_imp ?= format.implementation check a_format_imp /= Void then end {GTK2}.gtk_text_buffer_get_iter_at_offset (append_buffer, temp_start_iter.item, buffer_length - 1) {GTK2}.gtk_text_buffer_get_iter_at_offset (append_buffer, temp_end_iter.item, {GTK2}.gtk_text_buffer_get_char_count (append_buffer)) a_format_imp.apply_character_format_to_text_buffer (a_format_imp.dummy_character_format_range_information, append_buffer, temp_start_iter.item, temp_end_iter.item) end end end temp_start_iter, temp_end_iter: EV_GTK_TEXT_ITER_STRUCT -- Reusable GtkTextIter objects flush_buffer -- Flush contents of buffer. -- If `buffer_locked_for_append' then replace contents of `Current' with buffer contents. -- If `buffer_locked_for_format' then apply buffered formatting to contents of `Current'. do if buffer_locked_in_format_mode then {GTK2}.gtk_text_view_set_buffer (text_view, append_buffer) text_buffer := append_buffer initialize_buffer_events {GTK2}.object_unref (append_buffer) append_buffer := NULL buffer_locked_in_format_mode := False elseif buffer_locked_in_append_mode then {GTK2}.gtk_text_view_set_buffer (text_view, append_buffer) text_buffer := append_buffer initialize_buffer_events {GTK2}.object_unref (append_buffer) append_buffer := NULL buffer_locked_in_append_mode := False end end flush_buffer_to (start_position, end_position: INTEGER) -- Replace contents of current from caret position `start_position' to `end_position' with -- contents of buffer, since it was last flushed. If `start_position' and `end_position' -- are equal, insert the contents of the buffer at caret position `start_position'. local text_buffer_start_iter, text_buffer_end_iter, append_buffer_start_iter, append_buffer_end_iter: EV_GTK_TEXT_ITER_STRUCT a_caret_pos: INTEGER do a_caret_pos := caret_position create text_buffer_start_iter.make create text_buffer_end_iter.make create append_buffer_start_iter.make create append_buffer_end_iter.make {GTK2}.gtk_text_buffer_get_iter_at_offset (text_buffer, text_buffer_start_iter.item, start_position - 1) {GTK2}.gtk_text_buffer_get_iter_at_offset (text_buffer, text_buffer_end_iter.item, end_position - 1) {GTK2}.gtk_text_buffer_get_start_iter (append_buffer, append_buffer_start_iter.item) {GTK2}.gtk_text_buffer_get_end_iter (append_buffer, append_buffer_end_iter.item) {GTK2}.gtk_text_buffer_delete (text_buffer, text_buffer_start_iter.item, text_buffer_end_iter.item) {GTK2}.gtk_text_buffer_insert_range (text_buffer, text_buffer_start_iter.item, append_buffer_start_iter.item, append_buffer_end_iter.item) set_caret_position (a_caret_pos) dispose_append_buffer buffer_locked_in_append_mode := False end set_tab_width (a_width: INTEGER) -- Assign `a_width' to `tab_width'. do tab_width := a_width update_tab_positions (1) end pango_tab_array: POINTER -- Array of pango tabs used for `Current' tab_width: INTEGER -- Default width in pixels of each tab in `Current'. feature {EV_GTK_DEPENDENT_INTERMEDIARY_ROUTINES} -- Implementation on_key_event (a_key: detachable EV_KEY; a_key_string: detachable STRING_32; a_key_press: BOOLEAN) -- Used for key event actions sequences. local l_current_format: like current_format do Precursor {EV_TEXT_IMP} (a_key, a_key_string, a_key_press) l_current_format := current_format if l_current_format /= Void and then a_key_press and then a_key /= Void then if (a_key.code = {EV_KEY_CONSTANTS}.key_delete or a_key.is_arrow or a_key.code = {EV_KEY_CONSTANTS}.key_back_space) then current_format := Void else App_implementation.do_once_on_idle (agent format_region (caret_position, caret_position + 1, l_current_format)) end end end on_text_mark_changed (a_text_iter, a_text_mark: POINTER) -- Called when a text mark within `text_buffer' has been set. local a_caret_position: INTEGER a_selection_start, a_selection_end: INTEGER do if is_displayed then -- We only want text mark events when rich text is displayable to user. if not (a_text_mark = {GTK2}.gtk_text_buffer_get_insert (text_buffer)) then a_selection_start := start_selection a_selection_end := end_selection if selection_change_actions_internal /= Void and then (previous_selection_start /= a_selection_start or else previous_selection_end /= a_selection_end) then selection_change_actions_internal.call (Void) end previous_selection_start := a_selection_start previous_selection_end := a_selection_end else a_caret_position := caret_position if a_caret_position /= previous_caret_position then if caret_move_actions_internal /= Void then caret_move_actions_internal.call ([a_caret_position]) end -- Wipeout current format current_format := Void end end previous_caret_position := a_caret_position end end feature {NONE} -- Implementation previous_caret_position, previous_selection_start, previous_selection_end: INTEGER -- Values used for determining whether either the selection or caret_position has changed inorder to fire appropriate event feature {NONE} -- Implementation gtk_text_view_set_tabs (a_text_view, a_pango_tab_array: POINTER) external "C signature (GtkTextView*, PangoTabArray*) use " end pango_tab_array_new (initial_size: INTEGER; dimension_in_pixels: BOOLEAN): POINTER external "C inline use " alias "pango_tab_array_new ((gint) $initial_size, (gboolean) $dimension_in_pixels)" end gtk_text_attributes_struct_font_description (a_text_attributes: POINTER): POINTER external "C struct GtkTextAttributes access font use " end gtk_text_attributes_struct_text_appearance (a_text_attributes: POINTER): POINTER external "C struct GtkTextAttributes access &appearance use " end gtk_text_attributes_struct_justification (a_text_attributes: POINTER): INTEGER external "C struct GtkTextAttributes access justification use " end gtk_text_attributes_struct_left_margin (a_text_attributes: POINTER): INTEGER external "C struct GtkTextAttributes access left_margin use " end gtk_text_attributes_struct_right_margin (a_text_attributes: POINTER): INTEGER external "C struct GtkTextAttributes access right_margin use " end gtk_text_attributes_struct_pixels_above_lines (a_text_attributes: POINTER): INTEGER external "C struct GtkTextAttributes access pixels_above_lines use " end gtk_text_attributes_struct_pixels_below_lines (a_text_attributes: POINTER): INTEGER external "C struct GtkTextAttributes access pixels_below_lines use " end gtk_text_appearance_struct_rise (a_text_appearance: POINTER): INTEGER external "C struct GtkTextAppearance access rise use " end gtk_text_appearance_struct_bg_color (a_text_appearance: POINTER): POINTER external "C struct GtkTextAppearance access &bg_color use " end gtk_text_appearance_struct_underline (a_text_appearance: POINTER): INTEGER external "C struct GtkTextAppearance access underline use " end gtk_text_appearance_struct_strikethrough (a_text_appearance: POINTER): INTEGER external "C struct GtkTextAppearance access strikethrough use " end gtk_text_appearance_struct_fg_color (a_text_appearance: POINTER): POINTER external "C struct GtkTextAppearance access &fg_color use " end gtk_text_iter_get_attributes (a_text_iter: POINTER; a_text_values: POINTER): BOOLEAN external "C inline use " alias "gtk_text_iter_get_attributes ((GtkTextIter*) $a_text_iter, (GtkTextAttributes*) $a_text_values )" end gtk_text_view_get_default_attributes (a_text_view: POINTER): POINTER external "C inline use " alias "gtk_text_view_get_default_attributes ((GtkTextView*) $a_text_view)" end gtk_text_attributes_free (a_text_attributes: POINTER) external "C inline use " alias "free ((GtkTextAttributes*) $a_text_attributes)" end gtk_text_attributes_copy_values (a_text_attributes_src, a_text_attributes_dest: POINTER) external "C inline use " alias "gtk_text_attributes_copy_values ((GtkTextAttributes*) $a_text_attributes_src, (GtkTextAttributes*) $a_text_attributes_dest)" end gtk_text_attributes_copy (a_text_attributes_src: POINTER): POINTER external "C inline use " alias "gtk_text_attributes_copy ((GtkTextAttributes*) $a_text_attributes_src)" end gtk_text_view_get_iter_at_location (a_text_view, a_text_iter: POINTER; buffer_x, buffer_y: INTEGER) external "C inline use " alias "gtk_text_view_get_iter_at_location ((GtkTextView*) $a_text_view, (GtkTextIter*) $a_text_iter, (gint) $buffer_x, (gint) $buffer_y)" end gtk_text_view_window_to_buffer_coords (a_text_view: POINTER; window_x, window_y: INTEGER; buffer_x, buffer_y: POINTER) external "C inline use " alias "gtk_text_view_window_to_buffer_coords ((GtkTextView*) $a_text_view, GTK_TEXT_WINDOW_TEXT, (gint) $window_x, (gint) $window_y, (gint *) $buffer_x, (gint *) $buffer_y)" end gtk_text_view_buffer_to_window_coords (a_text_view: POINTER; buffer_x, buffer_y: INTEGER; window_x, window_y: POINTER) external "C inline use " alias "gtk_text_view_buffer_to_window_coords ((GtkTextView*) $a_text_view, GTK_TEXT_WINDOW_TEXT, (gint) $buffer_x, (gint) $buffer_y, (gint *) $window_x, (gint *) $window_y)" end modify_region_internal (a_text_buffer: POINTER; start_position, end_position: INTEGER; format_imp: EV_CHARACTER_FORMAT_IMP; applicable_attributes: EV_CHARACTER_FORMAT_RANGE_INFORMATION) -- Apply `format' to all characters between the caret positions `start_position' and `end_position'. -- Formatting is applied immediately. May or may not change the cursor position. local a_start_iter, a_end_iter: EV_GTK_TEXT_ITER_STRUCT a_tag_table, text_tag: POINTER do create a_start_iter.make create a_end_iter.make {GTK2}.gtk_text_buffer_get_iter_at_offset (a_text_buffer, a_start_iter.item, start_position - 1) {GTK2}.gtk_text_buffer_get_iter_at_offset (a_text_buffer, a_end_iter.item, end_position - 1) text_tag := format_imp.new_text_tag_from_applicable_attributes (applicable_attributes) a_tag_table := {GTK2}.gtk_text_buffer_get_tag_table (a_text_buffer) {GTK2}.gtk_text_tag_table_add (a_tag_table, text_tag) {GTK2}.gtk_text_buffer_apply_tag (a_text_buffer, text_tag, a_start_iter.item, a_end_iter.item) end modify_paragraph_internal (start_position, end_position: INTEGER; format_imp: EV_PARAGRAPH_FORMAT_IMP; applicable_attributes: EV_PARAGRAPH_FORMAT_RANGE_INFORMATION) -- Apply paragraph formatting `format' from position `start_position' to `end_position' based on `applicable_attributes' local a_start_position, a_end_position: INTEGER a_start_iter, a_end_iter: EV_GTK_TEXT_ITER_STRUCT a_tag_table, text_tag: POINTER a_start_line: INTEGER do a_start_position := start_position a_end_position := end_position create a_start_iter.make create a_end_iter.make {GTK2}.gtk_text_buffer_get_iter_at_offset (text_buffer, a_start_iter.item, a_start_position - 1) {GTK2}.gtk_text_buffer_get_iter_at_offset (text_buffer, a_end_iter.item, a_end_position - 1) a_start_line := {GTK2}.gtk_text_iter_get_line (a_start_iter.item) {GTK2}.gtk_text_iter_set_line (a_start_iter.item, a_start_line) {GTK2}.gtk_text_iter_forward_to_line_end (a_end_iter.item) {GTK2}.gtk_text_iter_forward_char (a_end_iter.item) text_tag := format_imp.new_paragraph_tag_from_applicable_attributes (applicable_attributes) a_tag_table := {GTK2}.gtk_text_buffer_get_tag_table (text_buffer) {GTK2}.gtk_text_tag_table_add (a_tag_table, text_tag) {GTK2}.gtk_text_buffer_apply_tag (text_buffer, text_tag, a_start_iter.item, a_end_iter.item) end update_tab_positions (value: INTEGER) -- Update tab widths based on contents of `tab_positions'. local i: INTEGER current_tab_position: INTEGER do {GTK2}.pango_tab_array_resize (pango_tab_array, tab_positions.count + 1) from i := 1 until i > tab_positions.count loop current_tab_position := current_tab_position + tab_positions.i_th (i) {GTK2}.pango_tab_array_set_tab (pango_tab_array, i - 1, 0, current_tab_position) i := i + 1 end -- Set the default tab width {GTK2}.pango_tab_array_set_tab (pango_tab_array, i - 1, 0, current_tab_position + tab_width) {GTK2}.gtk_text_view_set_tabs (text_view, pango_tab_array) end dispose_append_buffer -- Clean up `append_buffer'. do {GTK2}.object_unref (append_buffer) append_buffer := default_pointer end append_buffer: POINTER -- Pointer to the GtkTextBuffer used for append buffering. feature {EV_ANY, EV_ANY_I} -- Implementation interface: detachable EV_RICH_TEXT note option: stable attribute end; note copyright: "Copyright (c) 1984-2014, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software 5949 Hollister Ave., Goleta, CA 93117 USA Telephone 805-685-1006, Fax 805-685-6869 Website http://www.eiffel.com Customer support http://support.eiffel.com ]" end -- class EV_RICH_TEXT_IMP