note description: "Dialog to add a library." legal: "See notice at end of class." status: "See notice at end of class." date: "$Date$" revision: "$Revision$" class CREATE_LIBRARY_DIALOG inherit CREATE_GROUP_DIALOG redefine initialize, last_group end CONF_ACCESS undefine copy, default_create end PROPERTY_GRID_LAYOUT undefine copy, default_create end CONF_VALIDITY undefine copy, default_create end CONF_GUI_INTERFACE_CONSTANTS undefine default_create, copy end EIFFEL_LAYOUT export {NONE} all undefine default_create, copy end create make feature {NONE} -- Initialization initialize -- Initialize. local l_btn: EV_BUTTON main, vb, vb2, l_padding: EV_VERTICAL_BOX hb, hbf, hb1, hb2: EV_HORIZONTAL_BOX l_lbl: EV_LABEL l_filter: like filter l_clear_filter_button: EV_BUTTON l_update_index_button: EV_BUTTON nb: EV_NOTEBOOK lib_tab: detachable EV_NOTEBOOK_TAB iron_box: IRON_PACKAGE_COLLECTION_BOX do Precursor set_title (conf_interface_names.dialog_create_library_title) set_icon_pixmap (conf_pixmaps.new_library_icon) -- notebook create nb extend (nb) -- libraries create main main.set_padding (layout_constants.default_padding_size) main.set_border_width (layout_constants.default_border_size) nb.extend (main) if nb.has (main) then lib_tab := nb.item_tab (main) nb.item_tab (main).set_text ("Libraries") end create vb main.extend (vb) -- default libraries create vb2 vb.extend (vb2) vb2.set_padding (layout_constants.small_padding_size) vb2.set_border_width (layout_constants.small_border_size) create hb1 hb1.set_padding (layout_constants.small_padding_size) create l_lbl.make_with_text (conf_interface_names.dialog_create_library_defaults) l_lbl.align_text_left hb1.extend (l_lbl) hb1.disable_item_expand (l_lbl) hb1.extend (create {EV_CELL}) create hbf hb1.extend (hbf) hbf.extend (create {EV_LABEL}.make_with_text (Names.l_filter)) hbf.disable_item_expand (hbf.last) create l_filter filter := l_filter hbf.extend (l_filter) l_filter.change_actions.extend (agent request_update_filter) create l_clear_filter_button l_clear_filter_button.select_actions.extend (agent l_filter.remove_text) l_clear_filter_button.set_pixmap (conf_pixmaps.general_remove_icon) l_clear_filter_button.set_tooltip (names.b_reset) hbf.extend (l_clear_filter_button) hbf.disable_item_expand (l_clear_filter_button) vb2.extend (hb1) vb2.disable_item_expand (hb1) -- Create grid create libraries_box.make (target) libraries_box.set_minimum_size (400, 200) libraries_box.on_library_selected_actions.extend (agent on_library_selected) -- Create border for the grid create l_padding l_padding.set_border_width (1) l_padding.set_background_color ((create {EV_STOCK_COLORS}).color_3d_shadow) l_padding.extend (libraries_box.widget) vb2.extend (l_padding) -- Libraries cache update create hb1 hb1.set_padding (layout_constants.small_padding_size) vb2.extend (hb1) vb2.disable_item_expand (hb1) hb1.extend (create {EV_CELL}) create l_update_index_button l_update_index_button.select_actions.extend (agent update_index) l_update_index_button.set_text ("Update index") l_update_index_button.set_tooltip (names.b_reset) l_update_index_button.enable_sensitive hb1.extend (l_update_index_button) hb1.disable_item_expand (l_update_index_button) libraries_update_button := l_update_index_button create l_btn l_btn.set_pixmap (conf_pixmaps.project_settings_advanced_icon) l_btn.select_actions.extend (agent edit_libraries_cfg) hb1.extend (l_btn) hb1.disable_item_expand (l_btn) -- name create vb2 vb.extend (vb2) vb.disable_item_expand (vb2) vb2.set_padding (layout_constants.small_padding_size) vb2.set_border_width (layout_constants.small_border_size) create l_lbl.make_with_text (conf_interface_names.dialog_create_library_name) vb2.extend (l_lbl) vb2.disable_item_expand (l_lbl) l_lbl.align_text_left create name vb2.extend (name) vb2.disable_item_expand (name) -- location create vb2 vb.extend (vb2) vb.disable_item_expand (vb2) vb2.set_padding (layout_constants.small_padding_size) vb2.set_border_width (layout_constants.small_border_size) create l_lbl.make_with_text (conf_interface_names.dialog_create_library_location) vb2.extend (l_lbl) vb2.disable_item_expand (l_lbl) l_lbl.align_text_left create hb2 vb2.extend (hb2) vb2.disable_item_expand (hb2) hb2.set_padding (layout_constants.small_padding_size) create location hb2.extend (location) create l_btn.make_with_text_and_action (conf_interface_names.browse, agent browse) l_btn.set_pixmap (conf_pixmaps.general_open_icon) hb2.extend (l_btn) hb2.disable_item_expand (l_btn) ----------------- --| [Ok] [Cancel] create hb main.extend (hb) main.disable_item_expand (hb) hb.extend (create {EV_CELL}) hb.set_padding (layout_constants.default_padding_size) create l_btn.make_with_text (names.b_ok) hb.extend (l_btn) hb.disable_item_expand (l_btn) set_default_push_button (l_btn) l_btn.select_actions.extend (agent on_ok) layout_constants.set_default_width_for_button (l_btn) create l_btn.make_with_text (names.b_cancel) hb.extend (l_btn) hb.disable_item_expand (l_btn) set_default_cancel_button (l_btn) l_btn.select_actions.extend (agent on_cancel) layout_constants.set_default_width_for_button (l_btn) -- end of notebook tab #1 : Standard libraries -- notebook tab #2: Iron -- libraries create main main.set_padding (layout_constants.default_padding_size) main.set_border_width (layout_constants.default_border_size) create vb2 main.extend (vb2) nb.extend (main) if nb.has (main) and then attached nb.item_tab (main) as iron_tab then iron_tab.set_text ("Iron") create iron_box.make (target) vb2.extend (iron_box.widget) ----------------- --| [Back to libraries] create hb main.extend (hb) main.disable_item_expand (hb) hb.extend (create {EV_CELL}) hb.set_padding (layout_constants.default_padding_size) create l_btn.make_with_text ("Back") hb.extend (l_btn) hb.disable_item_expand (l_btn) if lib_tab /= Void then l_btn.select_actions.extend (agent lib_tab.enable_select) else l_btn.select_actions.extend (agent nb.select_item (nb.first)) end layout_constants.set_default_width_for_button (l_btn) nb.selection_actions.extend (agent (ia_nb: EV_NOTEBOOK_TAB; ia_iron_box: IRON_PACKAGE_COLLECTION_BOX) do if ia_nb.is_selected then ia_iron_box.populate end end (iron_tab, iron_box) ) end -- Initial Event show_actions.extend_kamikaze (agent do populate_libraries libraries_box.set_focus end) end feature {NONE} -- Update filter update_filter_timeout: detachable EV_TIMEOUT request_update_filter local l_update_filter_timeout: like update_filter_timeout do cancel_delayed_update_filter l_update_filter_timeout := update_filter_timeout if l_update_filter_timeout = Void then create l_update_filter_timeout update_filter_timeout := l_update_filter_timeout l_update_filter_timeout.actions.extend_kamikaze (agent delayed_update_filter) end l_update_filter_timeout.set_interval (700) end cancel_delayed_update_filter do if attached update_filter_timeout as l_update_filter_timeout then l_update_filter_timeout.destroy update_filter_timeout := Void end end delayed_update_filter do cancel_delayed_update_filter update_filter end update_filter local l_style: EV_POINTER_STYLE do l_style := pointer_style set_pointer_style (create {EV_POINTER_STYLE}.make_predefined ({EV_POINTER_STYLE_CONSTANTS}.busy_cursor)) libraries_box.set_filter_text (filter_text) libraries_box.update_grid set_pointer_style (l_style) end feature {NONE} -- GUI elements libraries_box: EIFFEL_LIBRARY_COLLECTION_BOX -- Libraries collection box. filter: EV_TEXT_FIELD -- Filter. filter_text: detachable STRING_32 -- Filter text. do Result := filter.text end name: EV_TEXT_FIELD -- Name of the library. location: EV_TEXT_FIELD -- Location of the library configuration file, choosen by the user. browse_dialog: EV_FILE_OPEN_DIALOG -- Dialog to browse to a library local l_dir: DIRECTORY once create Result create l_dir.make_with_path (target.system.directory) if l_dir.is_readable then Result.set_start_path (l_dir.path) end Result.filters.extend ([config_files_filter, config_files_description]) Result.filters.extend ([all_files_filter, all_files_description]) ensure Result_not_void: Result /= Void end libraries_update_button: EV_BUTTON -- Update libraries cache. feature -- Access last_group: CONF_LIBRARY -- Last created group. feature {NONE} -- Libraries cache. edit_libraries_cfg local dlg: EV_DIALOG txt: EV_TEXT v: EV_VERTICAL_BOX hb: EV_HORIZONTAL_BOX but: EV_BUTTON p: PATH f: RAW_FILE s: STRING_32 utf: UTF_CONVERTER do p := eiffel_layout.eifinit_path.extended ("libraries.cfg") create f.make_with_path (p) if f.exists and then f.is_readable then create dlg create v dlg.extend (v) create txt txt.set_minimum_size (200, 200) v.extend (txt) create hb v.extend (hb) v.disable_item_expand (hb) f.open_read from create s.make_empty until f.exhausted loop f.read_line_thread_aware s.append (utf.utf_8_string_8_to_string_32 (f.last_string)) s.append_character ('%N') end f.close txt.set_text (s) if f.is_writable then txt.enable_edit create but.make_with_text_and_action (names.b_save, agent (ia_dlg: EV_DIALOG; ia_text: EV_TEXTABLE; ia_file: FILE) require ia_file.is_writable do ia_file.open_write ia_file.put_string (ia_text.text) ia_file.put_new_line ia_file.close ia_dlg.destroy end (dlg, txt, f)) else txt.disable_edit create but.make_with_text (names.b_save) but.disable_sensitive end hb.extend (but) hb.disable_item_expand (but) create but.make_with_text_and_action (names.b_cancel, agent (ia_dlg: EV_DIALOG) do ia_dlg.destroy end(dlg)) hb.extend (but) hb.disable_item_expand (but) else create {EV_ERROR_DIALOG} dlg.make_with_text ("Can not edit libraries settings !") end dlg.show_modal_to_window (Current) end update_index do cache_data (Void, "iron_configuration_libraries.cache") cache_data (Void, "configuration_libraries.cache") populate_libraries end cached_data (a_name: READABLE_STRING_GENERAL): detachable ANY local p: PATH f: RAW_FILE retried: BOOLEAN sed: SED_STORABLE_FACILITIES sed_rw: SED_MEDIUM_READER_WRITER do if not retried then p := eiffel_layout.temporary_path.extended (a_name) create f.make_with_path (p) if f.exists and then f.is_readable then f.open_read create sed create sed_rw.make_for_reading (f) Result := sed.retrieved (sed_rw, False) f.close end end rescue retried := True retry end cache_data (a_data: ANY; a_name: READABLE_STRING_GENERAL) local p: PATH f: RAW_FILE retried: BOOLEAN sed: SED_STORABLE_FACILITIES sed_rw: SED_MEDIUM_READER_WRITER do if not retried then p := eiffel_layout.temporary_path.extended (a_name) create f.make_with_path (p) if a_data /= Void then if f.exists and then not f.is_writable then -- Ignored but should not occured. check False end else f.create_read_write create sed create sed_rw.make_for_writing (f) sed.basic_store (a_data, sed_rw, True) f.close end else if f.exists then f.delete end end end rescue retried := True retry end feature {NONE} -- Access libraries: SEARCH_TABLE [STRING_32] -- A set of libraries to display in the dialog require is_eiffel_layout_defined: is_eiffel_layout_defined local l_dirs: like lookup_directories l_libraries: SEARCH_TABLE [STRING_32] l_dir: DIRECTORY l_path: IMMUTABLE_STRING_32 l_location: CONF_DIRECTORY_LOCATION do l_dirs := lookup_directories create Result.make (l_dirs.count) from l_dirs.start until l_dirs.after loop create l_location.make (l_dirs.item_for_iteration.path, target) l_path := l_location.evaluated_path.name create l_dir.make (l_path) if l_dir.is_readable then create l_libraries.make (10) add_configs_in_directory (l_dir, l_dirs.item_for_iteration.depth, l_libraries) from l_libraries.start until l_libraries.after loop l_libraries.item_for_iteration.replace_substring (l_dirs.item_for_iteration.path, 1, l_path.count) l_libraries.forth end Result.merge (l_libraries) end l_dirs.forth end ensure result_attached: Result /= Void end conf_system_list_from (libs: ITERABLE [READABLE_STRING_32]; nb: INTEGER): STRING_TABLE [CONF_SYSTEM_VIEW] -- A set of libraries configurations from `lst' path name. require is_eiffel_layout_defined: is_eiffel_layout_defined local l_location: CONF_DIRECTORY_LOCATION l_cfg_data: CONF_SYSTEM_VIEW do create Result.make (nb) across libs as ic loop create l_location.make (ic.item, target) create l_cfg_data.make (l_location) if l_cfg_data.has_library_target then Result.force (l_cfg_data, ic.item) end end ensure result_attached: attached Result end configuration_libraries: STRING_TABLE [CONF_SYSTEM_VIEW] -- A set of libraries configurations to display in the dialog require is_eiffel_layout_defined: is_eiffel_layout_defined local l_libs: like libraries do if attached {like configuration_libraries} cached_data ("configuration_libraries.cache") as cfg_libs then Result := cfg_libs else l_libs := libraries Result := conf_system_list_from (l_libs, l_libs.count) cache_data (Result, "configuration_libraries.cache") end ensure result_attached: attached Result end iron_configuration_libraries: STRING_TABLE [CONF_SYSTEM_VIEW] local installation_api: IRON_INSTALLATION_API l_iron_layout: IRON_LAYOUT l_iron_url_builder: IRON_URL_BUILDER lst: ARRAYED_LIST [READABLE_STRING_32] p: PATH do if attached {like configuration_libraries} cached_data ("iron_configuration_libraries.cache") as cfg_libs then Result := cfg_libs else create l_iron_layout.make_with_path (eiffel_layout.iron_path, eiffel_layout.installation_iron_path) create l_iron_url_builder create installation_api.make_with_layout (l_iron_layout, l_iron_url_builder) if attached installation_api.installed_packages as l_packages then create lst.make (l_packages.count) across l_packages as ic loop p := installation_api.package_installation_path (ic.item) if attached installation_api.projects_from_installed_package (ic.item) as l_projects then across l_projects as proj_ic loop lst.force ({STRING} "iron:" + ic.item.identifier + {STRING_32} ":" + proj_ic.item.name) end end end Result := conf_system_list_from (lst, lst.count) else create Result.make (0) end cache_data (Result, "iron_configuration_libraries.cache") end end lookup_directories: ARRAYED_LIST [TUPLE [path: STRING_32; depth: INTEGER]] -- A list of lookup directories require is_eiffel_layout_defined: is_eiffel_layout_defined local l_filename: detachable PATH l_file: RAW_FILE do create Result.make (10) l_filename := eiffel_layout.libraries_config_name create l_file.make_with_path (l_filename) if l_file.exists then add_lookup_directories (l_filename.name, Result) end if eiffel_layout.is_user_files_supported then l_filename := eiffel_layout.user_priority_file_name (l_filename, True) if l_filename /= Void then l_file.reset_path (l_filename) if l_file.exists then add_lookup_directories (l_filename.name, Result) end end end if Result.is_empty then -- Extend the default library path Result.extend ([eiffel_layout.library_path.name.as_string_32, 2]) end ensure not_result_is_empty: not Result.is_empty end feature {NONE} -- Actions browse -- Browse for a location. local l_loc: CONF_FILE_LOCATION l_dir: DIRECTORY l_cfg_data: CONF_SYSTEM_VIEW do if not location.text.is_empty then create l_loc.make (location.text, target) create l_dir.make_with_path (l_loc.evaluated_directory) end if l_dir /= Void and then l_dir.exists then browse_dialog.set_start_path (l_dir.path) end browse_dialog.show_modal_to_window (Current) if attached browse_dialog.file_name as l_fn and then not l_fn.is_empty then create l_cfg_data.make_from_path (create {PATH}.make_from_string (l_fn)) if l_cfg_data.has_library_target then if attached l_cfg_data.conf_option as l_options and then l_options.is_void_safety_sufficient (target.options) then on_library_selected (l_cfg_data, l_fn) else prompts.show_question_prompt (conf_interface_names.add_non_void_safe_library, Current, agent on_library_selected (l_cfg_data, l_fn), Void) end else prompts.show_error_prompt (conf_interface_names.file_is_not_a_library, Current, Void) end end end feature {NONE} -- Action handlers on_library_selected (a_library: CONF_SYSTEM_VIEW; a_location: READABLE_STRING_GENERAL) -- Called when a library is selected require is_library_target: a_library.has_library_target do name.set_text (a_library.library_target_name) location.set_text (a_location) end on_ok -- Add library and close the dialog. local l_sys: CONF_SYSTEM do -- library choosen? if not location.text.is_empty and not name.text.is_empty then if not is_valid_group_name (name.text) then (create {ES_SHARED_PROMPT_PROVIDER}).prompts.show_error_prompt (conf_interface_names.invalid_library_name, Current, Void) elseif group_exists (name.text, target) then (create {ES_SHARED_PROMPT_PROVIDER}).prompts.show_error_prompt (conf_interface_names.group_already_exists (name.text), Current, Void) else last_group := factory.new_library (name.text, location.text, target) -- add an empty classes list that it get's displayed in the classes tree last_group.set_classes (create {STRING_TABLE [CONF_CLASS]}.make (0)) l_sys := factory.new_system_generate_uuid_with_file_name ("dummy_location", "dummy") l_sys.set_application_target (target) last_group.set_library_target (factory.new_target ("dummy", l_sys)) target.add_library (last_group) is_ok := True destroy end end ensure is_ok_last_library: is_ok implies last_group /= Void end on_cancel -- Close the dialog. do destroy end feature {NONE} -- Basic operation populate_libraries -- Populates the list of libraries in the UI local l_libraries: like configuration_libraries l_iron_libraries: like iron_configuration_libraries l_style: EV_POINTER_STYLE libs_box: like libraries_box libs: like configuration_libraries do l_style := pointer_style set_pointer_style (create {EV_POINTER_STYLE}.make_predefined ({EV_POINTER_STYLE_CONSTANTS}.busy_cursor)) l_libraries := configuration_libraries l_iron_libraries := iron_configuration_libraries libs_box := libraries_box create libs.make (l_libraries.count + l_iron_libraries.count) libs.merge (l_libraries) libs.merge (l_iron_libraries) libs_box.set_configuration_libraries (libs) libs_box.set_filter_text (filter_text) libs_box.update_grid set_pointer_style (l_style) end add_configs_in_directory (a_dir: DIRECTORY; a_depth: INTEGER; a_libraries: SEARCH_TABLE [STRING_32]) -- Add config files in `a_path' to `a_libraries'. require a_dir_attached: attached a_dir a_dir_is_readable: a_dir.is_readable a_depth_big_enough: a_depth >= -1 a_libraries_attached: attached a_libraries local l_dir_name: PATH l_lib_file: STRING_32 l_file_name: PATH l_file_string: STRING_32 l_file: RAW_FILE s32: STRING_32 do if attached a_dir.linear_representation_32 as l_items then across l_items as l_files loop l_lib_file := l_files.item if valid_config_extension (l_lib_file) then l_file_name := a_dir.path.extended (l_lib_file) create l_file.make_with_path (l_file_name) if l_file.exists and then l_file.is_plain then l_file_string := l_file_name.name.as_string_32 if {PLATFORM_CONSTANTS}.is_windows then l_file_string := l_file_string.as_lower end if not a_libraries.has (l_file_string) then a_libraries.force (l_file_string) end end end end if (a_depth = -1 or a_depth > 0) and then attached a_dir.entries as l_subdirs then -- Perform recursion across l_subdirs as l_dirs loop s32 := l_dirs.item.name if not s32.same_string (".") and not s32.same_string ("..") then -- FIXME: use upcoming PATH.is_dot, and related l_dir_name := a_dir.path.extended_path (l_dirs.item) create l_file.make_with_path (l_dir_name) if l_file.exists and then l_file.is_directory then add_configs_in_directory (create {DIRECTORY}.make_with_path (l_dir_name), (a_depth - 1).max (-1), a_libraries) end end end end end end add_lookup_directories (a_path: STRING_32; a_list: ARRAYED_LIST [TUPLE [path: READABLE_STRING_32; depth: INTEGER]]) -- Adds look up directories from a file located at `a_path' into `a_list' require a_path_attached: attached a_path not_a_path_is_empty: not a_path.is_empty a_path_exists: (create {RAW_FILE}.make_with_name (a_path)).exists a_list_attached: attached a_list local l_file: RAW_FILE l_line: STRING l_pos: INTEGER l_location: STRING l_depth_string: STRING l_depth: INTEGER do create l_file.make_with_name (a_path) if l_file.is_readable then from l_file.open_read until l_file.end_of_file loop l_file.read_line l_line := l_file.last_string if not l_line.is_empty then l_line.left_adjust l_line.right_adjust l_pos := l_line.last_index_of ('%T', l_line.count) if l_pos > 1 then l_location := l_line.substring (1, l_pos - 1) l_location.right_adjust if l_pos < l_line.count then l_depth_string := l_line.substring (l_pos + 1, l_line.count) l_depth_string.left_adjust end else l_location := l_line end if l_depth_string /= Void and then l_depth_string.is_integer then l_depth := l_depth_string.to_integer else l_depth := 1 end --| FIXME: Unicode content of the file, does not provide Unicode file name --| unless it is UTF-8 encoded ... a_list.extend ([l_location.as_string_32, l_depth]) end end end end feature {NONE} -- Constants name_column: INTEGER = 1 -- Index of a column with a library name. void_safety_column: INTEGER = 2 -- Index of a column with void-safety status. location_column: INTEGER = 3 -- Index of a column with a library location. invariant target_set_in_boxes: libraries_box.target = target ;note copyright: "Copyright (c) 1984-2014, 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