note
	description: "[
		Used as common ancestor to all "windows" in a system built using the
		"jj_vision" cluster.  It provides a way through feature `draw_views'
		for updating all the views which contain that `target'.
		Alternatively, the view
		can force the redraw of views containing any of several objects by passing
		a set of changed objects to feature `draw_views_with_set'.
		The class should be an ancestor ancestor along with
		some effected EV_WIDGET.
		NOTE:  Views which are `is_destroyed' are removed from the global `views' set
		in feature `draw_views'.
		]"
	date: "18 Jul 03"
	author:		"Jimmy J. Johnson"
	copyright:	"Copyright 2012, Jimmy J. Johnson"
	license:	"Eiffel Forum License v2 (see forum.txt)"
	URL: 		"$URL: file:///F:/eiffel_repositories/jj_vision/trunk/interface/views/view.e $"
	date:		"$Date: 2015-10-24 07:32:40 -0700 (Sat, 24 Oct 2015) $"
	revision:	"$Revision: 23 $"

deferred class
	VIEW

inherit

	DIMABLE
		undefine
			default_create
		redefine
			set_dimming_level
		end

	SHARED
		undefine
			default_create
		end

	PIXEL_BUFFERS
		undefine
			default_create
		end

--create
--	make

feature {NONE} -- Initialization

	make (a_target: like target)
			-- Create a new view to display `a_target'
		do
				-- This assignment is required to avoid violating a
				-- precondition "not_empty" later when calling
				-- feature `create_interface_objects".
			target_imp := a_target
				-- `default_create from:
				-- 1) {EV_ANY} calls `create_interface_objects', later `initialize'
				-- 2) {EV_MODEL calls only `create_interface_objects'
			default_create
			set_target (a_target)	-- calls `draw'
--			draw
		ensure
			target_imp /= Void
		end

	create_interface_objects
			-- Create objects to be used by `Current' in `initialize'
			-- Implemented by descendants to create attached objects
			-- in order to adhere to void-safety due to the implementation
			-- bridge pattern.
			-- Called by `default_create' from {EV_ANY} or {EV_MODEL}
		require
			not_interface_objects_created: not is_interface_objects_created
		do
			create pixmap.make_with_pixel_buffer (Icon_new_class_color_buffer)
			is_interface_objects_created := true
		ensure
			interface_objects_created: is_interface_objects_created
		end

	initialize
			-- Initialize the view and insert it into a global list of views.
		require
			view_not_initialized: not is_view_initialized
		do
--			initialize_dimable
			dimming_level := Dim
			previous_dimming_level := Dimmer
			add_actions
			is_view_initialized := True
		ensure
			initialized: is_view_initialized
		end

	add_actions
			-- Add any actions to Current.
		require
			not_initialized: not is_view_initialized
		do
			pointer_button_press_actions.extend (agent on_prepick_right_click)
			pick_actions.extend (agent on_picked)
			pointer_motion_actions.extend (agent on_postput_move)
		end

feature -- Status report

	is_interface_objects_created: BOOLEAN
			-- Has `create_interface_objects' been called?

	is_view_initialized: BOOLEAN
			-- Has `initialize' been called?

	is_pick_notifiable: BOOLEAN
			-- Should Current be notified by other views (normally
			-- a view contained in Current) that some event has
			-- occurred?
			-- Feature `set_parent_view' must have been called on
			-- the child view for this to take effect.

	has_parent_view: BOOLEAN
			-- Does Current know the view in which it resides?
		do
			Result := attached parent_view_imp
		end

feature -- Access

	application: JJ_APPLICATION
			-- The application in which Current resides.
		do
			check attached {JJ_APPLICATION} (create {EV_ENVIRONMENT}).application as app then
					-- because this class is for use in a {JJ_APPLICATION}.
				Result := app
			end
		end

	pixmap: EV_PIXMAP
			-- The pixmap associated with this view

--	application: JJ_APPLICATION is
--			-- Convience feature for obtaining the current application.
--		local
--			app: JJ_APPLICATION
--		once
--			app ?= (create {EV_ENVIRONMENT}).application
--			check
--				jj_application_exists: app /= Void
--					-- Because VIEWs are used in JJ_APPLICATIONs.
--			end
--		ensure
--			result_exists: Result /= Void
--		end

--	command_manager: COMMAND_MANAGER is
--			-- Manages the COMMAND's called by the system to allow undo/redo capabilities.
--			-- (This is a handle to the `command_manager' from a JJ_APPLICATION; putting it
--			-- here instead of in SHARED allows redefinition of the `command_manager' in
--			-- descendents of JJ_APPLICATION.
--		once
--			Result := application.command_manager
--		end

	frozen target: attached like target_imp
			-- The primary target in this view.  This feature along with
			-- `set_target' allows the view to handle one target specially.
		require
			not_empty: not is_view_empty
		do
			check attached target_imp as t then
				Result := t
			end
		end

	parent_window: JJ_MAIN_WINDOW
			-- The {JJ_MAIN_WINDOW} if any which contains this view.
			-- Must redefine to change type.
		do
			check attached {EV_CONTAINABLE} Current as c then
				Result := recursive_parent_window (c)
			end
		ensure
			valid_result: Result /= Void
		end

	parent_tool: detachable TOOL
			-- The TOOL, if any, in which Current resides.
		local
			con: detachable EV_CONTAINER
		do
			from con := parent
			until Result /= Void or else con = Void
			loop
				if attached {TOOL} con as t then
					Result := t
				else
					con := con.parent
				end
			end
		end

	parent_view: attached like parent_view_imp
			-- The view in which Current resides.
			-- Provides a way for a model view to notify a parent
			-- container of changes
		require
			has_parent_view: has_parent_view
		do
			check attached parent_view_imp as p then
				Result := p
			end
		end

	parent: detachable EV_CONTAINER
			-- Parent of the current view.
			-- To be effected by joining with an EV_ class
		deferred
		end

	state: VIEW_STATE
			-- A snapshot of the current look of the view
		do
			create Result.make (Current)
		end

feature -- Element change

	set_target (a_target: like target)
			-- Change the value of `target' and realign the table or list
			-- of views in which `a_target' is displayed.
		do
--			print ("VIEW.set_target:  a_target = " + target.out + "%N")
				-- Remove the old association
			if attached target_imp and then target /= a_target then
				check
					has_associated_view: views_table.has (target)
						-- because `target_imp' not Void and `make'
				end
				views_table.prune (Current)
				target_imp := Void
			end
			if target_imp = Void then
					-- the expected case, except of initial creation
--				print ("VIEW.set_target:  if statement target_imp = Void %N")
				target_imp := a_target
				views_table.extend (Current)
			elseif not views_table.has_view (Current) then
--				print ("VIEW.set_target:  not views_table.has (Current) %N")
				check
					same_object: target_imp = a_target
						-- because of assignment statement in `make'
				end
				views_table.extend (Current)
			end
			draw
		ensure
			has_target: has_target (a_target)
			target_imp /= Void
		end

	set_parent_view (a_view: VIEW)
			-- Set `parent_view' to `a_view', allowing Current to
			-- notify `a_view' of some event (e.g. right click).
		do
			parent_view_imp := a_view
		end

	set_dimming_level (a_level: like dimming_level)
			-- Change the `dimming_level'
		do
			Precursor (a_level)
--			paint
		end

feature -- Basic operations

--	paint
--			-- Draw the current view when some property like
--			-- background color changes
--		do
--		end

	draw
			-- Draw the current view.
			-- Default does nothing
		require
			view_is_drawable: not is_draw_disabled
		do
		end

	draw_other_views (a_target: ANY)
			-- Draw all views that contain `a_target' except Current
			-- This also cleans any "destroyed" views from the `views' set.
		require
			target_exists: a_target /= Void
		local
			b: BOOLEAN
		do
			b := is_draw_disabled
			disable_drawing
			draw_views (a_target)
			if not b then
				enable_drawing
			end
		end

	draw_views (a_target: ANY)
			-- Draw the views which contain `a_target'.
		local
			lin: LINEAR [VIEW]
			marks: LINKED_SET [VIEW]	-- Views marked for removal.
			v: VIEW
		do
			create marks.make
			lin := views_table.linear (a_target)
			from lin.start
			until lin.after
			loop
				v := lin.item
				if v.is_destroyed then
					marks.extend (v)
				elseif not v.is_draw_disabled then
					v.draw
				end
				lin.forth
			end
				-- Clean out any views that are no longer usable.
			from marks.start
			until marks.exhausted
			loop
				views_table.prune (marks.item)
				marks.forth
			end
		end

	draw_views_with_set (a_set: LINEAR [ANY])
			-- Draw the views which contain any of the objects in `a_set'.
			-- This also cleans any "destroyed" views from the `views' set.
		require
			set_exists: a_set /= Void
		local
			lin: LINEAR [VIEW]
			marks: LINKED_SET [VIEW]	-- Views marked for removal.
			v: VIEW
		do
			create marks.make
			lin := views_table.linear_with_set (a_set)
			from lin.start
			until lin.after
			loop
				v := lin.item
				if v.is_destroyed then
					marks.extend (v)
				elseif not v.is_draw_disabled then
					v.draw
				end
				lin.forth
			end
				-- Clean out any views that are no longer usable.
			from marks.start
			until marks.exhausted
			loop
				views_table.prune (marks.item)
				marks.forth
			end
		end

	draw_all_views
			-- Draw *all* the views in the system.
			-- Also cleans any "destroyed" views for the `views' set.
		local
			lin: LINEAR [VIEW]
			marks: LINKED_SET [VIEW]	-- Views marked for removal.
			v: VIEW
		do
			create marks.make
			lin := views_table.linear_representation
			from lin.start
			until lin.after
			loop
				v := lin.item
				if v.is_destroyed then
					marks.extend (v)
				elseif not v.is_draw_disabled then
					v.draw
				end
				lin.forth
			end
				-- Clean out any views that are no longer usable.
			from marks.start
			until marks.exhausted
			loop
				views_table.prune (marks.item)
				marks.forth
			end
		end

feature -- Status report

	is_in_default_state: BOOLEAN = true
			-- Is `Current' in its default state?
			-- The intent of this class is to be joined with an EV_WIDGET.
			-- The default state of a descendent is most likely not the same
			-- as the default state of the parent EV_WIDGET (after all, a
			-- new type widget is being defined that, by definition looks
			-- different from the parent.)
			-- This silly feature is the post-condition of `default_create'
			-- from EV_WIDGET, which required a choice--either redefine this
			-- feature in every EV_WIDGET descendant or, as chosen here, use
			-- this version (the EV_WIDGET version is irrelavent anyway) and
			-- undefine it in the inherit class of the EV_WIDGET parent.

	is_destroyed: BOOLEAN
			-- Has the view been destroyed?
			-- This will be joined with an EV_WIDGET feature
		deferred
		end

	is_view_empty: BOOLEAN
			-- Are there no objects in this VIEW?
		do
			Result := target_imp = Void
		end

	is_draw_disabled: BOOLEAN
			-- Can the view be drawn using 'draw'?

feature -- Status setting

	disable_drawing
			-- Block the view from being redrawn by calling draw.
			-- Used to reduce number of calls to draw.
		do
			is_draw_disabled := True
		ensure
			drawing_not_allowed: is_draw_disabled
		end

	enable_drawing
			-- Allow the view to be redrawn on a call to draw.
		do
			is_draw_disabled := False
		ensure
			drawing_allowed: not is_draw_disabled
		end

feature -- Query

	has_target (a_target: like target): BOOLEAN
			-- Does Current contain `a_target'?
		do
			Result := target_imp = a_target
		end

	display_name (a_object: ANY): STRING
			-- An identifying "out" value corresponding to `a_object'.
		require
			object_exists: a_object /= Void
		do
			if attached {EDITABLE} a_object as e then
				Result := e.display_name
			else
				Result := "fix me"
				print ("VIEW.display_name -- get from once table?")
			end
		ensure
			result_exists: Result /= Void
		end

	yes_cursor (a_target: ANY): EV_POINTER_STYLE
			-- Cursor for `a_target'.
		do
			create Result.make_with_pixel_buffer (Icon_new_class_color_buffer, 0, 0)
		end

	no_cursor (a_target: ANY): EV_CURSOR
			-- Cursor for `a_target'.
		do
			create Result
		end

	linear (a_object: ANY): LINEAR [VIEW]
			-- List of views in which `a_object' is displayed.  The
			-- resulting list could be empty.
		do
			Result := views_table.linear (a_object)
		end

	linear_with_set (a_set: LINEAR [ANY]): LINEAR [VIEW]
			-- List of views in which any of the objects in `a_set'
			-- is displayed.
		do
			Result := views_table.linear_with_set (a_set)
		end

	frozen post_pick_move_agent: PROCEDURE [TUPLE [a_x, a_y: INTEGER;
								 a_x_tilt, a_y_tilt, a_pressure: DOUBLE;
								 a_screen_x, a_screen_y: INTEGER]]
			-- Creates an agent out of `on_postput_move' which is added to
			-- the `pointer_motion_actions' of *ALL* views when a pick occurs
			-- (see `on_picked').  Holding on to this agent as a once feature
			-- allows `on_postput_move' to remove this agent from *ALL* the
			-- views after the pick has ended and the mouse is moved [in one
			-- of the views].
		once
			Result := agent on_postput_move
		end

feature -- Basic operations

	target_changed_operations
			-- This feature is called by `notify_views' to perform
			-- actions when that view's `target' was modified.
		do
			print ("{VIEW}.target_changed_operations %N")

		end

	pick_notified_operations (a_target: ANY)
			-- React to the pick of `a_target' from some view
		do
			print ("{VIEW}.pick_notified_operations on {" + generating_type.name + "}%N")

		end

	pick_ended_operations
			-- React to the end of a pick and drop operation
		do
			print ("{VIEW}.pick_ended_operations on {" + generating_type.name + "} %N")

		end

feature {NONE} -- Agents and support (actions)

	frozen on_prepick_right_click (x, y, button: INTEGER;
								x_tilt, y_tilt, pressure: DOUBLE;
								screen_x, screen_y: INTEGER)
			-- Notify the parent of all views that contain `target'
			-- that a pick event occurred involving `target'.
		local
			lin: LINEAR [VIEW]
			marks: LINKED_SET [VIEW]	-- Views marked for removal.
			v: VIEW
		do
--			is_picking.set_item (true)
			create marks.make
			lin := views_table.linear (target)
			from lin.start
			until lin.after
			loop
				v := lin.item
				if v.is_destroyed then
					marks.extend (v)
				elseif v.has_parent_view then
						-- Notifiy the parent view of the pick
					v.parent_view.pick_notified_operations (target)
						-- Save the view for notification when pick ends
					pick_notified_views.extend (v.parent_view)
				end
				lin.forth
			end
				-- Clean out any views that are no longer usable.
			from marks.start
			until marks.exhausted
			loop
				views_table.prune (marks.item)
				marks.forth
			end
		end

	frozen on_picked (a_x, a_y: INTEGER)
			-- Notify the parent of all views that contain `target'
			-- that a pick event occurred involving `target'.
		local
			lin: LINEAR [VIEW]
			marks: LINKED_SET [VIEW]	-- Views marked for removal.
			v: VIEW
		do
			is_picking.set_item (true)
--			create marks.make
--			lin := views_table.linear (target)
--			from lin.start
--			until lin.after
--			loop
--				v := lin.item
--				if v.is_destroyed then
--					marks.extend (v)
--				elseif v.has_parent_view then
--						-- Notifiy the parent view of the pick
--					v.parent_view.pick_notified_operations (target)
--						-- Save the view for notification when pick ends
--					pick_notified_views.extend (v.parent_view)
--				end
--				lin.forth
--			end
--				-- Clean out any views that are no longer usable.
--			from marks.start
--			until marks.exhausted
--			loop
--				views_table.prune (marks.item)
--				marks.forth
--			end
		end

	frozen on_postput_move (a_x, a_y: INTEGER;
								 a_x_tilt, a_y_tilt, a_pressure: DOUBLE;
								 a_screen_x, a_screen_y: INTEGER)
			-- Feature added as agent to `pointer_motion_actions'
			-- to react after a pnp operations has ended.  It calls
			-- feature `postput_operations'.
			-- Redefine `postput_operations' to clean up any operations
			-- that occurred in `do_prepick_operations'.
			-- See index clause for information on pick-and-put.
		local
 			v: VIEW
		do
			print ("{VIEW}.on_postput_move  on " + generating_type.name + "%N")
				-- Notify views when a transport has ended.
			if is_picking.item and then not application.transport_in_progress then
				print ("%T `is_picking and not transport_in_progress   pick_notified_views.count = " + pick_notified_views.count.out + "%N")
				is_picking.set_item (false)
					-- Inform any parent views that the pick has ended
				from pick_notified_views.start
				until pick_notified_views.after
				loop
					v := pick_notified_views.item
					v.pick_ended_operations
					pick_notified_views.forth
				end
				pick_notified_views.wipe_out
			end
		end

	notify_changed (a_target: ANY)
			-- Inform the views that have `a_target' that `a_target'
			-- has changed (i.e. call `on_target_changed' for those
			-- views).
			-- This also cleans any "destroyed" views from the `views' set.
		local
			lin: LINEAR [VIEW]
			marks: LINKED_SET [VIEW]	-- Views marked for removal.
			v: VIEW
		do
			create marks.make
			lin := views_table.linear (a_target)
			from lin.start
			until lin.after
			loop
				v := lin.item
				if v.is_destroyed then
					marks.extend (v)
				elseif not v.is_draw_disabled then
					v.target_changed_operations
				end
				lin.forth
			end
				-- Clean out any views that are no longer usable.
			from marks.start
			until marks.exhausted
			loop
				views_table.prune (marks.item)
				marks.forth
			end
		end

	frozen is_picking: BOOLEAN_REF
			-- Is a pick-and-put (PNP) operation in progress?
			-- It seems the PNP operations intercepts events system-
			-- wide, so this is a global reference.
			-- See index clause for information on pick-and-put.
		once
			create Result
		end

	pick_notified_views: LINKED_SET [VIEW]
			-- List of views that were notfied by `on_picked'
		once
			create Result.make
		end

feature -- Action sequences

	pointer_button_press_actions: EV_POINTER_BUTTON_ACTION_SEQUENCE
			-- Actions to be performed when screen pointer button is pressed.
			-- Defined here as place-holder to be undefined (i.e. joined) to
			-- the version from {EV_WIDGET} or {EV_MODEL}.
			-- See index clause for information on pick-and-put.
		deferred
		end

	pointer_motion_actions: EV_POINTER_MOTION_ACTION_SEQUENCE
			-- Actions to be performed when screen pointer moves.
			-- Defined here as place-holder to be undefined (i.e. joined) to
			-- the version from {EV_WIDGET} or {EV_MODEL}.
			-- See index clause for information on pick-and-put.
		deferred
		end

	pick_actions: EV_PND_START_ACTION_SEQUENCE
			-- Actions to be performed when `pebble' is picked up.
			-- Defined here as place-holder to be undefined by some
			-- {EV_WIDGET} or {EV_MODEL}.
		deferred
		end

	drop_actions: EV_PND_ACTION_SEQUENCE	--EV_PND_START_ACTION_SEQUENCE
			-- Actions to be performed when a pebble is dropped here.
		deferred
		end

feature {NONE} -- Implementation

	recursive_parent_window (a_containable: EV_CONTAINABLE): JJ_MAIN_WINDOW
			-- The {JJ_MAIN_WINDOW} which contains this tool.
			-- This procedure was provided by Julian Rodgers from the Eiffel
			-- users group.
		do
			check attached {EV_CONTAINER} a_containable.parent as cur_parent then
					-- because parent of EV_CONTAINABLE must be an EV_CONTAINER
				if attached {JJ_MAIN_WINDOW} cur_parent as w then
					Result := w
				else
					check attached {EV_CONTAINABLE} cur_parent as con_parent then
							-- because, if `cur_parent' is not a {JJ_MAIN_WINDOW} it must be EV_CONTAINABLE
						Result := recursive_parent_window (con_parent)
					end
				end
			end
		ensure
			result_exists: Result /= Void
		end

feature {NONE} -- Implemetation

	target_imp: detachable ANY
			-- Detachable implementation of `target' for void safety

	views_table: VIEW_TARGET_TABLE
			-- Global table associating objects to views
		once
			create Result
		end

	parent_view_imp: detachable VIEW
			-- Detachable implementation of `parent_view'

invariant

--	not_empty: target_imp /= Void

end
