note
	description: "Window manager for tools."
	legal: "See notice at end of class."
	status: "See notice at end of class."
	date: "$Date$"
	revision: "$Revision$"

class
	EB_WINDOW_MANAGER

inherit
	ANY

	EB_SHARED_INTERFACE_TOOLS
		export
			{NONE} all
		end

	EB_CONSTANTS
		export
			{NONE} all
		end

	SHARED_EIFFEL_PROJECT
		export
			{NONE} all
		end

	EB_CLUSTER_MANAGER_OBSERVER
			-- Just to have a reference to the cluster manager.
		export
			{NONE} all
		redefine
			on_project_loaded,
			on_project_unloaded
		end

	PROJECT_CONTEXT
		export
			{NONE} all
		end

	EB_SHARED_GRAPHICAL_COMMANDS
		export
			{NONE} all
		end

	EV_SHARED_APPLICATION
		export
			{NONE} all
		end

	ES_SHARED_DIALOG_BUTTONS
		export
			{NONE} all
		end

create
	make

feature {NONE} -- Initialization

	make
			-- Make a window manager.
		do
			create managed_windows.make (10)
			create focused_windows.make (5)

			create minimize_all_cmd.make
			minimize_all_cmd.set_pixmap (pixmaps.icon_pixmaps.windows_minimize_all_icon)
			minimize_all_cmd.set_tooltip (Interface_names.e_Minimize_all)
			minimize_all_cmd.set_menu_name (Interface_names.m_Minimize_all)
			minimize_all_cmd.set_tooltext (Interface_names.b_Minimize_all)
			minimize_all_cmd.set_name ("Minimize_all")
			minimize_all_cmd.add_agent (agent minimize_all)
			minimize_all_cmd.enable_sensitive

			create raise_all_cmd.make
			raise_all_cmd.set_pixmap (pixmaps.icon_pixmaps.windows_raise_all_icon)
			raise_all_cmd.set_tooltip (Interface_names.e_Raise_all)
			raise_all_cmd.set_menu_name (Interface_names.m_Raise_all)
			raise_all_cmd.set_tooltext (Interface_names.b_Raise_all)
			raise_all_cmd.set_name ("Raise_all")
			raise_all_cmd.add_agent (agent raise_all)
			raise_all_cmd.enable_sensitive

			create raise_all_unsaved_cmd.make
			raise_all_unsaved_cmd.set_pixmap (pixmaps.icon_pixmaps.windows_raise_all_unsaved_icon)
			raise_all_unsaved_cmd.set_tooltip (Interface_names.e_Raise_all_unsaved)
			raise_all_unsaved_cmd.set_menu_name (Interface_names.m_Raise_all_unsaved)
			raise_all_unsaved_cmd.set_name ("Raise_all_unsaved")
			raise_all_unsaved_cmd.add_agent (agent raise_all_unsaved)
			raise_all_unsaved_cmd.enable_sensitive

			Eiffel_project.manager.load_agents.extend (agent on_project_loaded)
			Eiffel_project.manager.create_agents.extend (agent on_project_created)
			Eiffel_project.manager.close_agents.extend (agent on_project_unloaded)

			initialize_prettify_suggestion_detector
		end

feature -- Basic operations

	new_menu: EB_WINDOW_MANAGER_MENU
			-- Menu corresponding to current: This is a menu with
			-- the following entries: New Window, Minimize All and
			-- an entry for each opened window.
			--
			-- When this menu is not anymore needed, call `recycle' on it.
		do
			create Result.make (Current)
		end

	new_widget: EB_WINDOW_MANAGER_LIST
			-- Widget corresponding to current: This is a list with
			-- all opened windows.
			--
			-- When this list is not anymore needed, call `recycle' on it.
		do
			create Result.make (Current)
		end

	create_window
			-- Create a new development window and update `last_created_window'.
		local
			l_director: EB_DEVELOPMENT_WINDOW_DIRECTOR
		do
			create l_director.make
			l_director.construct
			initialize_window (l_director.develop_window, True)
		end

	load_window_session_data (a_dev_window: EB_DEVELOPMENT_WINDOW)
			-- Load `a_dev_window''s session data.
			-- If `a_dev_window' is void, a new development window will be created.
		local
			l_director: EB_DEVELOPMENT_WINDOW_DIRECTOR
		do
			create l_director.make
			l_director.construct_with_session_data (a_dev_window)
			initialize_window (l_director.develop_window, False)
		end

	initialize_window (a_window: EB_DEVELOPMENT_WINDOW; a_new_window: BOOLEAN)
			-- Initialize `a_window'.
			-- If `a_window' is not `a_new_window', we ignore window title.
		require
			a_window_not_void: a_window /= Void
		do
			managed_windows.extend (a_window)
			if a_window.stone = Void and then a_new_window then
					-- Set the title if a stone hasn't already been set by session manager.
				a_window.set_title (new_title)
			end
			if a_new_window then
				notify_observers (a_window, Notify_added_window)
			else
				notify_observers (a_window, Notify_changed_window)
			end
			last_created_window := a_window

				-- Show the window if not already shown
			if not a_window.is_visible then
				a_window.show
			end
			a_window.give_focus
		end

	create_dynamic_lib_window
			-- Create a new dynamic library window if necessary and display it.
		local
			a_window: EB_DYNAMIC_LIB_WINDOW
		do
			if not dynamic_lib_window_is_valid then
				create a_window.make
				managed_windows.extend (a_window)
				notify_observers (a_window, Notify_added_window)
				set_dynamic_lib_window (a_window)
			end

				-- Show the window
			dynamic_lib_window.show
			dynamic_lib_window.give_focus
		ensure
			valid_dynamic_lib_window: dynamic_lib_window_is_valid
		end

feature -- Access

	development_windows_with_class (a_class: CLASS_I): LIST [EB_DEVELOPMENT_WINDOW]
			-- List of all windows with `cl_name' opened.
		require
			a_class_not_void: a_class /= Void
		local
			l_index: INTEGER
		do
			create {ARRAYED_LIST [EB_DEVELOPMENT_WINDOW]} Result.make (managed_windows.count)
			from
				l_index := managed_windows.index
				managed_windows.start
			until
				managed_windows.after
			loop
				if
					attached {EB_DEVELOPMENT_WINDOW} managed_windows.item as a_dev and then
					a_dev.editors_manager.is_class_editing (a_class)
				then
					Result.extend (a_dev)
				end
				managed_windows.forth
			end
			managed_windows.go_i_th (l_index)
		ensure
			not_void: Result /= Void
		end

	compile_start_actions: ACTION_SEQUENCE [TUPLE]
			-- Actions to be performed when Eiffel compilation starts
		do
			if compile_start_actions_internal = Void then
				create compile_start_actions_internal
			end
			Result := compile_start_actions_internal
		ensure
			result_attached: Result /= Void
		end

	windows: BILINEAR [EB_WINDOW]
			-- All development windows managed by Current
		do
			Result := managed_windows.twin
		ensure
			result_attached: Result /= Void
		end

	development_windows: attached ARRAYED_LIST [EB_DEVELOPMENT_WINDOW]
			-- All development windows managed by Current
		local
			l_windows: like managed_windows
		do
			from
				l_windows := managed_windows.twin
				l_windows.start

				create Result.make (l_windows.count)
			until
				l_windows.after
			loop
				if attached {EB_DEVELOPMENT_WINDOW} l_windows.item as lt_dev_window then
					Result.extend (lt_dev_window)
				end

				l_windows.forth
			end
		end

feature {EB_SHARED_INTERFACE_TOOLS, EB_COMMAND} -- Access

	all_modified_classes: ARRAYED_LIST [CLASS_I]
			-- Retrieves a list of all modified classes
		local
			l_index: INTEGER
		do
			create Result.make (0)
			from
				l_index := managed_windows.index
				managed_windows.start
			until
				managed_windows.after
			loop
				if
					attached {EB_DEVELOPMENT_WINDOW} managed_windows.item as a_dev and then
					not a_dev.is_recycled and then
					a_dev.editors_manager.changed
				then
					Result.append (a_dev.editors_manager.changed_classes)
				end
				managed_windows.forth
			end
			managed_windows.go_i_th (l_index)
		end

feature -- Status report

	last_created_window: EB_DEVELOPMENT_WINDOW
			-- Window created by the last call to `create_window'.
			-- Void if none.

	development_windows_count: INTEGER
			-- number of visible development windows
		local
			l_index: INTEGER
		do
			from
				l_index := managed_windows.index
				managed_windows.start
			until
				managed_windows.after
			loop
				if attached {EB_DEVELOPMENT_WINDOW} managed_windows.item then
					Result := Result + 1
				end
				managed_windows.forth
			end
			managed_windows.go_i_th (l_index)
		end

	has_active_development_windows: BOOLEAN
			-- Are there any active development window up?
		do
			Result := development_windows_count /= 0
		end

	has_modified_windows: BOOLEAN
			-- Are there any window having been modified and not yet saved?
		local
			l_index: INTEGER
		do
			from
				l_index := managed_windows.index
				managed_windows.start
			until
				Result or else managed_windows.after
			loop
				if attached {EB_DEVELOPMENT_WINDOW} managed_windows.item as a_dev then
					Result := a_dev.any_editor_changed
				end
				managed_windows.forth
			end
			if not Result then
				Result := dynamic_lib_window_is_valid and then dynamic_lib_window.changed
			end
			managed_windows.go_i_th (l_index)
		end

	development_window_from_window (a_window: EV_WINDOW): detachable EB_DEVELOPMENT_WINDOW
			-- Return the development window whose widget is `a_window'.
		local
			l_index: INTEGER
		do
			from
				l_index := managed_windows.index
				managed_windows.start
			until
				managed_windows.after or else
				Result /= Void
			loop
				if
					managed_windows.item /= Void and then
					managed_windows.item.window = a_window and then
					attached {EB_DEVELOPMENT_WINDOW} managed_windows.item as dev
				then
					Result := dev
				end
				managed_windows.forth
			end
			managed_windows.go_i_th (l_index)
		end

	last_focused_development_window: detachable EB_DEVELOPMENT_WINDOW
			-- Return the development window which last had the keyboard focus.
		do
			from
				focused_windows.finish
			until
				Result /= Void or else focused_windows.before
			loop
				if attached {EB_DEVELOPMENT_WINDOW} focused_windows.item as dev then
					Result := dev
				end
				focused_windows.back
			end
			if Result = Void then
				Result := a_development_window
			end
		end

	last_focused_window: detachable EB_WINDOW
			-- Return the window which last had the keyboard focus.
			-- Return Void if no window is focused.
		do
			if not focused_windows.is_empty then
				Result := focused_windows.last
			elseif not managed_windows.is_empty then
				Result := managed_windows.last
			end
		end

	a_development_window: detachable EB_DEVELOPMENT_WINDOW
			-- Return a random development window
		local
			l_index: INTEGER
		do
			from
				l_index := managed_windows.index
				managed_windows.start
			until
				managed_windows.after or Result /= Void
			loop
				if attached {EB_DEVELOPMENT_WINDOW} managed_windows.item as dev then
					Result := dev
				end
				managed_windows.forth
			end
			managed_windows.go_i_th (l_index)
		end

feature -- Query

	development_window_from_id (a_window_id: NATURAL_32): EB_DEVELOPMENT_WINDOW
			-- Retrieve a development window using a window id matched to {EB_DEVELOPMENT_WINDOW}.window_id
		require
			a_window_id_positive: a_window_id > 0
		local
			l_windows: like managed_windows
			i: INTEGER
		do
			l_windows := managed_windows
			i := l_windows.index
			from l_windows.start until l_windows.after or Result /= Void loop
				if attached {like development_window_from_id} l_windows.item as res then
					Result := res
				end
				if Result /= Void and then Result.window_id /= a_window_id then
					Result := Void
					l_windows.forth
				end
			end
			l_windows.go_i_th (i)
		end

	active_editor_for_class (a_class: CLASS_I): detachable EB_SMART_EDITOR
			-- Attempts to retrieve the most applicable editor for a given class.
			--
			-- `a_class': The class to retrieve the most applicable editor for.
			-- `Result': An editor which the supplied class is edited using, or Void if not being edited.
		require
			a_class_attached: a_class /= Void
		local
			l_editor: EB_SMART_EDITOR
			l_editors: DS_ARRAYED_LIST [EB_SMART_EDITOR]
		do
			l_editors := active_editors_for_class (a_class)
			if not l_editors.is_empty then
				from l_editors.start until l_editors.after loop
					l_editor := l_editors.item_for_iteration
					if Result = Void then
						Result := l_editor
					elseif l_editor.text_displayed.is_modified then
							-- Use modified version
						Result := l_editor
					end
					l_editors.forth
				end
			end
		end

	active_editors_for_class (a_class: CLASS_I): attached DS_ARRAYED_LIST [EB_SMART_EDITOR]
			-- Retrieves all applicable editors for a given class.
			--
			-- `a_class': The class to retrieve the most applicable editors for.
			-- `Result': A list of editors editing the supplied class.
		require
			a_class_attached: a_class /= Void
		do
			create Result.make_default
			if attached windows as l_windows then
				from l_windows.start until l_windows.after loop
					if
						attached {EB_DEVELOPMENT_WINDOW} l_windows.item_for_iteration as l_dev_window and then
						attached l_dev_window.editors_manager as l_editor_manager and then
						attached l_editor_manager.editor_editing (a_class) as l_editors
					then
						across
							l_editors as e
						loop
							Result.force_last (e.item)
						end
					end
					l_windows.forth
				end
			end
		ensure
			result_contains_attached_items: not Result.has (Void)
		end

feature -- Actions on a given window

	show_window (a_window: EB_WINDOW)
			-- Show the window.
		do
				-- We call `raise' since it takes care of really showing the window
				-- in case it is hidden or minimized.
			a_window.window.raise
			notify_observers (a_window, Notify_shown_window)
		end

	hide_window (a_window: EB_WINDOW)
			-- Hide the window
		do
			a_window.window.hide
			notify_observers (a_window, Notify_hidden_window)
		end

	destroy_window (a_window: EB_WINDOW)
			-- Destroy the window.
		do
				-- Remove this window from managed windows.
			managed_windows.prune_all (a_window)
			focused_windows.prune_all (a_window)

			if last_created_window = a_window then
				last_created_window := Void
			end

				-- Notify the observers
			notify_observers (a_window, Notify_removed_window)

				-- Destroy the window.
			a_window.destroy_imp
		end

	record_window_change (a_window: EB_WINDOW)
			-- Record that `a_window' has changed and notify the observers.
		do
			notify_observers (a_window, Notify_changed_window)
		end

feature -- Actions on all windows

	raise_all_unsaved
			-- Raise all the editors.
		do
			for_all (agent raise_unsaved_action)
		end

	refresh_all
			-- Refresh all the windows after a compilation
		do
			for_all (agent refresh_action)
		end

	save_all
			-- Ask each window to save its content.
		do
			for_all (agent save_action)
		end

	save_all_before_compiling
			-- Ask each window to save its content.
		do
			for_all (agent save_before_compiling_action)
		end

	backup_all
			-- Create a backup file (.swp) for all development window.
		do
			not_backuped := 0
			for_all (agent backup_action)
		end

	not_backuped: INTEGER
			-- Number of files that could not be backed up during a back up.

	minimize_all
			-- Minimize all windows
		do
			for_all (agent minimize_action)
		end

	disable_all
			-- Disable sensitivity on all windows.
		do
			for_all (agent disable_action)
		end

	enable_all
			-- Enable sensitivity on all windows.
		do
			for_all (agent enable_action)
		end

	quick_refresh_all_margins
			-- Redraws the margins of all the editor windows.
		do
			for_all (agent quick_refresh_margin_action)
		end

	quick_refresh_all_editors
			-- Redraws the editors of all the windows.
		do
			for_all (agent quick_refresh_action)
		end

	raise_all
			-- Raise all windows.
		do
			for_all (agent show_window)
		end

	close_all
			-- Close all windows.
		local
			l_snapshot: like managed_windows
		do
			from
				l_snapshot := managed_windows.twin
				l_snapshot.start
			until
				l_snapshot.after
			loop
				close_action (l_snapshot.item)
				l_snapshot.forth
			end
		end

	synchronize_all_about_breakpoints
		do
			quick_refresh_all_margins
			for_all (agent synchronize_breakpoints_action)
		end

	synchronize_all
			-- A compilation is over. Warn all windows and tools.
		do
				-- Reload the cluster tree in the cluster manager.
			manager.refresh

				-- Get rid of all dead classes in the favorites.
			favorites.refresh

				-- Update the state of some commands.

			if not Eiffel_project.compilation_modes.is_precompiling then
				freeze_project_cmd.enable_sensitive
				precompilation_cmd.disable_sensitive
				Finalize_project_cmd.enable_sensitive
			else
				freeze_project_cmd.disable_sensitive
				precompilation_cmd.enable_sensitive
				Finalize_project_cmd.disable_sensitive
			end
			for_all (agent c_compilation_start_action)
			melt_project_cmd.enable_sensitive
			terminate_c_compilation_cmd.disable_sensitive
			refactoring_manager.enable_sensitive
			discover_melt_cmd.enable_sensitive
			analyze_cmd.enable_sensitive
			analyze_editor_cmd.update_sensitive
			analyze_parent_cmd.update_sensitive
			analyze_target_cmd.update_sensitive

			project_cancel_cmd.disable_sensitive
			clean_compile_project_cmd.enable_sensitive
			if process_manager.is_c_compilation_running then
				on_c_compilation_start
			else
				for_all (agent for_all (agent c_compilation_stop_action))
			end
			for_all (agent synchronize_action)
		end

	synchronize_all_favorites_tool
			-- Synchronize all Favorites tool
		do
			for_all (agent (a_window: EB_WINDOW)
				do
					if attached {EB_DEVELOPMENT_WINDOW} a_window as l_win and then attached l_win.favorites_manager as l_fav then
						l_fav.refresh
					end
				end
			)
		end

	display_message_and_percentage (m: READABLE_STRING_GENERAL; a_value: INTEGER)
			-- Display message `m' and `a_value' percentage in status bars of all development windows.
		require
			one_line_message: m /= Void and then (not m.has_code (('%N').natural_32_code) and not m.has_code (('%R').natural_32_code))
			a_value_valid: a_value >= 0 and then a_value <= 100
		local
			l_managed_windows: like managed_windows
			l_status_bar: EB_DEVELOPMENT_WINDOW_STATUS_BAR
		do
			from
					-- Make a twin of the list incase window is destroyed during
					-- indirect call to status bar `process_events_and_idle' which
					-- is needed to update the label and progress bar display.
				l_managed_windows := managed_windows.twin
				l_managed_windows.start
			until
				l_managed_windows.after
			loop
				if
					attached {EB_DEVELOPMENT_WINDOW} l_managed_windows.item as cv_dev and then
					not cv_dev.destroyed
				then
					l_status_bar := cv_dev.status_bar
					l_status_bar.display_message (m)
					l_status_bar.progress_bar.reset
					l_status_bar.progress_bar.set_value (a_value)
				end
				l_managed_windows.forth
			end
		end

	display_message (m: READABLE_STRING_GENERAL)
			-- Display a message in status bars of all development windows.
		require
			one_line_message: m /= Void and then (not m.has_code (('%N').natural_32_code) and not m.has_code (('%R').natural_32_code))
		local
			l_managed_windows: like managed_windows
		do
			from
					-- Make a twin of the list incase window is destroyed during
					-- indirect call to status bar `process_events_and_idle' which
					-- is needed to update the label and progress bar display.
				l_managed_windows := managed_windows.twin
				l_managed_windows.start
			until
				l_managed_windows.after
			loop
				if
					attached {EB_DEVELOPMENT_WINDOW} l_managed_windows.item as cv_dev and then
					not cv_dev.destroyed
				then
					cv_dev.status_bar.display_message (m)
				end
				l_managed_windows.forth
			end
		end

	display_percentage (a_value: INTEGER)
			-- Display `a_value' percentage in status bars of all development windows.
		require
			a_value_valid: a_value >= 0 and then a_value <= 100
		local
			l_managed_windows: like managed_windows
			pb: EB_PERCENT_PROGRESS_BAR
		do
			from
					-- Make a twin of the list incase window is destroyed during
					-- indirect call to status bar `process_events_and_idle' which
					-- is needed to update the label and progress bar display.
				l_managed_windows := managed_windows.twin
				l_managed_windows.start
			until
				l_managed_windows.after
			loop
				if
					attached {EB_DEVELOPMENT_WINDOW} l_managed_windows.item as cv_dev and then
					not cv_dev.destroyed
				then
					pb := cv_dev.status_bar.progress_bar
					pb.reset
					pb.set_value (a_value)
					pb.refresh_now
				end
				l_managed_windows.forth
			end
		end

	display_c_compilation_progress (mess: STRING_GENERAL)
			-- Display `mess' in status bars of all development windows.
		require
			mess_not_void: mess /= Void
			mess_not_empty: not mess.is_empty
		local
			l_managed_windows: like managed_windows
		do
			from
					-- Make a twin of the list incase window is destroyed during
					-- indirect call to status bar `process_events_and_idle' which
					-- is needed to update the label and progress bar display.
				l_managed_windows := managed_windows.twin
				l_managed_windows.start
			until
				l_managed_windows.after
			loop
				if
					attached {EB_DEVELOPMENT_WINDOW} l_managed_windows.item as cv_dev and then
					not cv_dev.destroyed
				then
					if cv_dev.status_bar.message.is_empty then
						cv_dev.status_bar.display_message (mess)
						state_message_waiting_count := 0
					else
						state_message_waiting_count := state_message_waiting_count + 1
						if state_message_waiting_count > max_waiting_count then
							state_message_waiting_count := 0
							cv_dev.status_bar.display_message (mess)
						end
					end
				end
				l_managed_windows.forth
			end
		end

	for_all_development_windows (p: PROCEDURE [EB_DEVELOPMENT_WINDOW])
			-- Call `p' on all development windows.
		local
			l_index: INTEGER
		do
			from
				l_index := managed_windows.index
				managed_windows.start
			until
				managed_windows.after
			loop
				if attached {EB_DEVELOPMENT_WINDOW} managed_windows.item as cv_dev then
					p.call ([cv_dev])
				end
				managed_windows.forth
			end
			if managed_windows.valid_index (l_index) then
				managed_windows.go_i_th (l_index)
			end
		end

feature {EB_SHORTCUT_MANAGER} -- Actions on all windows

	refresh_commands
			-- Refresh all windows commands.
		do
			for_all (agent refresh_commands_action)
		end

	refresh_external_commands
			-- Only refresh external commands
		do
			for_all (agent refresh_external_commands_action)
		end

feature {EB_WINDOW, EB_DEVELOPMENT_WINDOW_BUILDER} -- Events

	set_focused_window (w: EB_WINDOW)
			-- Tell `Current' `w' has been given the focus.
		do
			focused_windows.prune_all (w)
			focused_windows.extend (w)
		end

	try_to_destroy_window (a_window: EB_WINDOW)
			-- Destroy the window if it is possible.
			-- The window-level checks should be performed in `{EB_WINDOW}.destroy`.
			-- This method only takes into account the cases when closing the
			-- window means exiting the application.
		do
			if development_windows_count = 1 and then attached {EB_DEVELOPMENT_WINDOW} a_window then
				confirm_and_quit
			else
				if attached {EB_DEVELOPMENT_WINDOW} a_window as loc_development_window then
					loc_development_window.save_window_data
				end
				destroy_window (a_window)
			end
		end

feature {NONE} -- Exit implementation

	confirm_and_quit
			-- If a compilation is under way, do not exit.
		local
			l_exit_save_prompt: ES_SAVE_CLASSES_PROMPT
			l_warning: ES_WARNING_PROMPT
			l_classes: DS_ARRAYED_LIST [CLASS_I]
			l_exit: BOOLEAN
		do
			if Eiffel_project.initialized and then Eiffel_project.is_compiling then
				create l_warning.make_standard_with_cancel (warning_messages.w_exiting_stops_compilation)
				l_warning.set_button_text ({ES_DIALOG_BUTTONS}.ok_button, interface_names.b_force_exit)
				l_warning.set_button_icon ({ES_DIALOG_BUTTONS}.ok_button, pixmaps.icon_pixmaps.general_warning_icon)
				l_warning.set_button_text ({ES_DIALOG_BUTTONS}.cancel_button, interface_names.b_ok)
				l_warning.show_on_active_window
				l_exit := l_warning.dialog_result = {ES_DIALOG_BUTTONS}.ok_button
			else
				l_exit := True
			end

			if l_exit then
				if has_modified_windows then
					exit_application_cmd.set_already_confirmed (True)

					create l_classes.make_default
					all_modified_classes.do_all (agent l_classes.force_last)
					create l_exit_save_prompt.make_standard_with_cancel (interface_names.l_exit_warning)
					l_exit_save_prompt.classes := l_classes
					l_exit_save_prompt.set_button_action (l_exit_save_prompt.dialog_buttons.yes_button, agent save_and_quit)
					l_exit_save_prompt.set_button_action (l_exit_save_prompt.dialog_buttons.no_button, agent quit)
					l_exit_save_prompt.show_on_active_window
				else
					quit
				end
			end
		end

	kill_process_and_confirm_quit
			-- Kill running c compilation and external command, if any and then quit.
		do
			process_manager.terminate_process
			quit
		end

	save_and_quit
			-- Save all windows and destroy the last development window.
		do
			save_all
				-- Exit only when all windows have been saved.
			if not has_modified_windows then
				quit
			end
		end

	quit
			-- Destroy the last development window.
		do
			if process_manager.is_process_running then
				process_manager.confirm_process_termination_for_quiting (agent kill_process_and_confirm_quit, Void, last_focused_development_window.window)
			else
				Exit_application_cmd.ask_confirmation
			end
		end

feature -- Prettify suggestion

	initialize_prettify_suggestion_detector
		do
			create prettify_suggestion_detector
			prettify_suggestion_detector.install
		end

	prettify_suggestion_detector: ES_PRETTIFY_SUGGESTION_DETECTOR

feature -- Access: session data

	load_favorites
			-- Try to initialize the favorites with the session data.
		local
			retried: BOOLEAN
		do
				-- Give up if loading favorites has failed.
			if not retried then
				if favorites_storage = Void then
					create favorites_storage
				end
				if attached favorites_storage.data_from_storage as l_data then
					favorites.make_with_string (l_data)
					if favorites.loading_error then
						favorites_storage.store_data (Void)
					end
				end
			end
		rescue
			retried := True
			retry
		end

	save_favorites
			-- Try to save the favorites' state within the session data.
		local
			retried: BOOLEAN
		do
			if not retried then
				if favorites_storage = Void then
					create favorites_storage
				end
				favorites_storage.store_data (favorites.string_representation)
			end
		rescue
			retried := True
			retry
		end

	load_session
			-- Try to recreate previous project session, if any.
		local
			l_i, l_window_count: INTEGER
			l_retried: BOOLEAN
			l_managed_windows: like managed_windows
			l_dev_window: EB_DEVELOPMENT_WINDOW
		do
			check system_defined: eiffel_project.system_defined end
			if not l_retried then
			-- Attempt to load the 'session.wb' file from the project directory.
			-- If load fails then do nothing.
			-- Recreate previous session from retrieved session data.

				-- Remove any existing managed windows.
				from
					l_managed_windows := managed_windows.twin
					l_managed_windows.start
				until
					l_managed_windows.after
				loop
					if l_managed_windows.item /= Void then
						if attached {EB_DYNAMIC_LIB_WINDOW} l_managed_windows.item as l_dl_window then
							destroy_window (l_dl_window)
						end
						if
							l_dev_window = Void and then
							attached {EB_DEVELOPMENT_WINDOW} l_managed_windows.item as dev
						then
							l_dev_window := dev
						end
					end
					l_managed_windows.forth
				end

				check at_least_one_develop_window: l_dev_window /= Void end
				if attached {INTEGER_32_REF} l_dev_window.project_session_data.value (l_dev_window.development_window_data.development_window_count_id) as l_window_count_ref then
					l_window_count := l_window_count_ref.item
				end
				if l_window_count = 0 then
					-- At least one
					l_window_count := 1
				end

				from
					l_managed_windows := managed_windows.twin
					if l_managed_windows.valid_cursor_index (l_window_count + 1) then
						l_managed_windows.go_i_th (l_window_count + 1)
					else
						l_managed_windows.go_i_th (l_managed_windows.index + 1)
					end
					-- Only destroy extra windows
				until
					l_managed_windows.after
				loop
					destroy_window (l_managed_windows.item)
					l_managed_windows.forth
				end

				from
					l_i := 1
					l_managed_windows.start
				until
					l_i > l_window_count
				loop
					if not l_managed_windows.after then
						if attached {EB_DEVELOPMENT_WINDOW} l_managed_windows.item as dev then
							l_dev_window := dev
							load_window_session_data (dev)
						else
							check is_dev_window: False end
						end
						l_managed_windows.forth
					else
						load_window_session_data (Void)
					end

					l_i := l_i + 1
				end
			end
		rescue
			l_retried := True
			retry
		end

	save_session
			-- Try to store current session data.
		local
			retried: BOOLEAN
			l_develop_window: EB_DEVELOPMENT_WINDOW
		do
			check system_defined: eiffel_project.system_defined end
			if not retried then
					-- Attempt to save the 'session.wb' file to the current project directory.
					-- If save cannot be made then do nothing.
				l_develop_window := a_development_window
				if l_develop_window /= Void then
						-- Maybe we can't save development window count data in `project_session_data' or `session_data' since they are all *window* related data?
					l_develop_window.project_session_data.set_value (development_windows_count, l_develop_window.development_window_data.development_window_count_id)
				end

				for_all_development_windows (agent (a_develop_window: EB_DEVELOPMENT_WINDOW)
					require
						not_void: a_develop_window /= Void
					do
						a_develop_window.project_session_data.set_value (
							a_develop_window.save_layout_to_session,
							a_develop_window.development_window_data.development_window_project_data_id)
					end)
			end
		rescue
			retried := True
			retry
		end

feature {NONE} -- Implementation: session data

	favorites_storage: FAVORITES_STORAGE
			-- Favorites' storage

feature -- Events

	on_compile
			-- A compilation has been launched.
			-- Update the display accordingly, ie gray out all forbidden commands.
		do
			Melt_project_cmd.disable_sensitive
			Freeze_project_cmd.disable_sensitive
			Finalize_project_cmd.disable_sensitive
			Precompilation_cmd.disable_sensitive
			Project_cancel_cmd.enable_sensitive
			discover_melt_cmd.disable_sensitive
			analyze_cmd.disable_sensitive
			analyze_editor_cmd.disable_sensitive
			analyze_parent_cmd.disable_sensitive
			analyze_target_cmd.disable_sensitive
			refactoring_manager.disable_sensitive
			for_all (agent c_compilation_start_action)
			compile_start_actions.call (Void)
		end

	on_refactoring_start
			-- A refactoring has been started.
		do
			Melt_project_cmd.disable_sensitive
			Freeze_project_cmd.disable_sensitive
			Finalize_project_cmd.disable_sensitive
			Precompilation_cmd.disable_sensitive
			discover_melt_cmd.disable_sensitive
			analyze_cmd.disable_sensitive
			analyze_editor_cmd.disable_sensitive
			analyze_parent_cmd.disable_sensitive
			analyze_target_cmd.disable_sensitive
			for_all (agent c_compilation_start_action)
		end

	on_refactoring_end
			-- A refactoring has finished.
		do
			Melt_project_cmd.enable_sensitive
			Freeze_project_cmd.enable_sensitive
			Finalize_project_cmd.enable_sensitive
			Precompilation_cmd.enable_sensitive
			discover_melt_cmd.enable_sensitive
			analyze_cmd.enable_sensitive
			analyze_editor_cmd.update_sensitive
			analyze_parent_cmd.update_sensitive
			analyze_target_cmd.update_sensitive
			for_all (agent c_compilation_stop_action)
		end

	on_c_compilation_start
			-- Freezing or finalizing has been launched.
			-- Update the display accordingly, ie gray out all forbidden commands.
		do
			Freeze_project_cmd.disable_sensitive
			Finalize_project_cmd.disable_sensitive
			terminate_c_compilation_cmd.enable_sensitive
			for_all (agent c_compilation_start_action)
			run_workbench_cmd.disable_sensitive
			if process_manager.is_finalizing_running then
				run_finalized_cmd.disable_sensitive
			end
		end

	on_c_compilation_stop
			-- Freezing or finalizing has finished.
			-- Update the display accordingly.
		do
			Freeze_project_cmd.enable_sensitive
			Finalize_project_cmd.enable_sensitive
			terminate_c_compilation_cmd.disable_sensitive
			for_all (agent c_compilation_stop_action)
			run_workbench_cmd.enable_sensitive
			run_finalized_cmd.enable_sensitive
		end

	on_project_created
			-- A new project has been created. Update the state of some commands.
		do
			Melt_project_cmd.enable_sensitive
			Freeze_project_cmd.enable_sensitive
			Finalize_project_cmd.enable_sensitive
			Precompilation_cmd.enable_sensitive
			clean_compile_project_cmd.enable_sensitive
			System_cmd.enable_sensitive
			System_information_cmd.enable_sensitive
			discover_melt_cmd.enable_sensitive
			analyze_cmd.enable_sensitive
			analyze_editor_cmd.update_sensitive
			analyze_parent_cmd.update_sensitive
			analyze_target_cmd.update_sensitive
			source_control_cmd.update_sensitive
			for_all (agent create_project_action)
		end

	on_project_loaded
			-- A new project has been loaded. Warn all managed windows.
		do
			if Eiffel_project.initialized then
				if Eiffel_project.compilation_modes.is_precompiling then
					Melt_project_cmd.disable_sensitive
					Freeze_project_cmd.disable_sensitive
					Finalize_project_cmd.disable_sensitive
					Precompilation_cmd.enable_sensitive
					Run_project_cmd.disable_sensitive
					Run_workbench_cmd.disable_sensitive
					Run_finalized_cmd.disable_sensitive
					discover_melt_cmd.disable_sensitive
				else
					Melt_project_cmd.enable_sensitive
					Freeze_project_cmd.enable_sensitive
					Finalize_project_cmd.enable_sensitive
					Precompilation_cmd.disable_sensitive
					Run_project_cmd.enable_sensitive
					Run_workbench_cmd.enable_sensitive
					Run_finalized_cmd.enable_sensitive
					discover_melt_cmd.enable_sensitive
				end
				analyze_cmd.enable_sensitive
				analyze_editor_cmd.update_sensitive
				analyze_parent_cmd.update_sensitive
				analyze_target_cmd.update_sensitive
				source_control_cmd.update_sensitive
				clean_compile_project_cmd.enable_sensitive
				System_cmd.enable_sensitive
				System_information_cmd.enable_sensitive
				Export_cmd.enable_sensitive
				Document_cmd.enable_sensitive
			else
				Melt_project_cmd.disable_sensitive
				Precompilation_cmd.disable_sensitive
				Freeze_project_cmd.disable_sensitive
				Finalize_project_cmd.disable_sensitive
				System_cmd.disable_sensitive
				System_information_cmd.disable_sensitive
				Export_cmd.disable_sensitive
				Document_cmd.disable_sensitive
				Run_project_cmd.disable_sensitive
				Run_workbench_cmd.disable_sensitive
				Run_finalized_cmd.disable_sensitive
				discover_melt_cmd.disable_sensitive
				analyze_cmd.disable_sensitive
				analyze_editor_cmd.disable_sensitive
				analyze_parent_cmd.disable_sensitive
				analyze_target_cmd.disable_sensitive
				source_control_cmd.disable_sensitive
			end
				-- Recreate window configuration from last session of project if any.
			load_session
			load_favorites

			Manager.on_project_loaded
			for_all (agent load_project_action)
		end

	on_project_unloaded
			-- Current project has been closed. Warn all managed windows.
		do
			Manager.on_project_unloaded
			save_favorites
				-- Make development window session data persistent for future reloading.
			if eiffel_project.system_defined then
				save_session
			end
			for_all (agent unload_project_action)
			Melt_project_cmd.disable_sensitive
			Freeze_project_cmd.disable_sensitive
			Finalize_project_cmd.disable_sensitive
			Precompilation_cmd.disable_sensitive
			clean_compile_project_cmd.disable_sensitive
			System_cmd.disable_sensitive
			System_information_cmd.disable_sensitive
			Export_cmd.disable_sensitive
			Document_cmd.disable_sensitive
			Run_project_cmd.disable_sensitive
			Run_workbench_cmd.disable_sensitive
			Run_finalized_cmd.disable_sensitive
			discover_melt_cmd.disable_sensitive
			analyze_cmd.disable_sensitive
			analyze_editor_cmd.disable_sensitive
			analyze_parent_cmd.disable_sensitive
			analyze_target_cmd.disable_sensitive
			source_control_cmd.disable_sensitive
		end

feature {NONE} -- Implementation

	exiting_application: BOOLEAN
			-- Is the application currently in its closing phase?
			--| Used in `destroy_window' to avoid infinite reentrance.

	focused_windows: ARRAYED_LIST [EB_WINDOW]
			-- Focused windows (chronological order).

	close_action (a_window: EB_WINDOW)
			-- Action to be performed on `item' in `close_all'.
		do
			destroy_window (a_window)
			notify_observers (a_window, Notify_removed_window)
		end

	minimize_action (a_window: EB_WINDOW)
			-- Action to be performed on `item' in `minimize_all'.
		do
			a_window.window.minimize
		end

	disable_action (a_window: EB_WINDOW)
			-- Action to be performed on `item' in `disable_all'.
		do
			a_window.window.disable_sensitive
		end

	enable_action (a_window: EB_WINDOW)
			-- Action to be performed on `item' in `enable_all'.
		do
			a_window.window.enable_sensitive
		end

	refresh_action (a_window: EB_WINDOW)
			-- Action to be performed on `item' in `refresh'.
		do
			a_window.refresh
			notify_observers (a_window, Notify_changed_window)
		end

	refresh_commands_action (a_window: EB_WINDOW)
			-- Action to be performed on `item' in `refresh_all_commands'.
		do
			a_window.refresh_all_commands
		end

	refresh_external_commands_action (a_window: EB_WINDOW)
			-- Action to be performed on `item' in `refresh_external_commands'.
		do
			a_window.refresh_external_commands
		end

	synchronize_breakpoints_action (a_window: EB_WINDOW)
			-- Action to synchronize `a_window' regarding the breakpoints data.
		do
			if attached {EB_DEVELOPMENT_WINDOW} a_window as a_dev_window then
				a_dev_window.tools.breakpoints_tool.synchronize
			end
		end

	synchronize_action (a_window: EB_WINDOW)
			-- Action to be performed on `item' in `refresh'.
		do
			if attached {EB_DEVELOPMENT_WINDOW} a_window as a_dev_window then
				a_dev_window.synchronize
			elseif attached {EB_DYNAMIC_LIB_WINDOW} a_window as conv_dyn then
				conv_dyn.synchronize
			else
				a_window.refresh
			end
			notify_observers (a_window, Notify_changed_window)
		end

	backup_action (a_window: EB_WINDOW)
			-- Create a backup file of the text contained in `a_window'.
		do
			if attached {EB_DEVELOPMENT_WINDOW} a_window as w then
				w.editors_manager.backup_all
				not_backuped := not_backuped + w.editors_manager.not_backuped
			end
		end

	quick_refresh_action (a_window: EB_WINDOW)
			-- Action to be performed on `a_window' in `quich_refresh_all_editors'.
		do
			if attached {EB_DEVELOPMENT_WINDOW} a_window as a_dev_window then
				a_dev_window.quick_refresh_editors
			end
		end

	quick_refresh_margin_action (a_window: EB_WINDOW)
			-- Action to be performed on `a_window' in `quick_refresh_all_margins'.
		do
			if attached {EB_DEVELOPMENT_WINDOW} a_window as a_dev_window then
				a_dev_window.quick_refresh_margins
			end
		end

	raise_unsaved_action (a_window: EB_WINDOW)
			-- Action to be performed on `item' in `raise_non_saved'.
		do
			if attached {EB_DEVELOPMENT_WINDOW} a_window as a_dev_window and then a_dev_window.changed then
				a_window.show
			end
		end

	save_action (a_window: EB_WINDOW)
			-- Action to be performed on `item' in `save_all'.
		do
			if
				attached {EB_DEVELOPMENT_WINDOW} a_window as a_dev_window and then
				a_dev_window.any_editor_changed
			then
				a_dev_window.save_all
			end
			if
				attached {EB_DYNAMIC_LIB_WINDOW} a_window as dyn_window and then
				dyn_window.changed
			then
				dyn_window.save
			end
		end

	save_before_compiling_action (a_window: EB_WINDOW)
			-- Action to be performed on `item' in `save_all_before_compiling'.
		do
			if
				attached {EB_DEVELOPMENT_WINDOW} a_window as a_dev_window and then
			   	a_dev_window.editors_manager.changed
			then
				a_dev_window.save_before_compiling
			end
		end

	create_project_action (a_window: EB_WINDOW)
			-- Action to be performed on `a_window' in `create_project'.
		do
			if attached {EB_DEVELOPMENT_WINDOW} a_window as a_dev_window then
				a_dev_window.agents.on_project_created
			end
		end

	load_project_action (a_window: EB_WINDOW)
			-- Action to be performed on `a_window' in `load_project'.
		do
			if attached {EB_DEVELOPMENT_WINDOW} a_window as a_dev_window then
				a_dev_window.agents.on_project_loaded
			end
		end

	unload_project_action (a_window: EB_WINDOW)
			-- Action to be performed on `a_window' in `unload_project'.
		do
			if attached {EB_DEVELOPMENT_WINDOW} a_window as a_dev_window then
				a_dev_window.agents.on_project_unloaded
			end
		end

	c_compilation_start_action (a_window: EB_WINDOW)
			-- Action to be performed on `a_window' when freezing or finalizing starts.
		do
			if attached {EB_DEVELOPMENT_WINDOW} a_window as a_dev_window then
				a_dev_window.agents.on_c_compilation_starts
			end
		end

	c_compilation_stop_action (a_window: EB_WINDOW)
			-- Action to be performed on `a_window' when freezing or finalizing stops.
		do
			if attached {EB_DEVELOPMENT_WINDOW} a_window as a_dev_window then
				a_dev_window.agents.on_c_compilation_stops
			end
		end

	for_all (action: PROCEDURE)
			-- Iterate `action' on every managed window.
		local
			l_index: INTEGER
		do
			l_index := managed_windows.index
			from
				managed_windows.start
			until
				managed_windows.after
			loop
				action.call ([managed_windows.item])
				managed_windows.forth
			end
			if managed_windows.valid_index (l_index) then
				managed_windows.go_i_th (l_index)
			end
		end

feature {EB_WINDOW_MANAGER_OBSERVER, EB_WINDOW, EB_DEVELOPMENT_WINDOW_BUILDER} -- Observer pattern

	add_observer (an_observer: EB_WINDOW_MANAGER_OBSERVER)
			-- Add `an_observer' to the list of observers for Current.
		require
			valid_observer: an_observer /= Void
		do
			if observers = Void then
				create observers.make (2)
			end
			observers.extend (an_observer)
		end

	remove_observer (an_observer: EB_WINDOW_MANAGER_OBSERVER)
			-- Remove `an_observer' from the list of observers for Current.
		require
			valid_observer: an_observer /= Void
		do
			if observers /= Void then
				observers.start
				observers.prune_all (an_observer)
			end
		end

	minimize_all_cmd: EB_STANDARD_CMD
			-- Command to minimize all windows.

	raise_all_cmd: EB_STANDARD_CMD
			-- Command to raise all windows.

	raise_all_unsaved_cmd: EB_STANDARD_CMD
			-- Command to raise all unsaved windows.

feature {EB_WINDOW} -- Implementation / Observer pattern

	notify_observers (an_item: EB_WINDOW; item_change: INTEGER)
			-- Notify all observers about the change of `an_item'.
		require
		--	valid_item_change:
		--		item_change = Notify_removed_window or
		--		item_change = Notify_added_window or
		--		item_change = Notify_changed_window
			valid_item: an_item /= Void
		do
			if observers /= Void then
				from
					observers.start
				until
					observers.after
				loop
					inspect item_change
					when Notify_added_window then
						observers.item.on_item_added (an_item)
					when Notify_changed_window then
						observers.item.on_item_changed (an_item)
					when Notify_removed_window then
						observers.item.on_item_removed (an_item)
					when Notify_shown_window then
						observers.item.on_item_shown (an_item)
					when Notify_hidden_window then
						observers.item.on_item_hidden (an_item)
					end
					observers.forth
				end
			end
		end

	observers: ARRAYED_LIST [EB_WINDOW_MANAGER_OBSERVER]
			-- All observers for Current.

feature {EB_C_COMPILER_LAUNCHER, EB_WINDOW_MANAGER_LIST, EB_WINDOW_MANAGER_MENU, EB_EXEC_FORMAT_CMD, EB_EXTERNAL_COMMANDS_EDITOR} -- Implementation

	managed_windows: ARRAYED_SET [EB_WINDOW]
			-- Managed windows.

feature {EB_DEVELOPMENT_WINDOW} -- Implementation

	new_title: STRING_GENERAL
			-- Find an empty titled not yet used.
		local
			l_index: INTEGER
			empty_title: STRING_32
			window_titles: ARRAYED_LIST [STRING_GENERAL]
			i: INTEGER
			l_found: BOOLEAN
			l_name, l_target_name: READABLE_STRING_32
		do
				-- Remember the title of all windows.
			create window_titles.make (managed_windows.count)
			window_titles.compare_objects

			l_index :=	managed_windows.index
			from
				managed_windows.start
			until
				managed_windows.after
			loop
				window_titles.extend (managed_windows.item.title)
				managed_windows.forth
			end
			managed_windows.go_i_th (l_index)

				-- Look for a title not yet used.
			if eiffel_project.system_defined and then attached eiffel_universe.target then
				l_name := eiffel_system.name
				l_target_name := eiffel_universe.target_name
			elseif attached eiffel_project.workbench.lace as l_lace and then attached l_lace.conf_system as s then
				l_name := s.name
				l_target_name := l_lace.target_name
			end
			empty_title :=
				if l_name /= Void and l_target_name /= Void then
					interface_names.l_empty_development_window_header (l_name, l_target_name)
				else
					interface_names.t_empty_development_window
				end +
				{STRING_32} " #"
			from
				i := 1
			until
				l_found
			loop
				Result := empty_title + i.out
				if not window_titles.has (Result) then
					l_found := True
				end
				i := i + 1
			end
		end

	stop_ev_application
			-- Stop `application' by killing main event loop of EiffelVision2.
		require
			no_more_development_windows: development_windows_count = 0
		do
			ev_application.destroy
		end

	store_last_focused_window
			-- Store layout of last focused window so new windows may be initialized
			-- to match.
		local
			a_window: EB_DEVELOPMENT_WINDOW
		do
			a_window := last_focused_development_window
			if a_window /= Void then
				a_window.save_layout
			end
		end

feature {EB_WINDOW} -- Implementation / Private Constants

	Notify_hidden_window: INTEGER = -2
		-- Notification constant for `notify_observers'.

	Notify_removed_window: INTEGER = -1
		-- Notification constant for `notify_observers'.

	Notify_added_window: INTEGER = 1
		-- Notification constant for `notify_observers'.

	Notify_shown_window: INTEGER = 2
		-- Notification constant for `notify_observers'.

	Notify_changed_window: INTEGER = 0
		-- Notification constant for `notify_observers'.

feature{NONE} -- Implementation

	state_message_waiting_count: INTEGER
			-- Times that we have been waiting for displaying a c compilation message.

	max_waiting_count: INTEGER = 30;
			-- Max value of `state_message_waiting_count'

	compile_start_actions_internal: like compile_start_actions;
			-- Implementation of `compile_start_actions'

note
	ca_ignore:
		"CA033", "CA033: too long class"
	copyright:	"Copyright (c) 1984-2023, 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
			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