note description : "Window to display results from a query." legal: "See notice at end of class." status: "See notice at end of class." author : "Christophe BONNARD, Arnaud PICHERY [ aranud@mail.dotcom.fr ]" date : "$Date$" revision : "$Revision$" class EB_PROFILE_QUERY_WINDOW inherit EV_TITLED_WINDOW EB_CONSTANTS export {NONE} all undefine default_create, copy end SHARED_QUERY_VALUES export {NONE} all undefine default_create, copy end EB_SHARED_INTERFACE_TOOLS export {NONE} all undefine default_create, copy end EB_VISION2_FACILITIES export {NONE} all undefine default_create, copy end EV_SHARED_APPLICATION export {NONE} all undefine default_create, copy end EB_SHARED_PREFERENCES export {NONE} all undefine default_create, copy end EV_KEY_CONSTANTS export {NONE} all undefine default_create, copy end E_PROFILER_CONSTANTS export {NONE} all undefine default_create, copy end create make_default feature {NONE} -- Initialization make_default -- Create Current. do default_create set_title (Interface_names.t_Profile_query_window) set_icon_pixmap (pixmaps.icon_pixmaps.general_dialog_icon) create all_subqueries.make create all_operators.make init_commands build_interface resize_actions.extend (agent resize_columns) dpi_changed_actions.extend (agent dpi_resize_columns) end init_commands do create run_query_cmd.make (Current) create save_result_cmd.make (Current) end resize_columns (a_x: INTEGER; a_y: INTEGER; a_width: INTEGER; a_height: INTEGER) -- Resize the columns for the active & inactive query lists do active_query_window.set_column_width (active_query_window.width, 1) inactive_subqueries_window.set_column_width (inactive_subqueries_window.width, 1) end dpi_resize_columns (a_dpi: NATURAL_32; a_x: INTEGER; a_y: INTEGER; a_width: INTEGER; a_height: INTEGER) -- Resize the columns for the active & inactive query lists do resize_columns (a_x, a_y, a_width, a_height) end build_interface -- Build the user interfac -- Initialize the commands local container: EV_VERTICAL_BOX -- Widget containing all others query_box: EV_HORIZONTAL_BOX -- Form for the query subquery_box: EV_HORIZONTAL_BOX -- Box for `subquery_text' query_button_form: EV_VERTICAL_BOX -- Form with buttons allowing subqueries activation/inactivation grid_border: EV_VERTICAL_BOX -- A border to be placed around `output_grid'. subquery_frame: EV_FRAME -- Frame for the subquery button_box: EV_HORIZONTAL_BOX -- Box for the buttons --| FIXME help_button: EV_BUTTON -- Button for `help' run_button: EV_BUTTON -- Button for `run_subquery_cmd' save_as_button: EV_BUTTON -- Button for `save_result_cmd' close_button: EV_BUTTON -- Button to close Current add_or_operator_button: EV_BUTTON -- Button to add a subquery with 'or' operator add_and_operator_button: EV_BUTTON -- Button to add a subquery with 'and' operator set_and_operator_button: EV_BUTTON -- Button to set all the selected subqueries operators to 'and' set_or_operator_button: EV_BUTTON -- Button to set all the selected subqueries operators to 'or' reactivate_button: EV_BUTTON -- Button to reactivate one or more subqueries inactivate_button: EV_BUTTON -- Button to inactivate one or more subqueries do close_request_actions.extend (agent close) --| Create the Active & Inactive list create active_query_window active_query_window.enable_multiple_selection active_query_window.set_column_title (Interface_names.l_Active_query, 1) create inactive_subqueries_window inactive_subqueries_window.enable_multiple_selection inactive_subqueries_window.set_column_title (Interface_names.l_Inactive_subqueries, 1) --| Query buttons (Activate, Inactivate, Or, And) create reactivate_button.make_with_text_and_action ("< Activate", agent reactivate) create inactivate_button.make_with_text_and_action ("Inactivate >", agent inactivate) create set_or_operator_button.make_with_text_and_action (Interface_names.b_Or, agent change_operator (profiler_or)) create set_and_operator_button.make_with_text_and_action (Interface_names.b_And, agent change_operator (profiler_and)) create query_button_form query_button_form.set_padding (Layout_constants.Small_border_size) extend_button (query_button_form, reactivate_button) extend_button (query_button_form, inactivate_button) extend_button (query_button_form, set_or_operator_button) extend_button (query_button_form, set_and_operator_button) query_button_form.extend (create {EV_CELL}) -- Expandable item --| Query box (contain Active Query, Add/Remove Buttons, Inactive Subqueries) create query_box query_box.set_padding (Layout_constants.Small_border_size) query_box.extend (active_query_window) -- expandable item query_box.extend (query_button_form) query_box.disable_item_expand (query_button_form) query_box.extend (inactive_subqueries_window) -- expandable item --| Subquery frame create add_and_operator_button.make_with_text_and_action (Interface_names.b_And, agent add_subquery (profiler_and)) create add_or_operator_button.make_with_text_and_action (Interface_names.b_Or, agent add_subquery (profiler_or)) create subquery_text create subquery_box subquery_box.set_border_width (Layout_constants.Small_border_size) subquery_box.set_padding (Layout_constants.Small_border_size) subquery_box.extend (subquery_text) -- expandable item extend_button (subquery_box, add_and_operator_button) extend_button (subquery_box, add_or_operator_button) create subquery_frame.make_with_text (Interface_names.l_Subquery) subquery_frame.extend (subquery_box) create output_grid output_grid.enable_multiple_row_selection output_grid.set_item_pebble_function (agent retrieve_pebble (?, True)) output_grid.pointer_motion_item_actions.extend (agent record_mouse_relative_to_item) output_grid.row_expand_actions.extend (agent row_expanded) output_grid.row_collapse_actions.extend (agent row_collapsed) output_grid.pointer_button_press_item_actions.extend (agent item_pressed) output_grid.key_press_actions.extend (agent key_pressed) output_grid.mouse_wheel_actions.extend (agent mouse_wheel_received) output_grid.set_configurable_target_menu_mode output_grid.set_configurable_target_menu_handler (agent context_menu_handler) create grid_border grid_border.set_border_width (1) grid_border.set_background_color (gray) grid_border.extend (output_grid) create tree_nodes_enabled_button.make_with_text ("Nest Output?") tree_nodes_enabled_button.select_actions.extend (agent tree_node_state_toggled) --| Build button bar --|FIXME: Put the Help button back when help is available. --|FIXME create help_button.make_with_text (Interface_names.b_Help) create run_button.make_with_text_and_action (Interface_names.b_Update, agent run_query_cmd.execute) create save_as_button.make_with_text_and_action (Interface_names.b_Save, agent save_result_cmd.execute) create close_button.make_with_text_and_action (Interface_names.b_Close, agent close) create button_box button_box.set_border_width (Layout_constants.Small_border_size) button_box.set_padding (Layout_constants.Small_border_size) --| FIXME extend_button (button_box, help_button) button_box.extend (create {EV_CELL}) -- expandable item extend_button (button_box, run_button) extend_button (button_box, save_as_button) extend_button (button_box, close_button) --| Build forms create container container.set_padding (Layout_constants.Small_padding_size) container.set_border_width (Layout_constants.Small_border_size) container.extend (query_box) container.disable_item_expand (query_box) container.extend (subquery_frame) container.disable_item_expand (subquery_frame) container.extend (tree_nodes_enabled_button) container.disable_item_expand (tree_nodes_enabled_button) container.extend (grid_border) -- expandable item container.extend (button_box) container.disable_item_expand (button_box) extend (container) --| Sizing set_window_size -- Retrieve colors from the preferences handle_color_change -- Connect agents to the preferences so we can update the display as required. colors_changed_agent := agent handle_color_change preferences.editor_data.feature_text_color_preference.change_actions.extend (colors_changed_agent) preferences.editor_data.cluster_text_color_preference.change_actions.extend (colors_changed_agent) preferences.editor_data.class_text_color_preference.change_actions.extend (colors_changed_agent) end context_menu_handler (a_menu: EV_MENU; a_target_list: ARRAYED_LIST [EV_PND_TARGET_DATA]; a_source: EV_PICK_AND_DROPABLE; a_pebble: ANY) -- Context menu handler. local l_win: EB_DEVELOPMENT_WINDOW do l_win := window_manager.last_focused_development_window if l_win /= Void then l_win.menus.context_menu_factory.standard_compiler_item_menu (a_menu, a_target_list, a_source, a_pebble) end end feature {EB_SAVE_RESULT_CMD} -- Save commands save_it (ptf: RAW_FILE) -- Save window content in `ptf'. require pft_not_void: ptf /= Void pft_closed: ptf.is_closed local a_string: STRING_32 utf: UTF_CONVERTER i: INTEGER current_row: EV_GRID_ROW do create a_string.make_empty ptf.create_read_write from i := 1 until i > output_grid.row_count loop current_row := output_grid.row (i) append_row_to_string (current_row, a_string) i := i + 1 end ptf.put_string (utf.utf_32_string_to_utf_8_string_8 (a_string)) ptf.put_new_line ptf.close end feature -- Status Setting set_window_size -- Set window size, according to preferences. do --| FIXME ARNAUD: To be fixed (Save window size into preferences and restore it) set_size (Layout_constants.Dialog_unit_to_pixels(600), Layout_constants.Dialog_unit_to_pixels(500)) --| END FIXME ARNAUD. end update_window (pq: PROFILER_QUERY; po: PROFILER_OPTIONS; pi: PROFILE_INFORMATION) -- Update User Interface Widgets to reflect the parameters: -- This window will be associated with `pq', will -- use `po' and `profinfo', and display `st'. local i, j, k, l: INTEGER profile_set: PROFILE_SET profile_set_count: INTEGER quick_sorter: QUICK_SORTER [EB_PROFILE_QUERY_GRID_ROW] equality_tester: AGENT_EQUALITY_TESTER [EB_PROFILE_QUERY_GRID_ROW] current_profile_data: PROFILE_DATA function: EIFFEL_FUNCTION last_cluster_string: STRING last_class_id: INTEGER class_lower, class_upper, cluster_lower, cluster_upper: INTEGER current_cluster_string, current_class_string: STRING current_class_id: INTEGER query_grid_item: EB_PROFILE_QUERY_GRID_ROW last_cluster, last_class: EB_PROFILE_QUERY_GRID_ROW row: EB_PROFILE_QUERY_GRID_ROW l_class: CLASS_C do profiler_query := pq profiler_options := po profinfo := pi i := 1 -- Feature name is always shown. show_feature_name := True i := i + 1 -- Now assign information from `profiler_options' to BOOLEAN -- attributes for speed and ease of use when querying. if profiler_options.output_names.valid_index (i) and then profiler_options.output_names.item (i).same_string (profiler_calls) then show_calls := True i := i + 1 end if profiler_options.output_names.valid_index (i) and then profiler_options.output_names.item (i).same_string (profiler_self) then show_self := True i := i + 1 end if profiler_options.output_names.valid_index (i) and then profiler_options.output_names.item (i).same_string (profiler_descendants) then show_descendents := True i := i + 1 end if profiler_options.output_names.valid_index (i) and then profiler_options.output_names.item (i).same_string (profiler_total) then show_total := True i := i + 1 end if profiler_options.output_names.valid_index (i) and then profiler_options.output_names.item (i).same_string (profiler_percentage) then show_percentage := True i := i + 1 end count_active_subqueries if profiler_query.subqueries.count > active_subqueries then from profiler_query.subqueries.go_i_th (active_subqueries + 1) profiler_query.subquery_operators.go_i_th (active_subqueries) if profiler_query.subquery_operators.before then profiler_query.subquery_operators.forth end until profiler_query.subqueries.after loop all_subqueries.extend (profiler_query.subqueries.item) profiler_query.subqueries.forth if not profiler_query.subquery_operators.after then all_operators.extend (profiler_query.subquery_operators.item) profiler_query.subquery_operators.forth end end end update_query_frame subquery_text.remove_text output_grid.wipe_out profile_set := pi.profile_data profile_set_count := profile_set.c_profiling_list.count + profile_set.cycle_profiling_list.count + profile_set.eiffel_profiling_list.count -- Store the flat profile information. create profile_array.make (profile_set_count) from profile_set := pi.profile_data profile_set.start until profile_set.after loop current_profile_data := profile_set.item if attached {EIFFEL_PROFILE_DATA} current_profile_data as current_eiffel_profile_data then create query_grid_item.make_node (current_profile_data.function.name, current_profile_data, 4) -- Now we perform special handling for the row as we must have access to each of the three -- feature, class and cluster texts individually. l_class := current_eiffel_profile_data.function.class_c if l_class /= Void then query_grid_item.set_cluster_class_feature_text (l_class.group.name + full_stop, l_class.name + full_stop, current_eiffel_profile_data.function.displayed_feature_name) else query_grid_item.set_cluster_class_feature_text ("Unknown cluster.", current_eiffel_profile_data.function.int_class_name + full_stop, current_eiffel_profile_data.function.displayed_feature_name) end else create query_grid_item.make_node (current_profile_data.function.name, current_profile_data, 5) end query_grid_item.set_values (current_profile_data.calls, current_profile_data.self, current_profile_data.descendants, current_profile_data.total, current_profile_data.percentage) profile_array.force (query_grid_item) profile_set.forth end -- Perform a sort to ensure that all data is in order before -- building the tree info. create equality_tester.make (agent compare_profile_query_grid_rows (?, ?, 1, True)) create quick_sorter.make (equality_tester) quick_sorter.sort (profile_array) -- Store the tree profile information needed for the tree mode. create cluster_array.make_empty create class_array.make_empty create feature_array.make_empty create c_functions_array.make_empty create cyclic_functions_array.make_empty create root_nodes_array.make_empty i := 1 cluster_lower := 1 class_lower := 1 class_upper := 1 cluster_upper := 1 last_cluster_string := "" last_class_id := 0 across profile_array as ic loop if attached {EIFFEL_PROFILE_DATA} ic.item.profile_data as current_eiffel_profile_data then function := current_eiffel_profile_data.function if function.class_c /= Void then current_cluster_string := function.class_c.group.name current_class_string := function.class_c.name else current_cluster_string := "Unknown cluster" current_class_string := function.int_class_name end current_class_id := function.class_id if not last_cluster_string.is_equal (current_cluster_string) then if last_cluster /= Void then last_cluster.set_upper (cluster_lower - 1) end create query_grid_item.make_parent (cluster_lower, cluster_lower, current_cluster_string, current_eiffel_profile_data, 3) cluster_array.force (query_grid_item, cluster_array.upper + 1) last_cluster := query_grid_item end if last_class_id /= current_class_id then if last_class /= Void then last_class.set_upper (class_lower - 1) end create query_grid_item.make_parent (class_lower, class_lower, current_class_string, current_eiffel_profile_data, 2) class_array.force (query_grid_item, class_array.upper + 1) cluster_lower := cluster_lower + 1 last_class := query_grid_item end create query_grid_item.make_node (function.displayed_feature_name, current_eiffel_profile_data, 1) query_grid_item.set_values (current_eiffel_profile_data.calls, current_eiffel_profile_data.self, current_eiffel_profile_data.descendants, current_eiffel_profile_data.total, current_eiffel_profile_data.percentage) feature_array.force (query_grid_item, feature_array.upper + 1) -- Now update totals for classes and clusters. if last_class /= Void then last_class.increase_values (current_eiffel_profile_data.calls, current_eiffel_profile_data.self, current_eiffel_profile_data.descendants, current_eiffel_profile_data.total, current_eiffel_profile_data.percentage) end last_cluster_string := current_cluster_string last_class_id := current_class_id class_lower := class_lower + 1 else current_profile_data := ic.item.profile_data if attached {C_PROFILE_DATA} current_profile_data as current_c_profile_data then create query_grid_item.make_node (current_c_profile_data.function.name, current_c_profile_data, 5) query_grid_item.set_values (current_c_profile_data.calls, current_c_profile_data.self, current_c_profile_data.descendants, current_c_profile_data.total, current_c_profile_data.percentage) c_functions_array.force (query_grid_item, c_functions_array.upper + 1) else if attached {CYCLE_PROFILE_DATA} current_profile_data as current_cycle_profile_data then create query_grid_item.make_node (current_cycle_profile_data.function.name, current_cycle_profile_data, 5) query_grid_item.set_values (current_cycle_profile_data.calls, current_cycle_profile_data.self, current_cycle_profile_data.descendants, current_cycle_profile_data.total, current_cycle_profile_data.percentage) else check supported_type: False end end cyclic_functions_array.force (query_grid_item, cyclic_functions_array.upper + 1) end end end -- The loop does not handle the upper of the final items, so -- set them explicitly here. if not cluster_array.is_empty then query_grid_item := cluster_array.item (cluster_array.upper) query_grid_item.set_upper (class_array.upper) end if not class_array.is_empty then query_grid_item := class_array.item (class_array.upper) query_grid_item.set_upper (feature_array.upper) end -- Now build the root nodes for Eiffel queries. if feature_array.count >= 1 then create query_grid_item.make_node (Profiler_eiffel_features, Void, 5) query_grid_item.set_display_agent (agent build_eiffel_functions) root_nodes_array.force (query_grid_item, root_nodes_array.upper + 1) end -- Now sum cluster totals. from i := 1 until i > cluster_array.count loop last_cluster := cluster_array [i] j := last_cluster.child_node_lower_index k := last_cluster.child_node_upper_index from l := j until l > k loop last_class := class_array [l] last_cluster.increase_values (last_class.calls, last_class.self, last_class.descendents, last_class.total, last_class.percentage) l := l + 1 end query_grid_item.increase_values (last_cluster.calls, last_cluster.self, last_cluster.descendents, last_cluster.total, last_cluster.percentage) i := i + 1 end if c_functions_array.count >= 1 then create query_grid_item.make_node (profiler_c_functions, Void, 5) query_grid_item.set_display_agent (agent build_c_functions) root_nodes_array.force (query_grid_item, root_nodes_array.upper + 1) -- Now Sum c_function totals. from i := 1 until i > c_functions_array.count loop row := c_functions_array [i] query_grid_item.increase_values (row.calls, row.self, row.descendents, row.total, row.percentage) i := i + 1 end end if cyclic_functions_array.count >= 1 then create query_grid_item.make_node (profiler_cyclic_functions, Void, 5) query_grid_item.set_display_agent (agent build_cyclic_functions) root_nodes_array.force (query_grid_item, root_nodes_array.upper + 1) -- Now Sum cyclic totals. from i := 1 until i > cyclic_functions_array.count loop row := cyclic_functions_array [i] query_grid_item.increase_values (row.calls, row.self, row.descendents, row.total, row.percentage) i := i + 1 end end create displayed_column_indexes.make_filled (0, 1, 6) -- Build the column headers -- Note that "Function" is always displayed. output_grid.insert_new_column (1) displayed_column_indexes.put (1, 1) output_grid.column (1).set_title ("Function") output_grid.column (1).header_item.pointer_button_press_actions.extend (agent sort_column (1, ?,?,?,?,?,?,?,?)) output_grid.column (1).header_item.pointer_double_press_actions.extend (agent resize_column (1, ?,?,?,?,?,?,?,?)) output_grid.column (1).set_data ([True, True]) if show_calls then i := output_grid.column_count + 1 output_grid.insert_new_column (i) output_grid.column (i).set_title ("Calls") output_grid.column (i).header_item.pointer_button_press_actions.extend (agent sort_column (2, ?,?,?,?,?,?,?,?)) displayed_column_indexes.put (i, 2) output_grid.column (i).header_item.pointer_double_press_actions.extend (agent resize_column (i, ?,?,?,?,?,?,?,?)) output_grid.column (i).set_data ([True, True]) end if show_self then i := output_grid.column_count + 1 output_grid.insert_new_column (i) output_grid.column (i).set_title ("Self") output_grid.column (i).header_item.pointer_button_press_actions.extend (agent sort_column (3, ?,?,?,?,?,?,?,?)) displayed_column_indexes.put (i, 3) output_grid.column (i).header_item.pointer_double_press_actions.extend (agent resize_column (i, ?,?,?,?,?,?,?,?)) output_grid.column (i).set_data ([True, True]) end if show_descendents then i := output_grid.column_count + 1 output_grid.insert_new_column (i) output_grid.column (i).set_title ("Descendants") output_grid.column (i).header_item.pointer_button_press_actions.extend (agent sort_column (4, ?,?,?,?,?,?,?,?)) displayed_column_indexes.put (i, 4) output_grid.column (i).header_item.pointer_double_press_actions.extend (agent resize_column (i, ?,?,?,?,?,?,?,?)) output_grid.column (i).set_data ([True, True]) end if show_total then i := output_grid.column_count + 1 output_grid.insert_new_column (i) output_grid.column (i).set_title ("Total") output_grid.column (i).header_item.pointer_button_press_actions.extend (agent sort_column (5, ?,?,?,?,?,?,?,?)) displayed_column_indexes.put (i, 5) output_grid.column (i).header_item.pointer_double_press_actions.extend (agent resize_column (i, ?,?,?,?,?,?,?,?)) output_grid.column (i).set_data ([True, True]) end if show_percentage then i := output_grid.column_count + 1 output_grid.insert_new_column (i) output_grid.column (i).set_title ("Percentage") output_grid.column (i).header_item.pointer_button_press_actions.extend (agent sort_column (6, ?,?,?,?,?,?,?,?)) displayed_column_indexes.put (i, 6) output_grid.column (i).header_item.pointer_double_press_actions.extend (agent resize_column (i, ?,?,?,?,?,?,?,?)) output_grid.column (i).set_data ([True, True]) end rebuild_grid end feature -- Access min_column_index: INTEGER = 1 max_column_index: INTEGER = 6 -- Various indexes of columns feature {NONE} -- Implementation displayed_column_indexes: ARRAY [INTEGER] -- Mapping of logical column index (1-6) to -- physical column index as displayed in `Current'. -- If a column is not shown, its entry is zero. profile_array: ARRAYED_LIST [EB_PROFILE_QUERY_GRID_ROW] -- All grid rows used in the flat list mode. cluster_array: ARRAY [EB_PROFILE_QUERY_GRID_ROW] class_array: ARRAY [EB_PROFILE_QUERY_GRID_ROW] feature_array: ARRAY [EB_PROFILE_QUERY_GRID_ROW] c_functions_array: ARRAY [EB_PROFILE_QUERY_GRID_ROW] cyclic_functions_array: ARRAY [EB_PROFILE_QUERY_GRID_ROW] -- Arrays to hold all grid rows used in the tree structure -- mode. Grouped accordingly. root_nodes_array: ARRAY [EB_PROFILE_QUERY_GRID_ROW] -- All root nodes to be shown in `output_grid'. -- Eiffel, C or Cyclic nodes. Only used in the -- tree structure mode. calls, self, descendants, total, percentage: REAL_64 cluster_calls, cluster_self, cluster_descendants, cluster_total, cluster_percentage: REAL_64 class_calls, class_self, class_descendants, class_total, class_percentage: REAL_64 -- Attributes to hold information totals during building of the structured tree mode. -- They are not defined as locals within `rebuild_grid' as this permits functions to -- be implemented which deal with them, thereby reducing repeated code. show_feature_name, show_calls, show_self, show_descendents, show_total, show_percentage: BOOLEAN -- Attributes to hold which columns of data are to be displayed. These -- are set from `profiler_options.output_names' so they can be quickly queried. fill_grid_row (query_grid_row: EB_PROFILE_QUERY_GRID_ROW; row_index: INTEGER) -- Fill `output_grid' row `row_index' with data from `query_grid_row'. require query_grid_row_not_void: query_grid_row /= Void valid_row_index: row_index >= 1 and row_index <= output_grid.row_count + 1 local i: INTEGER feature_grid_item: EV_GRID_DRAWABLE_ITEM calls_grid_item, self_grid_item, descendents_grid_item, total_grid_item, percentage_grid_item: EV_GRID_LABEL_ITEM do -- Create grid items to be associated with `query_grid_row' if not already created. if query_grid_row.feature_grid_item = Void then create feature_grid_item.make_with_expose_action_agent (agent draw_grid_item (?, query_grid_row)) if show_calls then create calls_grid_item.make_with_text (query_grid_row.calls.out) end if show_self then create self_grid_item.make_with_text (time_formatter.formatted (query_grid_row.self)) end if show_descendents then create descendents_grid_item.make_with_text (time_formatter.formatted (query_grid_row.descendents)) end if show_total then create total_grid_item.make_with_text (time_formatter.formatted (query_grid_row.total)) end if show_percentage then create percentage_grid_item.make_with_text (percentage_formatter.formatted (query_grid_row.percentage)) end query_grid_row.set_grid_items (feature_grid_item, calls_grid_item, self_grid_item, descendents_grid_item, total_grid_item, percentage_grid_item) end output_grid.set_item (1, row_index, query_grid_row.feature_grid_item) i := 2 if show_calls then output_grid.set_item (i, row_index, query_grid_row.calls_grid_item) i := i + 1 end if show_self then output_grid.set_item (i, row_index, query_grid_row.self_grid_item) i := i + 1 end if show_descendents then output_grid.set_item (i, row_index, query_grid_row.descendents_grid_item) i := i + 1 end if show_total then output_grid.set_item (i, row_index, query_grid_row.total_grid_item) i := i + 1 end if show_percentage then output_grid.set_item (i, row_index, query_grid_row.percentage_grid_item) i := i + 1 end -- Connect the `query_grid_row' and `output_grid.row' so -- once may be accessed from the other. output_grid.row (row_index).set_data (query_grid_row) query_grid_row.set_row (output_grid.row (row_index)) end rebuild_grid -- Perform a complete rebuild of `output_grid' based on current settings. local i: INTEGER query_grid_row: EB_PROFILE_QUERY_GRID_ROW do -- Remove all existing rows. We do not wish to call `wipe_out' -- as we do not want to loose the columns. if output_grid.row_count > 0 then output_grid.remove_rows (1, output_grid.row_count) end if not tree_structure_enabled then output_grid.disable_tree i := 1 across profile_array as ic loop query_grid_row := ic.item fill_grid_row (query_grid_row, i) i := i + 1 end else output_grid.enable_tree across root_nodes_array as ic loop ic.item.display_agent.call ([ic.item]) end end -- Now update the expanded state of all rows contained. -- Only if tree structure enabled. if tree_structure_enabled then from i := output_grid.row_count until i < 1 loop if attached {EB_PROFILE_QUERY_GRID_ROW} output_grid.row (i).data as l_query_grid_row and then l_query_grid_row.is_expanded then l_query_grid_row.row.expand end i := i - 1 end end end build_eiffel_functions (query_grid_row: EB_PROFILE_QUERY_GRID_ROW) -- Build all Eiffel functions into `output_grid' as child rows -- of `query_grid_row'. require query_grid_row_not_void: query_grid_row /= Void local cluster_index, class_index, feature_index: INTEGER cluster_query_grid_row, class_query_grid_row: EB_PROFILE_QUERY_GRID_ROW class_lower, class_upper, cluster_lower, cluster_upper: INTEGER cluster_row_index, class_row_index, feature_row_index: INTEGER cluster_node_index: INTEGER do if cluster_array.count >= 1 then cluster_node_index := output_grid.row_count + 1 fill_grid_row (query_grid_row, cluster_node_index) from cluster_index := 1 until cluster_index > cluster_array.count loop cluster_query_grid_row := cluster_array [cluster_index] cluster_lower := cluster_query_grid_row.child_node_lower_index cluster_upper := cluster_query_grid_row.child_node_upper_index cluster_row_index := output_grid.row_count + 1 fill_grid_row (cluster_query_grid_row, cluster_row_index) output_grid.row (cluster_node_index).add_subrow (output_grid.row (cluster_row_index)) from class_index := cluster_lower until class_index > cluster_upper loop class_query_grid_row := class_array [class_index] class_lower := class_query_grid_row.child_node_lower_index class_upper := class_query_grid_row.child_node_upper_index class_row_index := output_grid.row_count + 1 fill_grid_row (class_query_grid_row, class_row_index) output_grid.row (cluster_row_index).add_subrow (output_grid.row (class_row_index)) from feature_index := class_lower until feature_index > class_upper loop if attached {EB_PROFILE_QUERY_GRID_ROW} feature_array [feature_index] as feature_query_grid_row then feature_row_index := output_grid.row_count + 1 fill_grid_row (feature_query_grid_row, feature_row_index) output_grid.row (class_row_index).add_subrow (output_grid.row (feature_row_index)) end feature_index := feature_index + 1 end class_index := class_index + 1 end cluster_index := cluster_index + 1 end end end build_c_functions (query_grid_row: EB_PROFILE_QUERY_GRID_ROW) -- Build all c functions into `output_grid' as child rows -- of `query_grid_row'. require query_grid_row_not_void: query_grid_row /= Void local i, k, c_node_index: INTEGER c_function_query_grid_row: EB_PROFILE_QUERY_GRID_ROW do if c_functions_array.count >= 1 then c_node_index := output_grid.row_count + 1 fill_grid_row (query_grid_row, c_node_index) from i := 1 until i > c_functions_array.count loop c_function_query_grid_row := c_functions_array [i] k := output_grid.row_count + 1 fill_grid_row (c_function_query_grid_row, k) output_grid.row (c_node_index).add_subrow (output_grid.row (k)) i := i + 1 end end end build_cyclic_functions (query_grid_row: EB_PROFILE_QUERY_GRID_ROW) -- Build all cyclic functions into `output_grid' as child rows -- of `query_grid_row'. require query_grid_row_not_void: query_grid_row /= Void local i, k, cyclic_node_index: INTEGER cyclic_function_query_grid_row: EB_PROFILE_QUERY_GRID_ROW do if cyclic_functions_array.count >= 1 then cyclic_node_index := output_grid.row_count + 1 fill_grid_row (query_grid_row, cyclic_node_index) from i := 1 until i > cyclic_functions_array.count loop cyclic_function_query_grid_row := cyclic_functions_array [i] k := output_grid.row_count + 1 fill_grid_row (cyclic_function_query_grid_row, k) output_grid.row (cyclic_node_index).add_subrow (output_grid.row (k)) i := i + 1 end end end draw_grid_item (drawable: EV_DRAWABLE; query_grid_row: EB_PROFILE_QUERY_GRID_ROW) -- Draw feature grid item of `query_grid_row' into `drawable'. require drawable_not_void: drawable /= Void query_grid_row_not_void: query_grid_row /= Void local offset: INTEGER font: EV_FONT row_selected, focused: BOOLEAN adjusted_column_width: INTEGER do row_selected := query_grid_row.row.is_selected focused := output_grid.has_focus if row_selected then if focused then drawable.set_foreground_color (output_grid.focused_selection_color) else drawable.set_foreground_color (output_grid.non_focused_selection_color) end else drawable.set_foreground_color (white) end -- Firstly fill the background area of the grid. We respect the -- selected state of the row by drawing in the correct selection color -- if selected. drawable.fill_rectangle (0, 0, drawable.width, drawable.height) -- Calculate the width of the first column less the current horizontal indent -- of the item being drawn. This is required to draw the ellipsing correctly. adjusted_column_width := output_grid.column (1).width - query_grid_row.row.item (1).horizontal_indent font := drawing_font drawable.set_font (drawing_font) if query_grid_row.type = 1 then if row_selected and has_focus then drawable.set_foreground_color (white) elseif query_grid_row.is_feature_renamed then drawable.set_foreground_color (black) else drawable.set_foreground_color (feature_text_color) end drawable.draw_ellipsed_text_top_left (left_border, 1, query_grid_row.text, (adjusted_column_width - left_border).max (0)) elseif query_grid_row.type = 2 then if row_selected and has_focus then drawable.set_foreground_color (white) else drawable.set_foreground_color (class_text_color) end drawable.draw_ellipsed_text_top_left (left_border, 1, query_grid_row.text, (adjusted_column_width - left_border).max (0)) elseif query_grid_row.type = 3 then if row_selected and has_focus then drawable.set_foreground_color (white) else drawable.set_foreground_color (cluster_text_color) end drawable.draw_ellipsed_text_top_left (left_border, 1, query_grid_row.text, (adjusted_column_width - left_border).max (0)) elseif query_grid_row.type = 4 then if row_selected and has_focus then drawable.set_foreground_color (white) else drawable.set_foreground_color (cluster_text_color) end offset := left_border drawable.draw_ellipsed_text_top_left (offset, 1, query_grid_row.cluster_text, (adjusted_column_width - offset).max (0)) offset := offset + query_grid_row.cluster_text_width if row_selected and has_focus then drawable.set_foreground_color (white) else drawable.set_foreground_color (class_text_color) end drawable.draw_ellipsed_text_top_left (offset, 1, query_grid_row.class_text, (adjusted_column_width - offset).max (0)) offset := offset + query_grid_row.class_text_width if row_selected and has_focus then drawable.set_foreground_color (white) elseif query_grid_row.is_feature_renamed then drawable.set_foreground_color (black) else drawable.set_foreground_color (feature_text_color) end drawable.draw_ellipsed_text_top_left (offset, 1, query_grid_row.feature_text, (adjusted_column_width - offset).max (0)) elseif query_grid_row.type = 5 then if row_selected and has_focus then drawable.set_foreground_color (white) else drawable.set_foreground_color (black) end drawable.draw_ellipsed_text_top_left (left_border, 1, query_grid_row.text, (adjusted_column_width - left_border).max (0)) end end left_border: INTEGER = 3 -- Pixel position from left edge of first item in row -- where the text of the item begins. last_sorted_column_agent: FUNCTION [EB_PROFILE_QUERY_GRID_ROW, EB_PROFILE_QUERY_GRID_ROW, BOOLEAN] -- The last agent used to perform a full sort on `output_grid'. We need access to -- this for implementing sorts within sorts. last_equal_column_agent: FUNCTION [EB_PROFILE_QUERY_GRID_ROW, EB_PROFILE_QUERY_GRID_ROW, BOOLEAN] -- An agent used to determine if two profile query grid rows are equal based on the same -- seach criteria as `last_sorted_column_agent'. We need access to -- this for implementing sorts within sorts. sort_within_last: BOOLEAN -- Should the current sort be performed within the last sort? tree_structure_enabled: BOOLEAN -- Is `output_grid' to display its contents in -- a nested tree structure? tree_node_state_toggled -- The tree structured enabled button has been toggled -- so updated display in `output_grid' to reflect this. do tree_structure_enabled := tree_nodes_enabled_button.is_selected sort_column (1, 0,0,0,0,0,0,0,0) end sort_column (column_index: INTEGER; a_x, a_y, a_button: INTEGER; a_x_tilt, a_y_tilt, a_pressure: DOUBLE; a_screen_x, a_screen_y: INTEGER) -- Sort logical column `column_index'. require valid_column_index: column_index >= min_column_index and column_index <= max_column_index local ascending: BOOLEAN do if output_grid.header.pointed_divider_index = 0 then -- Should sorting be performed within the last sort? sort_within_last := ev_application.ctrl_pressed if attached {TUPLE [first: BOOLEAN; second: BOOLEAN]} output_grid.column (displayed_column_indexes.item (column_index)).data as sorted_tuple then if not ev_application.ctrl_pressed then -- Determine the direction for the sort. ascending := sorted_tuple.first -- Now store the new direction for the column state. sorted_tuple.first := not ascending else ascending := sorted_tuple.second sorted_tuple.second := not ascending end else check sorted_tuple_not_void: False end end -- Now perform actual sort on data. if tree_structure_enabled then sort_tree_structure (column_index, ascending) else sort_flat_structure (column_index, ascending) end rebuild_grid if not ev_application.ctrl_pressed then last_equal_column_agent := agent profile_equal (?, ?, column_index) last_sorted_column_agent := agent compare_profile_query_grid_rows (?, ?, column_index, ascending) end end end sort_flat_structure (column_index: INTEGER; ascending: BOOLEAN) -- Perform sorting within flat structure for column `column_index' with -- direction based on `ascending'. require flat_mode_enabled: not tree_structure_enabled valid_column_index: column_index >= min_column_index and column_index <= max_column_index local quick_sorter: QUICK_SORTER [EB_PROFILE_QUERY_GRID_ROW] equality_tester: AGENT_EQUALITY_TESTER [EB_PROFILE_QUERY_GRID_ROW] do create equality_tester.make (agent compare_profile_query_grid_rows (?, ?, column_index, ascending)) create quick_sorter.make (equality_tester) quick_sorter.sort (profile_array) end sort_tree_structure (column_index: INTEGER; ascending: BOOLEAN) -- Perform sorting within tree structure for column `column_index' with -- direction based on `ascending'. require tree_structure_enabled: tree_structure_enabled valid_column_index: column_index >= min_column_index and column_index <= max_column_index local quick_sorter: DS_ARRAY_QUICK_SORTER [EB_PROFILE_QUERY_GRID_ROW] equality_tester: AGENT_BASED_EQUALITY_TESTER [EB_PROFILE_QUERY_GRID_ROW] do create equality_tester.make (agent compare_profile_query_grid_rows (?, ?, column_index, ascending)) create quick_sorter.make (equality_tester) quick_sorter.sort (cluster_array) across cluster_array as ic loop quick_sorter.subsort (class_array, ic.item.child_node_lower_index, ic.item.child_node_upper_index) end across class_array as ic loop quick_sorter.subsort (feature_array, ic.item.child_node_lower_index, ic.item.child_node_upper_index) end quick_sorter.sort (root_nodes_array) end compare_profile_query_grid_rows (query_grid_row1, query_grid_row2: EB_PROFILE_QUERY_GRID_ROW; column_index: INTEGER; ascending: BOOLEAN): BOOLEAN -- Is `query_grid_row1' less than `query_grid_row2' with search criteria based on logical column `column_index' and direction -- `ascending'. require query_grid_row1_not_void: query_grid_row1 /= Void query_grid_row2_not_void: query_grid_row2 /= Void valid_column_index: column_index >= min_column_index and column_index <= max_column_index local last_equal: BOOLEAN do last_equal := True if sort_within_last then last_equal := last_equal_column_agent.item ([query_grid_row1, query_grid_row2, column_index]) end if last_equal then inspect column_index when 1 then if ascending then Result := query_grid_row1.text < query_grid_row2.text else Result := query_grid_row1.text > query_grid_row2.text end when 2 then if ascending then Result := query_grid_row1.calls < query_grid_row2.calls else Result := query_grid_row1.calls > query_grid_row2.calls end when 3 then if ascending then Result := query_grid_row1.self < query_grid_row2.self else Result := query_grid_row1.self > query_grid_row2.self end when 4 then if ascending then Result := query_grid_row1.descendents < query_grid_row2.descendents else Result := query_grid_row1.descendents > query_grid_row2.descendents end when 5 then if ascending then Result := query_grid_row1.total < query_grid_row2.total else Result := query_grid_row1.total > query_grid_row2.total end when 6 then if ascending then Result := query_grid_row1.percentage < query_grid_row2.percentage else Result := query_grid_row1.percentage > query_grid_row2.percentage end else check invalid_column: False end end else sort_within_last := False Result := last_sorted_column_agent.item ([query_grid_row1, query_grid_row2]) sort_within_last := True end end resize_column (a_column: INTEGER; a_x, a_y, a_button: INTEGER; a_x_tilt, a_y_tilt, a_pressure: DOUBLE; a_screen_x, a_screen_y: INTEGER) -- Resize column `a_column' in `output_grid' to required width to display -- its contents if the mouse pointer is currently over a column divider. require valid_column: a_column >= min_column_index and a_column <= max_column_index local pointed_index: INTEGER grid_row: EV_GRID_ROW -- query_grid_row: EB_PROFILE_QUERY_GRID_ROW required_width: INTEGER i: INTEGER do pointed_index := output_grid.header.pointed_divider_index -- If we are not currently pointing to a column divider then do nothing. if pointed_index /= 0 then if pointed_index > 1 then required_width := output_grid.column (a_column).required_width_of_item_span (1, output_grid.row_count) else -- In this case we are the first column so we must compute the width -- ourselves as we use drawable items. from i := 1 until i > output_grid.row_count loop grid_row := output_grid.row (i) if attached {EB_PROFILE_QUERY_GRID_ROW} grid_row.data as query_grid_row then if query_grid_row.type = 4 then -- If the row type is 4, then we must add the widths of all three -- components comprising the text. required_width := required_width.max (query_grid_row.cluster_text_width + query_grid_row.class_text_width + query_grid_row.feature_text_width + (left_border * 2)) else -- Although in this mode we only have a single text, we are in the tree mode so -- we also add on the indent of the first item. required_width := required_width.max (query_grid_row.text_width + (left_border * 2) + grid_row.item (1).horizontal_indent) end else check is_profile_query_grid_row: False end end -- We now ignore all rows that are not expanded. if grid_row.subrow_count > 0 and not grid_row.is_expanded then i := i + grid_row.subrow_count_recursive end i := i + 1 end end output_grid.column (a_column).set_width (required_width) end end profile_equal (query_grid_row1, query_grid_row2: EB_PROFILE_QUERY_GRID_ROW; column_index: INTEGER): BOOLEAN -- Are `query_grid_row1' and `query_grid_row2' considered equal for property defined by `column_index'? require profile_data_not_void: query_grid_row1 /= Void and query_grid_row2 /= Void valid_column_index: column_index >= min_column_index and column_index <= max_column_index do inspect column_index when 1 then Result := query_grid_row1.text.is_equal (query_grid_row2.text) when 2 then Result := query_grid_row1.calls = query_grid_row2.calls when 3 then Result := query_grid_row1.self = query_grid_row2.self when 4 then Result := query_grid_row1.descendents = query_grid_row2.descendents when 5 then Result := query_grid_row1.total = query_grid_row2.total when 6 then Result := query_grid_row1.percentage = query_grid_row2.percentage else check invalid_column: False end end end retrieve_pebble (an_item: EV_GRID_ITEM; a_pnd_mode: BOOLEAN): STONE -- Retrieve a pebble from `an_item' if it represents -- a pickable object. May return an instance of -- CLUSTER_STONE, CLASS_STONE, FEATURE_STONE or Void. -- Done only if we are not in pick and drop mode, or else if pick -- and drop mode but the Ctrl key is not pressed. local e_feature: E_FEATURE total_offset: INTEGER do if (not a_pnd_mode or else not ev_application.ctrl_pressed) and then an_item /= Void then if attached {EB_PROFILE_QUERY_GRID_ROW} an_item.data as query_grid_row then if query_grid_row.type = 1 then if last_x > left_border and last_x < query_grid_row.text_width + left_border then if attached {EIFFEL_PROFILE_DATA} query_grid_row.profile_data as eiffel_profile_data then e_feature := eiffel_profile_data.function.e_feature if e_feature /= Void then create {FEATURE_STONE} Result.make (e_feature) end else check only_eiffel_data_pickable: False end end end elseif query_grid_row.type = 2 then if last_x > left_border and last_x < query_grid_row.text_width + left_border then if attached {EIFFEL_PROFILE_DATA} query_grid_row.profile_data as eiffel_profile_data then if eiffel_profile_data.function.class_c /= Void then create {CLASSC_STONE} Result.make (eiffel_profile_data.function.class_c) end else check only_eiffel_data_pickable: False end end end elseif query_grid_row.type = 3 then if last_x > left_border and last_x < query_grid_row.text_width + left_border then if attached {EIFFEL_PROFILE_DATA} query_grid_row.profile_data as eiffel_profile_data then if eiffel_profile_data.function.class_c /= Void then create {CLUSTER_STONE} Result.make (eiffel_profile_data.function.class_c.group) end else check only_eiffel_data_pickable: False end end end elseif query_grid_row.type = 4 then if last_x > left_border then -- We ensure that no pick is performed from within the border at the -- left edge of the item. total_offset := left_border + query_grid_row.cluster_text_width if last_x < total_offset then if attached {EIFFEL_PROFILE_DATA} query_grid_row.profile_data as eiffel_profile_data then if eiffel_profile_data.function.class_c /= Void then create {CLUSTER_STONE} Result.make (eiffel_profile_data.function.class_c.group) end else check only_eiffel_data_pickable: False end end else total_offset := total_offset + query_grid_row.class_text_width if last_x < total_offset then if attached {EIFFEL_PROFILE_DATA} query_grid_row.profile_data as eiffel_profile_data then if eiffel_profile_data.function.class_c /= Void then create {CLASSC_STONE} Result.make (eiffel_profile_data.function.class_c) end else check only_eiffel_data_pickable: False end end else total_offset := total_offset + query_grid_row.feature_text_width if last_x < total_offset then if attached {EIFFEL_PROFILE_DATA} query_grid_row.profile_data as eiffel_profile_data then e_feature := eiffel_profile_data.function.e_feature if e_feature /= Void then create {FEATURE_STONE} Result.make (e_feature) end else check only_eiffel_data_pickable: False end end end end end end end if Result /= Void then output_grid.set_accept_cursor (Result.stone_cursor) output_grid.set_deny_cursor (Result.x_stone_cursor) end end end end key_pressed (a_key: EV_KEY) -- Respond to `a_key' being pressed in `output_grid'. require a_key_not_void: a_key /= Void local i: INTEGER j: INTEGER current_row: EV_GRID_ROW clipboard_text: STRING_32 do if a_key.code = key_a and then ev_application.ctrl_pressed then -- Select all rows in `output_grid'. from i := 1 until i > output_grid.row_count loop current_row := output_grid.row (i) current_row.enable_select if current_row.subrow_count > 0 and then not current_row.is_expanded then i := i + current_row.subrow_count_recursive end i := i + 1 end elseif a_key.code = key_c and then ev_application.ctrl_pressed then -- Copy selected rows to clipboard. clipboard_text := "" from j := 1 until j > output_grid.row_count loop current_row := output_grid.row (j) if current_row.is_selected then append_row_to_string (current_row, clipboard_text) end j := j + 1 end if not clipboard_text.is_empty then ev_application.clipboard.set_text (clipboard_text) end elseif a_key.code = key_page_up then scroll_output_grid ( - lines_to_move_in_per_page_scrolling) elseif a_key.code = key_page_down then scroll_output_grid (lines_to_move_in_per_page_scrolling) end end lines_to_move_in_per_page_scrolling: INTEGER -- Number of lines to be moved in per page scrolling mode. do Result := output_grid.last_visible_row.index - output_grid.first_visible_row.index - preferences.editor_data.scrolling_common_line_count end mouse_wheel_received (a_delta: INTEGER) -- Respond to movement of mouse wheel by `delta' on `output_grid'. local lines_to_move: INTEGER do if preferences.editor_data.mouse_wheel_scroll_full_page then lines_to_move := lines_to_move_in_per_page_scrolling if a_delta > 0 then lines_to_move := 0 - lines_to_move end else lines_to_move := - a_delta * preferences.editor_data.mouse_wheel_scroll_size end scroll_output_grid (lines_to_move) end scroll_output_grid (line_count: INTEGER) -- Scroll `output_grid' by `line_count' lines, -- restricted to maximum positions permitted by grid. do output_grid.set_virtual_position (output_grid.virtual_x_position, (output_grid.virtual_y_position + (output_grid.row_height * line_count)).min (output_grid.maximum_virtual_y_position).max (0)) end append_row_to_string (a_row: EV_GRID_ROW; a_string: STRING_32) -- Append output version of `a_row' to `string'. require a_row_not_void: a_row /= Void a_string_not_void: a_string /= Void local i: INTEGER do from i := 1 until i > a_row.count loop if attached {EV_GRID_LABEL_ITEM} a_row.item (i) as label_item then a_string.append (label_item.text) elseif attached {EB_PROFILE_QUERY_GRID_ROW} a_row.data as query_grid_row then a_string.append (full_feature_path (query_grid_row)) else check is_profile_query_grid_row: False end end if i < a_row.count then a_string.append (tab) else a_string.append (new_line) end i := i + 1 end end full_feature_path (query_grid_row: EB_PROFILE_QUERY_GRID_ROW): STRING_32 -- `Result' is expanded version of the the name a associated -- with `query_grid_row'. For Eiffel features, this is the full cluster, -- class and feature name. require query_grid_row_not_void: query_grid_row /= Void local function: EIFFEL_FUNCTION l_class: CLASS_C do Result := "" if query_grid_row.type = 4 then Result.append_string_general (query_grid_row.cluster_text) Result.append_string_general (query_grid_row.class_text) Result.append_string_general (query_grid_row.feature_text) else if attached {EIFFEL_PROFILE_DATA} query_grid_row.profile_data as eiffel_profile_data then function := eiffel_profile_data.function l_class := function.class_c if query_grid_row.type = 1 then if l_class /= Void then Result.append (l_class.group.name) Result.append (full_stop) Result.append (l_class.name) else Result.append (function.int_class_name) end Result.append (full_stop) Result.append (function.feature_name) elseif query_grid_row.type = 2 then if l_class /= Void then Result.append (l_class.group.name) Result.append (full_stop) Result.append (l_class.name) else Result.append (function.int_class_name) end elseif query_grid_row.type = 3 then if l_class /= Void then Result.append (l_class.group.name) else Result.append (function.int_class_name) end end else Result.append (query_grid_row.text) end end ensure result_not_void: Result /= Void end item_pressed (an_x, a_y, a_button: INTEGER; an_item: EV_GRID_ITEM) -- Respond to a press of button `a_button' at virtual position `an_x', `a_y' -- within `output_grid'. local menu: EV_MENU menu_item: EV_MENU_ITEM grid_row: EV_GRID_ROW l_indent: INTEGER spacing: INTEGER rows: ARRAYED_LIST [INTEGER] do if a_button = {EV_POINTER_CONSTANTS}.right then if ev_application.ctrl_pressed then if attached {STONE} retrieve_pebble (an_item, False) as l_stone and then l_stone.is_valid then (create {EB_CONTROL_PICK_HANDLER}).launch_stone (l_stone) end elseif tree_structure_enabled then -- We must now display a context menu for expanding or collapsing rows, -- but only if the mouse pointer is above the tree node. rows := output_grid.visible_row_indexes grid_row := output_grid.row (rows.i_th ((a_y // output_grid.row_height) + 1)) l_indent := grid_row.item (1).horizontal_indent spacing := output_grid.tree_node_spacing if an_x < l_indent - (spacing * 2) and an_x > l_indent - (spacing * 2) - output_grid.expand_node_pixmap.width then create menu create menu_item.make_with_text (profiler_expand_all) menu_item.select_actions.extend (agent expand_all (grid_row)) menu.extend (menu_item) create menu_item.make_with_text (profiler_collapse_all) menu_item.select_actions.extend (agent collapse_all (grid_row)) menu.extend (menu_item) menu.show end end end end expand_all (a_row: EV_GRID_ROW) -- Expand `a_row' and all subrows recursively. require a_row_not_void: a_row /= Void local i: INTEGER l_subrow: EV_GRID_ROW do a_row.expand from i := 1 until i > a_row.subrow_count loop l_subrow := a_row.subrow (i) if l_subrow.subrow_count > 0 then l_subrow.expand expand_all (l_subrow) end i := i + 1 end ensure row_expanded: a_row.is_expanded --subrows_expanded_recursively end collapse_all (a_row: EV_GRID_ROW) -- Collapse `a_row' and all subrows recursively. require a_row_not_void: a_row /= Void local i: INTEGER l_subrow: EV_GRID_ROW do a_row.collapse from i := 1 until i > a_row.subrow_count loop l_subrow := a_row.subrow (i) if l_subrow.subrow_count > 0 then l_subrow.collapse collapse_all (l_subrow) end i := i + 1 end ensure row_not_expanded: not a_row.is_expanded --subrows_not_expanded_recursively end drawing_font: EV_FONT -- Font used for drawing in `output_grid'. once Result := (create {EV_LABEL}).font end record_mouse_relative_to_item (an_x, a_y: INTEGER; grid_item: detachable EV_GRID_ITEM) -- Store the last position of the mouse relative to an item. do if grid_item /= Void then last_x := an_x - grid_item.virtual_x_position last_y := a_y - grid_item.virtual_y_position if grid_item.column.index = 1 then if attached {EB_PROFILE_QUERY_GRID_ROW} grid_item.row.data as query_grid_row then output_grid.set_tooltip (full_feature_path (query_grid_row)) else check data_pointed_to_query_grid_row: False end end else output_grid.remove_tooltip end else output_grid.remove_tooltip end end row_expanded (a_row: EV_GRID_ROW) -- Row `a_row' has been expanded so update -- the associated `query_grid_row' to reflect this. require a_row_not_void: a_row /= Void do if attached {EB_PROFILE_QUERY_GRID_ROW} a_row.data as query_grid_row then query_grid_row.set_is_expanded (True) else check query_grid_row_not_void: False end end end row_collapsed (a_row: EV_GRID_ROW) -- Row `a_row' has been collapsed so update -- the associated `query_grid_row' to reflect this. require a_row_not_void: a_row /= Void do if attached {EB_PROFILE_QUERY_GRID_ROW} a_row.data as query_grid_row then query_grid_row.set_is_expanded (False) else check query_grid_row_not_void: False end end end last_x, last_y: INTEGER -- Last x and y position relative to an item within `output_grid'. feature -- Update update_graphical_resources -- Update the graphical resources. do output_grid.wipe_out run_query_cmd.execute end update_query_frame -- Refresh active and inactive subquery frames. local i : INTEGER scrollable_subquery: EB_SUBQUERY_ITEM op: STRING do active_query_window.wipe_out inactive_subqueries_window.wipe_out if all_subqueries.count > 0 then all_subqueries.start create scrollable_subquery.make_first (all_subqueries.item.image) if all_subqueries.item.is_active then active_query_window.extend (scrollable_subquery) else inactive_subqueries_window.extend (scrollable_subquery) end if all_operators.count > 0 then from all_subqueries.forth all_operators.start i := 2 until all_subqueries.after or else all_operators.after loop if all_subqueries.item.is_active then if active_query_window.count = 0 then op := "" else op := all_operators.item.actual_operator end create scrollable_subquery.make_normal (op, all_subqueries.item.image, i) active_query_window.extend (scrollable_subquery) else create scrollable_subquery.make_normal (all_operators.item.actual_operator, all_subqueries.item.image, i) inactive_subqueries_window.extend (scrollable_subquery) end all_subqueries.forth all_operators.forth i := i + 1 end end end end update_profiler_query -- Refresh `profiler_query' according to changes made on subquery list do profiler_query.set_subqueries (all_subqueries) profiler_query.set_subquery_operators (all_operators) end feature {NONE} -- Attributes output_grid: EV_GRID -- Widget in which all output is displayed. active_subqueries: INTEGER -- Number of active subqueries in all_subqueries tree_nodes_enabled_button: EV_CHECK_BUTTON feature {NONE} -- Attributes active_query_window: EV_MULTI_COLUMN_LIST -- Scrollable list of active subqueries inactive_subqueries_window: EV_MULTI_COLUMN_LIST -- Scrollable list if inactive queries feature {NONE} -- Attributes subquery_text: EV_TEXT_FIELD -- Text field for eventual subqueries all_subqueries: LINKED_LIST[SUBQUERY] -- All the subqueries typed all_operators: LINKED_LIST[SUBQUERY_OPERATOR] -- All the subquery operators typed feature {EB_RUN_QUERY_CMD} -- Attributes profiler_query: PROFILER_QUERY -- Query from which `profinfo' is the result profiler_options: PROFILER_OPTIONS -- Options used to generate `profinfo' profinfo: PROFILE_INFORMATION -- Set of information about profiled system, generated -- with help of `profiler_query' and `profiler_options' feature {NONE} -- User Interface close -- Close Current and update `parent' do -- Remove agents added to preferences for color handling. preferences.editor_data.feature_text_color_preference.change_actions.prune_all (colors_changed_agent) preferences.editor_data.cluster_text_color_preference.change_actions.prune_all (colors_changed_agent) preferences.editor_data.class_text_color_preference.change_actions.prune_all (colors_changed_agent) destroy end feature {NONE} -- Access subquery: STRING -- Text typed in the subquery window do Result := subquery_text.text end feature -- Commands run_query_cmd: EB_RUN_QUERY_CMD -- Command to run a subquery from Current save_result_cmd: EB_SAVE_RESULT_CMD -- Command to save the result of currently displayed query feature {NONE} -- Implementation feature_text_color, class_text_color, cluster_text_color: EV_COLOR -- Colors retreived from the preferences. They must be set each time -- that the window is displayed or the preferences change, and are not onces as a user -- may change the colors. colors_changed_agent: PROCEDURE -- Agent connected to the color change events from EiffelStudio. count_active_subqueries -- Number of active subqueries do from all_subqueries.start active_subqueries := 0 until all_subqueries.after loop if all_subqueries.item.is_active then active_subqueries := active_subqueries + 1 end all_subqueries.forth end end inactivate -- Copy all the selected subqueries from `active_query_window' -- into `inactive_subqueries_window', inactivate the corresponding subqueries -- and operators in `all_subqueries' and `all_operators' local selected_subqueries: DYNAMIC_LIST [EV_MULTI_COLUMN_LIST_ROW] i: INTEGER do selected_subqueries := active_query_window.selected_items from selected_subqueries.start until selected_subqueries.after loop if attached {EB_SUBQUERY_ITEM} selected_subqueries.item as selected_subquery then i := selected_subquery.number --| inactivate the subquery in 'all_subqueries' all_subqueries.go_i_th (i) all_subqueries.item.inactivate --| inactivate the operator in 'all_subquery_operators' if i > 1 then all_operators.go_i_th (i-1) all_operators.item.inactivate end else check is_subquery: False end end selected_subqueries.forth end if active_query_window.count > 0 then if attached {EB_SUBQUERY_ITEM} active_query_window.first as selected_subquery then i := selected_subquery.number if i > 1 then all_operators.go_i_th (i-1) all_operators.item.inactivate end else check is_subquery: False end end end profiler_query.set_subqueries ( all_subqueries ) profiler_query.set_subquery_operators ( all_operators ) update_query_frame end reactivate -- Copy all the selected subqueries from `inactive_subqueries_window' -- into `active_query_window', activate the corresponding subqueries -- and operators in `all_subqueries' and `all_operators' local selected_subqueries: DYNAMIC_LIST [EV_MULTI_COLUMN_LIST_ROW] i, smallest_active: INTEGER is_window_empty: BOOLEAN -- is `active_subquery_window' empty? do if inactive_subqueries_window.count > 0 then selected_subqueries := inactive_subqueries_window.selected_items is_window_empty := (active_query_window.count = 0) if not is_window_empty then if attached {EB_SUBQUERY_ITEM} active_query_window.first as selected_subquery then smallest_active := selected_subquery.number end end from selected_subqueries.start until selected_subqueries.after loop if attached {EB_SUBQUERY_ITEM} selected_subqueries.item as selected_subquery then i := selected_subquery.number --| inactivate the subquery in 'all_subqueries' all_subqueries.go_i_th (i) all_subqueries.item.activate --| inactivate the operator in 'all_subquery_operators' if is_window_empty then smallest_active := i elseif i < smallest_active then all_operators.go_i_th (smallest_active - 1) all_operators.item.activate smallest_active := i else all_operators.go_i_th (i - 1) all_operators.item.activate end else check is_subquery: False end end selected_subqueries.forth end profiler_query.set_subqueries ( all_subqueries ) profiler_query.set_subquery_operators ( all_operators ) update_query_frame end end feature {NONE} -- execution add_subquery (string_arg: STRING) -- Add subquery to list of subqueries. -- Subquery operator is given by `string_arg'. local parser: EB_QUERY_PARSER txt: STRING operator: SUBQUERY_OPERATOR do txt := subquery if txt /= Void and then not txt.is_empty then clear_values create parser if parser.parse (txt, Current) then all_subqueries.append (subqueries) create operator.make (string_arg) all_operators.extend (operator) all_operators.append (subquery_operators) update_query_frame subquery_text.remove_text else prompts.show_error_prompt (Warning_messages.w_Profiler_bad_query, Current, Void) end end end change_operator (string_arg: STRING) -- Change selected subqueries -- operators according to `string_arg'. local selected_subqueries: DYNAMIC_LIST [EV_MULTI_COLUMN_LIST_ROW] do if active_query_window.count > 0 then from selected_subqueries := active_query_window.selected_items selected_subqueries.start until selected_subqueries.after loop if attached {EB_SUBQUERY_ITEM} selected_subqueries.item as selected_subquery then if selected_subquery.number > 1 then all_operators.go_i_th (selected_subquery.number - 1) all_operators.item.change_operator (string_arg) end else check valid_entry: False end end selected_subqueries.forth end end if inactive_subqueries_window.count > 0 then from selected_subqueries := inactive_subqueries_window.selected_items selected_subqueries.start until selected_subqueries.after loop if attached {EB_SUBQUERY_ITEM} selected_subqueries.item as selected_subquery then if selected_subquery.number > 1 then all_operators.go_i_th (selected_subquery.number - 1) all_operators.item.change_operator (string_arg) end else check valid_entry: False end end selected_subqueries.forth end end update_query_frame end feature {NONE} -- Implementation handle_color_change -- Respond to a color change occurring from the preferences. do cluster_text_color := preferences.editor_data.cluster_text_color class_text_color := preferences.editor_data.class_text_color feature_text_color := preferences.editor_data.feature_text_color -- Now redraw the contents of `output_grid'. output_grid.redraw end tab: STRING = "%T" new_line: STRING = "%N" full_stop: STRING = "." black: EV_COLOR -- Once access to black EV_COLOR. once Result := (create {EV_STOCK_COLORS}).black end gray: EV_COLOR -- Once access to gray EV_COLOR. once Result := (create {EV_STOCK_COLORS}).gray end white: EV_COLOR -- Once access to white EV_COLOR. once Result := (create {EV_STOCK_COLORS}).white end time_formatter: FORMAT_DOUBLE -- Consistent presentation of real numbers. once create Result.make (7, 6) Result.hide_trailing_zeros end percentage_formatter: FORMAT_DOUBLE -- Consistent presentation of real numbers. once create Result.make (4, 3) Result.hide_trailing_zeros end note copyright: "Copyright (c) 1984-2020, 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 -- class EB_PROFILE_QUERY_WINDOW