1 /*
   2  * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.tools.jconsole;
  27 
  28 import java.util.List;
  29 import java.awt.*;
  30 import java.awt.event.*;
  31 import java.util.*;
  32 
  33 import javax.swing.*;
  34 import javax.swing.border.*;
  35 import javax.swing.event.*;
  36 import javax.swing.plaf.basic.BasicRadioButtonUI;
  37 import javax.swing.table.*;
  38 
  39 import sun.tools.jconsole.resources.Messages;
  40 
  41 
  42 import static java.awt.BorderLayout.*;
  43 import static javax.swing.ListSelectionModel.*;
  44 import static sun.tools.jconsole.Utilities.*;
  45 
  46 @SuppressWarnings("serial")
  47 public class ConnectDialog extends InternalDialog
  48                 implements DocumentListener, FocusListener,
  49                            ItemListener, ListSelectionListener, KeyListener {
  50 
  51     private static final int COL_NAME = 0;
  52     private static final int COL_PID  = 1;
  53 
  54 
  55     JConsole jConsole;
  56     JTextField userNameTF, passwordTF;
  57     JRadioButton localRadioButton, remoteRadioButton;
  58     JLabel localMessageLabel, remoteMessageLabel;
  59     JTextField remoteTF;
  60     JButton connectButton, cancelButton;
  61     JPanel radioButtonPanel;
  62 
  63     private Icon mastheadIcon =
  64         new MastheadIcon(Messages.CONNECT_DIALOG_MASTHEAD_TITLE);
  65     private Color hintTextColor, disabledTableCellColor;
  66 
  67     // The table of managed VM (local process)
  68     JTable vmTable;
  69     ManagedVmTableModel vmModel = null;
  70 
  71     JScrollPane localTableScrollPane = null;
  72 
  73     private Action connectAction, cancelAction;
  74 
  75 
  76     public ConnectDialog(JConsole jConsole) {
  77         super(jConsole, Messages.CONNECT_DIALOG_TITLE, true);
  78 
  79         this.jConsole = jConsole;
  80         setAccessibleDescription(this,
  81                 Messages.CONNECT_DIALOG_ACCESSIBLE_DESCRIPTION);
  82         setDefaultCloseOperation(HIDE_ON_CLOSE);
  83         setResizable(false);
  84         Container cp = (JComponent)getContentPane();
  85 
  86         radioButtonPanel = new JPanel(new BorderLayout(0, 12));
  87         radioButtonPanel.setBorder(new EmptyBorder(6, 12, 12, 12));
  88         ButtonGroup radioButtonGroup = new ButtonGroup();
  89         JPanel bottomPanel = new JPanel(new BorderLayout());
  90 
  91         statusBar = new JLabel(" ", JLabel.CENTER);
  92         setAccessibleName(statusBar,
  93                 Messages.CONNECT_DIALOG_STATUS_BAR_ACCESSIBLE_NAME);
  94 
  95         Font normalLabelFont = statusBar.getFont();
  96         Font boldLabelFont = normalLabelFont.deriveFont(Font.BOLD);
  97         Font smallLabelFont = normalLabelFont.deriveFont(normalLabelFont.getSize2D() - 1);
  98 
  99         JLabel mastheadLabel = new JLabel(mastheadIcon);
 100         setAccessibleName(mastheadLabel,
 101                 Messages.CONNECT_DIALOG_MASTHEAD_ACCESSIBLE_NAME);
 102 
 103         cp.add(mastheadLabel, NORTH);
 104         cp.add(radioButtonPanel, CENTER);
 105         cp.add(bottomPanel, SOUTH);
 106 
 107         createActions();
 108 
 109         remoteTF = new JTextField();
 110         remoteTF.addActionListener(connectAction);
 111         remoteTF.getDocument().addDocumentListener(this);
 112         remoteTF.addFocusListener(this);
 113         remoteTF.setPreferredSize(remoteTF.getPreferredSize());
 114         setAccessibleName(remoteTF,
 115                 Messages.REMOTE_PROCESS_TEXT_FIELD_ACCESSIBLE_NAME);
 116 
 117         //
 118         // If the VM supports the local attach mechanism (is: Sun
 119         // implementation) then the Local Process panel is created.
 120         //
 121         if (JConsole.isLocalAttachAvailable()) {
 122             vmModel = new ManagedVmTableModel();
 123             vmTable = new LocalTabJTable(vmModel);
 124             vmTable.setSelectionMode(SINGLE_SELECTION);
 125             vmTable.setPreferredScrollableViewportSize(new Dimension(400, 250));
 126             vmTable.setColumnSelectionAllowed(false);
 127             vmTable.addFocusListener(this);
 128             vmTable.getSelectionModel().addListSelectionListener(this);
 129 
 130             TableColumnModel columnModel = vmTable.getColumnModel();
 131 
 132             TableColumn pidColumn = columnModel.getColumn(COL_PID);
 133             pidColumn.setMaxWidth(getLabelWidth("9999999"));
 134             pidColumn.setResizable(false);
 135 
 136             TableColumn cmdLineColumn = columnModel.getColumn(COL_NAME);
 137             cmdLineColumn.setResizable(false);
 138 
 139             localRadioButton = new JRadioButton(Messages.LOCAL_PROCESS_COLON);
 140             localRadioButton.setMnemonic(Resources.getMnemonicInt(Messages.LOCAL_PROCESS_COLON));
 141             localRadioButton.setFont(boldLabelFont);
 142             localRadioButton.addItemListener(this);
 143             radioButtonGroup.add(localRadioButton);
 144 
 145             JPanel localPanel = new JPanel(new BorderLayout());
 146 
 147             JPanel localTablePanel = new JPanel(new BorderLayout());
 148 
 149             radioButtonPanel.add(localPanel, NORTH);
 150 
 151             localPanel.add(localRadioButton, NORTH);
 152             localPanel.add(new Padder(localRadioButton), LINE_START);
 153             localPanel.add(localTablePanel, CENTER);
 154 
 155             localTableScrollPane = new JScrollPane(vmTable);
 156 
 157             localTablePanel.add(localTableScrollPane, NORTH);
 158 
 159             localMessageLabel = new JLabel(" ");
 160             localMessageLabel.setFont(smallLabelFont);
 161             localMessageLabel.setForeground(hintTextColor);
 162             localTablePanel.add(localMessageLabel, SOUTH);
 163         }
 164 
 165         remoteRadioButton = new JRadioButton(Messages.REMOTE_PROCESS_COLON);
 166         remoteRadioButton.setMnemonic(Resources.getMnemonicInt(Messages.REMOTE_PROCESS_COLON));
 167         remoteRadioButton.setFont(boldLabelFont);
 168         radioButtonGroup.add(remoteRadioButton);
 169 
 170         JPanel remotePanel = new JPanel(new BorderLayout());
 171         if (localRadioButton != null) {
 172             remotePanel.add(remoteRadioButton, NORTH);
 173             remotePanel.add(new Padder(remoteRadioButton), LINE_START);
 174 
 175             Action nextRadioButtonAction =
 176                 new AbstractAction("nextRadioButton") {
 177                     public void actionPerformed(ActionEvent ev) {
 178                         JRadioButton rb =
 179                             (ev.getSource() == localRadioButton) ? remoteRadioButton
 180                                                                  : localRadioButton;
 181                         rb.doClick();
 182                         rb.requestFocus();
 183                     }
 184                 };
 185 
 186             localRadioButton.getActionMap().put("nextRadioButton", nextRadioButtonAction);
 187             remoteRadioButton.getActionMap().put("nextRadioButton", nextRadioButtonAction);
 188 
 189             localRadioButton.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
 190                                                "nextRadioButton");
 191             remoteRadioButton.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),
 192                                                 "nextRadioButton");
 193         } else {
 194             JLabel remoteLabel = new JLabel(remoteRadioButton.getText());
 195             remoteLabel.setFont(boldLabelFont);
 196             remotePanel.add(remoteLabel, NORTH);
 197         }
 198         radioButtonPanel.add(remotePanel, SOUTH);
 199 
 200         JPanel remoteTFPanel = new JPanel(new BorderLayout());
 201         remotePanel.add(remoteTFPanel, CENTER);
 202 
 203         remoteTFPanel.add(remoteTF, NORTH);
 204 
 205         remoteMessageLabel = new JLabel("<html>" + Messages.REMOTE_TF_USAGE);
 206         remoteMessageLabel.setFont(smallLabelFont);
 207         remoteMessageLabel.setForeground(hintTextColor);
 208         remoteTFPanel.add(remoteMessageLabel, CENTER);
 209 
 210         JPanel userPwdPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
 211         userPwdPanel.setBorder(new EmptyBorder(12, 0, 0, 0)); // top padding
 212 
 213         int tfWidth = JConsole.IS_WIN ? 12 : 8;
 214 
 215         userNameTF = new JTextField(tfWidth);
 216         userNameTF.addActionListener(connectAction);
 217         userNameTF.getDocument().addDocumentListener(this);
 218         userNameTF.addFocusListener(this);
 219         setAccessibleName(userNameTF,
 220                 Messages.USERNAME_ACCESSIBLE_NAME);
 221         String labelKey = Messages.USERNAME_COLON_;
 222         LabeledComponent lc;
 223         lc = new LabeledComponent(labelKey,
 224                                   Resources.getMnemonicInt(labelKey),
 225                                   userNameTF);
 226         lc.label.setFont(boldLabelFont);
 227         userPwdPanel.add(lc);
 228 
 229         passwordTF = new JPasswordField(tfWidth);
 230         // Heights differ, so fix here
 231         passwordTF.setPreferredSize(userNameTF.getPreferredSize());
 232         passwordTF.addActionListener(connectAction);
 233         passwordTF.getDocument().addDocumentListener(this);
 234         passwordTF.addFocusListener(this);
 235         setAccessibleName(passwordTF,
 236                 Messages.PASSWORD_ACCESSIBLE_NAME);
 237         labelKey = "Password: ";
 238         lc = new LabeledComponent(labelKey,
 239                                   Resources.getMnemonicInt(labelKey),
 240                                   passwordTF);
 241         lc.setBorder(new EmptyBorder(0, 12, 0, 0)); // Left padding
 242         lc.label.setFont(boldLabelFont);
 243         userPwdPanel.add(lc);
 244 
 245         remoteTFPanel.add(userPwdPanel, SOUTH);
 246 
 247         String connectButtonToolTipText =
 248                 Messages.CONNECT_DIALOG_CONNECT_BUTTON_TOOLTIP;
 249         connectButton = new JButton(connectAction);
 250         connectButton.setToolTipText(connectButtonToolTipText);
 251 
 252         cancelButton = new JButton(cancelAction);
 253 
 254         JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
 255         buttonPanel.setBorder(new EmptyBorder(12, 12, 2, 12));
 256         if (JConsole.IS_GTK) {
 257             buttonPanel.add(cancelButton);
 258             buttonPanel.add(connectButton);
 259         } else {
 260             buttonPanel.add(connectButton);
 261             buttonPanel.add(cancelButton);
 262         }
 263         bottomPanel.add(buttonPanel, NORTH);
 264 
 265         bottomPanel.add(statusBar, SOUTH);
 266 
 267         updateButtonStates();
 268         Utilities.updateTransparency(this);
 269     }
 270 
 271     public void revalidate() {
 272         // Adjust some colors
 273         hintTextColor =
 274             ensureContrast(UIManager.getColor("Label.disabledForeground"),
 275                            UIManager.getColor("Panel.background"));
 276         disabledTableCellColor =
 277             ensureContrast(new Color(0x808080),
 278                            UIManager.getColor("Table.background"));
 279 
 280         if (remoteMessageLabel != null) {
 281             remoteMessageLabel.setForeground(hintTextColor);
 282             // Update html color setting
 283             String colorStr =
 284                 String.format("%06x", hintTextColor.getRGB() & 0xFFFFFF);
 285             remoteMessageLabel.setText("<html><font color=#" + colorStr + ">" +
 286                     Messages.REMOTE_TF_USAGE);
 287         }
 288         if (localMessageLabel != null) {
 289             localMessageLabel.setForeground(hintTextColor);
 290             // Update html color setting
 291             valueChanged(null);
 292         }
 293 
 294         super.revalidate();
 295     }
 296 
 297     private void createActions() {
 298         connectAction = new AbstractAction(Messages.CONNECT) {
 299             /* init */ {
 300                 putValue(Action.MNEMONIC_KEY, Resources.getMnemonicInt(Messages.CONNECT));
 301             }
 302 
 303             public void actionPerformed(ActionEvent ev) {
 304                 if (!isEnabled() || !isVisible()) {
 305                     return;
 306                 }
 307                 setVisible(false);
 308                 statusBar.setText("");
 309 
 310                 if (remoteRadioButton.isSelected()) {
 311                     String txt = remoteTF.getText().trim();
 312                     String userName = userNameTF.getText().trim();
 313                     userName = userName.equals("") ? null : userName;
 314                     String password = passwordTF.getText();
 315                     password = password.equals("") ? null : password;
 316                     try {
 317                         if (txt.startsWith(JConsole.ROOT_URL)) {
 318                             String url = txt;
 319                             jConsole.addUrl(url, userName, password, false);
 320                             remoteTF.setText(JConsole.ROOT_URL);
 321                             return;
 322                         } else {
 323                             String host = remoteTF.getText().trim();
 324                             String port = "0";
 325                             int index = host.lastIndexOf(":");
 326                             if (index >= 0) {
 327                                 port = host.substring(index + 1);
 328                                 host = host.substring(0, index);
 329                             }
 330                             if (host.length() > 0 && port.length() > 0) {
 331                                 int p = Integer.parseInt(port.trim());
 332                                 jConsole.addHost(host, p, userName, password);
 333                                 remoteTF.setText("");
 334                                 userNameTF.setText("");
 335                                 passwordTF.setText("");
 336                                 return;
 337                             }
 338                         }
 339                     } catch (Exception ex) {
 340                         statusBar.setText(ex.toString());
 341                     }
 342                     setVisible(true);
 343                 } else if (localRadioButton != null && localRadioButton.isSelected()) {
 344                     // Try to connect to selected VM. If a connection
 345                     // cannot be established for some reason (the process has
 346                     // terminated for example) then keep the dialog open showing
 347                     // the connect error.
 348                     //
 349                     int row = vmTable.getSelectedRow();
 350                     if (row >= 0) {
 351                         jConsole.addVmid(vmModel.vmAt(row));
 352                     }
 353                     refresh();
 354                 }
 355             }
 356         };
 357 
 358         cancelAction = new AbstractAction(Messages.CANCEL) {
 359             public void actionPerformed(ActionEvent ev) {
 360                 setVisible(false);
 361                 statusBar.setText("");
 362             }
 363         };
 364     }
 365 
 366 
 367     // a label used solely for calculating the width
 368     private static JLabel tmpLabel = new JLabel();
 369     public static int getLabelWidth(String text) {
 370         tmpLabel.setText(text);
 371         return (int) tmpLabel.getPreferredSize().getWidth() + 1;
 372     }
 373 
 374     private class LocalTabJTable extends JTable {
 375         ManagedVmTableModel vmModel;
 376         Border rendererBorder = new EmptyBorder(0, 6, 0, 6);
 377 
 378         public LocalTabJTable(ManagedVmTableModel model) {
 379             super(model);
 380             this.vmModel = model;
 381 
 382             // Remove vertical lines, expect for GTK L&F.
 383             // (because GTK doesn't show header dividers)
 384             if (!JConsole.IS_GTK) {
 385                 setShowVerticalLines(false);
 386                 setIntercellSpacing(new Dimension(0, 1));
 387             }
 388 
 389             // Double-click handler
 390             addMouseListener(new MouseAdapter() {
 391                 public void mouseClicked(MouseEvent evt) {
 392                     if (evt.getClickCount() == 2) {
 393                         connectButton.doClick();
 394                     }
 395                 }
 396             });
 397 
 398             // Enter should call default action
 399             getActionMap().put("connect", connectAction);
 400             InputMap inputMap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 401             inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "connect");
 402         }
 403 
 404         public String getToolTipText(MouseEvent e) {
 405             String tip = null;
 406             java.awt.Point p = e.getPoint();
 407             int rowIndex = rowAtPoint(p);
 408             int colIndex = columnAtPoint(p);
 409             int realColumnIndex = convertColumnIndexToModel(colIndex);
 410 
 411             if (realColumnIndex == COL_NAME) {
 412                 LocalVirtualMachine vmd = vmModel.vmAt(rowIndex);
 413                 tip = vmd.toString();
 414             }
 415             return tip;
 416         }
 417 
 418         public TableCellRenderer getCellRenderer(int row, int column) {
 419             return new DefaultTableCellRenderer() {
 420                 public Component getTableCellRendererComponent(JTable table,
 421                                                                Object value,
 422                                                                boolean isSelected,
 423                                                                boolean hasFocus,
 424                                                                int row,
 425                                                                int column) {
 426                     Component comp =
 427                         super.getTableCellRendererComponent(table, value, isSelected,
 428                                                             hasFocus, row, column);
 429 
 430                     if (!isSelected) {
 431                         LocalVirtualMachine lvm = vmModel.vmAt(row);
 432                         if (!lvm.isManageable() && !lvm.isAttachable()) {
 433                             comp.setForeground(disabledTableCellColor);
 434                         }
 435                     }
 436 
 437                     if (comp instanceof JLabel) {
 438                         JLabel label = (JLabel)comp;
 439                         label.setBorder(rendererBorder);
 440 
 441                         if (value instanceof Integer) {
 442                             label.setHorizontalAlignment(JLabel.RIGHT);
 443                         }
 444                     }
 445 
 446                     return comp;
 447                 }
 448             };
 449         }
 450     }
 451 
 452     public void setConnectionParameters(String url,
 453                                         String host,
 454                                         int port,
 455                                         String userName,
 456                                         String password,
 457                                         String msg) {
 458         if ((url != null && url.length() > 0) ||
 459             (host != null && host.length() > 0 && port > 0)) {
 460 
 461             remoteRadioButton.setSelected(true);
 462             if (url != null && url.length() > 0) {
 463                 remoteTF.setText(url);
 464             } else {
 465                 remoteTF.setText(host+":"+port);
 466             }
 467             userNameTF.setText((userName != null) ? userName : "");
 468             passwordTF.setText((password != null) ? password : "");
 469 
 470             statusBar.setText((msg != null) ? msg : "");
 471             if (getPreferredSize().width > getWidth()) {
 472                 pack();
 473             }
 474             remoteTF.requestFocus();
 475             remoteTF.selectAll();
 476         }
 477     }
 478 
 479 
 480     public void itemStateChanged(ItemEvent ev) {
 481         if (!localRadioButton.isSelected()) {
 482             vmTable.getSelectionModel().clearSelection();
 483         }
 484         updateButtonStates();
 485     }
 486 
 487     private void updateButtonStates() {
 488         boolean connectEnabled = false;
 489 
 490         if (remoteRadioButton.isSelected()) {
 491             connectEnabled = JConsole.isValidRemoteString(remoteTF.getText());
 492         } else if (localRadioButton != null && localRadioButton.isSelected()) {
 493             int row = vmTable.getSelectedRow();
 494             if (row >= 0) {
 495                 LocalVirtualMachine lvm = vmModel.vmAt(row);
 496                 connectEnabled = (lvm.isManageable() || lvm.isAttachable());
 497             }
 498         }
 499 
 500         connectAction.setEnabled(connectEnabled);
 501     }
 502 
 503     public void insertUpdate(DocumentEvent e) {
 504         updateButtonStates();
 505     }
 506 
 507     public void removeUpdate(DocumentEvent e) {
 508         updateButtonStates();
 509     }
 510 
 511     public void changedUpdate(DocumentEvent e) {
 512         updateButtonStates();
 513     }
 514 
 515     public void focusGained(FocusEvent e) {
 516         Object source = e.getSource();
 517         Component opposite = e.getOppositeComponent();
 518 
 519         if (!e.isTemporary() &&
 520             source instanceof JTextField &&
 521             opposite instanceof JComponent &&
 522             SwingUtilities.getRootPane(opposite) == getRootPane()) {
 523 
 524             ((JTextField)source).selectAll();
 525         }
 526 
 527         if (source == remoteTF) {
 528             remoteRadioButton.setSelected(true);
 529         } else if (source == vmTable) {
 530             localRadioButton.setSelected(true);
 531             if (vmModel.getRowCount() == 1) {
 532                 // if there's only one process then select the row
 533                 vmTable.setRowSelectionInterval(0, 0);
 534             }
 535         }
 536         updateButtonStates();
 537     }
 538 
 539     public void focusLost(FocusEvent e) {
 540     }
 541 
 542     public void keyTyped(KeyEvent e) {
 543         char c = e.getKeyChar();
 544         if (c == KeyEvent.VK_ESCAPE) {
 545             setVisible(false);
 546         } else if (!(Character.isDigit(c) ||
 547                      c == KeyEvent.VK_BACK_SPACE ||
 548                      c == KeyEvent.VK_DELETE)) {
 549             getToolkit().beep();
 550             e.consume();
 551         }
 552     }
 553 
 554     public void setVisible(boolean b) {
 555         boolean wasVisible = isVisible();
 556         super.setVisible(b);
 557         if (b && !wasVisible) {
 558             SwingUtilities.invokeLater(new Runnable() {
 559                 public void run() {
 560                     if (remoteRadioButton.isSelected()) {
 561                         remoteTF.requestFocus();
 562                         remoteTF.selectAll();
 563                     }
 564                 }
 565             });
 566         }
 567     }
 568 
 569     public void keyPressed(KeyEvent e) {
 570     }
 571 
 572     public void keyReleased(KeyEvent e) {
 573     }
 574 
 575 
 576     // ListSelectionListener interface
 577     public void valueChanged(ListSelectionEvent e) {
 578         updateButtonStates();
 579         String labelText = " "; // Non-empty to reserve vertical space
 580         int row = vmTable.getSelectedRow();
 581         if (row >= 0) {
 582             LocalVirtualMachine lvm = vmModel.vmAt(row);
 583             if (!lvm.isManageable()) {
 584                 if (lvm.isAttachable()) {
 585                     labelText = Messages.MANAGEMENT_WILL_BE_ENABLED;
 586                 } else {
 587                     labelText = Messages.MANAGEMENT_NOT_ENABLED;
 588                 }
 589             }
 590         }
 591         String colorStr =
 592             String.format("%06x", hintTextColor.getRGB() & 0xFFFFFF);
 593         localMessageLabel.setText("<html><font color=#" + colorStr + ">" + labelText);
 594     }
 595     // ----
 596 
 597 
 598     // Refresh the list of managed VMs
 599     public void refresh() {
 600         if (vmModel != null) {
 601             // Remember selection
 602             LocalVirtualMachine selected = null;
 603             int row = vmTable.getSelectedRow();
 604             if (row >= 0) {
 605                 selected = vmModel.vmAt(row);
 606             }
 607 
 608             vmModel.refresh();
 609 
 610             int selectRow = -1;
 611             int n = vmModel.getRowCount();
 612             if (selected != null) {
 613                 for (int i = 0; i < n; i++) {
 614                     LocalVirtualMachine lvm = vmModel.vmAt(i);
 615                     if (selected.vmid() == lvm.vmid() &&
 616                         selected.toString().equals(lvm.toString())) {
 617 
 618                         selectRow = i;
 619                         break;
 620                     }
 621                 }
 622             }
 623             if (selectRow > -1) {
 624                 vmTable.setRowSelectionInterval(selectRow, selectRow);
 625             } else {
 626                 vmTable.getSelectionModel().clearSelection();
 627             }
 628 
 629             Dimension dim = vmTable.getPreferredSize();
 630 
 631             // Tricky. Reduce height by one to avoid double line at bottom,
 632             // but that causes a scroll bar to appear, so remove it.
 633             dim.height = Math.min(dim.height-1, 100);
 634             localTableScrollPane.setVerticalScrollBarPolicy((dim.height < 100)
 635                                                 ? JScrollPane.VERTICAL_SCROLLBAR_NEVER
 636                                                 : JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
 637             localTableScrollPane.getViewport().setMinimumSize(dim);
 638             localTableScrollPane.getViewport().setPreferredSize(dim);
 639         }
 640         pack();
 641         setLocationRelativeTo(jConsole);
 642     }
 643 
 644     // Represents the list of managed VMs as a tabular data model.
 645     private static class ManagedVmTableModel extends AbstractTableModel {
 646         private static String[] columnNames = {
 647             Messages.COLUMN_NAME,
 648             Messages.COLUMN_PID,
 649         };
 650 
 651         private List<LocalVirtualMachine> vmList;
 652 
 653         public int getColumnCount() {
 654             return columnNames.length;
 655         }
 656 
 657         public String getColumnName(int col) {
 658             return columnNames[col];
 659         }
 660 
 661         public synchronized int getRowCount() {
 662             return vmList.size();
 663         }
 664 
 665         public synchronized Object getValueAt(int row, int col) {
 666             assert col >= 0 && col <= columnNames.length;
 667             LocalVirtualMachine vm = vmList.get(row);
 668             switch (col) {
 669                 case COL_NAME: return vm.displayName();
 670                 case COL_PID:  return vm.vmid();
 671                 default: return null;
 672             }
 673         }
 674 
 675         public Class<?> getColumnClass(int column) {
 676             switch (column) {
 677                 case COL_NAME: return String.class;
 678                 case COL_PID:  return Integer.class;
 679                 default: return super.getColumnClass(column);
 680             }
 681         }
 682 
 683         public ManagedVmTableModel() {
 684             refresh();
 685         }
 686 
 687 
 688         public synchronized LocalVirtualMachine vmAt(int pos) {
 689             return vmList.get(pos);
 690         }
 691 
 692         public synchronized void refresh() {
 693             Map<Integer, LocalVirtualMachine> map =
 694                 LocalVirtualMachine.getAllVirtualMachines();
 695             vmList = new ArrayList<LocalVirtualMachine>();
 696             vmList.addAll(map.values());
 697 
 698             // data has changed
 699             fireTableDataChanged();
 700         }
 701     }
 702 
 703     // A blank component that takes up as much space as the
 704     // button part of a JRadioButton.
 705     private static class Padder extends JPanel {
 706         JRadioButton radioButton;
 707 
 708         Padder(JRadioButton radioButton) {
 709             this.radioButton = radioButton;
 710 
 711             setAccessibleName(this, Messages.BLANK);
 712         }
 713 
 714         public Dimension getPreferredSize() {
 715             Rectangle r = getTextRectangle(radioButton);
 716             int w = (r != null && r.x > 8) ? r.x : 22;
 717 
 718             return new Dimension(w, 0);
 719         }
 720 
 721         private static Rectangle getTextRectangle(AbstractButton button) {
 722             String text = button.getText();
 723             Icon icon = (button.isEnabled()) ? button.getIcon() : button.getDisabledIcon();
 724 
 725             if (icon == null && button.getUI() instanceof BasicRadioButtonUI) {
 726                 icon = ((BasicRadioButtonUI)button.getUI()).getDefaultIcon();
 727             }
 728 
 729             if ((icon == null) && (text == null)) {
 730                 return null;
 731             }
 732 
 733             Rectangle paintIconR = new Rectangle();
 734             Rectangle paintTextR = new Rectangle();
 735             Rectangle paintViewR = new Rectangle();
 736             Insets paintViewInsets = new Insets(0, 0, 0, 0);
 737 
 738             paintViewInsets = button.getInsets(paintViewInsets);
 739             paintViewR.x = paintViewInsets.left;
 740             paintViewR.y = paintViewInsets.top;
 741             paintViewR.width = button.getWidth() - (paintViewInsets.left + paintViewInsets.right);
 742             paintViewR.height = button.getHeight() - (paintViewInsets.top + paintViewInsets.bottom);
 743 
 744             Graphics g = button.getGraphics();
 745             if (g == null) {
 746                 return null;
 747             }
 748                 SwingUtilities.layoutCompoundLabel(button,
 749                                                    g.getFontMetrics(),
 750                                                    text,
 751                                                    icon,
 752                                                    button.getVerticalAlignment(),
 753                                                    button.getHorizontalAlignment(),
 754                                                    button.getVerticalTextPosition(),
 755                                                    button.getHorizontalTextPosition(),
 756                                                    paintViewR,
 757                                                    paintIconR,
 758                                                    paintTextR,
 759                                                    button.getIconTextGap());
 760 
 761             return paintTextR;
 762         }
 763     }
 764 
 765 }