public Action_AddInstance(InstanceTableWidget widget) { super((widget.getState()).getAddInstanceButtonTooltip(), Icons.getAddIcon()); _widget = widget; _dialogTitle = (widget.getState()).getAddInstanceDialogTitle(); }
/** * An IssueBrowser is a program for finding and viewing issues. * * @author <a href="mailto:[email protected]">Jesse Wilson</a> */ public class IssuesBrowser implements Runnable { /** application appearance */ public static final Color GLAZED_LISTS_DARK_BROWN = new Color(36, 23, 10); public static final Color GLAZED_LISTS_MEDIUM_BROWN = new Color(69, 64, 56); public static final Color GLAZED_LISTS_MEDIUM_LIGHT_BROWN = new Color(150, 140, 130); public static final Color GLAZED_LISTS_LIGHT_BROWN = new Color(246, 237, 220); public static final Color GLAZED_LISTS_LIGHT_BROWN_DARKER = new Color(231, 222, 205); public static final Icon THROBBER_ACTIVE = loadIcon("resources/throbber-active.gif"); public static final Icon THROBBER_STATIC = loadIcon("resources/throbber-static.gif"); public static final Icon EXPANDED_ICON = Icons.triangle(9, SwingConstants.EAST, GLAZED_LISTS_MEDIUM_LIGHT_BROWN); public static final Icon COLLAPSED_ICON = Icons.triangle(9, SwingConstants.SOUTH, GLAZED_LISTS_MEDIUM_LIGHT_BROWN); public static final Icon X_ICON = Icons.x(10, 5, GLAZED_LISTS_MEDIUM_LIGHT_BROWN); public static final Border EMPTY_ONE_PIXEL_BORDER = BorderFactory.createEmptyBorder(1, 1, 1, 1); public static final Border EMPTY_TWO_PIXEL_BORDER = BorderFactory.createEmptyBorder(2, 2, 2, 2); /** an event list to host the issues */ private EventList<Issue> issuesEventList = new BasicEventList<Issue>(); /** all the filters currently applied to the issues list */ private FilterPanel filterPanel = new FilterPanel(issuesEventList); /** the currently selected issues */ private AdvancedListSelectionModel<Issue> issuesSelectionModel; /** the TableModel containing the filtered and sorted issues */ private AdvancedTableModel<Issue> issuesTableModel; /** the currently selected issue for which the details are displayed */ private Issue descriptionIssue; /** the component that displays the details of the selected issue, if any */ private IssueDetailsComponent issueDetails; /** monitor loading the issues */ private JLabel throbber; /** a label to display the count of issues in the issue table */ private IssueCounterLabel issueCounter; /** loads issues as requested */ private IssueLoader issueLoader; /** the application window */ private JFrame frame; /** things to handle when booting the issues loader */ private String[] startupArgs; /** Tell the IssuesBrowser how to configure itself when starting up. */ public void setStartupArgs(String[] startupArgs) { this.startupArgs = startupArgs; } /** Loads the issues browser as standalone application. */ public void run() { constructStandalone(); // we have advice for the user when we cannot connect to a host Exceptions.getInstance().addHandler(ExceptionHandlerFactory.unknownHostExceptionHandler(frame)); Exceptions.getInstance().addHandler(ExceptionHandlerFactory.connectExceptionHandler(frame)); Exceptions.getInstance() .addHandler(ExceptionHandlerFactory.noRouteToHostExceptionHandler(frame)); Exceptions.getInstance() .addHandler(ExceptionHandlerFactory.accessControlExceptionHandler(frame)); Exceptions.getInstance().addHandler(ExceptionHandlerFactory.ioExceptionCode500Handler(frame)); // create the issue loader and start loading issues issueLoader = new IssueLoader( issuesEventList, new IndeterminateToggler(throbber, THROBBER_ACTIVE, THROBBER_STATIC)); issueLoader.start(); // load issues from a file if requested if (startupArgs.length == 1) { issueLoader.setFileName(startupArgs[0]); } } /** Load the specified icon from the pathname on the classpath. */ private static ImageIcon loadIcon(String pathname) { ClassLoader jarLoader = IssuesBrowser.class.getClassLoader(); URL url = jarLoader.getResource(pathname); if (url == null) return null; return new ImageIcon(url); } /** Constructs the browser as a standalone frame. */ private void constructStandalone() { frame = new JFrame("Issues"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(1024, 600); frame.setLocationRelativeTo(null); frame.getContentPane().setLayout(new BorderLayout()); frame.getContentPane().add(constructView(), BorderLayout.CENTER); frame.setVisible(true); } /** Display a frame for browsing issues. */ private JPanel constructView() { // sort the original issues list final SortedList<Issue> issuesSortedList = new SortedList<Issue>(issuesEventList, null); // filter the sorted issues FilterList<Issue> filteredIssues = new FilterList<Issue>(issuesSortedList, filterPanel.getMatcherEditor()); SeparatorList<Issue> separatedIssues = new SeparatorList<Issue>( filteredIssues, GlazedLists.beanPropertyComparator(Issue.class, "subcomponent"), 0, Integer.MAX_VALUE); EventList<Issue> separatedIssuesProxyList = GlazedListsSwing.swingThreadProxyList(separatedIssues); // build the issues table issuesTableModel = GlazedListsSwing.eventTableModel(separatedIssuesProxyList, new IssueTableFormat()); final TableColumnModel issuesTableColumnModel = new EventTableColumnModel<TableColumn>(new BasicEventList<TableColumn>()); JSeparatorTable issuesJTable = new JSeparatorTable(issuesTableModel, issuesTableColumnModel); issuesJTable.setAutoCreateColumnsFromModel(true); issuesJTable.setSeparatorRenderer(new IssueSeparatorTableCell(separatedIssues)); issuesJTable.setSeparatorEditor(new IssueSeparatorTableCell(separatedIssues)); issuesSelectionModel = GlazedListsSwing.eventSelectionModel(separatedIssuesProxyList); issuesSelectionModel.setSelectionMode( ListSelection .MULTIPLE_INTERVAL_SELECTION_DEFENSIVE); // multi-selection best demos our awesome // selection management issuesSelectionModel.addListSelectionListener(new IssuesSelectionListener()); issuesJTable.setSelectionModel(issuesSelectionModel); issuesJTable.getColumnModel().getColumn(0).setPreferredWidth(150); issuesJTable.getColumnModel().getColumn(1).setPreferredWidth(400); issuesJTable.getColumnModel().getColumn(2).setPreferredWidth(300); issuesJTable.getColumnModel().getColumn(3).setPreferredWidth(300); issuesJTable.getColumnModel().getColumn(4).setPreferredWidth(250); issuesJTable.getColumnModel().getColumn(5).setPreferredWidth(300); issuesJTable.getColumnModel().getColumn(6).setPreferredWidth(300); issuesJTable.getColumnModel().getColumn(7).setPreferredWidth(1000); // turn off cell focus painting issuesJTable.setDefaultRenderer( String.class, new NoFocusRenderer(issuesJTable.getDefaultRenderer(String.class))); issuesJTable.setDefaultRenderer( Integer.class, new NoFocusRenderer(issuesJTable.getDefaultRenderer(Integer.class))); issuesJTable.setDefaultRenderer( Priority.class, new NoFocusRenderer(new PriorityTableCellRenderer())); LookAndFeelTweaks.tweakTable(issuesJTable); TableComparatorChooser.install( issuesJTable, issuesSortedList, TableComparatorChooser.MULTIPLE_COLUMN_KEYBOARD); JScrollPane issuesTableScrollPane = new JScrollPane( issuesJTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); issuesTableScrollPane.getViewport().setBackground(UIManager.getColor("EditorPane.background")); issuesTableScrollPane.setBorder(BorderFactory.createEmptyBorder()); issueDetails = new IssueDetailsComponent(filteredIssues); // projects EventList<Project> projects = Project.getProjects(); TransformedList<Project, Project> projectsProxyList = GlazedListsSwing.swingThreadProxyList(projects); // project select combobox DefaultEventComboBoxModel projectsComboModel = new DefaultEventComboBoxModel<Project>(projectsProxyList); JComboBox projectsCombo = new JComboBox(projectsComboModel); projectsCombo.setEditable(false); projectsCombo.setOpaque(false); projectsCombo.addItemListener(new ProjectChangeListener()); projectsComboModel.setSelectedItem(new Project(null, "Select a Java.net project...")); // build a label to display the number of issues in the issue table issueCounter = new IssueCounterLabel(filteredIssues); issueCounter.setHorizontalAlignment(SwingConstants.CENTER); issueCounter.setForeground(Color.WHITE); // throbber throbber = new JLabel(THROBBER_STATIC); throbber.setHorizontalAlignment(SwingConstants.RIGHT); // header bar JPanel iconBar = new GradientPanel(GLAZED_LISTS_MEDIUM_BROWN, GLAZED_LISTS_DARK_BROWN, true); iconBar.setLayout(new GridLayout(1, 3)); iconBar.add(projectsCombo); iconBar.add(issueCounter); iconBar.add(throbber); iconBar.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); // assemble all data components on a common panel JPanel dataPanel = new JPanel(); JComponent issueDetailsComponent = issueDetails; dataPanel.setLayout(new GridLayout(2, 1)); dataPanel.add(issuesTableScrollPane); dataPanel.add(issueDetailsComponent); // draw lines between components JComponent filtersPanel = filterPanel.getComponent(); filtersPanel.setBorder(BorderFactory.createEmptyBorder()); // filtersPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, // IssuesBrowser.GLAZED_LISTS_DARK_BROWN)); issueDetailsComponent.setBorder( BorderFactory.createMatteBorder(1, 0, 0, 0, GLAZED_LISTS_DARK_BROWN)); // the outermost panel to layout the icon bar with the other panels final JPanel mainPanel = new JPanel(new GridBagLayout()); mainPanel.add( iconBar, new GridBagConstraints( 0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); mainPanel.add( filtersPanel, new GridBagConstraints( 0, 1, 1, 1, 0.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); mainPanel.add( Box.createHorizontalStrut(240), new GridBagConstraints( 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); mainPanel.add( dataPanel, new GridBagConstraints( 1, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); return mainPanel; } /** Listens for changes in the selection on the issues table. */ class IssuesSelectionListener implements ListSelectionListener { public void valueChanged(ListSelectionEvent e) { // get the newly selected issue Issue selectedIssue = null; if (issuesSelectionModel.getSelected().size() > 0) { Object selectedObject = issuesSelectionModel.getSelected().get(0); if (selectedObject instanceof Issue) { selectedIssue = (Issue) selectedObject; } } // update the description issue if (selectedIssue == descriptionIssue) return; descriptionIssue = selectedIssue; issueDetails.setIssue(descriptionIssue); } } /** Listens for changes to the project combo box and updates which project is being loaded. */ class ProjectChangeListener implements ItemListener { public void itemStateChanged(ItemEvent e) { if (e.getStateChange() != ItemEvent.SELECTED) return; final Project selectedProject = (Project) e.getItem(); if (selectedProject.isValid()) issueLoader.setProject(selectedProject); } } /** When started via a main method, this creates a standalone issues browser. */ public static void main(final String[] args) { SwingUtilities.invokeLater(new IssuesBrowserStarter(args)); } /** * This Runnable contains the logic to start the IssuesBrowser application. It is guaranteed to be * executed on the EventDispatch Thread. */ private static class IssuesBrowserStarter implements Runnable { private final String[] args; public IssuesBrowserStarter(String[] args) { this.args = args; } public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { // do nothing - fall back to default look and feel } // load the issues and display the browser final IssuesBrowser browser = new IssuesBrowser(); browser.setStartupArgs(args); browser.run(); } } /** * A custom label designed for displaying the number of issues in the issue table. Use {@link * #setIssueCount(int)} to update the text of the label to reflect a new issue count. */ private static class IssueCounterLabel extends JLabel implements ListEventListener<Issue> { private static final MessageFormat issueCountFormat = new MessageFormat("{0} {0,choice,0#issues|1#issue|1<issues}"); private int issueCount = -1; public IssueCounterLabel(EventList<Issue> source) { source.addListEventListener(this); this.setIssueCount(source.size()); } public void setIssueCount(int issueCount) { if (this.issueCount == issueCount) return; this.issueCount = issueCount; this.setText(issueCountFormat.format(new Object[] {new Integer(issueCount)})); } public void listChanged(ListEvent<Issue> listChanges) { setIssueCount(listChanges.getSourceList().size()); } } /** Render the issues separator. */ public static class IssueSeparatorTableCell extends AbstractCellEditor implements TableCellRenderer, TableCellEditor, ActionListener { private final MessageFormat nameFormat = new MessageFormat("{0} ({1})"); /** the separator list to lock */ private final SeparatorList separatorList; private final JPanel panel = new JPanel(new BorderLayout()); private final JButton expandButton; private final JLabel nameLabel = new JLabel(); private SeparatorList.Separator<Issue> separator; public IssueSeparatorTableCell(SeparatorList separatorList) { this.separatorList = separatorList; this.expandButton = new JButton(EXPANDED_ICON); this.expandButton.setOpaque(false); this.expandButton.setBorder(EMPTY_TWO_PIXEL_BORDER); this.expandButton.setIcon(EXPANDED_ICON); this.expandButton.setContentAreaFilled(false); this.nameLabel.setFont(nameLabel.getFont().deriveFont(10.0f)); this.nameLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); this.expandButton.addActionListener(this); this.panel.setBackground(GLAZED_LISTS_LIGHT_BROWN); this.panel.add(expandButton, BorderLayout.WEST); this.panel.add(nameLabel, BorderLayout.CENTER); } public Component getTableCellEditorComponent( JTable table, Object value, boolean isSelected, int row, int column) { configure(value); return panel; } public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { configure(value); return panel; } public Object getCellEditorValue() { return this.separator; } private void configure(Object value) { this.separator = (SeparatorList.Separator<Issue>) value; Issue issue = separator.first(); if (issue == null) return; // handle 'late' rendering calls after this separator is invalid expandButton.setIcon(separator.getLimit() == 0 ? EXPANDED_ICON : COLLAPSED_ICON); nameLabel.setText( nameFormat.format(new Object[] {issue.getSubcomponent(), new Integer(separator.size())})); } public void actionPerformed(ActionEvent e) { final Lock writeLock = separatorList.getReadWriteLock().writeLock(); writeLock.lock(); boolean collapsed; try { collapsed = separator.getLimit() == 0; separator.setLimit(collapsed ? Integer.MAX_VALUE : 0); } finally { writeLock.unlock(); } expandButton.setIcon(collapsed ? COLLAPSED_ICON : EXPANDED_ICON); } } /** A button that shows icons in one of three states for up, over and down. */ public static class IconButton extends JButton implements MouseListener { private static final Border emptyBorder = BorderFactory.createEmptyBorder(); private static final int UP = 0; private static final int OVER = 1; private static final int DOWN = 2; private int state = -1; private Icon[] icons; public IconButton(Icon[] icons) { this.icons = icons; super.setBorder(emptyBorder); setState(UP); setContentAreaFilled(false); addMouseListener(this); } public Icon[] getIcons() { return icons; } public void setIcons(Icon[] icons) { this.icons = icons; super.setIcon(this.icons[state]); } private void setState(int state) { this.state = state; super.setIcon(icons[state]); } public void mouseClicked(MouseEvent e) { // do nothing } public void mousePressed(MouseEvent e) { setState(DOWN); } public void mouseReleased(MouseEvent e) { setState(OVER); } public void mouseEntered(MouseEvent e) { setState(OVER); } public void mouseExited(MouseEvent e) { setState(UP); } } }