note description : "[ This sets up buttons and menus for "selecting" multiple views and different formats for those views within a `cell'. The `cell', `menu', and `bar' can then be added to your container as appropriate. It manages the placement of VIEWs in a `cell', allowing nested split areas to be hidden/shown using buttons and/or menus. The views are placed in `cell' which can be placed in an EV_CONTAINER. Views are added to Current using `extend' or `extend_siblings'. Class JJ_MAIN_WINDOW has a feature `split_manager' (inherited from VIEW) which allows the placement of multiple panes (e.g. VIEWs, TOOLs, etc) into the window. The `split_manager' controls the layout of these VIEWs. The `initialize' feature from a class descended from JJ_MAIN_WINDOW may look like this feature initialize is -- Set up a window with three VIEWs. do Precursor {JJ_MAIN_WINDOW} -- Allow the user to choose if the children are displayed -- as tabs, multiple panes, or as a single cell. split_manager.enable_mode_changes -- Add two VIEWs to be displayed in an EV_VERTICAL_SPLIT_AREA (when -- the window mode `is_split_mode'.) split_manager.set_vertical split_manager.extend (view_one) split_manager.extend (view_two) -- Add a third view into an EV_HORIZONTAL_SPLIT_AREA with the -- previouse two views (contained in the EV_VERTICAL_SPLIT_AREA) -- below the new one split_manager.extend_siblings (view_three, split_manager.last_item) -- NOTE: a tool bar and menu items are automatically added. end If the number of views added to Current is at least two then the `bar' is filled with a set of buttons and 'menu' is built with menu items for selecting the "mode" of the `cell'. When `is_in_split_mode' (the default mode) a number of views will be displayed in EV_SPLIT_AREAs, recursively, within `cell'. When `is_maximized_mode' only the `selected_view' will be placed in `cell'. Finally, when `is_notebook_mode' the views will be displayed as tabs in an EV_NOTEBOOK within `cell'. Here is how the `bar' looks when `is_maximized_mode' or `is_notebook_mode'. PB = `preferences_button' SB = `split_button' MB = `maximize_button' NB = `notebook_button' RB = an EV_TOOL_BAR_RADIO_BUTTON |---------------------- bar --------------------------------------| | |--- mode_bar ---| |--------------- radio bar -------------| | | | |SB| |MB| |NB| | | |RB| |RB| ... to number of views | | When one of the radio buttons is pressed the corresponding view is brought to the top. When `is_split_mode' the radio buttons are replaced with EV_TOOL_BAR_TOGGLE_BUTTONs. TB = an EV_TOOL_BAR_TOGGLE_BUTTON |------------------------------- bar ---------------------------| | |--- mode_bar ---| |-------------- toggle bar -----------| | | | |SB| |MB| |NB| | | |TB| |TB| ... to number of views | | When a toggle button is selected the corresponding view becomes visible in a split area calculated on the fly. The items in `menu' are kept in parallel with the buttons, allowing menu selection to accomplish the same things as the buttons. ]" date: "12 Nov 07" 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/system/split_manager.e $" date: "$Date: 2014-05-31 08:53:58 -0400 (Sat, 31 May 2014) $" revision: "$Revision: 17 $" class SPLIT_MANAGER inherit SHARED export {NONE} all redefine default_create end S_TREE redefine default_create, root, set_root, extend, extend_siblings end PIXEL_BUFFERS undefine default_create end create default_create feature {NONE} -- Initialization default_create -- Initialize current do Precursor {S_TREE} -- create exported widgets create bar create menu create cell -- create root.make (default_view) last_node := root selected_node := root -- view := default_view -- Assume split mode is_split_mode := True -- Only need these widgets if more than one view is added. create notebook -- create tool bars create toggle_bar create radio_bar create mode_bar -- create buttons and menus create single_mode_button create split_mode_button create notebook_mode_button create mode_menu create toggle_menu create radio_menu create single_mode_item create split_mode_item create notebook_mode_item -- Set widget attributes single_mode_button.set_pixmap (create {EV_PIXMAP}.make_with_pixel_buffer (Icon_format_text_color_buffer)) single_mode_button.set_tooltip ("Single mode") split_mode_button.set_pixmap (create {EV_PIXMAP}.make_with_pixel_buffer (Icon_new_development_tool_color_buffer)) split_mode_button.set_tooltip ("Split mode") notebook_mode_button.set_pixmap (create {EV_PIXMAP}.make_with_pixel_buffer (Icon_folders_color_buffer)) notebook_mode_button.set_tooltip ("Tab mode") -- Set up the `mode_bar' and its buttons. -- mode_bar.extend (create {EV_TOOL_BAR_SEPARATOR}) mode_bar.extend (single_mode_button) mode_bar.extend (split_mode_button) mode_bar.extend (notebook_mode_button) -- mode_bar.extend (create {EV_TOOL_BAR_SEPARATOR}) split_mode_button.enable_select -- Set up menus which do not change menu.set_text ("Views") mode_menu.set_text ("Mode") radio_menu.set_text ("Select view") toggle_menu.set_text ("Select view") single_mode_item.set_text ("Maximize Mode") split_mode_item.set_text ("Split Mode") notebook_mode_item.set_text ("Notebook Mode") mode_menu.extend (single_mode_item) mode_menu.extend (split_mode_item) mode_menu.extend (notebook_mode_item) split_mode_item.enable_select -- Actions for `notebook' notebook.selection_actions.extend (agent on_tab_selected) -- Actions for buttons -- preferences_button.select_actions.extend (agent on_show_preferences_dialog) split_mode_button.select_actions.extend (agent set_split_mode) single_mode_button.select_actions.extend (agent set_single_mode) notebook_mode_button.select_actions.extend (agent set_notebook_mode) -- Actions for menu items -- preferences_item.select_actions.extend (agent on_show_preferences_dialog) single_mode_item.select_actions.extend (agent set_single_mode) split_mode_item.select_actions.extend (agent set_split_mode) notebook_mode_item.select_actions.extend (agent set_notebook_mode) end feature -- Access cell: EV_CELL -- The container which will be managed. The views will be placed -- into `cell' based on the user's button, or menu selections. -- This should be extended into a widget. menu: EV_MENU -- Menu of items to toggle views on and off. -- Can be inserted into window menu. bar: EV_HORIZONTAL_BOX -- Bar of buttons used to manage views. -- Can be inserted into a toolbar. selected_view: detachable VIEW -- The currently selected view. do if attached selected_node as sn then Result := sn.view end end view_count: INTEGER -- The number of VIEWs contained in and managed by Current. do Result := descendents.count end visible_count: INTEGER -- The number of VIEWs which are visible. local s: like leaf_nodes n: like root do s := leaf_nodes from s.start until s.exhausted loop n := s.item if n.is_visible then Result := Result + 1 end s.forth end end first_visible_view: VIEW -- The first view which is visible. local s: like leaf_nodes n: like root l_result: detachable VIEW do s := leaf_nodes from s.start until l_result /= Void or else s.exhausted loop n := s.item if n.is_visible then l_result := n.view end s.forth end if l_result = Void then Result := Default_view else Result := l_result end ensure result_exists: Result /= Void end default_view: DEFAULT_VIEW -- Create an EV_LABEL as a place holder do create Result Result.set_pebble (Result) Result.set_text ("Empty: no VIEW") end state: SPLIT_MANAGER_STATE -- Create a structure representing the state of Current; to be used -- by `restore_state' during execution start. do save_split_positions create Result.make (Current) ensure result_exists: Result /= Void end feature -- Element change set_root (a_root: like root) -- Change `root' and resync the manager. do Precursor {S_TREE} (a_root) syncronize end -- restore_with_state (a_state: SPLIT_MANAGER_STATE) is -- -- Restore Current to state as represented in `a_state' -- require -- state_exists: a_state /= Void -- local -- des: like descendents -- ns: SPLIT_MANAGER_NODE_STATE -- do -- if a_state.is_mode_frozen then -- disable_mode_changes -- else -- enable_mode_changes -- end -- if a_state.is_interface_frozen then -- disable_interface_changes -- else -- enable_interface_changes -- end -- if a_state.is_single_mode then -- set_single_mode -- elseif a_state.is_split_mode then -- set_split_mode -- elseif a_state.is_notebook_mode then -- set_notebook_mode -- else -- check -- should_not_happen: False -- -- because there are only three modes. -- end -- end -- -- Restore each node's state -- des := root.descendents -- check -- same_count: des.count = a_state.node_states.count -- -- The saved node states should have the same form as the -- -- node tree in the current window. -- end -- from -- a_state.node_states.start -- des.start -- until a_state.node_states.exhausted or else des.exhausted -- loop -- ns := a_state.node_states.item -- des.item.restore_with_state (ns) -- a_state.node_states.forth -- des.forth -- end -- syncronize -- end feature -- Basic operations extend (a_item: VIEW) -- Add `a_item' as a sibling to the previously added item. do Precursor {S_TREE} (a_item) if selected_node = Void then selected_node := corresponding_node (a_item) end add_agents (corresponding_node (a_item)) syncronize end extend_siblings (a_first, a_second: VIEW) -- Searching from `root', find the nodes containing `a_first', and/or -- `a_second' or, if not finding them, create new nodes containing -- `a_first', and `a_second' and make them children of a new node. -- Make `root' reference the new node. local n: like root had_first, had_second: BOOLEAN do -- Prevent agents from being added twice if the tree already has the item. if has (a_first) then had_first := True end if has (a_second) then had_second := True end Precursor {S_TREE} (a_first, a_second) n := corresponding_node (a_first) if selected_node = Void then selected_node := n end if n.is_view and then not had_first then add_agents (n) end n := corresponding_node (a_second) if n.is_view and then not had_second then add_agents (n) end syncronize end wipe_out_views -- Clean out all the views in preperation for rebuilding. do -- Wipe out the containers notebook.wipe_out -- Wipe out the EV_CONTAINERs toggle_bar.wipe_out radio_bar.wipe_out toggle_menu.wipe_out radio_menu.wipe_out -- Reset the view to default state selected_node := root -- build_bars end select_view (a_view: VIEW) -- Make `a_view' the `selected_view' require view_exists: a_view /= Void has_view: has_view (a_view) do check attached corresponding_node (a_view) as n then n.show selected_node := n end syncronize ensure view_was_selected: selected_view = a_view end toggle_view (a_view: VIEW) -- If `a_view' is hidden then show it; if it is shown hide it. -- The effect from this will show up in split mode. require view_exists: a_view /= Void has_view: has_view (a_view) local n: like root do n := corresponding_node (a_view) check is_view: n.is_view -- Because if `has_view' then `n' must be a view. end if n.is_hidden then n.show selected_node := n else if visible_count > 1 then save_split_positions n.hide end end syncronize end show_all_views -- Make all the views visible local s: like leaf_nodes do s := leaf_nodes from s.start until s.exhausted loop s.item.show s.forth end syncronize end show_view (a_view: VIEW) -- Make `a_view' visible require view_exists: a_view /= Void has_view: has_view (a_view) local n: like root do n := corresponding_node (a_view) if not n.is_visible then n.show restore_split_positions end select_view (a_view) ensure view_visible: corresponding_node (a_view).is_visible view_selected: selected_view = a_view end hide_view (a_view: VIEW) -- React to a request to hide `a_view' require view_exists: a_view /= Void has_view: has_view (a_view) is_in_split_mode: is_split_mode local n: like root do if visible_count > 1 then save_split_positions n := corresponding_node (a_view) n.hide if selected_view = a_view then -- Must select another select_view (first_visible_view) end syncronize end ensure view_not_visible: old visible_count > 1 implies not corresponding_node (a_view).is_visible end disable_view (a_view: VIEW) -- Prevent `a_view' from appearing and also remove the -- buttons and menus which will allow it to be shown. -- Use `enable_view' to restore it to normal. require view_exists: a_view /= Void has_view: has_view (a_view) do save_split_positions corresponding_node (a_view).disable -- build_bars -- build_cell syncronize ensure is_in_disabled_views: corresponding_node (a_view).is_disabled end enable_view (a_view: VIEW) -- Sets the view handling for `a_view' back to normal -- by restoring the corresponding buttons and menues. require view_exists: a_view /= Void has_view: has_view (a_view) do corresponding_node (a_view).enable -- build_bars -- build_cell restore_split_positions syncronize ensure view_not_disabled: not corresponding_node (a_view).is_disabled end set_single_mode_view (a_view: VIEW) -- Set to single mode with `a_view' visible, filling the `cell'. require view_exists: a_view /= Void has_view: has_view (a_view) -- mode_is_changable: is_mode_changable do set_single_mode if selected_view /= a_view then select_view (a_view) end ensure is_single_mode: is_single_mode correct_view_selected: selected_view = a_view end set_split_mode_view (a_view: VIEW) -- Multiple views can be visible within `cell'. -- Ensure `a_view' is the selected view. require view_exists: a_view /= Void has_view: has_view (a_view) -- mode_is_changable: is_mode_changable do set_split_mode if selected_view /= a_view then select_view (a_view) end ensure is_split_mode: is_split_mode correct_view_selected: selected_view = a_view end set_notebook_mode_view (a_view: VIEW) -- Change to notebook style with `a_view' on top. require view_exists: a_view /= Void has_view: has_view (a_view) -- mode_is_changable: is_mode_changable do set_notebook_mode if selected_view /= a_view then select_view (a_view) end ensure is_notebook_mode: is_notebook_mode correct_view_selected: selected_view = a_view end feature -- Status report is_interface_frozen: BOOLEAN -- Is the set of visible views frozen (i.e. can the user toggle a view -- on or off)? (Assuming there is more than one view.) -- This affects the interface, not the programmers ability to change. is_mode_frozen: BOOLEAN -- Is the "mode" frozen (i.e. if `is_single_mode' always single mode)? -- This affects the interface, not the programmers ability to change. -- See `enable_mode_changes' and `disable_mode_changes'. is_single_mode: BOOLEAN -- Is only one view in `cell'? is_split_mode: BOOLEAN -- Is the view in split mode (multiple views showing)? is_notebook_mode: BOOLEAN -- Is the view in notebook mode (multiple views in an EV_NOTEBOOK)? feature -- Status setting enable_mode_changes -- Allow the *user* (not to be confused with *programer*) to change -- the way the contained views (if there is more than one view) are -- presented in `cell' by setting `is_mode_frozen' to True. -- If more than one view is present and `is_interface_frozen' then -- buttons and menus for changing the mode will be made available to -- the programmer in `bar' and `menu' which can then be presented to -- the user. do is_mode_frozen := False if not is_empty and then view_count >= 2 then build_bars end build_cell ensure mode_is_changable: not is_mode_frozen end disable_mode_changes -- Prevent *user* changes to the "mode" by setting `is_mode_changable' -- to False. The user will see what ever appearance has been set -- by the programmer using the `set_..._mode' features. -- See `set_split_mode', `set_single_mode',and `set_notebook_mode'. do is_mode_frozen := True ensure mode_is_not_changable: is_mode_frozen end enable_interface_changes -- Allow the *user* to toggle the visible views. -- Used to add the buttons and menues from `bar'. do is_interface_frozen := False ensure views_are_changable: not is_interface_frozen end disable_interface_changes -- Prevent the *user* from toggling the visible views. -- Used to hide or remove the buttons and menues from `bar'. do is_interface_frozen := True ensure views_not_changable: is_interface_frozen end set_single_mode -- Make the `cell' hold only one widget, either the `selected_view' or, -- if there are no views, a `default_view'. -- require -- mode_is_changable: is_mode_changable do if not is_single_mode then if is_split_mode then save_split_positions end is_single_mode := True is_split_mode := False is_notebook_mode := False syncronize end -- select_view (selected_view) ensure is_single_mode: is_single_mode end set_split_mode -- Make the `cell' hold the views recursively in EV_SPLIT_AREAs. -- If there are no views then `cell' contains a `default_view'. require is_mode_changable: not is_mode_frozen local d: like descendents do if not is_split_mode then is_split_mode := True is_single_mode := False is_notebook_mode := False restore_split_positions syncronize end if not attached selected_view or else ( attached selected_view as v and then not has (v)) then d := descendents if not d.is_empty then selected_node := descendents.first end end check attached selected_view as v then select_view (v) end ensure is_split_mode: is_split_mode end set_notebook_mode -- Make `cell' hold the views in an EV_NOTEBOOK. -- If there are no views then `cell' contains a `default_view'. -- require -- is_mode_changable: is_mode_changable do if not is_notebook_mode then if is_split_mode then save_split_positions end is_notebook_mode := True is_single_mode := False is_split_mode := False syncronize end -- select_view (selected_view) ensure is_notebook_mode: is_notebook_mode end feature -- Query is_visible (a_view: VIEW): BOOLEAN -- Is `a_view' visible? (I.e. should it be added to `cell'?) do Result := has (a_view) and then corresponding_node (a_view).is_visible end is_disabled (a_view: VIEW): BOOLEAN -- Is `a_view' able to be shown? do Result := has (a_view) and then corresponding_node (a_view).is_disabled end has_split (a_split_area: EV_SPLIT_AREA): BOOLEAN -- Does Current contain/manage `a_split_area'? require split_exists: a_split_area /= Void local s: like internal_nodes do s := internal_nodes from s.start until Result or else s.exhausted loop Result := s.item.view = a_split_area s.forth end end has_view (a_view: VIEW): BOOLEAN -- Does Current contain/manage `a_view'? require view_exists: a_view /= Void local s: like leaf_nodes do s := leaf_nodes from s.start until Result or else s.exhausted loop Result := s.item.view = a_view s.forth end end is_conforming_type (a_view: ANY): BOOLEAN -- Does `a_view' conform to VIEW or EV_SPLIT_AREA? -- Would have preferred for `a_view' to be an EV_WIDGET but that caused -- too many name clashes in decendents of VIEW. require view_exists: a_view /= Void do Result := a_view.conforms_to ({VIEW}) or else a_view.conforms_to ({EV_SPLIT_AREA}) ensure definition: Result implies a_view.conforms_to ({VIEW}) or else a_view.conforms_to ({EV_SPLIT_AREA}) end feature {SPLIT_MANAGER_STATE} -- Implementation (Query) root: detachable SPLIT_MANAGER_NODE -- Root node of the tree. selected_node: like root -- The node corresponding the the view last selected by the user. feature {NONE} -- Implementation (Basic operations) add_agents (a_node: attached like root) -- Add agents to toggle/raise/minimize the widget -- (a VIEW) in `a_node'. require node_exists: a_node /= Void is_view: a_node.is_view has_view: has_node (a_node) do a_node.toggle_button.select_actions.extend (agent toggle_view (a_node.view)) a_node.radio_button.select_actions.extend (agent select_view (a_node.view)) a_node.toggle_item.select_actions.extend (agent toggle_view (a_node.view)) a_node.radio_item.select_actions.extend (agent select_view (a_node.view)) -- Add actions to the buttons of `a_view' if it is a TOOL if attached {TOOL} a_node.view as t then -- This item is a tool and therefore has buttons. t.maximize_actions.extend (agent set_single_mode_view (a_node.view)) t.restore_actions.extend (agent set_split_mode_view (a_node.view)) t.close_actions.extend (agent hide_view (a_node.view)) end end save_split_positions -- Save the position of splitters for restoration later. local n: SPLIT_MANAGER_NODE s: like internal_nodes do s := internal_nodes from s.start until s.exhausted loop n := s.item check node_holds_split_area: n.is_split -- Because only EV_SPLIT_AREA widgets are added to the nodes in `splits'. end n.save_split_position s.forth end end restore_split_positions -- Set the split position of each visible split area to what it was -- when it was last visible. local n: SPLIT_MANAGER_NODE s: like internal_nodes do s := internal_nodes from s.start until s.exhausted loop n := s.item check node_holds_split_area: n.is_split -- Because only EV_SPLIT_AREA widgets are added to the nodes in `splits'. end n.restore_split_position s.forth end end syncronize -- Rebuild all the externally accessed widgets such as `cell', `menu', -- and `button_bar' reflect the state of the buttons. -- Also ensure that at least one view is visible if there are any views. -- and update button and menu states. do build_cell if visible_count >= 1 then syncronize_tools -- Make sure the radio items and buttons for the `selected_node' are set to -- the correct state. This must be called after the radio buttons are added -- to the bar because the radio buttons for all the views must work together -- (when one selected the other turn off.) restore_split_positions -- syncronize_view_selection_items end if view_count >= 2 then build_bars syncronize_mode_selection_items check attached selected_node as sn then sn.syncronize_buttons end end end syncronize_tools -- Make sure any TOOLs handled by the manager are in the correct -- state, either maximized or normal. local s: like leaf_nodes do s := leaf_nodes from s.start until s.exhausted loop if attached {TOOL} s.item.view as t then if s.count <= 1 then t.disable_resize else t.enable_resize end if is_single_mode then if t.is_resizable then t.maximize end else if t.is_resizable then t.restore end end if is_split_mode then t.close_button.enable_sensitive t.maximize_button.enable_sensitive t.restore_button.enable_sensitive else t.close_button.disable_sensitive t.maximize_button.disable_sensitive -- t.restore_button.disable_sensitive end end s.forth end end -- syncronize_view_selection_items is -- -- Make sure the state of the radio and toggle reflect the value -- -- of `selected_view' and visible views. -- local -- n: like root -- do -- -- Fix the radio items -- -- i := views.index_of (selected_view, 1) -- rb := radio_buttons.i_th (i) -- ri := radio_items.i_th (i) -- ri.select_actions.block -- ri.enable_select -- ri.select_actions.resume -- rb.select_actions.block -- rb.enable_select -- rb.select_actions.resume -- -- Fix the toggle items -- from views.start -- until views.exhausted -- loop -- i := views.index -- wv := views.item -- if not disabled_views.has (wv) then -- tb := toggle_buttons.i_th (i) -- ti := toggle_items.i_th (i) -- ti.select_actions.block -- tb.select_actions.block -- if visible_views.has (wv) then -- ti.enable_select -- tb.enable_select -- else -- ti.disable_select -- tb.disable_select -- end -- ti.select_actions.resume -- tb.select_actions.resume -- end -- views.forth -- end -- ensure -- selected_view_unchanged: selected_view = old selected_view -- toggle_items_agree: toggles_agree_with_visible_items -- radio_items_agree: radios_agree_with_selected_view -- end syncronize_mode_selection_items -- Set the button states require at_least_two_views: view_count >= 2 do single_mode_button.select_actions.block split_mode_button.select_actions.block notebook_mode_button.select_actions.block single_mode_item.select_actions.block split_mode_item.select_actions.block notebook_mode_item.select_actions.block if is_split_mode then split_mode_button.enable_select split_mode_item.enable_select elseif is_notebook_mode then notebook_mode_button.enable_select notebook_mode_item.enable_select elseif is_single_mode then single_mode_button.enable_select single_mode_item.enable_select else check no_mode_should_not_happen: False end end single_mode_button.select_actions.resume split_mode_button.select_actions.resume notebook_mode_button.select_actions.resume single_mode_item.select_actions.resume split_mode_item.select_actions.resume notebook_mode_item.select_actions.resume -- for testing only if is_split_mode then check -- one: split_mode_button.is_selected -- two: split_mode_item.is_selected -- three: not notebook_mode_button.is_selected -- four: not notebook_mode_item.is_selected -- five: not single_mode_button.is_selected -- six: not single_mode_item.is_selected end end ensure -- split_mode_implication: is_split_mode implies -- split_mode_button.is_selected and split_mode_item.is_selected and -- not notebook_mode_button.is_selected and not notebook_mode_item.is_selected and -- not single_mode_button.is_selected and not single_mode_item.is_selected -- notebook_mode_implication: is_notebook_mode implies -- notebook_mode_button.is_selected and notebook_mode_item.is_selected and -- not single_mode_button.is_selected and not single_mode_item.is_selected and -- not split_mode_button.is_selected and not split_mode_item.is_selected -- single_mode_implication: is_single_mode implies -- single_mode_button.is_selected and single_mode_item.is_selected and -- not notebook_mode_button.is_selected and not notebook_mode_item.is_selected and -- not split_mode_button.is_selected and not split_mode_item.is_selected end block_leaf_actions -- Removing and adding radio widgets to the `radio_bar' or `radio_menu' causes -- problems if the select actions fire, therefore block all those actions. -- The leaf nodes only should have any corresponding button for selections. local s: like leaf_nodes do s := leaf_nodes from s.start until s.exhausted loop s.item.block_radio_actions s.forth end end resume_leaf_actions -- Resume actions for leaf node widgets (see `block_leaf_actions') local s: like leaf_nodes do s := leaf_nodes from s.start until s.exhausted loop s.item.resume_radio_actions s.forth end end build_bars -- Put the correct buttons in the `bar' and the correct -- menu items into `menu'. Make sure the button and menu -- states agree with the state of Current. require at_least_two_views: view_count >= 2 local n: SPLIT_MANAGER_NODE s: like leaf_nodes do -- toggle_bar.disable_sensitive -- radio_bar.disable_sensitive -- toggle_menu.disable_sensitive -- radio_menu.disable_sensitive -- Build the `radio_bar', `toggle_bar', `toggle_menu', and `radio_menu' -- Calling `wipeout' on an EV_TOGGLE_BUTTON or EV_TOGGLE_ITEM caused one -- of the other buttons or items in the group to be selected. This calls -- the select actions on that widget, one of which is to select a view. -- This will eventually lead to a call to `synchronize' and then back -- to this feature. This is undesirable, so to prevent the callback, -- we must block the select actions for each of the radio widgets. block_leaf_actions toggle_bar.wipe_out radio_bar.wipe_out toggle_menu.wipe_out radio_menu.wipe_out s := leaf_nodes from s.start until s.exhausted loop n := s.item if not n.is_disabled then toggle_bar.extend (n.toggle_button) radio_bar.extend (n.radio_button) toggle_menu.extend (n.toggle_item) -- radio_menu.extend (n.radio_item) end s.forth end -- Un-block the actions on any selectable widgets in the bar or menu from radio_bar.start until radio_bar.exhausted loop check attached {EV_TOOL_BAR_RADIO_BUTTON} radio_bar.item as b then b.select_actions.resume end radio_bar.forth end from radio_menu.start until radio_menu.exhausted loop check attached {EV_RADIO_MENU_ITEM} radio_menu.item as ri then ri.select_actions.resume end radio_menu.forth end -- Build the bar bar.wipe_out if leaf_nodes.count >= 2 then if not is_mode_frozen then bar.extend (mode_bar) bar.disable_item_expand (mode_bar) end if not is_interface_frozen then if is_single_mode or else is_notebook_mode then bar.extend (radio_bar) bar.disable_item_expand (radio_bar) elseif is_split_mode then bar.extend (toggle_bar) bar.disable_item_expand (toggle_bar) end end end -- Build the menu menu.wipe_out if not is_mode_frozen then menu.extend (mode_menu) end if not is_interface_frozen then if is_single_mode or else is_notebook_mode then menu.extend (radio_menu) elseif is_split_mode then menu.extend (toggle_menu) end end -- if visible_count >= 2 then if view_count >= 2 then toggle_bar.enable_sensitive mode_menu.enable_sensitive radio_menu.enable_sensitive toggle_menu.enable_sensitive else toggle_bar.disable_sensitive mode_menu.disable_sensitive radio_menu.disable_sensitive toggle_menu.disable_sensitive end resume_leaf_actions end build_cell -- Put the appropriate EV_SPLIT_AREA or VIEW into `cell' based on user -- button or menu selections. This must be done whenever the list -- of visible_views changes or when a view is disabled. local s: like leaf_nodes n: detachable like root v: VIEW -- b: EV_TOOL_BAR_RADIO_BUTTON do -- Must wipeout all the containers to prevent putting a view into -- more than one container at a time. -- This requires building the notebook here if `is_notebook_mode' cell.wipe_out if notebook /= Void then notebook.selection_actions.block notebook.wipe_out end wipeout_splits if view_count >= 1 then if is_single_mode or else view_count < 2 then check attached {EV_WIDGET} selected_view as ev_w then -- because all managed `views' should be EV_WIDGETS cell.extend (ev_w) end elseif is_notebook_mode then s := leaf_nodes from s.start until s.exhausted loop check s.item.is_view -- Because `views' should hold only nodes that contain a VIEW in `widget'. end n := s.item if not n.is_disabled then check attached {EV_WIDGET} n.view as ev_w then -- because all managed `views' should be EV_WIDGETS notebook.extend (ev_w) notebook.set_item_text (ev_w, ev_w.generating_type) end end s.forth end if n /= Void then check attached {EV_WIDGET} n.view as ev_w then -- because all managed `views' should be EV_WIDGETS notebook.select_item (ev_w) end end cell.extend (notebook) notebook.selection_actions.resume else check is_split_mode: is_split_mode -- because only mode left end if attached root as r then cell.extend (r.visible_widget) end restore_split_positions end end end wipeout_splits -- Clean out all the split areas. local s: like internal_nodes do s := internal_nodes from s.start until s.exhausted loop check is_split: s.item.is_split -- Because splits hold only nodes containing EV_SPLIT_AREA is `widget'. end s.item.split_area.wipe_out s.forth end end feature {NONE} -- Implementation (Actions) on_mode_change -- The "mode" has changed, so rebuild the `bar' and `menu' -- and syncronize other items. do save_split_positions syncronize end on_tab_selected -- React to a request from the `notebook' to select `a_view' require is_notebook_mode: is_notebook_mode do check attached {VIEW} notebook.selected_item as v then select_view (v) end end feature {NONE} -- Implementation has_invalid_leaf_type: BOOLEAN -- Used by invariant to make sure all leaf nodes contain VIEWs. local s: like leaf_nodes n: like root do s := leaf_nodes from s.start until Result or else s.exhausted loop n := s.item Result := not n.is_view if not n.is_view then Result := True end s.forth end end has_invalid_internal_type: BOOLEAN -- Used by invariant to make sure all internal nodes contain EV_SPLIT_AREAs. local s: like internal_nodes n: like root do s := internal_nodes from s.start until Result or else s.exhausted loop n := s.item Result := not n.is_split s.forth end end feature {NONE} -- Implementation (tool bars and buttons) notebook: EV_NOTEBOOK -- Used internally to build `cell' when `notebook_mode_button' is selected. mode_bar: EV_TOOL_BAR -- Holds the view mode buttons (`single_mode_button', -- `normal_mode_button', and `notebook_mode_button'. toggle_bar: EV_TOOL_BAR -- Holds buttons which toggle multiple views on and off. radio_bar: EV_TOOL_BAR -- Holds buttons used to select one of several views. single_mode_button: EV_TOOL_BAR_RADIO_BUTTON -- Used to put the views into a maximized state split_mode_button: EV_TOOL_BAR_RADIO_BUTTON -- Puts the manager into normal view mode (all selected views are visible). notebook_mode_button: EV_TOOL_BAR_RADIO_BUTTON -- Puts the manager into a mode in which the views are -- placed withing an EV_NOTEBOOK. feature {NONE} -- Implementation (menus and items) mode_menu: EV_MENU -- Holds menu items for changing the mode toggle_menu: EV_MENU -- Holds menu items used when not `is_maximized' and not `is_builder_mode' -- to toggle views on and off or to `maximize' the views. radio_menu: EV_MENU -- Holds menu items used when `is_maximized' and not `is_builder_mode' -- to select one of several views or to `restore' the views. single_mode_item: EV_RADIO_MENU_ITEM -- Maximizes the `selected_view' split_mode_item: EV_RADIO_MENU_ITEM -- Reverts to `is_split_mode' notebook_mode_item: EV_RADIO_MENU_ITEM -- Changes to notebook mode invariant cell_exists: cell /= Void not_has_invalid_leaf_type: not has_invalid_leaf_type not_has_invalid_internal_type: not has_invalid_internal_type -- one_view_implies_root_exists: count >= 1 implies root /= Void -- one_view_implies_selected_node_exists: count >= 1 implies selected_node /= Void -- two_views_implies_exported_widgets_exist: count >= 2 implies (menu /= Void and bar /= Void) -- two_views_implies_widgets_exist: count >= 2 implies -- (notebook /= Void and mode_bar /= Void and toggle_bar /= Void and radio_bar /= Void and -- single_mode_button /= Void and split_mode_button /= Void and notebook_mode_button /= Void and -- mode_menu /= Void and toggle_menu /= Void and radio_menu /= Void and -- single_mode_item /= Void and split_mode_item /= Void and notebook_mode_item /= Void) end