/* * 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. * * ClearanceMatrixWindow.java * * Created on 20. Februar 2005, 06:09 */ package gui; import rules.ClearanceMatrix; import datastructures.UndoableObjects; /** * Window for interactive editing of the clearance Matrix. * * @author Alfons Wirtz */ public class WindowClearanceMatrix extends BoardSavableSubWindow { /** Creates a new instance of ClearanceMatrixWindow */ public WindowClearanceMatrix(BoardFrame p_board_frame) { this.board_frame = p_board_frame; this.resources = java.util.ResourceBundle.getBundle("gui.resources.WindowClearanceMatrix", p_board_frame.get_locale()); this.setTitle(resources.getString("title")); this.main_panel = new javax.swing.JPanel(); main_panel.setBorder(javax.swing.BorderFactory.createEmptyBorder(20,20,20,20)); main_panel.setLayout(new java.awt.BorderLayout()); // Add the layer combo box. final javax.swing.JPanel north_panel = new javax.swing.JPanel(); north_panel.setBorder(javax.swing.BorderFactory.createEmptyBorder(0,0,20,0)); javax.swing.JLabel layer_label = new javax.swing.JLabel(resources.getString("layer") + " "); layer_label.setToolTipText(resources.getString("layer_tooltip")); north_panel.add(layer_label); interactive.BoardHandling board_handling = board_frame.board_panel.board_handling; layer_combo_box = new ComboBoxLayer(board_handling.get_routing_board().layer_structure, p_board_frame.get_locale()); north_panel.add(this.layer_combo_box); this.layer_combo_box.addActionListener(new ComboBoxListener()); main_panel.add(north_panel, java.awt.BorderLayout.NORTH); // Add the clearance table. this.center_panel = add_clearance_table(p_board_frame); main_panel.add(center_panel, java.awt.BorderLayout.CENTER); // Add panel with buttons. javax.swing.JPanel south_panel = new javax.swing.JPanel(); south_panel.setBorder(javax.swing.BorderFactory.createEmptyBorder(20,20,20,20)); south_panel.setLayout(new java.awt.BorderLayout()); this.add(south_panel); final javax.swing.JButton add_class_button = new javax.swing.JButton(resources.getString("add_class")); add_class_button.setToolTipText(resources.getString("add_class_tooltip")); add_class_button.addActionListener(new AddClassListener()); south_panel.add(add_class_button, java.awt.BorderLayout.WEST); final javax.swing.JButton prune_button = new javax.swing.JButton(resources.getString("prune")); prune_button.setToolTipText(resources.getString("prune_tooltip")); prune_button.addActionListener(new PruneListener()); south_panel.add(prune_button, java.awt.BorderLayout.EAST); main_panel.add(south_panel, java.awt.BorderLayout.SOUTH); p_board_frame.set_context_sensitive_help(this, "WindowClearanceMatrix"); this.add(main_panel); this.pack(); } /** * Recalculates all displayed values */ public void refresh() { board.BasicBoard routing_board = this.board_frame.board_panel.board_handling.get_routing_board(); if (this.clearance_table_model.getRowCount() != routing_board.rules.clearance_matrix.get_class_count()) { this.adjust_clearance_table(); } this.clearance_table_model.set_values(this.layer_combo_box.get_selected_layer().index); this.repaint(); } private javax.swing.JPanel add_clearance_table(BoardFrame p_board_frame) { this.clearance_table_model = new ClearanceTableModel(p_board_frame.board_panel.board_handling); this.clearance_table = new javax.swing.JTable(clearance_table_model); // Put the clearance table into a scroll pane. final int textfield_height = 16; final int textfield_width = Math.max(6 * max_name_length(), 100); int table_height = textfield_height * (this.clearance_table_model.getRowCount()); int table_width = textfield_width * this.clearance_table_model.getColumnCount(); this.clearance_table.setPreferredSize(new java.awt.Dimension(table_width, table_height)); this.clearance_table.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF); // Put a panel around the table and the header before putting the table into the scroll pane, // because otherwise there seems to be a redisplay bug in horizontal scrolling. javax.swing.JPanel scroll_panel = new javax.swing.JPanel(); scroll_panel.setLayout(new java.awt.BorderLayout()); scroll_panel.add(this.clearance_table.getTableHeader(), java.awt.BorderLayout.NORTH); scroll_panel.add(this.clearance_table, java.awt.BorderLayout.CENTER); javax.swing.JScrollPane scroll_pane = new javax.swing.JScrollPane( scroll_panel, javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); final int scroll_bar_width = 20; final int scroll_pane_height = textfield_height * this.clearance_table_model.getRowCount() + scroll_bar_width; final int scroll_pane_width = Math.min(table_width + scroll_bar_width, 1200); scroll_pane.setPreferredSize(new java.awt.Dimension(scroll_pane_width, scroll_pane_height)); // Change the background color of the header and the first column of the table. java.awt.Color header_background_color = new java.awt.Color(220, 220, 255); javax.swing.table.JTableHeader table_header = this.clearance_table.getTableHeader(); table_header.setBackground(header_background_color); javax.swing.table.TableColumn first_column = this.clearance_table.getColumnModel().getColumn(0); javax.swing.table.DefaultTableCellRenderer first_colunn_renderer = new javax.swing.table.DefaultTableCellRenderer(); first_colunn_renderer.setBackground(header_background_color); first_column.setCellRenderer(first_colunn_renderer); final javax.swing.JPanel result = new javax.swing.JPanel(); result.setLayout(new java.awt.BorderLayout()); result.add(scroll_pane, java.awt.BorderLayout.CENTER); // add message for german localisation bug if (p_board_frame.get_locale().getLanguage().equalsIgnoreCase("de")) { javax.swing.JLabel bug_label = new javax.swing.JLabel("Wegen eines Java-System-Bugs muss das Dezimalkomma in dieser Tabelle als Punkt eingegeben werden!"); result.add(bug_label, java.awt.BorderLayout.SOUTH); } return result; } /** * Adds a new class to the clearance matrix. */ private void add_class() { String new_name = null; // Ask for the name of the new class. for (;;) { new_name = javax.swing.JOptionPane.showInputDialog(resources.getString("new_name")); if (new_name == null) { return; } new_name = new_name.trim(); if (is_legal_class_name(new_name)) { break; } } final board.BasicBoard routing_board = this.board_frame.board_panel.board_handling.get_routing_board(); final rules.ClearanceMatrix clearance_matrix = routing_board.rules.clearance_matrix; // Check, if the name exists already. boolean name_exists = false; for (int i = 0; i < clearance_matrix.get_class_count(); ++i) { if (new_name.equals(clearance_matrix.get_name(i))) { name_exists = true; break; } } if (name_exists) { return; } clearance_matrix.append_class(new_name); if (routing_board.get_test_level() == board.TestLevel.RELEASE_VERSION) { // clearance compensation is only used, if there are only the clearance classes default and null. routing_board.search_tree_manager.set_clearance_compensation_used(false); } adjust_clearance_table(); } /** * Removes clearance classs, whose clearance values are all equal to a previous class. */ private void prune_clearance_matrix() { final board.BasicBoard routing_board = this.board_frame.board_panel.board_handling.get_routing_board(); ClearanceMatrix clearance_matrix = routing_board.rules.clearance_matrix; for (int i = clearance_matrix.get_class_count() - 1; i >= 2; --i) { for (int j = clearance_matrix.get_class_count() - 1; j >= 0; --j) { if (i == j) { continue; } if (clearance_matrix.is_equal(i, j)) { String message = resources.getString("confirm_remove") + " " + clearance_matrix.get_name(i); int selected_option = javax.swing.JOptionPane.showConfirmDialog(this, message, null, javax.swing.JOptionPane.YES_NO_OPTION); if (selected_option == javax.swing.JOptionPane.YES_OPTION) { java.util.Collection<board.Item> board_items = routing_board.get_items(); routing_board.rules.change_clearance_class_no(i, j, board_items); if (!routing_board.rules.remove_clearance_class(i, board_items)) { System.out.println("WindowClearanceMatrix.prune_clearance_matrix error removing clearance class"); return; } routing_board.search_tree_manager.clearance_class_removed(i); adjust_clearance_table(); } break; } } } } /** * Adjusts the displayed window with the clearance table after the size of the clearance matrix has changed. */ private void adjust_clearance_table() { this.clearance_table_model = new ClearanceTableModel(this.board_frame.board_panel.board_handling); this.clearance_table = new javax.swing.JTable(clearance_table_model); this.main_panel.remove(this.center_panel); this.center_panel = add_clearance_table(this.board_frame); this.main_panel.add(this.center_panel, java.awt.BorderLayout.CENTER); this.pack(); this.board_frame.refresh_windows(); } /** * Returns true, if p_string is a legal class name. */ private boolean is_legal_class_name(String p_string) { if (p_string.equals("")) { return false; } for (int i = 0; i < reserved_name_chars.length; ++i) { if (p_string.contains(reserved_name_chars[i])) { return false; } } return true; } private int max_name_length() { int result = 1; rules.ClearanceMatrix clearance_matrix = board_frame.board_panel.board_handling.get_routing_board().rules.clearance_matrix; for (int i = 0; i < clearance_matrix.get_class_count(); ++i) { result = Math.max(result, clearance_matrix.get_name(i).length()); } return result; } private class ComboBoxListener implements java.awt.event.ActionListener { public void actionPerformed(java.awt.event.ActionEvent evt) { refresh(); } } private final BoardFrame board_frame; private final javax.swing.JPanel main_panel; private javax.swing.JPanel center_panel; private final ComboBoxLayer layer_combo_box; private javax.swing.JTable clearance_table; private ClearanceTableModel clearance_table_model; private final java.util.ResourceBundle resources; /** Characters, which are not allowed in the name of a clearance class. */ private static final String[] reserved_name_chars = {"(", ")", " ", "_"}; private class AddClassListener implements java.awt.event.ActionListener { public void actionPerformed(java.awt.event.ActionEvent p_evt) { add_class(); } } private class PruneListener implements java.awt.event.ActionListener { public void actionPerformed(java.awt.event.ActionEvent p_evt) { prune_clearance_matrix(); } } /** * Table model of the clearance matrix. */ private class ClearanceTableModel extends javax.swing.table.AbstractTableModel implements java.io.Serializable { public ClearanceTableModel(interactive.BoardHandling p_board_handling) { rules.ClearanceMatrix clearance_matrix = p_board_handling.get_routing_board().rules.clearance_matrix; column_names = new String[clearance_matrix.get_class_count() + 1]; column_names[0] = resources.getString("class"); data = new Object[clearance_matrix.get_class_count()][]; for (int i = 0; i < clearance_matrix.get_class_count(); ++i) { this.column_names[i + 1] = clearance_matrix.get_name(i); this.data[i] = new Object[clearance_matrix.get_class_count() + 1]; this.data[i][0] = clearance_matrix.get_name(i); } this.set_values(0); } public String getColumnName(int p_col) { return column_names[p_col]; } public int getRowCount() { return data.length; } public int getColumnCount() { return column_names.length; } public Object getValueAt(int p_row, int p_col) { return data[p_row][p_col]; } public void setValueAt(Object p_value, int p_row, int p_col) { Number number_value = null; if (p_value instanceof Number) { // does ot work because of a localisation Bug in Java number_value = (Number) p_value; } else { // Workaround because of a localisation Bug in Java // The numbers are always displayed in the English Format. if (!(p_value instanceof String)) { return; } try { number_value = Float.parseFloat((String) p_value); } catch (Exception e) { return; } } int curr_row = p_row; int curr_column = p_col - 1; // check, if there are items on the board assigned to clearance class i or j. interactive.BoardHandling board_handling = board_frame.board_panel.board_handling; datastructures.UndoableObjects item_list = board_handling.get_routing_board().item_list; boolean items_already_assigned_row = false; boolean items_already_assigned_column = false; java.util.Iterator<UndoableObjects.UndoableObjectNode> it = item_list.start_read_object(); for(;;) { board.Item curr_item = (board.Item) item_list.read_object(it); if (curr_item == null) { break; } int curr_item_class_no = curr_item.clearance_class_no(); if (curr_item_class_no == curr_row) { items_already_assigned_row = true; } if (curr_item_class_no == curr_column) { items_already_assigned_column = true; } } ClearanceMatrix clearance_matrix = board_handling.get_routing_board().rules.clearance_matrix; boolean items_already_assigned = items_already_assigned_row && items_already_assigned_column; if ( items_already_assigned) { String message = resources.getString("already_assigned") + " "; if (curr_row == curr_column) { message += resources.getString("the_class") + " " + clearance_matrix.get_name(curr_row); } else { message += resources.getString("the_classes") + " " + clearance_matrix.get_name(curr_row) + " " + resources.getString("and") + " " + clearance_matrix.get_name(curr_column); } message += resources.getString("change_anyway"); int selected_option = javax.swing.JOptionPane.showConfirmDialog(board_frame.clearance_matrix_window, message, null, javax.swing.JOptionPane.YES_NO_OPTION); if (selected_option != javax.swing.JOptionPane.YES_OPTION) { return; } } this.data[p_row][p_col] = number_value; this.data[p_col- 1] [p_row + 1] = number_value; fireTableCellUpdated(p_row, p_col); fireTableCellUpdated(p_col - 1, p_row + 1); int board_value = (int) Math.round(board_handling.coordinate_transform.user_to_board((number_value).doubleValue())); int layer_no = layer_combo_box.get_selected_layer().index; if (layer_no == ComboBoxLayer.ALL_LAYER_INDEX) { // change the clearance on all layers clearance_matrix.set_value(curr_row, curr_column, board_value); clearance_matrix.set_value(curr_column, curr_row, board_value); } else if (layer_no == ComboBoxLayer.INNER_LAYER_INDEX) { // change the clearance on all inner layers clearance_matrix.set_inner_value(curr_row, curr_column, board_value); clearance_matrix.set_inner_value(curr_column, curr_row, board_value); } else { // change the clearance on layer with index layer_no clearance_matrix.set_value(curr_row, curr_column, layer_no, board_value); clearance_matrix.set_value(curr_column, curr_row, layer_no, board_value); } if (items_already_assigned) { // force reinserting all item into the searck tree, because their tree shapes may have changed board_handling.get_routing_board().search_tree_manager.clearance_value_changed(); } } public boolean isCellEditable(int p_row, int p_col) { return p_row > 0 && p_col > 1; } public Class<?> getColumnClass(int p_col) { if (p_col == 0) { return String.class; } return String.class; // Should be Number.class or Float.class. But that does not work because of a localisation bug in Java. } /** * Sets the values of this clearance table to the values of the clearance matrix on the input layer. */ private void set_values(int p_layer) { interactive.BoardHandling board_handling = board_frame.board_panel.board_handling; ClearanceMatrix clearance_matrix = board_handling.get_routing_board().rules.clearance_matrix; for (int i = 0; i < clearance_matrix.get_class_count(); ++i) { for (int j = 0; j < clearance_matrix.get_class_count(); ++j) { if (p_layer == ComboBoxLayer.ALL_LAYER_INDEX) { // all layers if (clearance_matrix.is_layer_dependent(i, j)) { this.data[i][j + 1] = -1; } else { Float curr_table_value = (float) board_handling.coordinate_transform.board_to_user(clearance_matrix.value(i, j, 0)); this.data[i][j + 1] = curr_table_value; } } else if (p_layer == ComboBoxLayer.INNER_LAYER_INDEX) { // all layers if (clearance_matrix.is_inner_layer_dependent(i, j)) { this.data[i][j + 1] = -1; } else { Float curr_table_value = (float) board_handling.coordinate_transform.board_to_user(clearance_matrix.value(i, j, 1)); this.data[i][j + 1] = curr_table_value; } } else { Float curr_table_value = (float) board_handling.coordinate_transform.board_to_user(clearance_matrix.value(i, j, p_layer)); this.data[i][j + 1] = curr_table_value; } } } } private Object[][] data = null; private String[] column_names = null; } }