/* * Copyright (C) 2014 Alfons Wirtz * website www.freerouting.net * * This program 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, either version 3 of the License, or * (at your option) any later version. * * This program 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 at <http://www.gnu.org/licenses/> * for more details. * * BoardHandling.java * * Created on 5. November 2003, 13:02 * */ package interactive; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.util.Collection; import java.util.Set; import geometry.planar.FloatPoint; import geometry.planar.IntBox; import geometry.planar.IntPoint; import geometry.planar.PolylineShape; import rules.BoardRules; import board.LayerStructure; import board.RoutingBoard; import board.Item; import board.PolylineTrace; import board.FixedState; import board.ItemSelectionFilter; import boardgraphics.GraphicsContext; import board.CoordinateTransform; import board.Unit; import board.TestLevel; import designformats.specctra.DsnFile; /** * * Central connection class between the graphical user interface and * the board database. * * @author Alfons Wirtz */ public class BoardHandling extends BoardHandlingImpl { /** * Creates a new BoardHandling */ public BoardHandling(gui.BoardPanel p_panel, java.util.Locale p_locale) { this.locale = p_locale; this.panel = p_panel; this.screen_messages = p_panel.screen_messages; this.set_interactive_state(SelectMenuState.get_instance(this, logfile)); this.resources = java.util.ResourceBundle.getBundle("interactive.resources.BoardHandling", p_locale); } /** * Sets the board to read only for example when running a seperate action thread * to avoid unsynchronized change of the board. */ public void set_board_read_only(boolean p_value) { this.board_is_read_only = p_value; this.settings.set_read_only(p_value); } /** * Return true, if the board is set to read only. */ public boolean is_board_read_only() { return this.board_is_read_only; } /** * Return the current language for the GUI messages. */ @Override public java.util.Locale get_locale() { return this.locale; } /** * returns the number of layers of the board design. */ public int get_layer_count() { if (board == null) { return 0; } return board.get_layer_count(); } /** * Returns the current position of the mouse pointer. */ public FloatPoint get_current_mouse_position() { return this.current_mouse_position; } /** * Sets the current mouse position to the input point. * Used while reading a logfile. */ void set_current_mouse_position(FloatPoint p_point) { this.current_mouse_position = p_point; } /** * * Tells the router, if conduction areas should be ignored.. */ public void set_ignore_conduction(boolean p_value) { if (board_is_read_only) { return; } board.change_conduction_is_obstacle(!p_value); logfile.start_scope(LogfileScope.SET_IGNORE_CONDUCTION, p_value); } public void set_pin_edge_to_turn_dist(double p_value) { if (board_is_read_only) { return; } double edge_to_turn_dist = this.coordinate_transform.user_to_board(p_value); if (edge_to_turn_dist != board.rules.get_pin_edge_to_turn_dist()) { // unfix the pin exit stubs Collection<board.Pin> pin_list = board.get_pins(); for (board.Pin curr_pin : pin_list) { if (curr_pin.has_trace_exit_restrictions()) { Collection<Item> contact_list = curr_pin.get_normal_contacts(); for (Item curr_contact : contact_list) { if ((curr_contact instanceof PolylineTrace) && curr_contact.get_fixed_state() == FixedState.SHOVE_FIXED) { if (((PolylineTrace) curr_contact).corner_count() == 2) { curr_contact.set_fixed_state(FixedState.UNFIXED); } } } } } } board.rules.set_pin_edge_to_turn_dist(edge_to_turn_dist); } /** * Changes the visibility of the input layer to the input value. * p_value is expected between 0 and 1 */ public void set_layer_visibility(int p_layer, double p_value) { if (p_layer >= 0 && p_layer < graphics_context.layer_count()) { graphics_context.set_layer_visibility(p_layer, p_value); if (p_value == 0 && settings.layer == p_layer) { // change the current layer to the best visible layer, if it becomes invisible; double best_visibility = 0; int best_visible_layer = 0; for (int i = 0; i < graphics_context.layer_count(); ++i) { if (graphics_context.get_layer_visibility(i) > best_visibility) { best_visibility = graphics_context.get_layer_visibility(i); best_visible_layer = i; } } settings.layer = best_visible_layer; } } } /** * Gets the trace half width used in interactive routing for the input net on the input layer. */ public int get_trace_halfwidth(int p_net_no, int p_layer) { int result; if (settings.manual_rule_selection) { result = settings.manual_trace_half_width_arr[p_layer]; } else { result = board.rules.get_trace_half_width(p_net_no, p_layer); } return result; } /** * Returns if p_layer is active for interactive routing of traces. */ public boolean is_active_routing_layer(int p_net_no, int p_layer) { if (settings.manual_rule_selection) { return true; } rules.Net curr_net = this.board.rules.nets.get(p_net_no); if (curr_net == null) { return true; } rules.NetClass curr_net_class = curr_net.get_class(); if (curr_net_class == null) { return true; } return curr_net_class.is_active_routing_layer(p_layer); } /** Gets the trace clearance class used in interactive routing. */ public int get_trace_clearance_class(int p_net_no) { int result; if (settings.manual_rule_selection) { result = settings.manual_trace_clearance_class; } else { result = board.rules.nets.get(p_net_no).get_class().get_trace_clearance_class(); } return result; } /** Gets the via rule used in interactive routing. */ public rules.ViaRule get_via_rule(int p_net_no) { rules.ViaRule result = null; if (settings.manual_rule_selection) { result = board.rules.via_rules.get(this.settings.manual_via_rule_index); } if (result == null) { result = board.rules.nets.get(p_net_no).get_class().get_via_rule(); } return result; } /** * Changes the default trace halfwidth currently used in interactive routing on the input layer. */ public void set_default_trace_halfwidth(int p_layer, int p_value) { if (board_is_read_only) { return; } if (p_layer >= 0 && p_layer <= board.get_layer_count()) { board.rules.set_default_trace_half_width(p_layer, p_value); logfile.start_scope(LogfileScope.SET_TRACE_HALF_WIDTH, p_layer); logfile.add_int(p_value); } } /** * Switches clearance compansation on or off. */ public void set_clearance_compensation(boolean p_value) { if (board_is_read_only) { return; } board.search_tree_manager.set_clearance_compensation_used(p_value); logfile.start_scope(LogfileScope.SET_CLEARANCE_COMPENSATION, p_value); } /** * Changes the current snap angle in the interactive board handling. */ public void set_current_snap_angle(board.AngleRestriction p_snap_angle) { if (board_is_read_only) { return; } board.rules.set_trace_angle_restriction(p_snap_angle); logfile.start_scope(LogfileScope.SET_SNAP_ANGLE, p_snap_angle.get_no()); } /** * Changes the current layer in the interactive board handling. */ public void set_current_layer(int p_layer) { if (board_is_read_only) { return; } int layer = Math.max(p_layer, 0); layer = Math.min(layer, board.get_layer_count() - 1); set_layer(layer); logfile.start_scope(LogfileScope.SET_LAYER, p_layer); } /** * Changes the current layer without saving the change to logfile. * Only for internal use inside this package. */ void set_layer(int p_layer_no) { board.Layer curr_layer = board.layer_structure.arr[p_layer_no]; screen_messages.set_layer(curr_layer.name); settings.layer = p_layer_no; // Change the selected layer in the select parameter window. int signal_layer_no = board.layer_structure.get_signal_layer_no(curr_layer); if (!this.board_is_read_only) { this.panel.set_selected_signal_layer(signal_layer_no); } // make the layer visible, if it is invisible if (graphics_context.get_layer_visibility(p_layer_no) == 0) { graphics_context.set_layer_visibility(p_layer_no, 1); panel.board_frame.refresh_windows(); } graphics_context.set_fully_visible_layer(p_layer_no); repaint(); } /** * Displays the current layer in the layer message field, * and clears the field for the additional message. */ public void display_layer_messsage() { screen_messages.clear_add_field(); board.Layer curr_layer = board.layer_structure.arr[this.settings.layer]; screen_messages.set_layer(curr_layer.name); } /** * Sets the manual trace half width used in interactive routing. * If p_layer_no < 0, the manual trace half width is changed on all layers. */ public void set_manual_trace_half_width(int p_layer_no, int p_value) { if (p_layer_no == gui.ComboBoxLayer.ALL_LAYER_INDEX) { for (int i = 0; i < settings.manual_trace_half_width_arr.length; ++i) { this.settings.set_manual_trace_half_width(i, p_value); } } else if (p_layer_no == gui.ComboBoxLayer.INNER_LAYER_INDEX) { for (int i = 1; i < settings.manual_trace_half_width_arr.length - 1; ++i) { this.settings.set_manual_trace_half_width(i, p_value); } } else { this.settings.set_manual_trace_half_width(p_layer_no, p_value); } } /** * Changes the interactive selectability of p_item_type. */ public void set_selectable(ItemSelectionFilter.SelectableChoices p_item_type, boolean p_value) { settings.set_selectable(p_item_type, p_value); if (!p_value && this.interactive_state instanceof SelectedItemState) { set_interactive_state(((SelectedItemState) interactive_state).filter()); } } /** * Displays all incomplete connections, if they are not visible, * or hides them, if they are visible. */ public void toggle_ratsnest() { if (ratsnest == null || ratsnest.is_hidden()) { create_ratsnest(); } else { ratsnest = null; } repaint(); } public void toggle_clearance_violations() { if (clearance_violations == null) { clearance_violations = new ClearanceViolations(this.board.get_items()); Integer violation_count = new Integer((clearance_violations.list.size() + 1) / 2); String curr_message = violation_count.toString() + " " + resources.getString("clearance_violations_found"); screen_messages.set_status_message(curr_message); } else { clearance_violations = null; screen_messages.set_status_message(""); } repaint(); } /** * Displays all incomplete connections. */ public void create_ratsnest() { ratsnest = new RatsNest(this.board, this.locale); Integer incomplete_count = ratsnest.incomplete_count(); Integer length_violation_count = ratsnest.length_violation_count(); String curr_message; if (length_violation_count == 0) { curr_message = incomplete_count.toString() + " " + resources.getString("incomplete_connections_to_route"); } else { curr_message = incomplete_count.toString() + " " + resources.getString("incompletes") + " " + length_violation_count.toString() + " " + resources.getString("length_violations"); } screen_messages.set_status_message(curr_message); } /** * Recalculates the incomplete connections for the input net. */ void update_ratsnest(int p_net_no) { if (ratsnest != null && p_net_no > 0) { ratsnest.recalculate(p_net_no, this.board); ratsnest.show(); } } /** * Recalculates the incomplete connections for the input net for the items in p_item_list. */ void update_ratsnest(int p_net_no, Collection<Item> p_item_list) { if (ratsnest != null && p_net_no > 0) { ratsnest.recalculate(p_net_no, p_item_list, this.board); ratsnest.show(); } } /** * Recalculates the incomplete connections, if the ratsnest is active. */ void update_ratsnest() { if (ratsnest != null) { ratsnest = new RatsNest(this.board, this.locale); } } /** * Hides the incomplete connections on the screen. */ public void hide_ratsnest() { if (ratsnest != null) { ratsnest.hide(); } } /** * Shows the incomplete connections on the screen, if the ratsnest is active. */ public void show_ratsnest() { if (ratsnest != null) { ratsnest.show(); } } /** * Removes the incomplete connections. */ public void remove_ratsnest() { ratsnest = null; } /** * Returns the ratsnest with the information about the incomplete connections. */ public RatsNest get_ratsnest() { if (ratsnest == null) { ratsnest = new RatsNest(this.board, this.locale); } return this.ratsnest; } public void recalculate_length_violations() { if (this.ratsnest != null) { if (this.ratsnest.recalculate_length_violations()) { if (!this.ratsnest.is_hidden()) { this.repaint(); } } } } /** * Sets the visibility filter for the incompletes of the input net. */ public void set_incompletes_filter(int p_net_no, boolean p_value) { if (ratsnest != null) { ratsnest.set_filter(p_net_no, p_value); } } /** * Creates the Routingboard, the graphic context and the interactive settings. */ @Override public void create_board(IntBox p_bounding_box, LayerStructure p_layer_structure, PolylineShape[] p_outline_shapes, String p_outline_clearance_class_name, BoardRules p_rules, board.Communication p_board_communication, TestLevel p_test_level) { super.create_board(p_bounding_box, p_layer_structure, p_outline_shapes, p_outline_clearance_class_name, p_rules, p_board_communication, p_test_level); // create the interactive settings with default double unit_factor = p_board_communication.coordinate_transform.board_to_dsn(1); this.coordinate_transform = new CoordinateTransform(1, p_board_communication.unit, unit_factor, p_board_communication.unit); // create a graphics context for the board Dimension panel_size = panel.getPreferredSize(); graphics_context = new GraphicsContext(p_bounding_box, panel_size, p_layer_structure, this.locale); } /** * Changes the factor of the user unit. */ public void change_user_unit_factor(double p_new_factor) { CoordinateTransform old_transform = this.coordinate_transform; this.coordinate_transform = new CoordinateTransform(p_new_factor, old_transform.user_unit, old_transform.board_unit_factor, old_transform.board_unit); } /** * Changes the user unit. */ public void change_user_unit(Unit p_unit) { CoordinateTransform old_transform = this.coordinate_transform; this.coordinate_transform = new CoordinateTransform(old_transform.user_unit_factor, p_unit, old_transform.board_unit_factor, old_transform.board_unit); } /** * From here on the interactive actions are written to a logfile. */ public void start_logfile(File p_filename) { if (board_is_read_only) { return; } logfile.start_write(p_filename); } /** * Repaints the board panel on the screen. */ public void repaint() { if (this.paint_immediately) { final Rectangle MAX_RECTAMGLE = new Rectangle(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE); panel.paintImmediately(MAX_RECTAMGLE); } else { panel.repaint(); } } /** * Repaints a rectangle of board panel on the screen. */ public void repaint(Rectangle p_rect) { if (this.paint_immediately) { panel.paintImmediately(p_rect); } else { panel.repaint(p_rect); } } /** * Gets the panel for graphical display of the board. */ gui.BoardPanel get_panel() { return this.panel; } /** * Gets the popup menu used in the current interactive state. * Returns null, if the current state uses no popup menu. */ public javax.swing.JPopupMenu get_current_popup_menu() { javax.swing.JPopupMenu result; if (interactive_state != null) { result = interactive_state.get_popup_menu(); } else { result = null; } return result; } /** * Draws the board and all temporary construction graphics in the * current interactive state. */ public void draw(Graphics p_graphics) { if (board == null) { return; } board.draw(p_graphics, graphics_context); if (ratsnest != null) { ratsnest.draw(p_graphics, graphics_context); } if (clearance_violations != null) { clearance_violations.draw(p_graphics, graphics_context); } if (interactive_state != null) { interactive_state.draw(p_graphics); } if (interactive_action_thread != null) { interactive_action_thread.draw(p_graphics); } } public void generate_snapshot() { if (board_is_read_only) { return; } board.generate_snapshot(); logfile.start_scope(LogfileScope.GENERATE_SNAPSHOT); } /** * Restores the situation before the previous snapshot. */ public void undo() { if (board_is_read_only || !(interactive_state instanceof MenuState)) { return; } java.util.Set<Integer> changed_nets = new java.util.TreeSet<Integer>(); if (board.undo(changed_nets)) { for (Integer changed_net : changed_nets) { this.update_ratsnest(changed_net); } if (changed_nets.size() > 0) { // reset the start pass number in the autorouter in case // a batch autorouter is undone. this.settings.autoroute_settings.set_pass_no(1); } screen_messages.set_status_message(resources.getString("undo")); } else { screen_messages.set_status_message(resources.getString("no_more_undo_possible")); } logfile.start_scope(LogfileScope.UNDO); repaint(); } /** * Restores the sitiation before the last undo. */ public void redo() { if (board_is_read_only || !(interactive_state instanceof MenuState)) { return; } java.util.Set<Integer> changed_nets = new java.util.TreeSet<Integer>(); if (board.redo(changed_nets)) { for (Integer changed_net : changed_nets) { this.update_ratsnest(changed_net); } screen_messages.set_status_message(resources.getString("redo")); } else { screen_messages.set_status_message(resources.getString("no_more_redo_possible")); } logfile.start_scope(LogfileScope.REDO); repaint(); } /** * Actions to be taken in the current interactive state * when the left mouse butten is clicked. */ public void left_button_clicked(Point2D p_point) { if (board_is_read_only) { if (this.interactive_action_thread != null) { // The left button is used to stop the interactive action thread. this.interactive_action_thread.request_stop(); } return; } if (interactive_state != null && graphics_context != null) { FloatPoint location = graphics_context.coordinate_transform.screen_to_board(p_point); InteractiveState return_state = interactive_state.left_button_clicked(location); if (return_state != interactive_state && return_state != null) { set_interactive_state(return_state); repaint(); } } } /** * Actions to be taken in the current interactive state * when the mouse pointer has moved. */ public void mouse_moved(Point2D p_point) { if (board_is_read_only) { // no interactive action when logfile is running return; } if (interactive_state != null && graphics_context != null) { this.current_mouse_position = graphics_context.coordinate_transform.screen_to_board(p_point); InteractiveState return_state = interactive_state.mouse_moved(); // An automatic repaint here would slow down the display // performance in interactive route. // If a repaint is necessary, it should be done in the individual mouse_moved // method of the class derived from InteractiveState if (return_state != this.interactive_state) { set_interactive_state(return_state); repaint(); } } } /** * Actions to be taken when the mouse button is pressed. */ public void mouse_pressed(Point2D p_point) { if (interactive_state != null && graphics_context != null) { this.current_mouse_position = graphics_context.coordinate_transform.screen_to_board(p_point); set_interactive_state(interactive_state.mouse_pressed(this.current_mouse_position)); } } /** * Actions to be taken in the current interactive state * when the mouse is dragged. */ public void mouse_dragged(Point2D p_point) { if (interactive_state != null && graphics_context != null) { this.current_mouse_position = graphics_context.coordinate_transform.screen_to_board(p_point); InteractiveState return_state = interactive_state.mouse_dragged(this.current_mouse_position); if (return_state != interactive_state) { set_interactive_state(return_state); repaint(); } } } /** * Actions to be taken in the current interactive state * when a mouse button is released. */ public void button_released() { if (interactive_state != null) { InteractiveState return_state = interactive_state.button_released(); if (return_state != interactive_state) { set_interactive_state(return_state); repaint(); } } } /** * Actions to be taken in the current interactive state * when the mouse wheel is moved */ public void mouse_wheel_moved(int p_rotation) { if (interactive_state != null) { InteractiveState return_state = interactive_state.mouse_wheel_moved(p_rotation); if (return_state != interactive_state) { set_interactive_state(return_state); repaint(); } } } /** * Action to be taken in the current interactive state * when a key on the keyboard is typed. */ public void key_typed_action(char p_key_char) { if (board_is_read_only) { // no interactive action when logfile is running return; } InteractiveState return_state = interactive_state.key_typed(p_key_char); if (return_state != null && return_state != interactive_state) { set_interactive_state(return_state); panel.board_frame.hilight_selected_button(); repaint(); } } /** * Completes the curreent interactive state and returns to * its return state. */ public void return_from_state() { if (board_is_read_only) { // no interactive action when logfile is running return; } InteractiveState new_state = interactive_state.complete(); { if (new_state != interactive_state) { set_interactive_state(new_state); repaint(); } } } /** * Cancels the current interactive state. */ public void cancel_state() { if (board_is_read_only) { // no interactive action when logfile is running return; } InteractiveState new_state = interactive_state.cancel(); { if (new_state != interactive_state) { set_interactive_state(new_state); repaint(); } } } /** * Actions to be taken in the current interactive state when * the current board layer is changed. * Returns false, if the layer change failed. */ public boolean change_layer_action(int p_new_layer) { boolean result = true; if (interactive_state != null && !board_is_read_only) { result = interactive_state.change_layer_action(p_new_layer); } return result; } /** * Sets the interactive state to SelectMenuState */ public void set_select_menu_state() { this.interactive_state = SelectMenuState.get_instance(this, logfile); screen_messages.set_status_message(resources.getString("select_menu")); } /** * Sets the interactive state to RouteMenuState */ public void set_route_menu_state() { this.interactive_state = RouteMenuState.get_instance(this, logfile); screen_messages.set_status_message(resources.getString("route_menu")); } /** * Sets the interactive state to DragMenuState */ public void set_drag_menu_state() { this.interactive_state = DragMenuState.get_instance(this, logfile); screen_messages.set_status_message(resources.getString("drag_menu")); } /** * Reads an existing board design from the input stream. * Returns false, if the input stream does not contains a legal board design. */ public boolean read_design(java.io.ObjectInputStream p_design, TestLevel p_test_level) { try { board = (RoutingBoard) p_design.readObject(); settings = (Settings) p_design.readObject(); settings.set_logfile(this.logfile); coordinate_transform = (CoordinateTransform) p_design.readObject(); graphics_context = (GraphicsContext) p_design.readObject(); } catch (Exception e) { return false; } board.set_test_level(p_test_level); screen_messages.set_layer(board.layer_structure.arr[settings.layer].name); return true; } /** * Imports a board design from a Specctra dsn-file. * The parameters p_item_observers and p_item_id_no_generator are used, * in case the board is embedded into a host system. * Returns false, if the dsn-file is currupted. */ public DsnFile.ReadResult import_design(java.io.InputStream p_design, board.BoardObservers p_observers, datastructures.IdNoGenerator p_item_id_no_generator, TestLevel p_test_level) { if (p_design == null) { return DsnFile.ReadResult.ERROR; } DsnFile.ReadResult read_result; try { read_result = DsnFile.read(p_design, this, p_observers, p_item_id_no_generator, p_test_level); } catch (Exception e) { read_result = DsnFile.ReadResult.ERROR; } if (read_result == DsnFile.ReadResult.OK) { this.board.reduce_nets_of_route_items(); this.set_layer(0); for (int i = 0; i < board.get_layer_count(); ++i) { if (!settings.autoroute_settings.get_layer_active(i)) { graphics_context.set_layer_visibility(i, 0); } } } try { p_design.close(); } catch (java.io.IOException e) { read_result = DsnFile.ReadResult.ERROR; } return read_result; } /** * Writes the currently edited board design to a text file in the Specctra dsn format. * If p_compat_mode is true, only standard speecctra dsn scopes are written, so that any * host system with an specctra interface can read them. */ public boolean export_to_dsn_file(OutputStream p_output_stream, String p_design_name, boolean p_compat_mode) { if (board_is_read_only || p_output_stream == null) { return false; } return designformats.specctra.DsnFile.write(this, p_output_stream, p_design_name, p_compat_mode); } /** * Writes a session file ins the Eaglea scr format. */ public boolean export_eagle_session_file(java.io.InputStream p_input_stream, OutputStream p_output_stream) { if (board_is_read_only) { return false; } return designformats.specctra.SessionToEagle.get_instance(p_input_stream, p_output_stream, this.board); } /** * Writes a session file ins the Specctra ses-format. */ public boolean export_specctra_session_file(String p_design_name, OutputStream p_output_stream) { if (board_is_read_only) { return false; } return designformats.specctra.SessionFile.write(this.get_routing_board(), p_output_stream, p_design_name); } /** * Saves the currently edited board design to p_design_file. */ public boolean save_design_file(java.io.ObjectOutputStream p_object_stream) { boolean result = true; try { p_object_stream.writeObject(board); p_object_stream.writeObject(settings); p_object_stream.writeObject(coordinate_transform); p_object_stream.writeObject(graphics_context); } catch (Exception e) { screen_messages.set_status_message(resources.getString("save_error")); result = false; } return result; } /** * Processes the actions stored in the input logfile. */ public void read_logfile(InputStream p_input_stream) { if (board_is_read_only || !(interactive_state instanceof MenuState)) { return; } this.interactive_action_thread = InteractiveActionThread.get_read_logfile_instance(this, p_input_stream); this.interactive_action_thread.start(); } /** * Closes all currently used files so that the file buffers are written to disk. */ public void close_files() { if (logfile != null) { logfile.close_output(); } } /** * Starts interactive routing at the input location. */ public void start_route(Point2D p_point) { if (board_is_read_only) { // no interactive action when logfile is running return; } FloatPoint location = graphics_context.coordinate_transform.screen_to_board(p_point); InteractiveState new_state = RouteState.get_instance(location, this.interactive_state, this, logfile); set_interactive_state(new_state); } /** * Selects board items at the input location. */ public void select_items(Point2D p_point) { if (board_is_read_only || !(this.interactive_state instanceof MenuState)) { return; } FloatPoint location = graphics_context.coordinate_transform.screen_to_board(p_point); InteractiveState return_state = ((MenuState) interactive_state).select_items(location); set_interactive_state(return_state); } /** * Selects all items in an interactive defined rectangle. */ public void select_items_in_region() { if (board_is_read_only || !(this.interactive_state instanceof MenuState)) { return; } set_interactive_state(SelectItemsInRegionState.get_instance(this.interactive_state, this, logfile)); } /** * Selects all items in the input collection. */ public void select_items(Set<Item> p_items) { if (board_is_read_only) { // no interactive action when logfile is running return; } this.display_layer_messsage(); if (interactive_state instanceof MenuState) { set_interactive_state(SelectedItemState.get_instance(p_items, interactive_state, this, logfile)); } else if (interactive_state instanceof SelectedItemState) { ((SelectedItemState) interactive_state).get_item_list().addAll(p_items); repaint(); } } /** * Looks for a swappable pin at p_location. * Prepares for pin swap if a swappable pin was found. */ public void swap_pin(Point2D p_location) { if (board_is_read_only || !(this.interactive_state instanceof MenuState)) { return; } FloatPoint location = graphics_context.coordinate_transform.screen_to_board(p_location); InteractiveState return_state = ((MenuState) interactive_state).swap_pin(location); set_interactive_state(return_state); } /** * Displays a window containing all selected items. */ public void zoom_selection() { if (!(interactive_state instanceof SelectedItemState)) { return; } IntBox bounding_box = this.board.get_bounding_box(((SelectedItemState) interactive_state).get_item_list()); bounding_box = bounding_box.offset(this.board.rules.get_max_trace_half_width()); Point2D lower_left = this.graphics_context.coordinate_transform.board_to_screen(bounding_box.ll.to_float()); Point2D upper_right = this.graphics_context.coordinate_transform.board_to_screen(bounding_box.ur.to_float()); this.panel.zoom_frame(lower_left, upper_right); } /** * Picks item at p_point. * Removes it from the selected_items list, if it is already in there, * otherwise adds it to the list. * Changes to the selected items state, if something was selected. */ public void toggle_select_action(Point2D p_point) { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } FloatPoint location = graphics_context.coordinate_transform.screen_to_board(p_point); InteractiveState return_state = ((SelectedItemState) interactive_state).toggle_select(location); if (return_state != this.interactive_state) { set_interactive_state(return_state); repaint(); } } /** * Fixes the selected items. */ public void fix_selected_items() { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } ((SelectedItemState) interactive_state).fix_items(); } /** * Unfixes the selected items. */ public void unfix_selected_items() { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } ((SelectedItemState) interactive_state).unfix_items(); } /** * Displays information about the selected item into a graphical text window. */ public void display_selected_item_info() { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } ((SelectedItemState) interactive_state).info(); } /** * Makes all selected items connectable and assigns * them to a new net. */ public void assign_selected_to_new_net() { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } InteractiveState new_state = ((SelectedItemState) interactive_state).assign_items_to_new_net(); set_interactive_state(new_state); } /** * Assigns all selected items to a new group ( new component for example) */ public void assign_selected_to_new_group() { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } InteractiveState new_state = ((SelectedItemState) interactive_state).assign_items_to_new_group(); set_interactive_state(new_state); } /** * Deletes all unfixed selected items. */ public void delete_selected_items() { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } InteractiveState new_state = ((SelectedItemState) interactive_state).delete_items(); set_interactive_state(new_state); } /** * Deletes all unfixed selected traces and vias inside a rectangle. */ public void cutout_selected_items() { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } InteractiveState new_state = ((SelectedItemState) interactive_state).cutout_items(); set_interactive_state(new_state); } /** * Assigns the input clearance class to the selected items */ public void assign_clearance_classs_to_selected_items(int p_cl_class_index) { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } InteractiveState new_state = ((SelectedItemState) interactive_state).assign_clearance_class(p_cl_class_index); set_interactive_state(new_state); } /** * Moves or rotates the selected items */ public void move_selected_items(Point2D p_from_location) { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } SelectedItemState curr_state = (SelectedItemState) interactive_state; Collection<Item> item_list = curr_state.get_item_list(); FloatPoint from_location = graphics_context.coordinate_transform.screen_to_board(p_from_location); InteractiveState new_state = MoveItemState.get_instance(from_location, item_list, interactive_state, this, logfile); set_interactive_state(new_state); repaint(); } /** * Copies all selected items. */ public void copy_selected_items(Point2D p_from_location) { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } SelectedItemState curr_state = (SelectedItemState) interactive_state; curr_state.extent_to_whole_components(); Collection<Item> item_list = curr_state.get_item_list(); FloatPoint from_location = graphics_context.coordinate_transform.screen_to_board(p_from_location); InteractiveState new_state = CopyItemState.get_instance(from_location, item_list, interactive_state.return_state, this, logfile); set_interactive_state(new_state); } /** * Optimizes the selected items. */ public void optimize_selected_items() { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } board.generate_snapshot(); this.interactive_action_thread = InteractiveActionThread.get_pull_tight_instance(this); this.interactive_action_thread.start(); } /** * Autoroute the selected items. */ public void autoroute_selected_items() { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } board.generate_snapshot(); this.interactive_action_thread = InteractiveActionThread.get_autoroute_instance(this); this.interactive_action_thread.start(); } /** * Fanouts the selected items. */ public void fanout_selected_items() { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } board.generate_snapshot(); this.interactive_action_thread = InteractiveActionThread.get_fanout_instance(this); this.interactive_action_thread.start(); } /** * Start the batch autorouter on the whole Board */ public void start_batch_autorouter() { if (board_is_read_only) { return; } board.generate_snapshot(); this.interactive_action_thread = InteractiveActionThread.get_batch_autorouter_instance(this); this.interactive_action_thread.start(); } /** * Selects also all items belonging to a net of a currently selecte item. */ public void extend_selection_to_whole_nets() { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } set_interactive_state(((SelectedItemState) interactive_state).extent_to_whole_nets()); } /** * Selects also all items belonging to a component of a currently selecte item. */ public void extend_selection_to_whole_components() { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } set_interactive_state(((SelectedItemState) interactive_state).extent_to_whole_components()); } /** * Selects also all items belonging to a connected set of a currently selecte item. */ public void extend_selection_to_whole_connected_sets() { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } set_interactive_state(((SelectedItemState) interactive_state).extent_to_whole_connected_sets()); } /** * Selects also all items belonging to a connection of a currently selecte item. */ public void extend_selection_to_whole_connections() { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } set_interactive_state(((SelectedItemState) interactive_state).extent_to_whole_connections()); } /** * Shows or hides the clearance violations of the selected items. */ public void toggle_selected_item_violations() { if (board_is_read_only || !(interactive_state instanceof SelectedItemState)) { return; } ((SelectedItemState) interactive_state).toggle_clearance_violations(); } public void turn_45_degree(int p_factor) { if (board_is_read_only || !(interactive_state instanceof MoveItemState)) { // no interactive action when logfile is running return; } ((MoveItemState) interactive_state).turn_45_degree(p_factor); } public void change_placement_side() { if (board_is_read_only || !(interactive_state instanceof MoveItemState)) { // no interactive action when logfile is running return; } ((MoveItemState) interactive_state).change_placement_side(); } /** * Zooms display to an interactive defined rectangle. */ public void zoom_region() { interactive_state = ZoomRegionState.get_instance(this.interactive_state, this, this.logfile); } /** * Start interactively creating a circle obstacle. */ public void start_circle(Point2D p_point) { if (board_is_read_only) { // no interactive action when logfile is running return; } FloatPoint location = graphics_context.coordinate_transform.screen_to_board(p_point); set_interactive_state(CircleConstructionState.get_instance(location, this.interactive_state, this, logfile)); } /** * Start interactively creating a tile shaped obstacle. */ public void start_tile(Point2D p_point) { if (board_is_read_only) { // no interactive action when logfile is running return; } FloatPoint location = graphics_context.coordinate_transform.screen_to_board(p_point); set_interactive_state(TileConstructionState.get_instance(location, this.interactive_state, this, logfile)); } /** * Start interactively creating a polygon shaped obstacle. */ public void start_polygonshape_item(Point2D p_point) { if (board_is_read_only) { // no interactive action when logfile is running return; } FloatPoint location = graphics_context.coordinate_transform.screen_to_board(p_point); set_interactive_state(PolygonShapeConstructionState.get_instance(location, this.interactive_state, this, logfile)); } /** * Actions to be taken, when adding a hole to an existing obstacle shape * on the board is started. */ public void start_adding_hole(Point2D p_point) { if (board_is_read_only) { // no interactive action when logfile is running return; } FloatPoint location = graphics_context.coordinate_transform.screen_to_board(p_point); InteractiveState new_state = HoleConstructionState.get_instance(location, this.interactive_state, this, logfile); set_interactive_state(new_state); } /** * Gets a surrounding rectangle of the area, where an update of the * graphics is needed caused by the previous interactive actions. */ Rectangle get_graphics_update_rectangle() { Rectangle result; IntBox update_box = board.get_graphics_update_box(); if (update_box == null || update_box.is_empty()) { result = new Rectangle(0, 0, 0, 0); } else { IntBox offset_box = update_box.offset(board.get_max_trace_half_width()); result = graphics_context.coordinate_transform.board_to_screen(offset_box); } return result; } /** * Gets all items at p_location on the active board layer. * If nothing is found on the active layer and settings.select_on_all_layers * is true, all layers are selected. */ java.util.Set<Item> pick_items(FloatPoint p_location) { return pick_items(p_location, settings.item_selection_filter); } /** * Gets all items at p_location on the active board layer with the inputt item filter. * If nothing is found on the active layer and settings.select_on_all_layers * is true, all layers are selected. */ java.util.Set<Item> pick_items(FloatPoint p_location, ItemSelectionFilter p_item_filter) { IntPoint location = p_location.round(); java.util.Set<Item> result = board.pick_items(location, settings.layer, p_item_filter); if (result.size() == 0 && settings.select_on_all_visible_layers) { for (int i = 0; i < graphics_context.layer_count(); ++i) { if (i == settings.layer || graphics_context.get_layer_visibility(i) <= 0) { continue; } result.addAll(board.pick_items(location, i, p_item_filter)); } } return result; } /** * Moves the mouse pointer to p_to_location. */ void move_mouse(FloatPoint p_to_location) { if (!board_is_read_only) { panel.move_mouse(graphics_context.coordinate_transform.board_to_screen(p_to_location)); } } /** * Gets the current interactive state. */ public InteractiveState get_interactive_state() { return this.interactive_state; } public void set_interactive_state(InteractiveState p_state) { if (p_state != null && p_state != interactive_state) { this.interactive_state = p_state; if (!this.board_is_read_only) { p_state.set_toolbar(); this.panel.board_frame.set_context_sensitive_help(this.panel, p_state.get_help_id()); } } } /** * Adjust the design bounds, so that also all items being still placed outside the * board outline are contained in the new bounds. */ public void adjust_design_bounds() { IntBox new_bounding_box = this.board.get_bounding_box(); Collection<Item> board_items = this.board.get_items(); for (Item curr_item : board_items) { IntBox curr_bounding_box = curr_item.bounding_box(); if (curr_bounding_box.ur.x < Integer.MAX_VALUE) { new_bounding_box = new_bounding_box.union(curr_bounding_box); } } this.graphics_context.change_design_bounds(new_bounding_box); } /** * Sets all references inside this class to null, so that it can be recycled * by the garbage collector. */ public void dispose() { close_files(); graphics_context = null; coordinate_transform = null; settings = null; interactive_state = null; ratsnest = null; clearance_violations = null; board = null; } /** The graphical context for drawing the board. */ public GraphicsContext graphics_context = null; /** For ransforming coordinates between the user and the board coordinate space */ public CoordinateTransform coordinate_transform = null; /** The text message fields displayed on the screen */ public final ScreenMessages screen_messages; /** The currently active interactive state. */ InteractiveState interactive_state = null; /** * Used for running an interactive action in a seperate thread. */ private InteractiveActionThread interactive_action_thread = null; /** To display all incomplete connections on the screen. */ private RatsNest ratsnest = null; /** To display all clearance violations between items on the screen. */ private ClearanceViolations clearance_violations = null; /** The graphical panel used for displaying the board. */ private final gui.BoardPanel panel; /** * True if currently a logfile is being processed. * Used to prevent interactive changes of the board database * in this case. */ private boolean board_is_read_only = false; /** The current position of the mouse pointer. */ private FloatPoint current_mouse_position = null; /** * To repaint the board immediately for example when reading a logfile. */ boolean paint_immediately = false; private final java.util.ResourceBundle resources; private final java.util.Locale locale; }