indexing description: "Tool that displays the call stack during a debugging session." legal: "See notice at end of class." status: "See notice at end of class." author: "$Author$" date: "$Date$" revision: "$Revision$" class EB_CALL_STACK_TOOL inherit EB_STONABLE_TOOL redefine menu_name, pixmap, mini_toolbar, build_mini_toolbar, internal_recycle, show end EB_RECYCLABLE APPLICATION_STATUS_CONSTANTS export {NONE} all end SHARED_EIFFEL_PROJECT export {NONE} all end EV_KEY_CONSTANTS export {NONE} all end EB_SHARED_DEBUGGER_MANAGER EB_SHARED_PREFERENCES export {NONE} all end EB_FILE_DIALOG_CONSTANTS export {NONE} all end DEBUGGING_UPDATE_ON_IDLE redefine update, real_update end create make feature {NONE} -- Initialization build_interface is -- Build all the tool's widgets. local development_window: EB_DEVELOPMENT_WINDOW box: EV_VERTICAL_BOX box2: EV_VERTICAL_BOX tb_but: like exception_dialog_button box_exception: EV_HORIZONTAL_BOX tb_exception: SD_TOOL_BAR t_label: EV_LABEL special_label_col: EV_COLOR do development_window ?= develop_window --| UI look row_highlight_bg_color := Preferences.debug_tool_data.row_highlight_background_color set_row_highlight_bg_color_agent := agent set_row_highlight_bg_color Preferences.debug_tool_data.row_highlight_background_color_preference.change_actions.extend (set_row_highlight_bg_color_agent) row_unsensitive_fg_color := Preferences.debug_tool_data.row_unsensitive_foreground_color set_row_unsensitive_fg_color_agent := agent set_row_unsensitive_fg_color Preferences.debug_tool_data.row_unsensitive_foreground_color_preference.change_actions.extend (set_row_unsensitive_fg_color_agent) --| UI structure create box create box2 box2.set_padding (3) special_label_col := (create {EV_STOCK_COLORS}).blue --| Thread box create box_thread create t_label.make_with_text (" Thread = ") t_label.align_text_left t_label.set_foreground_color (special_label_col) box_thread.extend (t_label) box_thread.disable_item_expand (t_label) create thread_id thread_id.align_text_left thread_id.set_foreground_color (special_label_col) thread_id.set_text ("..Unknown..") thread_id.set_minimum_height (20) thread_id.set_minimum_width (thread_id.font.string_width ("0x00000000") + 10) thread_id.pointer_enter_actions.extend (agent bold_this_label (True, thread_id)) thread_id.pointer_leave_actions.extend (agent bold_this_label (False, thread_id)) thread_id.pointer_button_press_actions.extend (agent select_call_stack_thread (thread_id, ?,?,?,?,?,?,?,?)) box_thread.extend (thread_id) create t_label.make_with_text (" -- Click to change. ") t_label.disable_sensitive t_label.align_text_right t_label.set_foreground_color (special_label_col) box_thread.extend (t_label) box_thread.disable_item_expand (t_label) --| Stop cause (step completed ...) create box_stop_cause create t_label.make_with_text (" Status = ") t_label.align_text_left t_label.set_foreground_color (special_label_col) box_stop_cause.extend (t_label) box_stop_cause.disable_item_expand (t_label) box_stop_cause.set_foreground_color (special_label_col) create stop_cause stop_cause.align_text_left stop_cause.set_minimum_width (stop_cause.font.string_width (interface_names.l_explicit_exception_pending)) box_stop_cause.extend (stop_cause) --| Exception message create box_exception create exception box_exception.extend (exception) create tb_exception.make create tb_but.make exception_dialog_button := tb_but tb_but.disable_sensitive tb_but.set_pixmap (pixmaps.icon_pixmaps.debug_exception_dialog_icon) tb_but.set_pixel_buffer (pixmaps.icon_pixmaps.debug_exception_dialog_icon_buffer) tb_but.set_tooltip (interface_names.l_open_exception_dialog_tooltip) tb_but.select_actions.extend (agent show_call_stack_message) tb_exception.extend (tb_but) tb_exception.compute_minimum_size box_exception.extend (tb_exception) box_exception.disable_item_expand (tb_exception) --| Top box2 box2.extend (box_stop_cause) box2.disable_item_expand (box_stop_cause) box2.extend (box_thread) box2.disable_item_expand (box_thread) display_box_thread (False) box2.extend (box_exception) box2.disable_item_expand (box_exception) box.extend (box2) box.disable_item_expand (box2) if Debugger_manager.application_is_executing then display_stop_cause (False) end exception.remove_text -- We set a minimum width to prevent the label from resizing the call stack. exception.set_minimum_width (40) exception.set_foreground_color (create {EV_COLOR}.make_with_8_bit_rgb (255, 0, 0)) exception.disable_edit --| Stack grid create stack_grid stack_grid.enable_single_row_selection stack_grid.enable_border ----| FIXME XR: Use preferences to store/restore column widths stack_grid.set_column_count_to (3) stack_grid.column (1).set_title (Interface_names.t_Feature) stack_grid.column (1).set_width (120) stack_grid.column (2).set_title (Interface_names.t_Dynamic_type) stack_grid.column (2).set_width (100) stack_grid.column (3).set_title (Interface_names.t_Static_type) stack_grid.column (3).set_width (100) --| Action/event on call stack grid stack_grid.drop_actions.extend (agent on_element_drop) stack_grid.key_press_actions.extend (agent key_pressed) stack_grid.set_item_pebble_function (agent on_grid_item_pebble_function) stack_grid.set_item_accept_cursor_function (agent on_grid_item_accept_cursor_function) --| Call stack level selection mode if preferences.debug_tool_data.select_call_stack_level_on_double_click then stack_grid.pointer_double_press_item_actions.extend (agent on_grid_item_pointer_pressed) else stack_grid.pointer_button_press_item_actions.extend (agent on_grid_item_pointer_pressed) end stack_grid.set_configurable_target_menu_mode stack_grid.set_configurable_target_menu_handler (agent (development_window.menus.context_menu_factory).call_stack_menu) preferences.debug_tool_data.select_call_stack_level_on_double_click_preference.change_actions.extend (agent update_call_stack_level_selection_mode) box.extend (stack_grid) stack_grid.build_delayed_cleaning create_update_on_idle_agent widget := box end update_call_stack_level_selection_mode (dbl_click_bpref: BOOLEAN_PREFERENCE) is -- Update call stack level selection mode when preference select_call_stack_level_on_double_click_preference -- changes. -- if true then use double click -- otherwise use single click do stack_grid.pointer_double_press_item_actions.wipe_out stack_grid.pointer_button_press_item_actions.wipe_out if dbl_click_bpref.value then stack_grid.pointer_double_press_item_actions.extend (agent on_grid_item_pointer_pressed) else stack_grid.pointer_button_press_item_actions.extend (agent on_grid_item_pointer_pressed) end end build_mini_toolbar is -- Build the associated tool bar local cmd: EB_STANDARD_CMD do create mini_toolbar.make create save_call_stack_cmd.make save_call_stack_cmd.set_mini_pixmap (pixmaps.mini_pixmaps.general_save_icon) save_call_stack_cmd.set_mini_pixel_buffer (pixmaps.mini_pixmaps.general_save_icon_buffer) save_call_stack_cmd.set_tooltip (Interface_names.e_Save_call_stack) save_call_stack_cmd.add_agent (agent save_call_stack) mini_toolbar.extend (save_call_stack_cmd.new_mini_sd_toolbar_item) create cmd.make cmd.set_mini_pixmap (pixmaps.mini_pixmaps.general_copy_icon) cmd.set_mini_pixel_buffer (pixmaps.mini_pixmaps.general_copy_icon_buffer) cmd.set_tooltip (Interface_names.e_Copy_call_stack_to_clipboard) cmd.add_agent (agent copy_call_stack_to_clipboard) mini_toolbar.extend (cmd.new_mini_sd_toolbar_item) copy_call_stack_cmd := cmd create set_stack_depth_cmd.make set_stack_depth_cmd.set_mini_pixmap (pixmaps.mini_pixmaps.debugger_callstack_depth_icon) set_stack_depth_cmd.set_mini_pixel_buffer (pixmaps.mini_pixmaps.debugger_callstack_depth_icon_buffer) set_stack_depth_cmd.set_tooltip (Interface_names.e_Set_stack_depth) set_stack_depth_cmd.add_agent (agent set_stack_depth) set_stack_depth_cmd.enable_sensitive mini_toolbar.extend (set_stack_depth_cmd.new_mini_sd_toolbar_item) mini_toolbar.compute_minimum_size ensure then mini_toolbar_exists: mini_toolbar /= Void end feature -- Box management box_thread: EV_HORIZONTAL_BOX box_stop_cause: EV_HORIZONTAL_BOX display_box_thread (b: BOOLEAN) is -- Show or hide box related to available Thread ids do if b then box_thread.show else box_thread.hide end end feature -- Access mini_toolbar: SD_TOOL_BAR -- Associated mini toolbar. widget: EV_WIDGET -- Widget representing Current. thread_id: EV_LABEL -- Thread Identifier stop_cause: EV_LABEL -- Why did the execution stop? (encountered breakpoint, raised exception,...) exception: EV_TEXT_FIELD -- Exception application has encountered. exception_dialog_button: SD_TOOL_BAR_BUTTON -- Button to display exception dialog. title_for_pre: STRING is -- Title of the tool. do Result := Interface_names.to_Call_stack_tool end title: STRING_GENERAL is -- Title for prefence, STRING_8 do Result := Interface_names.t_Call_stack_tool end stack_grid: ES_GRID -- Graphical representation of the execution stack. menu_name: STRING_GENERAL is -- Name as it may appear in a menu. do Result := Interface_names.m_Call_stack_tool end pixmap: EV_PIXMAP is -- Pixmap as it may appear in toolbars and menus. do Result := pixmaps.icon_pixmaps.tool_call_stack_icon end stone: STONE -- Not used. feature -- Status setting set_callstack_thread (tid: INTEGER) is require application_is_executing: debugger_manager.application_is_executing do if debugger_manager.application_current_thread_id /= tid then debugger_manager.change_current_thread_id (tid) update end end set_stone (a_stone: STONE) is -- Assign `a_stone' as new stone. local st: CALL_STACK_STONE new_level: INTEGER count: INTEGER l_row: EV_GRID_ROW old_current_level: INTEGER do st ?= a_stone if st /= Void then old_current_level := arrowed_level new_level := st.level_number -- Stack grid count := stack_grid.row_count if old_current_level >= 1 and then count >= old_current_level then l_row := stack_grid.row (old_current_level) refresh_stack_grid_row (l_row, new_level) end if new_level >= 1 and then count >= new_level then l_row := stack_grid.row (new_level) refresh_stack_grid_row (l_row, new_level) arrowed_level := new_level end end end update is -- Refresh `Current's display. local l_is_stopped: BOOLEAN do cancel_process_real_update_on_idle request_clean_stack_grid if Debugger_manager.application_is_executing then l_is_stopped := Debugger_manager.application_is_stopped display_stop_cause (l_is_stopped) refresh_threads_info process_real_update_on_idle (l_is_stopped) else save_call_stack_cmd.disable_sensitive copy_call_stack_cmd.disable_sensitive display_stop_cause (False) display_box_thread (False) end end show is -- Show tool do Precursor {EB_STONABLE_TOOL} if stack_grid.is_sensitive then stack_grid.set_focus end end feature -- Memory management reset_tool is do reset_update_on_idle Preferences.debug_tool_data.row_highlight_background_color_preference.change_actions.prune_all (set_row_highlight_bg_color_agent) Preferences.debug_tool_data.row_unsensitive_foreground_color_preference.change_actions.prune_all (set_row_unsensitive_fg_color_agent) exception.remove_text exception.remove_tooltip exception_dialog_button.disable_sensitive stop_cause.remove_text display_box_thread (False) save_call_stack_cmd.disable_sensitive copy_call_stack_cmd.disable_sensitive clean_stack_grid end feature {NONE} -- Memory management internal_recycle is -- Recycle `Current', but leave `Current' in an unstable state, -- so that we know whether we're still referenced or not. do reset_tool end feature {NONE} -- Grid Implementation level_from_row (a_row: EV_GRID_ROW): INTEGER is -- Call stack level related to `a_row'. require a_row /= Void local level_r: INTEGER_REF do level_r ?= a_row.data if level_r /= Void then Result := level_r.item end end on_grid_item_pointer_pressed (a_x, a_y, a_button: INTEGER; a_item: EV_GRID_ITEM) is -- Action when mouse click on `stack_grid' local l_row: EV_GRID_ROW level: INTEGER do if a_button = 1 and a_item /= Void then if not ev_application.ctrl_pressed and not ev_application.shift_pressed and not ev_application.alt_pressed then l_row := a_item.row if l_row /= Void then level := level_from_row (l_row) if level > 0 then select_element_by_level (level) end end end end end on_grid_item_pebble_function (a_item: EV_GRID_ITEM): CALL_STACK_STONE is -- Returns the call_stack_stone of row related to a_item local l_row: EV_GRID_ROW level: INTEGER elem: EIFFEL_CALL_STACK_ELEMENT do if a_item /= Void then l_row := a_item.row if l_row /= Void then level := level_from_row (l_row) if level > 0 then elem ?= Debugger_manager.application_status.current_call_stack.i_th (level) if elem /= Void and then elem.dynamic_class /= Void and then elem.dynamic_class.has_feature_table then if elem.routine /= Void then create Result.make (level) end end end end end end on_grid_item_accept_cursor_function (a_item: EV_GRID_ITEM): EV_POINTER_STYLE is do Result := Cursors.cur_Setstop end feature {NONE} -- Implementation arrowed_level: INTEGER -- Line number in the stack that has an arrow displayed. save_call_stack_cmd: EB_STANDARD_CMD -- Command that saves the call stack to a file. copy_call_stack_cmd: EB_STANDARD_CMD -- Command that copy the call stack to the clipboard. set_stack_depth_cmd: EB_STANDARD_CMD -- Command that alters the displayed depth of the call stack. clean_stack_grid is -- Clean the stack_grid do stack_grid.call_delayed_clean ensure stack_grid_cleaned: stack_grid.row_count = 0 end request_clean_stack_grid is -- Clean the stack_grid do stack_grid.request_delayed_clean end real_update (dbg_was_stopped: BOOLEAN) is -- Display current execution status. -- dbg_was_stopped is ignore if Application/Debugger is not running local i: INTEGER stack: EIFFEL_CALL_STACK l_tooltipable_grid_row: EV_GRID_ROW glab: EV_GRID_LABEL_ITEM l_status: APPLICATION_STATUS do Precursor {DEBUGGING_UPDATE_ON_IDLE} (dbg_was_stopped) request_clean_stack_grid save_call_stack_cmd.disable_sensitive copy_call_stack_cmd.disable_sensitive if Debugger_manager.application_is_executing then l_status := Debugger_manager.application_status if dbg_was_stopped then l_status.update_on_stopped_state end display_stop_cause (dbg_was_stopped) refresh_threads_info if dbg_was_stopped and l_status.is_stopped then clean_stack_grid stack := l_status.current_call_stack if stack /= Void and then not stack.is_empty then save_call_stack_cmd.enable_sensitive copy_call_stack_cmd.enable_sensitive from stack.start stack_grid.insert_new_rows (stack.count, 1) i := 1 until stack.after loop attach_element_to_row (stack.item, i, stack_grid.row (i)) if not stack.item.is_eiffel_call_stack_element then stack_grid.row (i).disable_select end i := i + 1 stack.forth end if l_status.is_stopped then select_element_by_level (1) end else l_tooltipable_grid_row := stack_grid.grid_extended_new_row (stack_grid) create glab.make_with_text ("Unable to get call stack data") glab.set_tooltip ("Double click to refresh call stack") glab.set_pixmap (pixmaps.icon_pixmaps.general_mini_error_icon) glab.pointer_double_press_actions.force_extend (agent update) l_tooltipable_grid_row.set_item (1, glab) end stack_grid.request_columns_auto_resizing end end end display_stop_cause (arg_is_stopped: BOOLEAN) is -- Fill in the `stop_cause' label with a message describing the application status. -- arg_is_stopped is ignore if Application/Debugger is not running local m: STRING_32 do exception_dialog_button.disable_sensitive if not Debugger_manager.application_is_executing then stop_cause.set_text (Interface_names.l_System_launched) exception.remove_text exception.remove_tooltip elseif not arg_is_stopped then stop_cause.set_text (Interface_names.l_System_running) exception.remove_text exception.remove_tooltip else -- Application is stopped. create m.make (100) inspect Debugger_manager.application_status.reason when Pg_step then stop_cause.set_text (Interface_names.l_Stepped) m.append (Interface_names.l_Stepped) when Pg_break then stop_cause.set_text (Interface_names.l_Stop_point_reached) m.append (Interface_names.l_Stop_point_reached) when Pg_interrupt then stop_cause.set_text (Interface_names.l_Execution_interrupted) m.append (Interface_names.l_Execution_interrupted) when Pg_overflow then stop_cause.set_text (Interface_names.l_Possible_overflow) m.append (Interface_names.l_Possible_overflow) set_focus_is_visible when Pg_raise then stop_cause.set_text (Interface_names.l_Explicit_exception_pending) m.append (Interface_names.l_Explicit_exception_pending) m.append (": ") m.append (exception_tag_text) display_exception set_focus_is_visible when Pg_viol then stop_cause.set_text (Interface_names.l_Implicit_exception_pending) m.append (Interface_names.l_Implicit_exception_pending) m.append (": ") m.append (exception_tag_text) display_exception set_focus_is_visible when Pg_new_breakpoint then stop_cause.set_text (Interface_names.l_New_breakpoint) m.append (Interface_names.l_New_breakpoint) else stop_cause.set_text (Interface_names.l_Unknown_status) m.append (Interface_names.l_Unknown_status) end Eb_debugger_manager.debugging_window.status_bar.display_message ( first_line_of (m) ) end end set_focus_is_visible is -- Set focus if visible do if content /= Void and then content.is_visible then content.set_focus end end display_exception is -- Fill in the `exception' label with a text describing the exception, if any. local m: STRING_32 do m := exception_tag_text --| FIXME jfiat [2004/03/19] : NewFeature keep only first line of exception text for callstack_tool --| We'll enable this, once we have an exception window to display the full message exception.set_text (first_line_of (m)) exception.set_tooltip (m) exception_dialog_button.enable_sensitive end show_call_stack_message is local dlg: EB_DEBUGGER_EXCEPTION_DIALOG wdlg: EB_WARNING_DIALOG w: EV_WINDOW do w := Eb_debugger_manager.debugging_window.window if debugger_manager.safe_application_is_stopped then create dlg dlg.set_exception_tag (exception_tag_text) dlg.set_exception_message (exception_message_text) dlg.show_modal_to_window (w) else create wdlg.make_with_text (interface_names.l_Only_available_for_stopped_application) wdlg.show_modal_to_window (w) end end refresh_threads_info is -- Refresh thread info according to debugger data require application_is_executing: debugger_manager.application_is_executing local ctid: INTEGER s: APPLICATION_STATUS do s := Debugger_manager.application_status if s.all_thread_ids_count > 1 then ctid := s.current_thread_id thread_id.set_text ("0x" + ctid.to_hex_string) thread_id.set_data (ctid) display_box_thread (True) else display_box_thread (False) end end first_line_of (m: STRING_32): STRING_32 is -- keep the first line of the exception message -- the rest can be seen by double clicking on the widget local pos: INTEGER do Result := m pos := Result.index_of ('%N', 1) if pos > 0 then Result := Result.substring (1, pos -1) end pos := Result.index_of ('%R', 1) if pos > 0 then Result := Result.substring (1, pos -1) end ensure one_line: Result /= Void and then (not Result.has ('%R') and not Result.has ('%N')) end exception_tag_text: STRING_32 is -- Text corresponding to the current exception. local l_status: APPLICATION_STATUS s32: STRING_32 do l_status := Debugger_manager.application_status if l_status /= Void then if l_status.exception_occurred then s32 := l_status.exception_description if s32 /= Void then Result := s32 else Result := "" end else Result := "No exception occurred" end end if Result = Void then Result := "" end -- FIXME JFIAT: 2003/03/12 : what for this postcondition limitation ? -- need to check if there is any reason for that ... -- except we are using a TEXT_FIELD instead of multiple line widget -- ensure -- one_line: Result /= Void and then (not Result.has ('%R') and not Result.has ('%N')) end exception_message_text: STRING_32 is -- Text corresponding to the current exception. local l_status: APPLICATION_STATUS do l_status := Debugger_manager.application_status if l_status /= Void then if l_status.exception_occurred then Result := l_status.exception_message else Result := "No exception occurred" end end if Result = Void then Result := "" end end on_element_drop (st: CALL_STACK_STONE) is -- Change stack level to the one described by `st'. do Eb_debugger_manager.launch_stone (st) end attach_element_to_row (elem: CALL_STACK_ELEMENT; level: INTEGER; a_row: EV_GRID_ROW) is -- Display information about associated routine on `a_row'. require a_row_not_void: a_row /= Void local dc, oc: CLASS_C l_tooltip: STRING_32 l_nb_stack: INTEGER e_cse: EIFFEL_CALL_STACK_ELEMENT ext_cse: EXTERNAL_CALL_STACK_ELEMENT dotnet_cse: CALL_STACK_ELEMENT_DOTNET l_feature_name: STRING l_is_melted: BOOLEAN l_has_rescue: BOOLEAN l_class_info: STRING l_orig_class_info: STRING_GENERAL l_same_name: BOOLEAN l_breakindex_info: STRING l_obj_address_info: STRING l_extra_info: STRING glab: EV_GRID_LABEL_ITEM glabp: EV_GRID_PIXMAPS_ON_RIGHT_LABEL_ITEM app_exec: APPLICATION_EXECUTION do create l_tooltip.make (10) e_cse ?= elem --| Class name l_class_info := elem.class_name if l_class_info /= Void then l_tooltip.append ("{" + l_class_info + "}.") end --| Routine name l_feature_name := elem.routine_name if l_feature_name /= Void then l_feature_name := l_feature_name.twin end l_tooltip.append (l_feature_name) --| Break Index l_breakindex_info := elem.break_index.out --| Object address l_obj_address_info := elem.object_address if e_cse /= Void then --| Origin class dc := e_cse.dynamic_class oc := e_cse.written_class if oc /= Void then l_orig_class_info := oc.name_in_upper l_tooltip.prepend_string (interface_names.l_from_class (l_orig_class_info)) l_same_name := dc /= Void and then oc.same_type (dc) and then oc.is_equal (dc) else l_orig_class_info := Interface_names.l_Same_class_name end --| Routine name l_has_rescue := e_cse.has_rescue if l_has_rescue then l_tooltip.append_string (interface_names.l_feature_has_rescue_clause) end l_is_melted := e_cse.is_melted if l_is_melted then l_tooltip.append_string (interface_names.l_compilation_equal_melted) end dotnet_cse ?= e_cse if dotnet_cse /= Void and then dotnet_cse.dotnet_module_name /= Void then l_tooltip.append_string (interface_names.l_module_is (dotnet_cse.dotnet_module_name)) end a_row.set_data (level) else --| It means, this is an EXTERNAL_CALL_STACK_ELEMENT l_orig_class_info := "" ext_cse ?= elem if ext_cse /= Void then l_extra_info := ext_cse.info end a_row.set_data (-1) -- This is not a valid Eiffel call stack element. end check debugger_manager.application_is_executing end app_exec := Debugger_manager.application --| Tooltip addition l_nb_stack := app_exec.status.current_call_stack.count l_tooltip.prepend_string ((elem.level_in_stack).out + "/" + l_nb_stack.out + ": ") l_tooltip.append_string (interface_names.l_break_index_is (l_breakindex_info)) l_tooltip.append_string (interface_names.l_address_is (l_obj_address_info)) if l_extra_info /= Void then l_tooltip.append_string ("%N + " + l_extra_info) end --| Fill columns if l_is_melted or l_has_rescue then create glabp.make_with_text (l_feature_name) glabp.set_pixmaps_on_right_count (2) if l_is_melted then glabp.put_pixmap_on_right (pixmaps.mini_pixmaps.callstack_is_melted_icon, 1) end if l_has_rescue then glabp.put_pixmap_on_right (pixmaps.mini_pixmaps.callstack_has_rescue_icon, 2) end glab := glabp else create glab.make_with_text (l_feature_name) end glab.set_tooltip (l_tooltip) a_row.set_item (1, glab) create glab.make_with_text (l_class_info) glab.set_tooltip (l_class_info) a_row.set_item (2, glab) create glab.make_with_text (l_orig_class_info) if l_same_name then glab.set_foreground_color ((create {EV_STOCK_COLORS}).grey) end glab.set_tooltip (l_orig_class_info) a_row.set_item (3, glab) --| Set GUI behavior refresh_stack_grid_row (a_row, app_exec.current_execution_stack_number) if level = app_exec.current_execution_stack_number then arrowed_level := level end end refresh_stack_grid_row (a_row: EV_GRID_ROW; current_level: INTEGER) is -- Refresh row of stack_grid regarding the current_level information require a_row /= Void local glab: EV_GRID_LABEL_ITEM level: INTEGER do level := level_from_row (a_row) a_row.set_foreground_color (Void) a_row.set_background_color (Void) --| Set GUI behavior glab ?= a_row.item (1) if level = current_level then glab.set_pixmap (pixmaps.icon_pixmaps.callstack_active_arrow_icon) a_row.set_background_color (row_highlight_bg_color) elseif level >= 0 then glab.set_pixmap (pixmaps.icon_pixmaps.callstack_empty_arrow_icon) else glab.remove_pixmap glab.set_left_border (glab.left_border + glab.spacing + pixmaps.icon_pixmaps.callstack_active_arrow_icon.width) a_row.set_foreground_color (row_unsensitive_fg_color) end end select_element_by_level (level: INTEGER) is -- Set stone in the develpment window. require valid_level: level > 0 and level <= stack_grid.row_count local st: CALL_STACK_STONE do create st.make (level) if st.is_valid then Eb_debugger_manager.launch_stone (st) end end select_element (a_x, a_y, a_button: INTEGER; a_x_tilt, a_y_tilt, a_pressure: DOUBLE; a_screen_x, a_screen_y: INTEGER; level: INTEGER) is -- Set `a_stone' in the development window. do if a_button = 1 then select_element_by_level (level) end end key_pressed (a_key: EV_KEY) is -- If `a_key' is enter, set the selected stack element as the new stone. local level: INTEGER_REF l_row: EV_GRID_ROW do if a_key.code = Key_enter then l_row := stack_grid.single_selected_row if l_row /= Void then level ?= l_row.data if level > 0 then select_element_by_level (level.item) stack_grid.set_focus end end end end save_call_stack is -- Saves the current call stack representation in a file. local fd: EB_FILE_SAVE_DIALOG standard_path: DIRECTORY_NAME f: RAW_FILE fn: FILE_NAME last_path: STRING i: INTEGER do --| Get last path from the preferences. last_path := preferences.debug_tool_data.last_saved_stack_path if last_path = Void or else last_path.is_empty then --| The first time, start in the project directory. create standard_path.make standard_path.extend (Eiffel_project.project_directory.path) else create standard_path.make standard_path.extend (last_path) end create fd.make_with_preference (preferences.dialog_data.last_saved_call_stack_directory_preference) set_dialog_filters_and_add_all (fd, <>) fd.set_start_directory (standard_path) --| We try to find a file_name that does not exist. create fn.make fn.set_directory (standard_path) fn.set_file_name (Interface_names.default_stack_file_name) fn.add_extension ("txt") create f.make (fn) from i := 1 until not f.exists loop create fn.make fn.set_directory (standard_path) fn.set_file_name (Interface_names.default_stack_file_name + i.out) fn.add_extension ("txt") create f.make (fn) i := i + 1 end --| OK, now `fn' represents a file that does not exist. fd.set_file_name (fn) fd.save_actions.extend (agent save_call_stack_to_file (fd)) fd.show_modal_to_window (Eb_debugger_manager.debugging_window.window) end save_call_stack_to_file (fd: EV_FILE_DIALOG) is -- Actually saves the call stack. require valid_dialog: fd /= Void local f: RAW_FILE fn, fp: STRING retried: BOOLEAN wd: EB_WARNING_DIALOG dlg: EB_QUESTION_DIALOG do if not retried then if debugger_manager.safe_application_is_stopped then --| We create a file (or open it). fn := fd.file_name fp := fd.file_path create f.make (fn) if f.exists then create dlg.make_with_text (interface_names.l_file_exits (fn)) dlg.set_buttons_and_actions (<>, << agent save_call_stack_to_filename (fp, fn, False), agent save_call_stack_to_filename (fp, fn, True), agent dlg.destroy >> ) dlg.show_modal_to_window (Eb_debugger_manager.debugging_window.window) else save_call_stack_to_filename (fp, fn, False) end else create wd.make_with_text (interface_names.l_only_available_for_stopped_application) wd.show_modal_to_window (Eb_debugger_manager.debugging_window.window) end else -- The file name was probably incorrect (not creatable). if fd /= Void then create wd.make_with_text (Warning_messages.w_Not_creatable (fd.file_name)) wd.show_modal_to_window (Eb_debugger_manager.debugging_window.window) end end rescue retried := True retry end save_call_stack_to_filename (a_fp: STRING; a_fn: STRING; is_append: BOOLEAN) is -- Save call stack into file named `a_fn'. -- if the file already exists and `is_append' is True -- then append the stack in the same file local fn: FILE_WINDOW retried: BOOLEAN wd: EB_WARNING_DIALOG do if not retried then --| We create a file (or open it). create fn.make (a_fn) if fn.exists and is_append then fn.open_append else fn.create_read_write end --| We generate the call stack. Eb_debugger_manager.text_formatter_visitor.append_stack (Debugger_manager.application_status.current_call_stack, fn) --| We put it in the file. if not fn.is_closed then fn.close end --| Save the path to the preferences. preferences.debug_tool_data.last_saved_stack_path_preference.set_value (a_fp) preferences.preferences.save_preference (preferences.debug_tool_data.last_saved_stack_path_preference) else -- The file name was probably incorrect (not creatable). create wd.make_with_text (Warning_messages.w_Not_creatable (a_fn)) wd.show_modal_to_window (Eb_debugger_manager.debugging_window.window) end rescue retried := True retry end copy_call_stack_to_clipboard is local l_output: YANK_STRING_WINDOW retried: BOOLEAN s: STRING t: STRING do if not retried then if debugger_manager.safe_application_is_stopped then --| We generate the call stack. create s.make_empty if debugger_manager.application_status.exception_occurred then t := exception_tag_text if t /= Void and then not t.is_empty then s.append_string ("Exception tag:%N" + t + "%N") end t := exception_message_text if t /= Void and then not t.is_empty then s.append_string ("Exception message:%N" + t + "%N") end end create l_output.make; Eb_debugger_manager.text_formatter_visitor.append_stack (Debugger_manager.application_status.current_call_stack, l_output) t := l_output.stored_output if t /= Void and then not t.is_empty then s.append_string (t) end ev_application.clipboard.set_text (s) l_output.reset_output else ev_application.clipboard.set_text ("") end else ev_application.clipboard.set_text ("") end rescue retried := True retry end show_thread_panel is local th_tools: ES_DBG_THREADS_TOOL do th_tools := Eb_debugger_manager.threads_tool th_tools.show end select_call_stack_thread (lab: EV_LABEL; x, y, button: INTEGER; x_tilt, y_tilt, pressure: DOUBLE; screen_x, screen_y: INTEGER) is local m: EV_MENU mi: EV_MENU_ITEM tid: INTEGER arr: LIST [INTEGER] wd: EB_WARNING_DIALOG l_item_text, s: STRING l_status: APPLICATION_STATUS do l_status := Debugger_manager.application_status if l_status /= Void then if l_status.is_stopped then arr := l_status.all_thread_ids if arr /= Void and then not arr.is_empty then create m create mi.make_with_text_and_action ("Show threads panel", agent show_thread_panel) m.extend (mi) m.extend (create {EV_MENU_SEPARATOR}) from arr.start until arr.after loop tid := arr.item l_item_text := "0x" + tid.to_hex_string s := l_status.thread_name (tid) if s /= Void then l_item_text.append_string (" - " + s) end create mi if tid = l_status.current_thread_id then mi.set_pixmap (pixmaps.icon_pixmaps.callstack_active_arrow_icon) end mi.set_text (l_item_text) mi.set_data (tid) mi.select_actions.extend (agent set_callstack_thread (tid)) m.extend (mi) arr.forth end m.show_at (lab, 0, thread_id.height) else create wd.make_with_text ("Sorry no information available on Threads for now") wd.show end else create wd.make_with_text (interface_names.l_only_available_for_stopped_application) wd.show end else create wd.make_with_text (interface_names.l_only_available_for_stopped_application) wd.show end end feature {NONE} -- Implementation: set stack depth command set_stack_depth is -- Display a dialog that lets the user change the maximum depth of the call stack. local rb2: EV_RADIO_BUTTON l: EV_LABEL okb, cancelb: EV_BUTTON hb: EV_HORIZONTAL_BOX vb: EV_VERTICAL_BOX do -- Create widgets. create dialog create show_all_radio.make_with_text (Interface_names.l_show_all_call_stack) create rb2.make_with_text (Interface_names.l_Show_only_n_elements) create l.make_with_text (Interface_names.l_Elements) create set_as_default.make_with_text (Interface_names.l_Set_as_default) create element_nb.make_with_value_range (1 |..| 500) create okb.make_with_text (Interface_names.b_Ok) create cancelb.make_with_text (Interface_names.b_Cancel) -- Set widget properties. dialog.set_title (Interface_names.t_Set_stack_depth) dialog.set_icon_pixmap (pixmaps.icon_pixmaps.general_dialog_icon) dialog.disable_user_resize Layout_constants.set_default_width_for_button (okb) Layout_constants.set_default_width_for_button (cancelb) -- Organize widgets. create vb vb.set_border_width (Layout_constants.Default_border_size) vb.set_padding (Layout_constants.Small_padding_size) vb.extend (show_all_radio) vb.extend (rb2) rb2.enable_select if Debugger_manager.maximum_stack_depth > 500 then element_nb.set_value (500) elseif Debugger_manager.maximum_stack_depth = -1 then element_nb.set_value (50) show_all_radio.enable_select element_nb.disable_sensitive else element_nb.set_value (Debugger_manager.maximum_stack_depth) end create hb hb.set_padding (Layout_constants.Small_padding_size) hb.extend (element_nb) hb.disable_item_expand (element_nb) hb.extend (l) hb.disable_item_expand (l) hb.extend (create {EV_CELL}) vb.extend (hb) vb.extend (set_as_default) create hb hb.set_padding (Layout_constants.Small_padding_size) hb.extend (create {EV_CELL}) hb.extend (okb) hb.disable_item_expand (okb) hb.extend (cancelb) hb.disable_item_expand (cancelb) hb.extend (create {EV_CELL}) vb.extend (hb) dialog.extend (vb) -- Set up actions. cancelb.select_actions.extend (agent close_dialog) okb.select_actions.extend (agent accept_dialog) show_all_radio.select_actions.extend (agent element_nb.disable_sensitive) rb2.select_actions.extend (agent element_nb.enable_sensitive) dialog.set_default_push_button (okb) dialog.set_default_cancel_button (cancelb) dialog.show_actions.extend (agent element_nb.set_focus) dialog.show_modal_to_window (Eb_debugger_manager.debugging_window.window) end set_as_default: EV_CHECK_BUTTON -- Button that decides whether the chosen stack depth should be saved in the preferences. element_nb: EV_SPIN_BUTTON -- Spin button that indicates how many stack elements should be displayed. dialog: EV_DIALOG -- Dialog that lets the user choose how many stack elements he wishes to see. show_all_radio: EV_RADIO_BUTTON -- Radio button that indicates whether all stack elements should be displayed. close_dialog is -- Close `dialog' without doing anything. do dialog.destroy dialog := Void element_nb := Void set_as_default := Void show_all_radio := Void end accept_dialog is -- Close `dialog' without doing anything. local nb: INTEGER do if show_all_radio.is_selected then nb := -1 else nb := element_nb.value end if set_as_default.is_selected then preferences.debugger_data.default_maximum_stack_depth_preference.set_value (nb) end close_dialog debugger_manager.set_maximum_stack_depth (nb) end feature {NONE} -- Implementation, cosmetic bold_this_label (is_bold: BOOLEAN; lab: EV_LABEL) is local f: EV_FONT do f := lab.font if is_bold then f.set_weight ({EV_FONT_CONSTANTS}.weight_bold) else f.set_weight ({EV_FONT_CONSTANTS}.weight_regular) end lab.set_font (f) end set_row_unsensitive_fg_color_agent, set_row_highlight_bg_color_agent: PROCEDURE [ANY, TUPLE [COLOR_PREFERENCE]] -- Store agents for `set_row_highlight_bg_color' and `set_row_unsensitive_fg_color' so that they -- get properly removed when recycling. set_row_highlight_bg_color (v: COLOR_PREFERENCE) is do row_highlight_bg_color := v.value end set_row_unsensitive_fg_color (v: COLOR_PREFERENCE) is do row_unsensitive_fg_color := v.value end row_highlight_bg_color: EV_COLOR row_unsensitive_fg_color: EV_COLOR; indexing copyright: "Copyright (c) 1984-2006, 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 356 Storke Road, 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_CALL_STACK_TOOL