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