1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * ident        "%Z%%M% %I%     %E% SMI"
  24  *
  25  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  26  * Use is subject to license terms.
  27  */
  28 package com.sun.dhcpmgr.client;
  29 
  30 import javax.swing.*;
  31 import javax.swing.event.*;
  32 import javax.swing.table.*;
  33 import java.awt.*;
  34 import java.awt.event.*;
  35 import java.util.*;
  36 import java.text.*;
  37 import java.net.*;
  38 
  39 import com.sun.dhcpmgr.ui.*;
  40 import com.sun.dhcpmgr.server.*;
  41 import com.sun.dhcpmgr.data.*;
  42 import com.sun.dhcpmgr.bridge.BridgeException;
  43 import com.sun.dhcpmgr.bridge.NoEntryException;
  44 
  45 /**
  46  * Address View displays the networks currently under DHCP management, and
  47  * as a network is selected from the list its addresses are displayed.
  48  */
  49 public class AddressView implements View {
  50     private JPanel displayPanel;
  51     protected static AutosizingTable addressTable;
  52     private JScrollPane addressPane;
  53     private boolean firstActivation = true;
  54     private NetworkListModel networkListModel;
  55     private JList networkList;
  56     protected static AddressTableModel addressTableModel = null;
  57     private TableSorter sortedTableModel;
  58     private JCheckBoxMenuItem showGrid;
  59     private JCheckBoxMenuItem showAddresses;
  60     private JMenuItem addAddrs;
  61     private JMenuItem releaseAddrs;
  62     private JMenuItem addNet;
  63     private JMenuItem deleteNets;
  64     private JMenuItem addressHelp;
  65     private Vector[] menuItems;
  66     private Frame myFrame;
  67     private Vector selectionListeners = new Vector();
  68     private int sortModelIndex = -1;
  69     private static final String NO_NETWORKS =
  70         ResourceStrings.getString("no_networks");
  71 
  72     // Model class for the network list
  73     class NetworkListModel extends AbstractListModel {
  74         private Object currentValue;
  75         private Network data[] = null;
  76 
  77         public void load() {
  78             try {
  79                 MainFrame.setStatusText(
  80                     ResourceStrings.getString("loading_networks"));
  81                 data = DataManager.get().getNetworks(true);
  82             } catch (Throwable e) {
  83                 e.printStackTrace();
  84             } finally {
  85                 int len = 0;
  86                 if (data != null) {
  87                     len = data.length;
  88                 }
  89                 MainFrame.setStatusText(
  90                     MessageFormat.format(
  91                     ResourceStrings.getString("networks_loaded"), len));
  92             }
  93         }
  94 
  95         public void reload() {
  96             load();
  97             fireContentsChanged(this, -1, -1);
  98         }
  99 
 100         public int getSize() {
 101             if (data == null) {
 102                 load();
 103             }
 104             if (data == null) {
 105                 return 0;
 106             } else {
 107                 return data.length;
 108             }
 109         }
 110 
 111         public Object getElementAt(int index) {
 112             if (data == null) {
 113                 load();
 114             }
 115             if (data == null || index >= data.length) {
 116                 return "";
 117             } else {
 118                 return data[index].toString();
 119             }
 120         }
 121 
 122         public Network getNetworkAt(int index) {
 123             if (data == null || index >= data.length) {
 124                 return null;
 125             } else {
 126                 return data[index];
 127             }
 128         }
 129     }
 130 
 131     // Container class for the address data
 132     class AddressTableModel extends AbstractTableModel {
 133         private DhcpClientRecord [] data;
 134         private String network;
 135         private boolean showAddresses;
 136         private boolean firstLoad;
 137 
 138         public AddressTableModel() {
 139             data = null;
 140             network = "";
 141             showAddresses = false;
 142             firstLoad = true;
 143         }
 144 
 145         public void load(String network) {
 146             data = null;
 147             fireTableDataChanged();
 148             if (network.length() == 0) {
 149                 // No network number supplied, so can't load
 150                 return;
 151             }
 152             this.network = network;
 153 
 154             // Update the status line
 155             Object [] objs = {network};
 156             String s = MessageFormat.format(
 157                 ResourceStrings.getString("loading_addresses"), objs);
 158             MainFrame.setStatusText(s);
 159 
 160             // Kick off background loading of addresses
 161             AddressLoader loader = new AddressLoader();
 162 
 163         }
 164 
 165         // Loading is done, re-sort and tell the view to repaint
 166         protected void doneLoading() {
 167             sortedTableModel.reallocateIndexes();
 168             if (firstLoad) {
 169                 sortedTableModel.sortByColumn(0);
 170                 firstLoad = false;
 171             }
 172             fireTableDataChanged();
 173         }
 174 
 175         protected String getNetwork() {
 176             return network;
 177         }
 178 
 179         protected void setData(DhcpClientRecord [] newdata) {
 180             data = newdata;
 181         }
 182 
 183         public void setShowAddresses(boolean state) {
 184             showAddresses = state;
 185             fireTableStructureChanged();
 186             sortedTableModel.sortByColumn(sortModelIndex);
 187         }
 188 
 189         public int getRowCount() {
 190             if (data == null) {
 191                 return 0;
 192             } else {
 193                 return data.length;
 194             }
 195         }
 196 
 197         public int getColumnCount() {
 198             return 7;
 199         }
 200 
 201         public Object getValueAt(int row, int column) {
 202             switch (column) {
 203             case 0:
 204                 if (showAddresses) {
 205                     return data[row].getClientIP();
 206                 } else {
 207                     return data[row].getClientName();
 208                 }
 209             case 1:
 210                 if (data[row].isUnusable()) {
 211                     return ResourceStrings.getString("unusable");
 212                 } else if (data[row].isBootp()) {
 213                     return ResourceStrings.getString("bootp");
 214                 } else if (data[row].isManual()) {
 215                     return ResourceStrings.getString("manual");
 216                 } else if (data[row].isPermanent()) {
 217                     return ResourceStrings.getString("permanent");
 218                 } else {
 219                     return ResourceStrings.getString("dynamic");
 220                 }
 221             case 2:
 222                 return data[row].getExpiration();
 223             case 3:
 224                 if (showAddresses) {
 225                     return data[row].getServerIP();
 226                 } else {
 227                     return data[row].getServerName();
 228                 }
 229             case 4:
 230                 return data[row].getMacro();
 231             case 5:
 232                 return data[row].getClientId();
 233             case 6:
 234                 return data[row].getComment();
 235             default:
 236                 return null;
 237             }
 238         }
 239 
 240         public Class getColumnClass(int column) {
 241             switch (column) {
 242             case 0:
 243             case 3:
 244                 if (showAddresses) {
 245                     return IPAddress.class;
 246                 } else {
 247                     return String.class;
 248                 }
 249             case 2:
 250                 return Date.class;
 251             case 1:
 252             case 4:
 253             case 5:
 254             case 6:
 255                 return String.class;
 256             default:
 257                 return super.getColumnClass(column);
 258             }
 259         }
 260 
 261         public String getColumnName(int column) {
 262             switch (column) {
 263             case 0:
 264                 if (showAddresses) {
 265                     return ResourceStrings.getString("address_column");
 266                 } else {
 267                     return ResourceStrings.getString("client_name_column");
 268                 }
 269             case 1:
 270                 return ResourceStrings.getString("flags_column");
 271             case 2:
 272                 return ResourceStrings.getString("expires_column");
 273             case 3:
 274                 return ResourceStrings.getString("server_column");
 275             case 4:
 276                 return ResourceStrings.getString("macro_column");
 277             case 5:
 278                 return ResourceStrings.getString("client_column");
 279             case 6:
 280                 return ResourceStrings.getString("comment_column");
 281             default:
 282                 return super.getColumnName(column);
 283             }
 284         }
 285 
 286         protected DhcpClientRecord getClientAt(int row) {
 287             return data[row];
 288         }
 289     }
 290 
 291     // Background loader for addresses.
 292     class AddressLoader extends com.sun.dhcpmgr.ui.SwingWorker {
 293         public Object construct() {
 294             try {
 295                 String net = addressTableModel.getNetwork();
 296                 return DataManager.get().getClients(net, true);
 297             } catch (final BridgeException e) {
 298                 // Since we're in a background thread, ask Swing to run ASAP.
 299                 SwingUtilities.invokeLater(new Runnable() {
 300                     Object [] args = new Object[] { e.getMessage() };
 301                     public void run() {
 302                         MessageFormat form = new MessageFormat(
 303                             ResourceStrings.getString("error_loading_addrs"));
 304                         JOptionPane.showMessageDialog(null, form.format(args),
 305                             ResourceStrings.getString("server_error_title"),
 306                             JOptionPane.ERROR_MESSAGE);
 307                     }
 308                 });
 309             }
 310             return null;
 311         }
 312 
 313         public void finished() {
 314             addressTableModel.setData((DhcpClientRecord [])get());
 315             addressTableModel.doneLoading();
 316             MainFrame.setStatusText(
 317                 MessageFormat.format(
 318                 ResourceStrings.getString("address_status_message"),
 319                 addressTableModel.getRowCount()));
 320             addressTable.clearSelection();
 321         }
 322     }
 323 
 324     // Renderer class used to make unusable addresses bold in the display
 325     class AddressTableCellRenderer extends ExtendedCellRenderer {
 326         public Component getTableCellRendererComponent(JTable table,
 327                 Object value, boolean isSelected, boolean hasFocus, int row,
 328                 int column) {
 329             Component c = super.getTableCellRendererComponent(table, value,
 330                 isSelected, hasFocus, row, column);
 331             int modelRow = sortedTableModel.mapRowAt(row);
 332             if (modelRow != -1) {
 333                 if (addressTableModel.getClientAt(modelRow).isUnusable()) {
 334                     Font f = c.getFont();
 335                     c.setFont(new Font(f.getName(), Font.BOLD, f.getSize()));
 336                 }
 337             }
 338             return c;
 339         }
 340     }
 341 
 342     // Recipient of update messages sent when the editing dialogs exit
 343     class DialogListener implements ActionListener {
 344         public void actionPerformed(ActionEvent e) {
 345             // Don't reload if cancel happened
 346             if (!e.getActionCommand().equals(DialogActions.CANCEL)) {
 347                 AddressView.this.reload();
 348             }
 349         }
 350     }
 351 
 352     public AddressView() {
 353         displayPanel = new JPanel(new BorderLayout());
 354 
 355         // Create network selection list, tie it to table
 356         networkListModel = new NetworkListModel();
 357         networkList = new JList(networkListModel);
 358         networkList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
 359         networkList.addListSelectionListener(new ListSelectionListener() {
 360             public void valueChanged(ListSelectionEvent e) {
 361                 // Ignore all but the last in a series of these events
 362                 if (e.getValueIsAdjusting()) {
 363                     return;
 364                 }
 365                 String net = "";
 366                 int index = networkList.getSelectedIndex();
 367                 if (index != -1) {
 368                     net = (String)networkListModel.getElementAt(
 369                         networkList.getSelectedIndex());
 370                 }
 371                 if (net.length() == 0) {
 372                     // No networks are selected; disable menu items
 373                     deleteNets.setEnabled(false);
 374                     addAddrs.setEnabled(false);
 375                     showAddresses.setEnabled(false);
 376                     showGrid.setEnabled(false);
 377                 } else {
 378                     deleteNets.setEnabled(true);
 379                     addAddrs.setEnabled(true);
 380                     showAddresses.setEnabled(true);
 381                     showGrid.setEnabled(true);
 382                 }
 383                 addressTableModel.load(net);
 384             }
 385         });
 386 
 387         // Use a prototype value as a performance enhancement
 388         networkList.setPrototypeCellValue("222.222.222.222");
 389         JScrollPane networkPane = new JScrollPane(networkList);
 390         JPanel networkPanel = new JPanel(new BorderLayout());
 391         networkPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
 392 
 393         Mnemonic mnNetwork = new Mnemonic(ResourceStrings.getString("network"));
 394         JLabel nwLbl = new JLabel(mnNetwork.getString());
 395         nwLbl.setLabelFor(networkPanel);
 396         nwLbl.setToolTipText(mnNetwork.getString());
 397         networkPanel.add(nwLbl, BorderLayout.NORTH);
 398         nwLbl.setDisplayedMnemonic(mnNetwork.getMnemonic());
 399 
 400         networkPanel.add(networkPane, BorderLayout.CENTER);
 401         displayPanel.add(networkPanel, BorderLayout.WEST);
 402 
 403         // Create table to display in data area
 404         addressTableModel = new AddressTableModel();
 405         sortedTableModel = new TableSorter(addressTableModel);
 406         addressTable = new AutosizingTable(sortedTableModel);
 407         sortedTableModel.addMouseListenerToHeaderInTable(addressTable);
 408         addressTable.getTableHeader().setReorderingAllowed(true);
 409         addressTable.getTableHeader().setResizingAllowed(true);
 410         addressTable.setSelectionMode(
 411             ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
 412 
 413         sortedTableModel.addActionListener(new ActionListener() {
 414             private SortedHeaderRenderer sortedRenderer =
 415                 new SortedHeaderRenderer(addressTable);
 416             private TableCellRenderer savedRenderer;
 417             public void actionPerformed(ActionEvent e) {
 418                 // Clear the selection when sorting is changed
 419                 addressTable.clearSelection();
 420                 /*
 421                  * Change the header rendering to show which column is
 422                  * being used for sorting of the data.
 423                  */
 424                 int modelIndex = Integer.parseInt(e.getActionCommand());
 425                 int viewIndex =
 426                     addressTable.convertColumnIndexToView(modelIndex);
 427                 if (sortModelIndex != -1) {
 428                     int sortViewIndex =
 429                         addressTable.convertColumnIndexToView(sortModelIndex);
 430                     addressTable.getColumnModel().getColumn(
 431                         sortViewIndex).setHeaderRenderer(savedRenderer);
 432                 }
 433                 /*
 434                  * Save the column currently being sorted so we can restore
 435                  * the renderer later.  We save model columns rather than
 436                  * view columns because model columns are invariant while
 437                  * view columns can be reordered with confusion resulting.
 438                  */
 439                 TableColumn c =
 440                     addressTable.getColumnModel().getColumn(viewIndex);
 441                 savedRenderer = c.getHeaderRenderer();
 442                 c.setHeaderRenderer(sortedRenderer);
 443                 sortModelIndex = modelIndex;
 444             }
 445         });
 446 
 447         // Make double-clicks the same as Edit->Properties
 448         addressTable.addMouseListener(new MouseAdapter() {
 449             public void mouseClicked(MouseEvent e) {
 450                 if (e.getClickCount() == 2) {
 451                         handleProperties();
 452                 }
 453             }
 454         });
 455 
 456         // Install custom renderer to bold the entries which are unusable
 457         TableCellRenderer renderer = new AddressTableCellRenderer();
 458         addressTable.setDefaultRenderer(String.class, renderer);
 459         addressTable.setDefaultRenderer(IPAddress.class, renderer);
 460         addressTable.setDefaultRenderer(Date.class, renderer);
 461 
 462         // Wrap it in a scroll pane
 463         addressPane = new JScrollPane(addressTable);
 464 
 465         displayPanel.add(addressPane, BorderLayout.CENTER);
 466 
 467         // Create menu items
 468         Mnemonic mnShowAddrs =
 469             new Mnemonic(ResourceStrings.getString("show_addresses"));
 470         showAddresses = new JCheckBoxMenuItem(mnShowAddrs.getString(),
 471             false);
 472         showAddresses.setMnemonic(mnShowAddrs.getMnemonic());
 473         showAddresses.addActionListener(new ActionListener() {
 474             public void actionPerformed(ActionEvent e) {
 475                 addressTableModel.setShowAddresses(showAddresses.getState());
 476             }
 477         });
 478 
 479         Mnemonic mnShowGrid =
 480             new Mnemonic(ResourceStrings.getString("show_grid"));
 481         showGrid = new JCheckBoxMenuItem(mnShowGrid.getString(),
 482             true);
 483         showGrid.setMnemonic(mnShowGrid.getMnemonic());
 484         showGrid.addActionListener(new ActionListener() {
 485             public void actionPerformed(ActionEvent e) {
 486                 addressTable.setShowGrid(showGrid.getState());
 487             }
 488         });
 489 
 490         Mnemonic mnAddNet =
 491             new Mnemonic(ResourceStrings.getString("add_network"));
 492         addNet = new JMenuItem(mnAddNet.getString());
 493         addNet.setMnemonic(mnAddNet.getMnemonic());
 494         addNet.addActionListener(new ActionListener() {
 495             public void actionPerformed(ActionEvent e) {
 496                 ConfigWizard wiz = new ConfigWizard(myFrame,
 497                     ResourceStrings.getString("net_wiz_title"), false);
 498                 wiz.addActionListener(new ActionListener() {
 499                     public void actionPerformed(ActionEvent e) {
 500                         if (e.getActionCommand().equals("finished")) {
 501                             reload();
 502                         }
 503                     }
 504                 });
 505                 wiz.pack();
 506                 wiz.setVisible(true);
 507             }
 508         });
 509 
 510 
 511         Mnemonic mnDelNets =
 512             new Mnemonic(ResourceStrings.getString("delete_networks"));
 513         deleteNets = new JMenuItem(mnDelNets.getString());
 514         deleteNets.setMnemonic(mnDelNets.getMnemonic());
 515         deleteNets.addActionListener(new ActionListener() {
 516             public void actionPerformed(ActionEvent e) {
 517                 DeleteNetworksDialog d = new DeleteNetworksDialog(myFrame);
 518                 d.addActionListener(new ActionListener() {
 519                     public void actionPerformed(ActionEvent e) {
 520                         if (e.getActionCommand().equals(DialogActions.OK)) {
 521                             reload();
 522                         }
 523                     }
 524                 });
 525                 d.pack();
 526                 d.setVisible(true);
 527             }
 528         });
 529 
 530         Mnemonic mnAddAddr =
 531             new Mnemonic(ResourceStrings.getString("add_addresses"));
 532         addAddrs = new JMenuItem(mnAddAddr.getString());
 533         addAddrs.setMnemonic(mnAddAddr.getMnemonic());
 534         addAddrs.setEnabled(false); // Start out disabled
 535         addAddrs.addActionListener(new ActionListener() {
 536             public void actionPerformed(ActionEvent e) {
 537                 AddressWizard wiz = new AddressWizard(myFrame,
 538                     networkListModel.getNetworkAt(
 539                     networkList.getSelectedIndex()));
 540                 wiz.addActionListener(new DialogListener());
 541                 wiz.pack();
 542                 wiz.setVisible(true);
 543             }
 544         });
 545 
 546         Mnemonic mnRelAddr =
 547             new Mnemonic(ResourceStrings.getString("release_addresses"));
 548         releaseAddrs = new JMenuItem(mnRelAddr.getString());
 549         releaseAddrs.setMnemonic(mnRelAddr.getMnemonic());
 550         releaseAddrs.setEnabled(false); // Start out disabled
 551         releaseAddrs.addActionListener(new ActionListener() {
 552             public void actionPerformed(ActionEvent e) {
 553                 int [] rows = addressTable.getSelectedRows();
 554                 if (rows.length == 0) {
 555                     return;
 556                 }
 557                 DhcpClientRecord [] recs = new DhcpClientRecord[rows.length];
 558                 for (int i = 0; i < rows.length; ++i) {
 559                     recs[i] = addressTableModel.getClientAt(
 560                         sortedTableModel.mapRowAt(rows[i]));
 561                 }
 562                 ReleaseAddressDialog d = new ReleaseAddressDialog(myFrame, recs,
 563                     (String)networkListModel.getElementAt(
 564                     networkList.getSelectedIndex()),
 565                     showAddresses.isSelected());
 566                 d.addActionListener(new DialogListener());
 567                 d.pack();
 568                 d.setVisible(true);
 569             }
 570         });
 571 
 572         Mnemonic mnOnAddrs =
 573             new Mnemonic(ResourceStrings.getString("on_addresses_item"));
 574         addressHelp = new JMenuItem(mnOnAddrs.getString());
 575         addressHelp.setMnemonic(mnOnAddrs.getMnemonic());
 576         addressHelp.addActionListener(new ActionListener() {
 577             public void actionPerformed(ActionEvent e) {
 578                 DhcpmgrApplet.showHelp("addresses_reference");
 579             }
 580         });
 581 
 582         /*
 583          * Construct the menu lists
 584          */
 585         menuItems = new Vector[MainFrame.MENU_COUNT];
 586         for (int i = 0; i < menuItems.length; ++i) {
 587             menuItems[i] = new Vector();
 588         }
 589         menuItems[MainFrame.VIEW_MENU].addElement(showAddresses);
 590         menuItems[MainFrame.VIEW_MENU].addElement(showGrid);
 591         menuItems[MainFrame.EDIT_MENU].addElement(addAddrs);
 592         menuItems[MainFrame.EDIT_MENU].addElement(releaseAddrs);
 593         menuItems[MainFrame.EDIT_MENU].addElement(addNet);
 594         menuItems[MainFrame.EDIT_MENU].addElement(deleteNets);
 595         menuItems[MainFrame.HELP_MENU].addElement(addressHelp);
 596 
 597         // Listen for selections events, manipulate menu item state as needed
 598         addressTable.getSelectionModel().addListSelectionListener(
 599                 new ListSelectionListener() {
 600             public void valueChanged(ListSelectionEvent e) {
 601                 if (addressTable.getSelectionModel().isSelectionEmpty()) {
 602                     // Disable menu items
 603                     releaseAddrs.setEnabled(false);
 604                 } else {
 605                     // Enable menu items
 606                     releaseAddrs.setEnabled(true);
 607                 }
 608                 // Notify listeners that our selection state may have changed
 609                 notifySelectionListeners();
 610             }
 611         });
 612     }
 613 
 614     public String getName() {
 615         return ResourceStrings.getString("address_view_name");
 616     }
 617 
 618     // Return custom menus for this view, which is nothing at this time
 619     public Enumeration menus() {
 620         return null;
 621     }
 622 
 623     // Return custom menu items for each menu as requested.
 624     public Enumeration menuItems(int menu) {
 625         return menuItems[menu].elements();
 626     }
 627 
 628     public Component getDisplay() {
 629         return displayPanel;
 630     }
 631 
 632     public void setActive(boolean state) {
 633         if (state) {
 634             if (firstActivation) {
 635                 // Find frame we're in for use when creating dialogs
 636                 myFrame = (Frame)SwingUtilities.getAncestorOfClass(
 637                     MainFrame.class, addressTable);
 638                 if (networkListModel.getSize() != 0) {
 639                         networkList.setSelectedIndex(0);
 640                 }
 641                 firstActivation = false;
 642             } else {
 643                 // Clear any messages left from other views
 644                 MainFrame.setStatusText("");
 645             }
 646         }
 647     }
 648 
 649     // Handle a find
 650     public void find(String s) {
 651         int startRow = addressTable.getSelectedRow() + 1;
 652         for (int i = startRow; i < sortedTableModel.getRowCount(); ++i) {
 653             DhcpClientRecord rec =
 654                 addressTableModel.getClientAt(sortedTableModel.mapRowAt(i));
 655             if (rec.getClientName().indexOf(s) != -1 ||
 656                     rec.toString().indexOf(s) != -1) {
 657                 addressTable.setRowSelectionInterval(i, i);
 658                 addressTable.scrollRectToVisible(
 659                     addressTable.getCellRect(i, 0, false));
 660                 return;
 661             }
 662         }
 663         // Got to the end, wrap around
 664         for (int i = 0; i < startRow; ++i) {
 665             DhcpClientRecord rec =
 666                 addressTableModel.getClientAt(sortedTableModel.mapRowAt(i));
 667             if (rec.getClientName().indexOf(s) != -1 ||
 668                     rec.toString().indexOf(s) != -1) {
 669                 addressTable.setRowSelectionInterval(i, i);
 670                 addressTable.scrollRectToVisible(
 671                     addressTable.getCellRect(i, 0, false));
 672                 return;
 673             }
 674         }
 675     }
 676 
 677     public void handleCreate() {
 678         if (networkList.getSelectedIndex() == -1) {
 679             // Tell user to use Network Wizard
 680             JOptionPane.showMessageDialog(myFrame,
 681                 ResourceStrings.getString("run_network_wizard"),
 682                 ResourceStrings.getString("error_message"),
 683                 JOptionPane.ERROR_MESSAGE);
 684         } else {
 685             CreateAddressDialog d = new CreateAddressDialog(myFrame,
 686                 CreateAddressDialog.CREATE, new DhcpClientRecord(),
 687                 networkListModel.getNetworkAt(networkList.getSelectedIndex()));
 688             d.addActionListener(new DialogListener());
 689             d.pack();
 690             d.setVisible(true);
 691         }
 692     }
 693 
 694     public void handleDelete() {
 695         int [] rows = addressTable.getSelectedRows();
 696         if (rows.length == 0) {
 697             return;
 698         }
 699         DhcpClientRecord [] recs = new DhcpClientRecord[rows.length];
 700         for (int i = 0; i < rows.length; ++i) {
 701             recs[i] = addressTableModel.getClientAt(
 702                 sortedTableModel.mapRowAt(rows[i]));
 703         }
 704         DeleteAddressDialog d = new DeleteAddressDialog(myFrame, recs,
 705             (String)networkListModel.getElementAt(
 706             networkList.getSelectedIndex()));
 707         d.addActionListener(new DialogListener());
 708         d.pack();
 709         d.setVisible(true);
 710     }
 711 
 712     public void handleDuplicate() {
 713         int row = addressTable.getSelectedRow();
 714         if (row == -1) {
 715             return;
 716         }
 717         DhcpClientRecord rec =
 718             addressTableModel.getClientAt(sortedTableModel.mapRowAt(row));
 719         if (rec == null) {
 720             return;
 721         }
 722         CreateAddressDialog d = new CreateAddressDialog(myFrame,
 723             CreateAddressDialog.DUPLICATE, (DhcpClientRecord)rec.clone(),
 724             networkListModel.getNetworkAt(networkList.getSelectedIndex()));
 725         d.addActionListener(new DialogListener());
 726         d.pack();
 727         d.setVisible(true);
 728     }
 729 
 730     public void handleProperties() {
 731         int [] rows = addressTable.getSelectedRows();
 732         if (rows.length == 0) {
 733             return;
 734         }
 735         DhcpClientRecord [] recs = new DhcpClientRecord[rows.length];
 736         for (int i = 0; i < rows.length; ++i) {
 737             recs[i] =
 738                 addressTableModel.getClientAt(
 739                 sortedTableModel.mapRowAt(rows[i]));
 740         }
 741         if (recs.length == 1) {
 742             // Edit a single address
 743             CreateAddressDialog d = new CreateAddressDialog(myFrame,
 744                 CreateAddressDialog.EDIT, (DhcpClientRecord)recs[0].clone(),
 745                 networkListModel.getNetworkAt(networkList.getSelectedIndex()));
 746             d.addActionListener(new DialogListener());
 747             d.pack();
 748             d.setVisible(true);
 749         } else {
 750             // Edit a group of addresses
 751             ModifyAddressesDialog d = new ModifyAddressesDialog(myFrame, recs,
 752                 (String)networkListModel.getElementAt(
 753                 networkList.getSelectedIndex()));
 754             d.addActionListener(new DialogListener());
 755             d.pack();
 756             d.setVisible(true);
 757         }
 758     }
 759 
 760     public void handleUpdate() {
 761         reload();
 762     }
 763 
 764     protected void reload() {
 765         Object value = networkList.getSelectedValue();
 766         networkListModel.reload();
 767         networkList.clearSelection();
 768         networkList.setSelectedValue(value, true);
 769         if (networkListModel.getSize() != 0 &&
 770             networkList.getSelectedIndex() == -1) {
 771             // Didn't get selected, must be gone.  Select first item in list
 772             networkList.setSelectedIndex(0);
 773         }
 774     }
 775 
 776     public void addSelectionListener(SelectionListener listener) {
 777         selectionListeners.addElement(listener);
 778     }
 779 
 780     public void removeSelectionListener(SelectionListener listener) {
 781         selectionListeners.removeElement(listener);
 782     }
 783 
 784     private void notifySelectionListeners() {
 785         Enumeration en = selectionListeners.elements();
 786         while (en.hasMoreElements()) {
 787             SelectionListener l = (SelectionListener)en.nextElement();
 788             l.valueChanged();
 789         }
 790     }
 791 
 792     public boolean isSelectionEmpty() {
 793         return addressTable.getSelectionModel().isSelectionEmpty();
 794     }
 795 
 796     public boolean isSelectionMultiple() {
 797         return (addressTable.getSelectedRowCount() > 1);
 798     }
 799 
 800     public void startAddressWizard() {
 801         addAddrs.doClick();
 802     }
 803 }