note
	description: "Objects that represents a database table row%
			%set and its graphical display."
	legal: "See notice at end of class."
	status: "See notice at end of class."
	date: "$Date$"
	revision: "$Revision$"

class
	DV_TABLE_COMPONENT

inherit
	DV_DATABASE_HANDLE

	DV_TABLEROWS_COMPONENT
		redefine
			change_selection
		end

	DB_TABLES_ACCESS_USE

	DV_MESSAGES

create
	make

feature -- Initialization

	make
			-- Initialize.
		do
			create necessary_table_list.make (0)
			create dependent_table_list.make (0)
			create subsearcher_list.make (0)
			create tablerow_set.make (0)
		end

feature -- Access

	table_description: detachable DB_TABLE_DESCRIPTION
			-- Description of table represented by component.

	selected_tablerows: ARRAYED_LIST [DB_TABLE]
			-- Last selected table row set.
		do
			Result := tablerow_set
		end

	selected_tablerow: detachable DB_TABLE
			-- Currently selected table row.
		do
			if not tablerow_set.is_empty then
				Result := tablerow_set.item
			end
		end

feature -- Status report

	can_be_activated: BOOLEAN
			-- Can component be activated?
		do
			Result := database_handler_set and then table_description /= Void
		end

	is_activated: BOOLEAN
			-- Is component activated?

	is_necessary_tablecode (code: INTEGER): BOOLEAN
			-- Does `code' correspond to a necessary
			-- table for represented table?
		do
			Result := attached table_description as l_descr and then l_descr.to_create_fkey_from_table.has (code)
		end

	is_dependent_tablecode (code: INTEGER): BOOLEAN
			-- Does `code' correspond to a dependent
			-- table on represented table?
		do
			Result := attached table_description as l_descr and then l_descr.to_delete_fkey_from_table.has (code)
		end

	is_writing: BOOLEAN
			-- Can the component write the database?
		do
			Result := writing_control /= Void
		end

	is_refreshing: BOOLEAN
			-- Can the component refresh display from the database?
		do
			Result := refreshing_control /= Void
		end

	is_creating: BOOLEAN
			-- Can the component create elements
			-- in the database?
		do
			Result := db_creator /= Void
		end

	is_deleting: BOOLEAN
			-- Can the component delete elements
			-- in the database?
		do
			Result := deleting_control /= Void
		end

	is_subcomponent: BOOLEAN
			-- Is this component determined by a calling
			-- component?
		do
			Result := is_dependent or else is_necessary
		end

	is_topcomponent: BOOLEAN
			-- Is this component not determined by any calling
			-- component?
		do
			Result := not is_subcomponent
		end

	is_necessary: BOOLEAN
			-- Is this subcomponent table necessary for
			-- calling component table?

	is_dependent: BOOLEAN
			-- Is this subcomponent table dependent on
			-- calling component table?

	db_searcher_set: BOOLEAN
			-- Is a component to search table rows set?
		do
			Result := db_searcher /= Void
		end

	status_handler_set: BOOLEAN
			-- Is a status information handler set?
		do
			Result := status_handler /= Void
		end

	warning_handler_set: BOOLEAN
			-- Is a warning handler set?
		do
			Result := warning_handler /= Void
		end

	confirmation_handler_set: BOOLEAN
			-- Is a confirmation handler set?
		do
			Result := confirmation_handler /= Void
		end

	table_corresponds (tr_list: ARRAYED_LIST [DB_TABLE]): BOOLEAN
			-- Does type of `tr_list' elements corresponds to represented
			-- table?
			--| Warning: verification is only done on first element.
		require
			not_void: tr_list /= Void
		do
			if not tr_list.is_empty then
				if attached table_description as l_descr then
					Result := tr_list.first.table_description.Table_code = l_descr.Table_code
				end
			else
				Result := True
			end
		end

feature -- Status setting

	set_writing_control (writing_ctrl: DV_SENSITIVE_CONTROL)
			-- Enable that component can
			-- write the database.
		require
			not_void: writing_ctrl /= Void
			not_activated: not is_activated
		do
			writing_control := writing_ctrl
			writing_ctrl.set_action (agent write)
			writing_ctrl.disable_sensitive
		ensure
			is_writing: is_writing
		end

	set_refreshing_control (refreshing_ctrl: DV_SENSITIVE_CONTROL)
			-- Enable that component can
			-- write the database.
		require
			not_void: refreshing_ctrl /= Void
			not_activated: not is_activated
		do
			refreshing_control := refreshing_ctrl
			refreshing_ctrl.set_action (agent refresh_from_database)
			refreshing_ctrl.disable_sensitive
		ensure
			is_refreshing: is_refreshing
		end

	set_db_creator (creator: DV_CREATOR)
			-- Enable that component can create
			-- elements in the database.
		require
			not_void: creator /= Void
			not_activated: not is_activated
		do
			db_creator := creator
			creator.set_table_component (Current)
		ensure
			is_creating: is_creating
		end

	set_deleting_control (deleting_ctrl: DV_SENSITIVE_CONTROL)
			-- Enable that component can
			-- write the database.
		require
			not_void: deleting_ctrl /= Void
			not_activated: not is_activated
		do
			deleting_control := deleting_ctrl
			deleting_ctrl.set_action (agent delete_after_confirmation)
			deleting_ctrl.disable_sensitive
		ensure
			is_deleting: is_deleting
		end

feature -- Basic operations

	set_warning_handler (w_handler: PROCEDURE [STRING])
			-- Set `w_handler' to `warning_handler'
		require
			not_activated: not is_activated
			not_void: w_handler /= Void
		do
			warning_handler := w_handler
		end

	set_status_handler (s_handler: PROCEDURE [STRING])
			-- Set `s_handler' to `status_handler'
		require
			not_activated: not is_activated
			not_void: s_handler /= Void
		do
			status_handler := s_handler
		end

	set_confirmation_handler (c_handler: PROCEDURE [STRING, PROCEDURE])
			-- Set `c_handler' to `confirmation_handler'
		require
			not_activated: not is_activated
			not_void: c_handler /= Void
		do
			confirmation_handler := c_handler
		end

	set_tablecode (tablecode: INTEGER)
			-- Initialize with `fields_set_comp' to display
			-- current selection.
		require
			not_activated: not is_activated
		do
			table_description := tables.description (tablecode)
		end

	add_necessary_table (comp: DV_TABLE_COMPONENT)
			-- Add a subcomponent representing a necessary
			-- table for represented table.
		require
			is_necessary_table: attached comp.table_description as l_descr and then is_necessary_tablecode (l_descr.Table_code)
			no_db_searcher_set: not comp.db_searcher_set
			not_activated: not is_activated
		local
			db_srcher: DV_TYPED_SEARCHER
			td: detachable DB_TABLE_DESCRIPTION
		do
			comp.enable_necessary (Current)
			necessary_table_list.extend (comp)
			create db_srcher.make
			db_srcher.set_behavior_type (db_srcher.Qualified_selection)
			td := comp.table_description
			check td /= Void and attached table_description as l_descr then
				db_srcher.set_criterion (td.Id_code)
				db_srcher.set_table_code (td.Table_code)
				db_srcher.set_row_attribute_code (l_descr.to_create_fkey_from_table.item (td.Table_code))
			end
			subsearcher_list.extend (db_srcher)
			comp.set_db_searcher (db_srcher)
		end

	add_dependent_table (comp: DV_TABLE_COMPONENT)
			-- Add a subcomponent representing a dependent
			-- table on represented table.
		require
			is_dependent_table: attached comp.table_description as l_descr and then is_dependent_tablecode (l_descr.Table_code)
			no_db_searcher_set: not comp.db_searcher_set
			not_activated: not is_activated
		local
			db_srcher: DV_TYPED_SEARCHER
			td: detachable DB_TABLE_DESCRIPTION
			tc: INTEGER
		do
			if attached table_description as l_descr then
				tc := l_descr.Table_code
			end
			comp.enable_dependent (Current)
			dependent_table_list.extend (comp)
			create db_srcher.make
			db_srcher.set_behavior_type (db_srcher.Qualified_selection)
			td := comp.table_description
			check td /= Void then
				db_srcher.set_criterion (td.to_create_fkey_from_table.item (tc))
				db_srcher.set_table_code (td.Table_code)
				db_srcher.set_row_attribute_code (td.Id_code)
			end
			subsearcher_list.extend (db_srcher)
			comp.set_db_searcher (db_srcher)
			set_handlers (comp)
		end

	set_db_searcher (db_srcher: DV_SEARCHER)
			-- Set component enabling to load table rows, eventually from
			-- database, to `db_srcher'.
		require
			not_void: db_srcher /= Void
			not_activated: not is_activated
		do
			db_searcher := db_srcher
			db_srcher.set_user_component (Current)
			if attached table_description as l_descr then
				db_srcher.set_table_code (l_descr.Table_code)
			end
			db_srcher.activate
		ensure
			db_searcher_set: db_searcher_set
		end

	set_db_fields_component (db_fields_comp: DV_TABLEROW_FIELDS)
			-- Set `db_fields_comp' to component displaying selected table row
			-- content.
		require
			not_void: db_fields_comp /= Void
			not_activated: not is_activated
		do
			db_fields_component := db_fields_comp
		end

	activate
			-- Activate component.
		do
			if attached db_fields_component as l_comp and attached table_description as l_descr then
				l_comp.set_table_description (l_descr)
				l_comp.activate
			end
			if attached db_tablerow_navigator as l_nav then
				l_nav.activate
			end
			if attached db_creator as l_db_creator then
				l_db_creator.activate
			end
			if not status_handler_set then
				status_handler := agent basic_message_handler
			end
			if not warning_handler_set then
				warning_handler := agent basic_message_handler
			end
			if not confirmation_handler_set then
				confirmation_handler := agent basic_confirmation_handler
			end
			from
				dependent_table_list.start
			until
				dependent_table_list.after
			loop
				set_handlers (dependent_table_list.item)
				dependent_table_list.item.activate
				dependent_table_list.forth
			end
			from
				necessary_table_list.start
			until
				necessary_table_list.after
			loop
				set_handlers (necessary_table_list.item)
				necessary_table_list.item.activate
				necessary_table_list.forth
			end
			is_activated := True
		end

	desactivate
			-- Desactivate component to add subcomponents.
		do
			is_activated := False
		ensure
			not_activated: not is_activated
		end

	replace_selected_tablerows (tr_set: ARRAYED_LIST [DB_TABLE])
			-- Replace current table row set with `tr_set'. Use this capability
			-- when component is activated to switch between different
			-- results. Database searcher set may have to afford to
			-- refresh different searches.
		require
			is_activated: is_activated
			not_void: tr_set /= Void
			table_corresponds: table_corresponds (tr_set)
		do
			tablerow_set := tr_set
		end

feature {DV_COMPONENT} -- Access

	warning_handler: detachable PROCEDURE [STRING]
			-- Warning handler.

	status_handler: detachable PROCEDURE [STRING]
			-- Status information handler.

	confirmation_handler: detachable PROCEDURE [STRING, PROCEDURE]
			-- Confirmation handler.

	parent: detachable DV_TABLE_COMPONENT
			-- Calling component if this component is subcomponent table
			-- is necessary for calling component table.

feature {DV_COMPONENT} -- Status setting

	set_just_created
			-- Set `is_just_created' to `True'.
		require
			is_activated: is_activated
		do
			is_just_created := True
		end

	enable_dependent (par: DV_TABLE_COMPONENT)
			-- Enable that represented table is dependent on
			-- calling component table.
		require
			not_void: par /= Void
			is_current_dependent_table: attached par.table_description as l_descr and then is_necessary_tablecode (l_descr.Table_code)
			not_activated: not is_activated
		do
			parent := par
			is_dependent := True
			is_necessary := False
		ensure
			parent_set: parent /= Void
		end

	enable_necessary (par: DV_TABLE_COMPONENT)
			-- Enable that represented table is necessary for
			-- calling component table.
		require
			not_void: par /= Void
			not_is_creating: not is_creating
			is_current_necessary_table: attached par.table_description as l_descr and then is_dependent_tablecode (l_descr.Table_code)
			not_activated: not is_activated
		do
			parent := par
			is_dependent := False
			is_necessary := True
		ensure
			parent_set: parent /= Void
		end

feature {DV_COMPONENT} -- Basic operations

	display (tr_set: ARRAYED_LIST [DB_TABLE])
			-- Display `tr_set' in the component.
		require
			is_activated: is_activated
			not_void: tr_set /= Void
			table_corresponds: table_corresponds (tr_set)
		do
			tablerow_set := tr_set
			if is_topcomponent then
				if attached status_handler as l_handler then
					l_handler.call ([tablerows_selected (tablerow_set.count)])
				end
			elseif is_dependent and then attached parent as l_par and then attached db_creator as l_db_creator then
				l_db_creator.set_calling_fkey_value (l_par.selected_tablerows.item.table_description.id)
			end
			is_cleared := False
			is_just_created := False
			if not tablerow_set.is_empty then
				tablerow_set.start
			end
			refresh
		ensure
			is_not_cleared: not is_cleared
			is_not_created: not is_just_created
		end

	refresh_from_database
			-- Refresh display from database.
		require
			is_activated: is_activated
		local
			last_current_tablerow: DB_TABLE
		do
			if not tablerow_set.off then
				last_current_tablerow := tablerow_set.item
				if is_just_created then
					check attached db_creator as l_db_creator then
						tablerow_set := l_db_creator.refresh
					end
				else
					check attached db_searcher as l_db_searcher then
						tablerow_set := l_db_searcher.refresh
					end
				end
				if not tablerow_set.is_empty then
					search_or_start (last_current_tablerow)
				end
			else
				if is_just_created then
					check attached db_creator as l_db_creator then
						tablerow_set := l_db_creator.refresh
					end
				else
					check attached db_searcher as l_db_searcher then
						tablerow_set := l_db_searcher.refresh
					end
				end
				tablerow_set.start
			end
			refresh
			if attached status_handler as l_handler then
				l_handler.call ([tablerows_selected (tablerow_set.count)])
			end
		end

	clear
			-- Erase component content.
		require
			is_not_on_top: not is_topcomponent
			is_activated: is_activated
		do
			tablerow_set.wipe_out
			is_cleared := True
			refresh
		ensure
			is_cleared: is_cleared
		end

	change_selection (position: INTEGER)
			-- Select `tablerow_set' element at `position'.
			-- Remove any selection if `position' = `No_selection'.
		do
			if selected_tablerows.index /= position then
				Precursor (position)
				if attached db_fields_component as l_comp then
					if position = No_selection then
						l_comp.clear
					else
						l_comp.refresh (tablerow_set.item.table_description)
					end
				end
				if tablerow_set.before then
					other_clear
				else
					other_read
				end
			end
		end

feature {NONE} -- Access

	tablerow_set: ARRAYED_LIST [DB_TABLE]
			-- Current set of table rows reference.

	db_fields_component: detachable DV_TABLEROW_FIELDS
			-- Component displaying selected database table row.

	db_searcher: detachable DV_SEARCHER
			-- Component giving database table rows to display.

	db_creator: detachable DV_CREATOR
			-- Component creating database table rows.

	necessary_table_list: ARRAYED_LIST [DV_TABLE_COMPONENT]
			-- List of subcomponents representing a necessary table
			-- for represented table.

	dependent_table_list: ARRAYED_LIST [DV_TABLE_COMPONENT]
			-- List of subcomponents representing a dependent table
			-- on represented table.

	subsearcher_list: ARRAYED_LIST [DV_TYPED_SEARCHER]
			-- List of searchers corresponding to subcomponents.

	writing_control: detachable DV_SENSITIVE_CONTROL
			-- Enable the user to write the database at run-time.

	refreshing_control: detachable DV_SENSITIVE_CONTROL
			-- Enable the user to refresh display from the database at run-time.

	deleting_control: detachable DV_SENSITIVE_CONTROL
			-- Enable the user to delete rows in the database at run-time.

feature {NONE} -- Status report

	is_cleared: BOOLEAN
			-- Is component cleared?
			-- Component is cleared when a table row would not
			-- make sense. If component is not cleared but displays
			-- no table row, a table row can be created.
			-- Top component cannot be cleared as a new table row
			-- always makes sense.

	is_just_created: BOOLEAN
			-- Has a database table row just been created?

feature {NONE} -- Basic operations

	update_controls_sensitiveness
			-- Update controls sensitiveness according to `tablerow_set'.
		do
			if is_cleared then
				if attached writing_control as l_write_ctrl then
					l_write_ctrl.disable_sensitive
				end
				if attached refreshing_control as l_refresh_ctrl then
					l_refresh_ctrl.disable_sensitive
				end
				if attached db_creator as l_db_creator then
					l_db_creator.disable_sensitive
				end
				if attached deleting_control as l_del_ctrl then
					l_del_ctrl.disable_sensitive
				end
			elseif tablerow_set.is_empty or else tablerow_set.before then
				if attached writing_control as l_write_ctrl then
					l_write_ctrl.disable_sensitive
				end
				if attached refreshing_control as l_refresh_ctrl then
					l_refresh_ctrl.enable_sensitive
				end
				if attached db_creator as l_db_creator then
					l_db_creator.enable_sensitive
				end
				if attached deleting_control as l_del_ctrl then
					l_del_ctrl.disable_sensitive
				end
			else
				if attached writing_control as l_write_ctrl then
					l_write_ctrl.enable_sensitive
				end
				if attached refreshing_control as l_refresh_ctrl then
					l_refresh_ctrl.enable_sensitive
				end
				if attached db_creator as l_db_creator then
					l_db_creator.enable_sensitive
				end
				if attached deleting_control as l_del_ctrl then
					l_del_ctrl.enable_sensitive
				end
			end
		end

	delete_after_confirmation
			-- Ask for confirmation and delete currently displayed table row in the database.
		require
			is_activated: is_activated
		do
			if attached confirmation_handler as l_conf_handler and attached table_description as l_descr then
				l_conf_handler.call ([deletion_confirmation (l_descr.Table_name), agent delete])
			end
		end

	delete
			-- Delete currently displayed table row in the database.
		require
			is_activated: is_activated
		do
			database_handler.delete_tablerow (tablerow_set.item)
			if database_handler.has_error then
				if attached warning_handler as l_warn_handler then
					if attached database_handler.error_message as l_msg then
						l_warn_handler.call ([l_msg])
					else
						l_warn_handler.call (["Unknown error"])
					end
				end
			else
						if attached status_handler as l_handler and attached table_description as l_descr then
					l_handler.call ([deletion_done (l_descr.Table_name)])
				end
				refresh_from_database
			end
		end

	write
			-- Write currently displayed table row in the database.
		require
			is_activated: is_activated
		local
			l_updated_table_row: detachable DB_TABLE
		do
			if attached db_fields_component as l_comp then
				l_updated_table_row := l_comp.updated_tablerow (tablerow_set.item)
				if l_updated_table_row /= Void then
					database_handler.update_tablerow (l_updated_table_row)
					if database_handler.has_error then
						if attached warning_handler as l_warn_handler then
							if attached database_handler.error_message as l_msg then
								l_warn_handler.call ([l_msg])
							else
								l_warn_handler.call (["Unknown error"])
							end
						end
					else
						if attached status_handler as l_handler and attached table_description as l_descr then
							l_handler.call ([update_done (l_descr.Table_name)])
						end
						refresh_from_database
					end
				elseif attached warning_handler as l_warn_handler then
					l_warn_handler.call ([l_comp.error_message])
				end
			end
		end

	refresh
			-- Refresh display from `tablerow_set'.
		require
			is_activated: is_activated
		do
			if attached db_tablerow_navigator as l_nav then
				l_nav.refresh
			end
			if attached db_fields_component as l_comp then
				if tablerow_set.is_empty then
					l_comp.clear
				else
					l_comp.refresh (tablerow_set.item.table_description)
				end
			end
			update_controls_sensitiveness
			if tablerow_set.is_empty then
				other_clear
			else
				other_read
			end
		end

	other_read
			-- Refresh subcomponents from `tablerow_set.item'.
		require
			is_activated: is_activated
			not_empty: not tablerow_set.is_empty
			valid_index: tablerow_set.valid_index (tablerow_set.index)
		do
			from
				subsearcher_list.start
			until
				subsearcher_list.after
			loop
				subsearcher_list.item.read_from_tablerow (tablerow_set.item)
				subsearcher_list.forth
			end
		end

	other_clear
			-- Clear subcomponents.
		require
			is_activated: is_activated
		do
			from
				subsearcher_list.start
			until
				subsearcher_list.after
			loop
				subsearcher_list.item.clear
				subsearcher_list.forth
			end
		end

	set_handlers (comp: DV_TABLE_COMPONENT)
			-- Set lacking handlers of `comp' with existing handlers.
		require
			not_activated: not is_activated
		do
			if attached status_handler as l_status_handler and then not comp.status_handler_set then
				comp.set_status_handler (l_status_handler)
			end
			if attached warning_handler as l_warn_handler and then not comp.warning_handler_set then
				comp.set_warning_handler (l_warn_handler)
			end
			if attached confirmation_handler as l_conf_handler and then not comp.confirmation_handler_set then
				comp.set_confirmation_handler (l_conf_handler)
			end
		end

	search_or_start (item: DB_TABLE)
			-- If `tablerow_set' contains `item', go to `item'. Otherwise,
			-- go to first element.
			-- Equality used is ID equality.
			--| FIXME: Remove this awful thing.
		require
			is_activated: is_activated
			set_not_empty: not tablerow_set.is_empty
		local
			searched_id: ANY
			found: BOOLEAN
		do
			searched_id := item.table_description.id
			from
				tablerow_set.start
			until
				found or else tablerow_set.after
			loop
				found := searched_id.is_equal (tablerow_set.item.table_description.id)
				if not found then
					tablerow_set.forth
				end
			end
			if tablerow_set.after then
				tablerow_set.start
			end
		end

invariant
	top_is_not_cleared: is_topcomponent implies not is_cleared

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 DV_TABLE_COMPONENT