note
	description: "Header control for dealing with multiple documents in TEXT_PANEL."
	legal: "See notice at end of class."
	status: "See notice at end of class."
	date: "$Date$"
	revision: "$Revision$"

class
	TEXT_PANEL_HEADER

inherit
	DOCUMENT_LIST [TEXT_PANEL_HEADER_ITEM]
		rename
			remove as remove_document
		redefine
			item,
			remove_document
		end

	SHARED_EDITOR_DATA
		undefine
			is_equal,
			copy
		end

	SHARED_EDITOR_FONT
		undefine
			is_equal,
			copy
		end

	TEXT_OBSERVER
		undefine
			is_equal,
			copy
		redefine
			on_text_loaded,
			on_text_fully_loaded
		end

create
	make_with_panel

feature -- Creation

	make_with_panel (a_text_panel: TEXT_PANEL)
			-- Make with `a_text_panel'
		require
			panel_not_void: a_text_panel /= Void
			panel_has_icons: a_text_panel.has_icons
		do
			make (0)
			panel := a_text_panel
			create open_linked_list.make
			create selection_actions
			create before_selection_actions
			create close_actions
			open_linked_list.compare_objects
			build_header_area
			a_text_panel.text_displayed.add_edition_observer (Current)
		ensure
			has_panel: panel /= Void
		end

feature -- Basic Operations

	open_document (a_doc: like item)
			-- Opens `a_doc' and selects it in the tab list.
		local
			l_type: STRING
			l_text: TEXT
		do
			compare_objects

				-- Update structure
			if not has (a_doc) then
				extend (a_doc)
				go_i_th (count)
				open_linked_list.extend (a_doc)
			else
				go_i_th (index_of (a_doc, 1))
			end
			update_list_order (item)
			update_items

			index := index.min (count)

			l_type := a_doc.document_type
			if l_type /= Void and then panel.known_document_type (l_type) then
				panel.set_current_document_class (panel.get_class_from_type (l_type))
			end

				-- Update editor panel				
			if attached item.data as l_data then
				panel.set_text (l_data, a_doc.name)
				if item.cursor_line > 0 then
					panel.setup_editor (item.cursor_line)
				else
					panel.setup_editor (1)
				end
			else
				close_button.disable_sensitive
				l_text := panel.new_text_displayed
				l_text.add_edition_observer (Current)
				panel.set_text (l_text, a_doc.name)
				panel.load_file_path (create {PATH}.make_from_string (item.name))
				item.set_data (l_text)
			end
			on_text_loaded
			update_buffered_screen
			update_display
			panel.refresh_now
--			if panel.editor_drawing_area.is_sensitive then
--				panel.set_focus
--			end

			index := index_of (item, 1)
			selection_actions.call ([Current])
		end

	remove_document
			-- Remove document
		require else
			has_open_document: count > 0
		do
			open_linked_list.remove
			if open_linked_list.after and then not open_linked_list.is_empty then
				open_linked_list.finish
			end
			Precursor
			update_items
		end

feature -- Access

	container: EV_HORIZONTAL_BOX
			-- Top level widget container

	previous_selected_index: INTEGER
			-- Index of previously selected item

feature -- Actions

	selection_actions: ACTION_SEQUENCE [TUPLE]
			-- Actions to be performed when selected item changes.

	before_selection_actions: ACTION_SEQUENCE [TUPLE]
			-- Actions to be performed just before selected item changes.

	close_actions: ACTION_SEQUENCE [TUPLE]
			-- Actions to be performed when document is closed.		

feature {NONE} -- Initialization

	build_header_area
			-- Build the header area
		require
			has_icon: panel.has_icons
		local
			l_inner_box: EV_HORIZONTAL_BOX
			l_toolbar: EV_TOOL_BAR
			l_icons: detachable EDITOR_ICONS
		do
			l_icons := panel.icons
			check l_icons /= Void end -- Implied by precondition

			create header_area
			update_height
			header_area.set_minimum_size (1, height)

				-- Add widgets to our window
			create container
			container.extend (header_area)

				-- Add top right buttons for scrolling and closing
			create l_inner_box
			create l_toolbar

			create left_scroll_button
			left_scroll_button.set_pixmap (l_icons.header_left_scroll_pixmap)
			left_scroll_button.select_actions.extend (agent scroll_left)
			left_scroll_button.disable_sensitive
			l_toolbar.extend (left_scroll_button)

			create right_scroll_button
			right_scroll_button.set_pixmap (l_icons.header_right_scroll_pixmap)
			right_scroll_button.select_actions.extend (agent scroll_right)
			right_scroll_button.disable_sensitive
			l_toolbar.extend (right_scroll_button)

			create close_button
			close_button.set_pixmap (l_icons.header_close_current_document_pixmap)
			close_button.select_actions.extend (agent close_document)
			close_button.disable_sensitive
			l_toolbar.extend (close_button)

			l_inner_box.extend (l_toolbar)
			container.extend (l_inner_box)
			container.disable_item_expand (l_inner_box)


					-- Set up the screen.
			create buffered_screen.make_with_size (header_area.width, header_area.height)
			buffered_screen.set_background_color (editor_preferences.normal_background_color)
			l_toolbar.set_background_color (buffered_screen.background_color)

					-- Events
			header_area.expose_actions.extend (agent on_repaint)
			header_area.resize_actions.extend (agent on_size)
			header_area.focus_in_actions.extend (agent on_focus)
			header_area.pointer_button_press_actions.extend (agent on_mouse_button_down)
		end

feature {NONE} -- Display functions

	on_focus
			--
		do
			--	print ("Focus in %N")
		end

	on_repaint (x, y, a_width, a_height: INTEGER)
			-- Repaint the part of the panel between in the rectangle between
			-- (`x', `y') and (`x' + `a_width', `y' + `a_height').
			--| Actually, rectangle defined by (0, y) -> (editor_area.width, y + height) is redrawn.
		do
			if a_width /= 0 and a_height /= 0 then
				update_buffered_screen
				update_display
			end
		end

	on_size (a_x, a_y: INTEGER; a_width, a_height: INTEGER)
			-- Refresh the panel after it has been resized (and moved) to new coordinates (`a_x', `a_y') and
			-- new size (`a_width', `a_height').
			--| Note: This feature is called during the creation of the window
		local
			old_height: INTEGER
			w,h: INTEGER
		do
				-- Resize & redraw the buffered screen.
			if buffered_screen /= Void then -- System initialized.
				in_resize := True

				if buffered_screen.width < a_width then
					if in_resize then
						w := container.width.max (a_width)
						h := a_height.max (1)
					else
						w := container.width.max (a_width).max (buffered_screen.width)
						h := a_height.max (1).max (h)
					end
					buffered_screen.set_size (w, h)
					update_buffered_screen
				else
					old_height := buffered_screen.height
					if in_resize then
						w := container.width.max (header_area.width)
						h := a_height.max (1)
					else
						w := container.width.max (header_area.width).max (buffered_screen.width)
						h := a_height.max (1).max (buffered_screen.height)
					end
					buffered_screen.set_size (w, h)
					if old_height < a_height then
						update_buffered_screen
					end
				end
				in_resize := False
			end

		end

 	update_buffered_screen
 			-- Update buffered pixmap from `a_x'.
 		local
 			l_index: INTEGER
 		do
			buffered_screen.set_background_color (container.background_color)
			buffered_screen.clear_rectangle (0, 0, buffered_screen.width, buffered_screen.height)

 				-- Draw all items				
 			from
 				l_index := index
 				start
 			until
 				after
 			loop
 				if index = l_index then
 					item.display_selected (buffered_screen, panel)
 				else
	 				item.display (buffered_screen, panel)
	 			end
 				forth
 			end
 			go_i_th (l_index)
 		end

	update_display
			-- Update display by drawing the buffered pixmap on `header_area'.
		do
			header_area.clear
			header_area.draw_sub_pixmap (
				0,
				0,
				buffered_screen,
				create {EV_RECTANGLE}.make (
					offset,
					0,
					header_area.width + offset,
					buffered_screen.height)
			)

				-- Update the buttons
			if (width - offset) > header_area.width then
				right_scroll_button.enable_sensitive
			else
				right_scroll_button.disable_sensitive
			end
			if offset > 0 then
				left_scroll_button.enable_sensitive
			else
				left_scroll_button.disable_sensitive
			end
		end

	update_items
			-- Update the items
		local
			l_index: INTEGER
			l_prev_item: detachable like item
		do
				-- Update the open document items
			from
				width := 0
				l_index := index
				start
			until
				after
			loop
				item.set_previous (l_prev_item)
				if l_prev_item = Void then
					item.update_width
				else
					l_prev_item.set_next (item)
				end
				width := width + item.width
				l_prev_item := item
				forth
			end
			go_i_th (l_index)
		end

	update_height
			-- Update height
		require
			has_header_font: has_header_font
		do
			height := header_font.height + 10
		end

feature {NONE} -- Widgets

	left_scroll_button: EV_TOOL_BAR_BUTTON

	right_scroll_button: EV_TOOL_BAR_BUTTON

	close_button: EV_TOOL_BAR_BUTTON

feature {NONE} -- Events

	on_mouse_button_down (x_pos, y_pos, button: INTEGER; unused1,unused2,unused3: DOUBLE; a_screen_x, a_screen_y: INTEGER)
			-- Mouse button was pressed.
		local
			l_index: INTEGER
			l_item: detachable like item
			found: BOOLEAN
			l_cursor: detachable TEXT_CURSOR
		do
			if button = 1 and then panel.text_is_fully_loaded then

					-- Store current index
				l_index := index

					-- Get the newly selected item
				from
					start
				until
					after or found
				loop
					if item.position > x_pos then
						found := True
					else
						l_item := item
					end
					forth
				end
				index := l_index

				check l_item /= Void end -- item must be found, otherwise a bug.
				if l_index /= index_of (l_item, 1) then
						-- It is not the same item
					before_selection_actions.call ([Current])
					if item /= Void then
							-- Store item information for when we change back
						item.set_data (panel.text_displayed)
						if attached {KEYBOARD_SELECTABLE_TEXT_PANEL} panel as l_keyboard_panel then
							l_cursor := l_keyboard_panel.text_displayed.cursor
							check l_cursor /= Void end -- Not void, other wise a bug.
							item.set_cursor_line (l_cursor.y_in_lines)
							item.set_cursor_char (l_cursor.x_in_characters)
						end
						item.set_first_line_displayed (panel.first_line_displayed)
					end
					index := index_of (l_item, 1)
					previous_selected_index := l_index
					open_document (l_item)
				end
			end
		end

	scroll_left
			--
		do
			offset := offset - 10
			if offset < 0 then
				offset := 0
			end
			update_display
		end

	scroll_right
			--
		do
			offset := offset + 10
			if offset > buffered_screen.width then
				offset := buffered_screen.width
			end
			update_display
		end

	close_document
			--
		do
			close_actions.call ([Current])
			remove_document
			if is_empty then
				panel.clear_window
				update_buffered_screen
				update_display
			else
				open_document (open_linked_list.item)
			end
		end

	on_text_loaded
			-- Update `Current' when the text has been completely loaded.
		local
			l_cursor: detachable TEXT_CURSOR
		do
			if attached {KEYBOARD_SELECTABLE_TEXT_PANEL}panel as l_keyboard_panel and then not is_empty and then item /= Void then
				if item.first_line_displayed > 0 then
					l_keyboard_panel.set_first_line_displayed (item.first_line_displayed, False)
				end
				l_cursor := l_keyboard_panel.text_displayed.cursor
				check l_cursor /= Void end -- Never, otherwise a bug.
				if item.cursor_line > 0 then
					l_cursor.set_y_in_lines (item.cursor_line)
				end
				if item.cursor_char > 0 then
					l_cursor.set_x_in_characters (item.cursor_char)
				end
			end
		end

	on_text_fully_loaded
			-- Update `Current' when the text has been completely loaded.
		do
			if attached {KEYBOARD_SELECTABLE_TEXT_PANEL}panel as l_keyboard_panel and then not is_empty and then item /= Void then
				close_button.enable_sensitive
			end
		end

feature {NONE} -- Implementation

	panel: TEXT_PANEL
		-- Panel

	height: INTEGER
		-- Height

	width: INTEGER
		-- Width

	header_area: EV_DRAWING_AREA
		-- Area	

	buffered_screen: EV_PIXMAP
		-- Buffered area

	in_resize: BOOLEAN
		-- Is Current currently being resized

	item: TEXT_PANEL_HEADER_ITEM
			-- Item
		do
			Result := area.item (index - 1)
		end

	offset: INTEGER

	open_linked_list: LINKED_LIST [like item]
			--

	update_list_order (a_item: like item)
			-- Update list order with `a_item' at the top.
		require
			item_valid: a_item /= Void
			item_known: open_linked_list.has (a_item)
		do
			open_linked_list.start
			open_linked_list.search (a_item)
			open_linked_list.remove
			open_linked_list.extend (a_item)
			open_linked_list.finish
		end

feature {NONE} -- Implementation

	new_filled_list (n: INTEGER_32): like Current
		do
			create Result.make_with_panel (panel)
		end

invariant
	has_panel: panel /= Void

note
	copyright:	"Copyright (c) 1984-2018, 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