note
description: "Notebook Editor for multiple document editing. Notebook holds widgets%
%of type DOCUMENT_WIDGET, a composite widget class for DOCUMENTs."
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
class
DOCUMENT_EDITOR
inherit
EDITABLE_TEXT_PANEL
redefine
on_mouse_button_up,
handle_extended_key,
handle_extended_ctrled_key,
reference_window,
initialize_editor_context
end
SHARED_OBJECTS
undefine
default_create,
is_equal,
copy
end
UTILITY_FUNCTIONS
undefine
default_create,
is_equal,
copy
end
EDITOR_CURSORS
undefine
default_create,
is_equal,
copy
end
create
make
feature -- Initialization
make
--
do
default_create
register_document ("xml", xml_class)
register_document ("java", java_class)
set_current_document_class (xml_class)
editor_drawing_area.pointer_button_release_actions.force_extend (agent pointer_released)
editor_drawing_area.pointer_button_press_actions.extend (agent pointer_pressed (?,?,?,?,?,?,?,?))
editor_drawing_area.drop_actions.extend (agent pebble_dropped)
end
update_observers
-- Update observers of text editing
do
add_edition_observer (shared_web_browser)
add_edition_observer (application_window)
end
handle_extended_key (ev_key: EV_KEY)
-- Process the push on an extended key.
local
l_cursor: TEXT_CURSOR
do
l_cursor := text_displayed.cursor
inspect ev_key.code
when key_comma then
if shifted_key then
--build_tag_menu_popup
--tag_menu.show_at (application_window, calculate_menu_popup_x_position, calculate_menu_popup_y_position)
end
else
-- Key not handled
Precursor {EDITABLE_TEXT_PANEL} (ev_key)
end
end
handle_extended_ctrled_key (ev_key: EV_KEY)
-- Process the push on Ctrl + an extended key.
do
inspect
ev_key.code
when Key_s then
-- Ctrl-S (save)
shared_document_manager.save_document
when Key_f then
-- Ctrl-F (search)
open_search_dialog
when Key_r then
-- Ctrl-R (refresh web browser)
shared_document_manager.save_document
shared_web_browser.refresh
when Key_d then
-- Ctrl-D (validate to XML/Schema)
shared_document_editor_commands.validate_document
else
Precursor (ev_key)
end
end
initialize_editor_context
-- Here initialize editor contextual settings. For example, set location of cursor
-- pixmaps.
do
-- set_cursors (create {DOC_BUILDER_CURSORS})
-- set_icons (create {DOC_BUILDER_ICONS})
end
feature -- Editing
pretty_print_text
-- Pretty XML format the current document
do
if current_document /= Void then
if current_document.is_valid_xml (text) then
current_document.set_text (current_document.pretty_xml (text))
reset
load_text (current_document.text)
else
shared_error_reporter.show
end
end
end
pretty_format_code_text
-- Pretty format the selected text as Eiffel code
local
l_code_formatter: CODE_FORMATTER
do
if current_document /= Void and then text_displayed.has_selection then
create l_code_formatter
l_code_formatter.format (text_displayed.selected_string.twin)
clipboard.set_text (l_code_formatter.text)
paste
end
end
reference_window: EV_WINDOW
--
once
Result := application_window
end
feature -- Commands
update_date (a_date: INTEGER)
--
do
date_of_file_when_loaded := a_date
end
validate_document
-- Validate current document to loaded schema
do
if Shared_document_manager.has_schema then
if Shared_document_manager.has_open_document then
if not current_document.is_valid_xml (text) then
shared_error_reporter.show
elseif not current_document.is_valid_to_schema then
shared_error_reporter.show
else
application_window.update_status_report (False, (create {MESSAGE_CONSTANTS}).file_schema_valid_report)
Shared_project.remove_invalid_file (current_document.name)
end
end
end
end
validate_document_links
-- Validate current document links/hrefs
local
l_has_error: BOOLEAN
l_manager: LINK_MANAGER
l_links: ARRAYED_LIST [DOCUMENT_LINK]
l_error: ERROR
do
if current_document /= Void then
if not current_document.is_valid_xml (text) then
shared_error_reporter.show
else
create l_manager
l_manager.add_document (current_document)
l_manager.check_links
if l_manager.invalid_links.is_empty then
application_window.update_status_report (True, ("All links valid"))
else
from
l_has_error := True
l_links := l_manager.invalid_links
l_links.start
until
l_links.after
loop
create l_error.make (l_links.item.url)
shared_error_reporter.set_error (l_error)
l_links.forth
end
shared_error_reporter.show
end
end
if l_has_error and then Shared_constants.Application_constants.is_gui_mode then
shared_error_reporter.show
end
end
end
open_search_dialog
-- Open the search dialog for text searching
do
if Shared_document_manager.has_open_document then
shared_search_control.search_text.set_focus
end
end
tag_selection (a_tag: STRING)
-- Enclose `selected_text' in XML `a_tag'. Eg, `some_text'
-- becomes 'some_text'. If there is no selection
-- just insert ''.
local
l_text: STRING
do
if a_tag.has_substring ("[tag]") then
l_text := a_tag.twin
if current_document /= Void and then text_displayed.has_selection then
cut_selection
l_text.replace_substring_all ("[tag]", clipboard.text)
end
else
create l_text.make_from_string ("<" + a_tag + ">")
if current_document /= Void and then text_displayed.has_selection then
cut_selection
l_text.append (clipboard.text)
end
l_text.append ("" + a_tag + ">")
end
l_text := unescape_content (l_text)
clipboard.set_text (l_text)
paste
-- if l_selected then
-- select_region (caret_position + (a_tag.count + 2), caret_position + (l_text.count - 1) - (a_tag.count + 3))
-- else
-- set_caret_position (caret_position + a_tag.count + 2)
-- end
end
feature -- Query
clipboard_empty: BOOLEAN
-- Is clipboard empty?
do
Result := clipboard.text.is_empty
end
feature -- Access
current_document: DOCUMENT
-- Currently open document
do
-- TODO: Remove, make all call direct to manager
Result := shared_document_manager.current_document
end
feature {NONE} -- Implementation
xml_class: DOCUMENT_CLASS
--
-- (export status {NONE})
local
l_file_name: FILE_NAME
once
create l_file_name.make_from_string (shared_constants.application_constants.syntax_files_directory)
l_file_name.extend ("xml.syn")
create Result.make ("xml", "xml", l_file_name.string)
end
java_class: DOCUMENT_CLASS
--
-- (export status {NONE})
local
l_file_name: FILE_NAME
once
create l_file_name.make_from_string (shared_constants.application_constants.syntax_files_directory)
l_file_name.extend ("java.syn")
create Result.make ("java", "java", l_file_name.string)
end
unescape_content (a_content: STRING): STRING
-- Content unescaped.
do
Result := a_content.twin
Result.replace_substring_all ("<", "<")
Result.replace_substring_all (">", ">")
end
feature -- Cursors
cur_cut_selection: EV_POINTER_STYLE
-- Editor cut cursor icon
local
l_filename: FILE_NAME
l_pixmap: EV_PIXMAP
once
create l_filename.make_from_string (shared_constants.application_constants.cursor_resources_directory)
l_filename.extend ("cut_selection.png")
create l_pixmap
l_pixmap.set_with_named_file (l_filename.string)
create Result.make_with_pixmap (l_pixmap, l_pixmap.width // 2, l_pixmap.height // 2)
end
cur_copy_selection: EV_POINTER_STYLE
-- Editor copy cursor icon
local
l_filename: FILE_NAME
l_pixmap: EV_PIXMAP
once
create l_filename.make_from_string (shared_constants.application_constants.cursor_resources_directory)
l_filename.extend ("copy_selection.png")
create l_pixmap
l_pixmap.set_with_named_file (l_filename.string)
create Result.make_with_pixmap (l_pixmap, l_pixmap.width // 2, l_pixmap.height // 2)
end
feature {NONE} -- Events
on_mouse_button_up (x_pos, y_pos, button: INTEGER; unused1,unused2,unused3: DOUBLE; unused4,unused5:INTEGER)
-- Process release of mouse buttons.
do
Precursor {EDITABLE_TEXT_PANEL} (x_pos, y_pos, button, unused1, unused2, unused3, unused4, unused5)
application_window.update_toolbar
application_window.update_menus
end
pointer_pressed (a_x, a_y, a_button: INTEGER; a_x_tilt, a_y_tilt, a_pressure: DOUBLE; a_screen_x, a_screen_y: INTEGER)
-- Pointer was pressed, catch right-click
do
if Shared_document_manager.has_schema then
if a_button = 3 then
build_tag_menu_popup
tag_menu.show_at (application_window, a_screen_x - application_window.x_position, a_screen_y - application_window.y_position - a_y)
end
end
end
pointer_released
-- Pointer was released
local
l_parent: STRING
do
if Shared_document_manager.has_schema then
l_parent := xml_parent
if l_parent /= Void and then not l_parent.is_empty then
Application_window.update_sub_element_list (l_parent, sub_element_list (l_parent))
end
end
end
xml_parent: STRING
-- Determine the parent XML text based on the current
-- cursor position
local
found_end_tag, found_start_tag, is_child_tag, invalid: BOOLEAN
start_pos, end_pos, curr_pos, cnt, ch_cnt: INTEGER
do
if text_displayed.cursor /= Void then
from
Result := text.substring (1, text_displayed.cursor.pos_in_text)
cnt := Result.count
ch_cnt := 1
until
((found_end_tag and found_start_tag and not is_child_tag) or (cnt = 0)) or invalid
loop
curr_pos := cnt - 1
if found_end_tag and found_start_tag and is_child_tag then
found_end_tag := False
found_start_tag := False
end
if not found_end_tag then
found_end_tag := Result.substring (cnt, cnt).is_equal (">")
if found_end_tag then
if Result.substring (curr_pos, curr_pos).is_equal ("/") then
ch_cnt := ch_cnt + 1
end
end
end_pos := curr_pos
else
found_start_tag := Result.substring (curr_pos, curr_pos).is_equal ("<")
if found_start_tag then
if Result.substring (cnt, cnt).is_equal ("/") then
found_start_tag := True
ch_cnt := ch_cnt + 1
else
ch_cnt := ch_cnt - 1
end
end
start_pos := curr_pos
end
is_child_tag := ch_cnt /= 0
cnt := cnt - 1
end
Result := Result.substring (start_pos + 1, end_pos)
if not Result.is_empty then
-- Tidy result
Result.prune_all_leading (' ')
Result.prune_all_leading ('%T')
Result.prune_all_leading ('%N')
Result.prune_all_trailing (' ')
Result.prune_all_trailing ('%T')
Result.prune_all_trailing ('%N')
end
-- Prune out any attribute declarations
if Result.occurrences (' ') > 0 then
Result := Result.substring (1, Result.index_of (' ', 1) - 1)
end
end
end
sub_element_list (a_parent: STRING): SORTED_TWO_WAY_LIST [STRING]
-- Alphabetically sorted list of sub elements of `a_parent'
local
schema_element: DOCUMENT_SCHEMA_ELEMENT
children: ARRAYED_LIST [DOCUMENT_SCHEMA_ELEMENT]
do
schema_element ?= shared_document_manager.schema.get_element_by_name (a_parent)
if schema_element /= void then
if not schema_element.children.is_empty then
children := schema_element.children.twin
elseif not schema_element.type_children.is_empty then
children := schema_element.type_children.twin
end
if children /= Void then
from
create Result.make
children.start
until
children.after
loop
Result.extend (children.item.name)
children.forth
end
Result.sort
end
end
end
pebble_dropped (a_url: STRING)
-- Pebble dropped on target
local
l_start_pos,
l_end_pos: INTEGER
l_link: DOCUMENT_LINK
l_url: STRING
do
if current_document /= Void then
set_focus
l_start_pos := text_displayed.cursor.pos_in_text
if current_document.is_persisted then
create l_link.make (current_document.name, a_url)
l_url := l_link.relative_url
else
l_url := a_url
end
l_end_pos := l_start_pos + l_url.count
if has_selection then
cut_selection
end
text_displayed.insert_string (l_url)
select_region (l_start_pos, l_end_pos)
end
end
build_tag_menu_popup
-- Build `tag_menu'
local
l_menu_item: EV_MENU_ITEM
menu_children: SORTED_TWO_WAY_LIST [STRING]
l_parent: STRING
do
l_parent := xml_parent
if l_parent /= Void and then not l_parent.is_empty then
-- Retrieve element list and build popup menu
menu_children ?= sub_element_list (l_parent)
if menu_children /= Void and then not menu_children.is_empty then
from
create tag_menu
menu_children.start
until
menu_children.after
loop
create l_menu_item.make_with_text (menu_children.item)
l_menu_item.select_actions.extend (agent tag_selection (menu_children.item))
tag_menu.extend (l_menu_item)
menu_children.forth
end
end
end
-- Add closing tag for convenience
create l_menu_item.make_with_text (l_parent)
l_menu_item.select_actions.extend (agent tag_selection (menu_children.item))
tag_menu.extend (l_menu_item)
end
tag_menu: EV_MENU
calculate_menu_popup_x_position: INTEGER
-- Determine the x position to display the popup
local
tok: EDITOR_TOKEN
cursor: EDITOR_CURSOR
do
-- Get current x position of cursor
cursor := text_displayed.cursor
tok := cursor.token
tok.update_position
Result := tok.position + tok.get_substring_width (cursor.pos_in_token) + widget.screen_x + left_margin_width - offset
end
calculate_menu_popup_y_position: INTEGER
-- Determine the y position to display the completion list
local
cursor: EDITOR_CURSOR
screen: EV_SCREEN
show_below: BOOLEAN
do
-- Get y pos of cursor
create screen
cursor := text_displayed.cursor
show_below := True
Result := widget.screen_y + ((cursor.y_in_lines - first_line_displayed) * line_height)
Result := Result + line_height + 5
end
note
copyright: "Copyright (c) 1984-2006, Eiffel Software"
license: "GPL version 2 (see http://www.eiffel.com/licensing/gpl.txt)"
licensing_options: "http://www.eiffel.com/licensing"
copying: "[
This file is part of Eiffel Software's Eiffel Development Environment.
Eiffel Software's Eiffel Development Environment 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, version 2 of the License
(available at the URL listed under "license" above).
Eiffel Software's Eiffel Development Environment 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 Eiffel Software's Eiffel Development
Environment; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
]"
source: "[
Eiffel Software
356 Storke Road, 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 DOCUMENT_EDITOR