note
	description: "Objects that with responsibility for save all docking library config."
	legal: "See notice at end of class."
	status: "See notice at end of class."
	date: "$Date$"
	revision: "$Revision$"
class
	SD_SAVE_CONFIG_MEDIATOR

inherit
	SD_ACCESS

create
	make

feature {NONE} -- Initialization

	make (a_docking_manager: SD_DOCKING_MANAGER)
			-- Creation method.
		require
			a_docking_manager_not_void: a_docking_manager /= Void
		do
			create internal_shared
			internal_docking_manager := a_docking_manager
		ensure
			set: internal_docking_manager = a_docking_manager
		end

feature -- Save inner container data.

	save_config_with_name_and_path (a_file: PATH; a_name: READABLE_STRING_GENERAL): BOOLEAN
			-- Save all docking library data to `a_file' with `a_name'.
		require
			a_file_not_void: a_file /= Void
			a_file_not_void: a_name /= Void
		local
			l_config_data: SD_CONFIG_DATA
		do
			create l_config_data.make
			save_config_with_name_maximized_data (l_config_data, a_name, True)
			Result := save_config_data_to_file (l_config_data, a_file)
		end

	save_config_with_name_maximized_data (a_config_data: SD_CONFIG_DATA; a_name: READABLE_STRING_GENERAL; a_save_maximized_data: BOOLEAN)
			-- Save all docking library data to `a_file' with `a_name'
		require
			a_config_data_not_void: a_config_data /= Void
			a_file_not_void: a_name /= Void
		do
			debug ("docking")
				io.put_string ("%N ================= SD_CONFIG_MEDIATOR save config ===============")
			end

			if a_save_maximized_data then
				save_maximized_tool_data (a_config_data)
			end

			internal_docking_manager.command.recover_normal_state

			save_tool_bar_item_data (a_config_data)
			save_editor_minimized_data (a_config_data)

			save_all_inner_containers_data (a_config_data)

			-- Second save auto hide zones data.
			save_auto_hide_panel_data (a_config_data.auto_hide_panels_data)

			save_tool_bar_data (a_config_data.tool_bar_data)

			a_config_data.set_name (a_name)
			a_config_data.set_is_docking_locked (internal_docking_manager.is_locked)
			a_config_data.set_is_editor_docking_locked (internal_docking_manager.is_editor_locked)
			a_config_data.set_is_tool_bar_locked (internal_docking_manager.tool_bar_manager.is_locked)

			top_container := Void
		ensure
			cleared: top_container = Void
		end

	save_config_with_path (a_file: PATH): BOOLEAN
			-- Save all docking library data to `a_file'.
		require
			a_file_not_void: a_file /= Void
		do
			Result := save_config_with_name_and_path (a_file, "")
		end

	save_editors_config_with_path (a_file: PATH): BOOLEAN
			-- Save main window editor config data.
		require
			not_void: a_file /= Void
			has_editor:
		local
			l_dock_area: SD_MULTI_DOCK_AREA
			l_container: detachable EV_CONTAINER
			l_config_data: detachable SD_INNER_CONTAINER_DATA
			l_maximized_zone: detachable SD_ZONE
		do
			if not internal_docking_manager.contents.has (internal_docking_manager.zones.place_holder_content) then
				l_dock_area := internal_docking_manager.query.inner_container_main

				l_maximized_zone := internal_docking_manager.zones.maximized_zone_in_main_window
				if l_maximized_zone /= Void then
					l_maximized_zone.on_normal_max_window
				end

				l_container := l_dock_area.editor_parent
				-- Maybe the widget structure is corrupt at the moment.
				if l_container /= Void then
					create l_config_data
					if l_container = internal_docking_manager.query.inner_container_main then
						save_inner_container_data (l_container.item, l_config_data)
					else
						save_inner_container_data (l_container, l_config_data)
					end
				end

				if l_maximized_zone /= Void then
					-- We restore tool maximized state.
					l_maximized_zone.on_normal_max_window
				end
			else
				-- There is no editor in main window.	
				create l_config_data
				save_place_holder_data (l_config_data)
			end
			if l_config_data /= Void then
				Result := save_config_data_to_file (l_config_data, a_file)
			end
			top_container := Void
		ensure
			cleared: top_container = Void
		end

	save_tools_config_with_path (a_file: PATH): BOOLEAN
			-- Save tools config, except all editors.
		require
			not_void: a_file /= Void
		do
			Result := save_tools_config_with_name_and_path (a_file, "")
		end

	save_tools_config_with_name_and_path (a_file: PATH; a_name: READABLE_STRING_GENERAL): BOOLEAN
			-- Save tools config to `a_file' with `a_name'.
		require
			not_called: top_container = Void
			a_file_not_void: a_file /= Void
			a_name_not_void: a_name /= Void
		local
			l_widget_structure_corrupted: BOOLEAN
			l_config_data: SD_CONFIG_DATA
		do
			create l_config_data.make
			save_tool_bar_item_data (l_config_data)
			save_maximized_tool_data (l_config_data)

			internal_docking_manager.command.recover_normal_state

			if not internal_docking_manager.has_content (internal_docking_manager.zones.place_holder_content) then
				top_container := internal_docking_manager.query.inner_container_main.editor_parent
				if top_container = Void then
					l_widget_structure_corrupted := True
				elseif top_container = internal_docking_manager.query.inner_container_main then
					if attached {EV_CONTAINER} top_container as l_container then
						top_container := l_container.item
					else
						check False end -- Implied by design of `top_container'
					end
				end
			else
				check real_has_place_holder: attached internal_docking_manager.zones.place_holder_content.state.zone end

				-- If following check violated, it normally means when you saving tools' layout, there are BOTH editor type zones and editor place holder visible.
				-- So docking library don't know how to separate tool area and editor area.
				-- You should either close all editors or close editor place holder area, then try saving tool data again.
				check place_holder_visible: internal_docking_manager.zones.place_holder_content.is_visible end
			end

			if not l_widget_structure_corrupted then
				save_config_with_name_maximized_data (l_config_data, a_name, False)
			end

			top_container := Void

			Result := save_config_data_to_file (l_config_data, a_file)
		ensure
			cleared: top_container = Void
		end

	top_container: detachable EV_WIDGET
		-- When only save tools config, and zone place holder not in, this is top contianer of all editors.	

feature -- Obsolete

	save_config_with_name (a_file: READABLE_STRING_GENERAL; a_name: READABLE_STRING_GENERAL): BOOLEAN
			-- Save all docking library data to `a_file' with `a_name'
		obsolete
			"Use save_config_with_name_and_path instead. [2017-05-31]"
		require
			a_file_not_void: a_file /= Void
			a_file_not_void: a_name /= Void
		do
			Result := save_config_with_name_and_path (create {PATH}.make_from_string (a_file), a_name)
		end

	save_config (a_file: READABLE_STRING_GENERAL): BOOLEAN
			-- Save all docking library data to `a_file'.
		obsolete
			"Use save_config_with_path instead. [2017-05-31]"
		require
			a_file_not_void: a_file /= Void
		do
			Result := save_config_with_path (create {PATH}.make_from_string (a_file))
		end

	save_editors_config (a_file: READABLE_STRING_GENERAL): BOOLEAN
			-- Save main window editor config data.
		obsolete
			"Use save_editors_config_with_path instead. [2017-05-31]"
		require
			not_void: a_file /= Void
		do
			Result := save_editors_config_with_path (create {PATH}.make_from_string (a_file))
		ensure
			cleared: top_container = Void
		end

	save_tools_config (a_file: READABLE_STRING_GENERAL): BOOLEAN
			-- Save tools config, except all editors.
		obsolete
			"Use save_tools_config_with_path instead. [2017-05-31]"
		require
			not_void: a_file /= Void
		do
			Result := save_tools_config_with_path (create {PATH}.make_from_string (a_file))
		end

	save_tools_config_with_name (a_file: READABLE_STRING_GENERAL; a_name: READABLE_STRING_GENERAL): BOOLEAN
			-- Save tools config to `a_file' with `a_name'.
		obsolete
			"Use save_tools_config_with_name_and_path instead. [2017-05-31]"
		require
			not_called: top_container = Void
			a_file_not_void: a_file /= Void
			a_name_not_void: a_name /= Void
		do
			Result := save_tools_config_with_name_and_path (create {PATH}.make_from_string (a_file), a_name)
		ensure
			cleared: top_container = Void
		end

feature {NONE} -- Implementation

	save_all_inner_containers_data (a_config_data: SD_CONFIG_DATA)
			-- Save all SD_MULTI_DOCK_AREA data, include main dock area in main window and floating zones.
		require
			a_config_data_not_void: a_config_data /= Void
		local
			l_inner_containers: ARRAYED_LIST [SD_MULTI_DOCK_AREA]
			l_datum: detachable SD_INNER_CONTAINER_DATA
			l_data: ARRAYED_LIST [SD_INNER_CONTAINER_DATA]
			l_floating_zone: detachable SD_FLOATING_ZONE
		do
			l_inner_containers := internal_docking_manager.inner_containers
			from
				l_inner_containers.start
				create l_data.make (1)
			until
				l_inner_containers.after
			loop
				if l_inner_containers.item.readable then
					create l_datum
					save_inner_container_data (l_inner_containers.item.item, l_datum)
					l_floating_zone := l_inner_containers.item.parent_floating_zone
					if l_floating_zone /= Void then
						if not l_floating_zone.is_displayed and then l_floating_zone.is_last_size_or_position_recorded then
							-- See bug#13685 which only happens on GTK.
							l_datum.set_screen_x (l_floating_zone.last_screen_x)
							l_datum.set_screen_y (l_floating_zone.last_screen_y)
							l_datum.set_width (l_floating_zone.last_width)
							l_datum.set_height (l_floating_zone.last_height)
						else
							l_datum.set_screen_x (l_floating_zone.screen_x)
							l_datum.set_screen_y (l_floating_zone.screen_y)
							l_datum.set_width (l_floating_zone.width)
							l_datum.set_height (l_floating_zone.height)
						end
					end
				else
					l_datum := Void
				end
				if l_datum /= Void then
					l_data.extend (l_datum)
				end
				l_inner_containers.forth
			end
			a_config_data.set_inner_container_data (l_data)
		end

	save_inner_container_data (a_widget: EV_WIDGET; a_config_data: SD_INNER_CONTAINER_DATA)
			-- Save one inner container which is SD_MULTI_DOCK_AREA data.
		require
			a_widget_not_void: a_widget /= Void
			a_config_data_not_void: a_config_data /= Void
		do
			if a_widget = top_container then
				-- We are saving tools config data.
				save_place_holder_data (a_config_data)
			else
				if attached {SD_MIDDLE_CONTAINER} a_widget as l_split_area then
					save_inner_container_data_split_area (l_split_area, a_config_data)
				else -- It must be a zone area
					if attached {SD_ZONE} a_widget as l_zone then
						a_config_data.set_is_split_area (False)
						l_zone.save_content_title (a_config_data)
						a_config_data.set_state (l_zone.content.state.generating_type.name_32)
						a_config_data.set_direction (l_zone.content.state.direction)
						if attached {EV_WIDGET} l_zone as lt_widget then
							a_config_data.set_visible (lt_widget.is_displayed)
						else
							check not_possible: False end
						end

						check valid: l_zone.content.state.last_floating_width > 0 end
						check valid: l_zone.content.state.last_floating_height > 0 end
						a_config_data.set_width (l_zone.content.state.last_floating_width)
						a_config_data.set_height (l_zone.content.state.last_floating_height)
						if attached {SD_UPPER_ZONE} a_widget as l_upper_zone then
							a_config_data.set_is_minimized (l_upper_zone.is_minimized)
						end
					else
						check False end -- Implied by must be zone area
					end
				end
			end
		end

	save_place_holder_data (a_config_data: SD_INNER_CONTAINER_DATA)
			-- Save a place holder data.
		require
			not_void: a_config_data /= Void
		do
			a_config_data.set_is_split_area (False)
			a_config_data.add_title (internal_shared.editor_place_holder_content_name)
			a_config_data.set_state (({SD_DOCKING_STATE}).name)
			a_config_data.set_direction ({SD_ENUMERATION}.top)
			a_config_data.set_width (1)
			a_config_data.set_height (1)
		ensure
			is_editor: not a_config_data.is_split_area
			title_correct: attached a_config_data.titles as le_titles implies le_titles.first.same_string (internal_shared.editor_place_holder_content_name)
		end

	save_inner_container_data_split_area (a_split_area: SD_MIDDLE_CONTAINER; a_config_data: SD_INNER_CONTAINER_DATA)
			-- `save_inner_container_data' save split area data part.
		require
			a_split_area_not_void: a_split_area /= Void
			a_config_data_not_void: a_config_data /= Void
		local
			l_temp: SD_INNER_CONTAINER_DATA
		do
			debug ("docking")
				io.put_string ("%N SD_DOCKING_MANAGER ")
				io.put_string ("%N  split area first : " + (a_split_area.first /= Void).out)
				io.put_string ("%N  split area second: " + (a_split_area.second /= Void).out)
			end

			a_config_data.set_is_horizontal_split_area (a_split_area.is_horizontal)

			a_config_data.set_is_split_area (True)

			-- We don't save the split area minized state surround `top_container', this will handled by `save_editor_minimized_data' feature,
			-- otherwise the split position when opening will not correct.
			if a_split_area.first /= top_container and a_split_area.second /= top_container then
				a_config_data.set_is_minimized (a_split_area.is_minimized)
			end

			if attached a_split_area.first as l_first then
				create l_temp
				a_config_data.set_children_left (l_temp)
				l_temp.set_parent (a_config_data)
				save_inner_container_data (l_first, l_temp)
			else
				check False end -- Implied by widget hierarchy is full two fork tree
			end

			if attached a_split_area.second as l_second then
				create l_temp
				a_config_data.set_children_right (l_temp)
				l_temp.set_parent (a_config_data)
				save_inner_container_data (l_second, l_temp)
			else
				check False end -- Implied by widget hierarchy is full two fork tree
			end

			if a_split_area.full then
				if attached {EV_SPLIT_AREA} a_split_area as lt_split then
					lt_split.update_proportion
					a_config_data.set_split_proportion (lt_split.proportion)
				else
					a_config_data.set_split_proportion (-1)
				end

			else
				check all_split_area_must_full: False end
			end
		end

	save_auto_hide_panel_data (a_data: SD_AUTO_HIDE_PANEL_DATA)
			-- Save auto hide zones config data.
		require
			a_data_not_void: a_data /= Void
		do
			save_one_auto_hide_panel_data (a_data, {SD_ENUMERATION}.top)
			save_one_auto_hide_panel_data (a_data, {SD_ENUMERATION}.bottom)
			save_one_auto_hide_panel_data (a_data, {SD_ENUMERATION}.left)
			save_one_auto_hide_panel_data (a_data, {SD_ENUMERATION}.right)
		end

	save_one_auto_hide_panel_data (a_data: SD_AUTO_HIDE_PANEL_DATA; a_direction: INTEGER)
			-- Save one auto hide panel tab stub config data.
		require
			not_void: a_data /= Void
			a_direction_valid: a_direction = {SD_ENUMERATION}.top or a_direction = {SD_ENUMERATION}.bottom
				or a_direction = {SD_ENUMERATION}.left or a_direction = {SD_ENUMERATION}.right
		local
			l_auto_hide_panel: SD_AUTO_HIDE_PANEL
			l_tab_groups: ARRAYED_LIST [ARRAYED_LIST [SD_TAB_STUB]]
			l_one_group_data: ARRAYED_LIST [TUPLE [READABLE_STRING_GENERAL, INTEGER, INTEGER, INTEGER]]
			l_one_group: ARRAYED_LIST [SD_TAB_STUB]
		do
			l_auto_hide_panel := internal_docking_manager.query.auto_hide_panel (a_direction)
			l_tab_groups := l_auto_hide_panel.tab_groups
			from
				l_tab_groups.start
			until
				l_tab_groups.after
			loop
				create l_one_group_data.make (1)
				from
					l_one_group := l_tab_groups.item
					l_one_group.start
				until
					l_one_group.after
				loop
					if attached {SD_AUTO_HIDE_STATE} l_one_group.item.content.state as l_auto_hide_state then
						l_one_group_data.extend ([l_one_group.item.content.unique_title, l_auto_hide_state.width_height, l_auto_hide_state.last_floating_width, l_auto_hide_state.last_floating_height])
					else
						check False end -- Implied by in auto hide panel, content's state must be {SD_AUTO_HIDE_STATE}
					end

					l_one_group.forth
				end
				a_data.add_zone_group_data (a_direction, l_one_group_data)
				l_tab_groups.forth
			end
		end

	save_tool_bar_data (a_tool_bar_data: ARRAYED_LIST [SD_TOOL_BAR_DATA])
			-- Save four area tool bar and floating tool bar config data.
		require
			not_void: a_tool_bar_data /= Void
		local
			l_tool_bar_data: SD_TOOL_BAR_DATA
			l_float_tool_bars: ARRAYED_LIST [SD_FLOATING_TOOL_BAR_ZONE]
			l_float_tool_bars_item: SD_FLOATING_TOOL_BAR_ZONE
			l_tool_bar_contents: ARRAYED_LIST [SD_TOOL_BAR_CONTENT]
			l_tool_bar_contents_item: SD_TOOL_BAR_CONTENT
		do
			-- Top
			l_tool_bar_data := save_one_tool_bar_data ({SD_ENUMERATION}.top)
			a_tool_bar_data.extend (l_tool_bar_data)
			-- Bottom
			l_tool_bar_data := save_one_tool_bar_data ({SD_ENUMERATION}.bottom)
			a_tool_bar_data.extend (l_tool_bar_data)
			-- Left
			l_tool_bar_data := save_one_tool_bar_data ({SD_ENUMERATION}.left)
			a_tool_bar_data.extend (l_tool_bar_data)
			-- Right	
			l_tool_bar_data := save_one_tool_bar_data ({SD_ENUMERATION}.right)
			a_tool_bar_data.extend (l_tool_bar_data)

				-- Floating tool bars data
			l_float_tool_bars := internal_docking_manager.tool_bar_manager.floating_tool_bars
			from
				l_float_tool_bars.start
			until
				l_float_tool_bars.after
			loop
				l_float_tool_bars_item := l_float_tool_bars.item
				if attached l_float_tool_bars_item.zone as l_tool_bar_zone then
					create l_tool_bar_data.make
					l_tool_bar_data.set_floating (True)
					if attached l_tool_bar_zone.content as c then
						l_tool_bar_data.set_title (c.unique_title)
						l_tool_bar_data.set_visible (c.is_visible)
					end
					l_tool_bar_data.set_screen_x_y (l_float_tool_bars_item.screen_x, l_float_tool_bars_item.screen_y)
					l_tool_bar_zone.assistant.save_items_layout (Void)
					l_tool_bar_data.set_last_state (l_tool_bar_zone.assistant.last_state)
					a_tool_bar_data.extend (l_tool_bar_data)
				end
				l_float_tool_bars.forth
			end

			-- Hidden docking tool bar data.
			from
				l_tool_bar_contents := internal_docking_manager.tool_bar_manager.hidden_docking_contents
				l_tool_bar_contents.start
			until
				l_tool_bar_contents.after
			loop
				l_tool_bar_contents_item := l_tool_bar_contents.item
				if not l_tool_bar_contents_item.is_visible then
					create l_tool_bar_data.make
					l_tool_bar_data.set_visible (False)
					l_tool_bar_data.set_floating (False)
					l_tool_bar_data.set_title (l_tool_bar_contents_item.unique_title)

					if
						attached l_tool_bar_contents_item.zone as l_zone
						and then l_zone.assistant.last_state /= Void
					then
						l_tool_bar_data.set_last_state (l_zone.assistant.last_state)
					end
					a_tool_bar_data.extend (l_tool_bar_data)
				end
				l_tool_bar_contents.forth
			end
		end

	save_one_tool_bar_data (a_direction: INTEGER): SD_TOOL_BAR_DATA
			-- Save one tool bar area config data.
		require
			a_direction_valid: (create {SD_ENUMERATION}).is_direction_valid (a_direction)
		local
			l_tool_bar_area: EV_CONTAINER
			l_rows: LINEAR [EV_WIDGET]
			l_tool_bars: ARRAYED_LIST [SD_TOOL_BAR_ZONE]
			l_zone: SD_TOOL_BAR_ZONE
			l_row_data: ARRAYED_LIST [TUPLE [READABLE_STRING_GENERAL, INTEGER, SD_TOOL_BAR_ZONE_STATE]]
		do
			l_tool_bar_area := internal_docking_manager.tool_bar_manager.tool_bar_container (a_direction)
			l_rows := l_tool_bar_area.linear_representation
			create Result.make
			from
				l_rows.start
			until
				l_rows.after
			loop
				if attached {SD_TOOL_BAR_ROW} l_rows.item as l_tool_bar then
					create l_row_data.make (1)
					Result.rows.extend (l_row_data)
					from
						l_tool_bars := l_tool_bar.zones
						l_tool_bars.start
					until
						l_tool_bars.after
					loop
						l_zone := l_tool_bars.item_for_iteration
						l_zone.assistant.save_items_layout (Void)
						if attached l_zone.content as c then
							l_row_data.extend ([c.unique_title, l_zone.position, l_zone.assistant.last_state])
						end
						l_tool_bars.forth
					end
				else
					check tool_bar_area_only_has_tool_bar_row: False end -- Implied by tool bar area only has tool bar row
				end

				l_rows.forth
			end
		ensure
			not_void: Result /= Void
		end

	save_editor_minimized_data (a_config_data: SD_CONFIG_DATA)
			-- If only one editor zone, save if it's minimized.
		local
			l_editor_zone: SD_UPPER_ZONE
		do
			l_editor_zone := internal_docking_manager.query.only_one_editor_zone
			if l_editor_zone /= Void then
				a_config_data.set_is_one_editor_zone (True)
				a_config_data.set_is_editor_minimized (l_editor_zone.is_minimized)
			else
				a_config_data.set_is_one_editor_zone (False)
			end
		end

	save_maximized_tool_data (a_config_data: SD_CONFIG_DATA)
			-- Save maximized data.
		require
			not_void: a_config_data /= Void
		local
			l_maximized_zones: ARRAYED_LIST [SD_ZONE]
		do
			l_maximized_zones := internal_docking_manager.zones.maximized_zones
			from
				l_maximized_zones.start
			until
				l_maximized_zones.after
			loop
				a_config_data.maximized_tools.extend (l_maximized_zones.item.content.unique_title)
				l_maximized_zones.forth
			end
		ensure
			valid: a_config_data.maximized_tools.count = internal_docking_manager.zones.maximized_zones.count
		end

	save_tool_bar_item_data (a_config_data: SD_CONFIG_DATA)
			-- Save tool bar resizable item data.
		do
			a_config_data.set_resizable_items_data (internal_docking_manager.property.resizable_items_data)
		end

	save_config_data_to_file (a_config_data: ANY; a_file: PATH): BOOLEAN
			-- Save `a_config_data' to `a_file'
			-- Result true means saving successed
			-- Result false means saving failed
		require
			a_config_data_not_void: a_config_data /= Void
			a_file_not_void: a_file /= Void
		local
			l_file: RAW_FILE
			l_retried: BOOLEAN
		do
			if not l_retried then
				create l_file.make_with_path (a_file)
				l_file.create_read_write
				;(create {SED_STORABLE_FACILITIES}).store
					(a_config_data, create {SED_MEDIUM_READER_WRITER}.make (l_file))
				l_file.close
				Result := True
			end
		rescue
			l_retried := True
			-- see bug#14426
			if l_file /= Void and then not l_file.is_closed then
				l_file.close
			end
			retry
		end

feature {NONE} -- Implementation attributes

	internal_docking_manager: SD_DOCKING_MANAGER
			-- Docking manager which Current belong to.

	internal_shared: SD_SHARED;
			-- All singletons.

note
	library:	"SmartDocking: Library of reusable components for Eiffel."
	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