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 1998-2002 by 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.border.*;
32 import javax.swing.event.*;
33 import javax.swing.table.*;
34
35 import java.awt.*;
36 import java.awt.event.*;
37 import java.text.MessageFormat;
38 import java.util.*;
39
40 import com.sun.dhcpmgr.server.*;
41 import com.sun.dhcpmgr.data.*;
42 import com.sun.dhcpmgr.ui.*;
43 import com.sun.dhcpmgr.bridge.NotRunningException;
44
45
46 /**
47 * Dialog to create/duplicate/edit an option.
48 */
49 public class CreateOptionDialog extends JDialog implements ButtonPanelListener {
50 public static final int CREATE = 0;
51 public static final int EDIT = 1;
52 public static final int DUPLICATE = 2;
53
54 private int mode = CREATE;
55 private OptionNameField name;
56 private JComboBox category;
57 private IntegerField code;
58 private JComboBox type;
59 private JList classList;
60 private JTextField clientClass;
61 private IntegerField granularity;
62 private IntegerField maximum;
63 private JCheckBox signalBox;
64 private Vector listeners;
65 private Option option, originalOption;
66 private ButtonPanel buttonPanel;
67 private ClassListModel classListModel;
68 private JButton add, delete;
69 private UpButton moveUp;
70 private DownButton moveDown;
71 private OptionContext [] categories = {
72 Option.ctxts[Option.EXTEND],
73 Option.ctxts[Option.VENDOR],
74 Option.ctxts[Option.SITE]
75 };
76
77 // Model for the list of vendor classes
78 class ClassListModel extends AbstractListModel {
79
80 public ClassListModel() {
81 super();
82 }
83
84 public int getSize() {
85 return option.getVendorCount();
86 }
87
88 public Object getElementAt(int index) {
89 return option.getVendorAt(index);
90 }
91
92 public void addElement(String v) throws ValidationException {
93 option.addVendor(v);
94 fireIntervalAdded(this, option.getVendorCount()-1,
95 option.getVendorCount()-1);
96 }
97
98 public void removeElementAt(int index) {
99 option.removeVendorAt(index);
100 fireIntervalRemoved(this, index, index);
101 }
102
103 public void moveUp(int index) {
104 String t = (String)option.getVendorAt(index-1);
105 option.setVendorAt(option.getVendorAt(index), index-1);
106 option.setVendorAt(t, index);
107 fireContentsChanged(this, index-1, index);
108 }
109
110 public void moveDown(int index) {
111 String t = (String)option.getVendorAt(index+1);
112 option.setVendorAt(option.getVendorAt(index), index+1);
113 option.setVendorAt(t, index);
114 fireContentsChanged(this, index, index+1);
115 }
116
117 public void reset() {
118 fireContentsChanged(this, 0, getSize());
119 }
120 }
121
122 public CreateOptionDialog(Frame f, int mode) {
123 super(f);
124 setLocationRelativeTo(f);
125 JPanel classPanel;
126
127 listeners = new Vector();
128
129 this.mode = mode;
130 switch (mode) {
131 case CREATE:
132 setTitle(ResourceStrings.getString("create_option_title"));
133 break;
134 case EDIT:
135 setTitle(ResourceStrings.getString("edit_option_title"));
136 break;
137 case DUPLICATE:
138 setTitle(ResourceStrings.getString("duplicate_option_title"));
139 break;
140 default:
141 break;
142 }
143
144 getContentPane().setLayout(new BoxLayout(getContentPane(),
145 BoxLayout.Y_AXIS));
146
147 JPanel mainPanel = new JPanel(new BorderLayout());
148 mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
149
150 JPanel fieldPanel = new JPanel();
151 GridBagLayout bag = new GridBagLayout();
152 fieldPanel.setLayout(bag);
153
154 // Initialize constraints
155 GridBagConstraints c = new GridBagConstraints();
156 c.gridx = c.gridy = 0;
157 c.gridwidth = c.gridheight = 1;
158 c.fill = GridBagConstraints.HORIZONTAL;
159 c.insets = new Insets(5, 5, 5, 5);
160 c.weightx = c.weighty = 1.0;
161
162 // Label and text field for name
163 Mnemonic mnOname =
164 new Mnemonic(ResourceStrings.getString("op_name"));
165 JLabel l = new JLabel(mnOname.getString(), SwingConstants.RIGHT);
166
167 bag.setConstraints(l, c);
168 fieldPanel.add(l);
169 name = new OptionNameField("");
170
171 l.setLabelFor(name);
172 l.setToolTipText(mnOname.getString());
173 l.setDisplayedMnemonic(mnOname.getMnemonic());
174
175 l.setLabelFor(name);
176 ++c.gridx;
177 bag.setConstraints(name, c);
178 fieldPanel.add(name);
179
180 // Label and combo box for category
181 Mnemonic mnCat =
182 new Mnemonic(ResourceStrings.getString("category_label"));
183 l = new JLabel(mnCat.getString(), SwingConstants.RIGHT);
184
185 c.gridx = 0;
186 ++c.gridy;
187 bag.setConstraints(l, c);
188 fieldPanel.add(l);
189 category = new JComboBox(categories);
190
191 l.setLabelFor(category);
192 l.setToolTipText(mnCat.getString());
193 l.setDisplayedMnemonic(mnCat.getMnemonic());
194
195 category.setEditable(false);
196 ++c.gridx;
197 bag.setConstraints(category, c);
198 fieldPanel.add(category);
199
200 // Label and text field for code
201 Mnemonic mnCode =
202 new Mnemonic(ResourceStrings.getString("option_code_label"));
203 l = new JLabel(mnCode.getString(), SwingConstants.RIGHT);
204
205 c.gridx = 0;
206 ++c.gridy;
207 bag.setConstraints(l, c);
208 fieldPanel.add(l);
209 code = new IntegerField();
210
211 l.setLabelFor(code);
212 l.setToolTipText(mnCode.getString());
213 l.setDisplayedMnemonic(mnCode.getMnemonic());
214
215 ++c.gridx;
216 bag.setConstraints(code, c);
217 fieldPanel.add(code);
218
219 // Label and combo box for data type
220 Mnemonic mnType =
221 new Mnemonic(ResourceStrings.getString("data_type_label"));
222 l = new JLabel(mnType.getString(), SwingConstants.RIGHT);
223
224 c.gridx = 0;
225 ++c.gridy;
226 bag.setConstraints(l, c);
227 fieldPanel.add(l);
228 type = new JComboBox(Option.types);
229
230 l.setLabelFor(type);
231 l.setToolTipText(mnType.getString());
232 l.setDisplayedMnemonic(mnType.getMnemonic());
233
234 type.setEditable(false);
235 ++c.gridx;
236 bag.setConstraints(type, c);
237 fieldPanel.add(type);
238
239 // Label and text field for granularity
240 Mnemonic mnGran =
241 new Mnemonic(ResourceStrings.getString("granularity_label"));
242 l = new JLabel(mnGran.getString(), SwingConstants.RIGHT);
243
244 c.gridx = 0;
245 ++c.gridy;
246 bag.setConstraints(l, c);
247 fieldPanel.add(l);
248 granularity = new IntegerField(5);
249
250 l.setLabelFor(granularity);
251 l.setToolTipText(mnGran.getString());
252 l.setDisplayedMnemonic(mnGran.getMnemonic());
253
254 ++c.gridx;
255 bag.setConstraints(granularity, c);
256 fieldPanel.add(granularity);
257
258 // Label and text field for maximum
259 Mnemonic mnMax =
260 new Mnemonic(ResourceStrings.getString("maximum_label"));
261 l = new JLabel(mnMax.getString(), SwingConstants.RIGHT);
262
263 c.gridx = 0;
264 ++c.gridy;
265 bag.setConstraints(l, c);
266 fieldPanel.add(l);
267 maximum = new IntegerField(5);
268
269 l.setLabelFor(maximum);
270 l.setToolTipText(mnMax.getString());
271 l.setDisplayedMnemonic(mnMax.getMnemonic());
272
273 ++c.gridx;
274 bag.setConstraints(maximum, c);
275 fieldPanel.add(maximum);
276
277 mainPanel.add(fieldPanel, BorderLayout.WEST);
278
279 // Editing controls for client classes
280 bag = new GridBagLayout();
281 classPanel = new JPanel(bag);
282 Border tb = BorderFactory.createTitledBorder(
283 BorderFactory.createLineBorder(Color.black),
284 ResourceStrings.getString("client_classes_label"));
285 classPanel.setBorder(BorderFactory.createCompoundBorder(tb,
286 BorderFactory.createEmptyBorder(5, 5, 5, 5)));
287
288 c = new GridBagConstraints();
289 c.gridx = c.gridy = 0;
290 c.weightx = c.weighty = 1.0;
291 c.gridheight = 1;
292 c.gridwidth = 1;
293
294 // Field to type in new classes
295 clientClass = new JTextField("", 20);
296 c.fill = GridBagConstraints.HORIZONTAL;
297 bag.setConstraints(clientClass, c);
298 classPanel.add(clientClass);
299
300 // Button for Add operation
301 Mnemonic mnAdd =
302 new Mnemonic(ResourceStrings.getString("add"));
303 add = new JButton(mnAdd.getString());
304 add.setToolTipText(mnAdd.getString());
305 add.setMnemonic(mnAdd.getMnemonic());
306
307 c.fill = GridBagConstraints.NONE;
308 ++c.gridx;
309 c.weightx = 0.5;
310 bag.setConstraints(add, c);
311 classPanel.add(add);
312
313 // List for classes
314 classListModel = new ClassListModel();
315 classList = new JList(classListModel);
316
317 // Make sure it's approximately wide enough for our purposes, 20 chars
318 classList.setPrototypeCellValue("abcdefghijklmnopqrst");
319 classList.setSelectionMode(
320 ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
321 JScrollPane scrollPane = new JScrollPane(classList);
322 c.fill = GridBagConstraints.BOTH;
323 c.gridx = 0;
324 ++c.gridy;
325 c.weightx = 1.0;
326 bag.setConstraints(scrollPane, c);
327 classPanel.add(scrollPane);
328
329 // Buttons to manipulate the list contents
330 JPanel editButtonPanel = new JPanel(new VerticalButtonLayout());
331 moveUp = new UpButton();
332 editButtonPanel.add(moveUp);
333 moveDown = new DownButton();
334 editButtonPanel.add(moveDown);
335
336 Mnemonic mnDelete =
337 new Mnemonic(ResourceStrings.getString("delete"));
338 delete = new JButton(mnDelete.getString());
339 delete.setToolTipText(mnDelete.getString());
340 delete.setMnemonic(mnDelete.getMnemonic());
341
342 editButtonPanel.add(delete);
343 ++c.gridx;
344 c.weightx = 0.5;
345 bag.setConstraints(editButtonPanel, c);
346 classPanel.add(editButtonPanel);
347
348 /*
349 * Disable all buttons to start; selection changes will adjust button
350 * state as necessary
351 */
352 add.setEnabled(false);
353 delete.setEnabled(false);
354 moveUp.setEnabled(false);
355 moveDown.setEnabled(false);
356
357 // Create listener for button presses, take action as needed
358 ActionListener al = new ActionListener() {
359 public void actionPerformed(ActionEvent e) {
360 if (e.getSource() == add || e.getSource() == clientClass) {
361 try {
362 classListModel.addElement(clientClass.getText());
363 } catch (ValidationException ex) {
364 // Something wrong with class name
365 MessageFormat form = new MessageFormat(
366 ResourceStrings.getString("invalid_client_class"));
367 Object [] args = new Object[] { clientClass.getText() };
368 JOptionPane.showMessageDialog(CreateOptionDialog.this,
369 form.format(args),
370 ResourceStrings.getString("input_error"),
371 JOptionPane.ERROR_MESSAGE);
372 return;
373 }
374 } else if (e.getSource() == delete) {
375 int [] indices = classList.getSelectedIndices();
376 if (indices.length > 1) {
377 /*
378 * Need to sort them so that the delete's don't
379 * interfere with each other
380 */
381 for (int i = 0; i < indices.length; ++i) {
382 for (int j = i; j < indices.length; ++j) {
383 if (indices[i] > indices[j]) {
384 int k = indices[i];
385 indices[i] = indices[j];
386 indices[j] = k;
387 }
388 }
389 }
390 }
391 // Now delete from high index to low
392 for (int i = indices.length - 1; i >= 0; --i) {
393 classListModel.removeElementAt(indices[i]);
394 }
395 if (indices.length > 1) {
396 // Clear selection if multiple deleted
397 classList.clearSelection();
398 /*
399 * XXX We don't get a selection event for some reason,
400 * make it work for now
401 */
402 delete.setEnabled(false);
403 } else {
404 // Make sure to select something in the list
405 if (classListModel.getSize() == 0) {
406 // List is empty, so disable delete
407 delete.setEnabled(false);
408 } else if (indices[0] >= classListModel.getSize()) {
409 // Select last one if we're off the end
410 classList.setSelectedIndex(
411 classListModel.getSize()-1);
412 } else {
413 // Select next one in list
414 classList.setSelectedIndex(indices[0]);
415 }
416 }
417 } else if (e.getSource() == moveUp) {
418 int i = classList.getSelectedIndex();
419 classListModel.moveUp(i);
420 // Keep item selected so repeated moveUp's affect same item
421 classList.setSelectedIndex(i-1);
422 } else if (e.getSource() == moveDown) {
423 int i = classList.getSelectedIndex();
424 classListModel.moveDown(i);
425 // Keep item selected so repeated moveDowns affect same item
426 classList.setSelectedIndex(i+1);
427 }
428 }
429 };
430 clientClass.addActionListener(al);
431 add.addActionListener(al);
432 delete.addActionListener(al);
433 moveUp.addActionListener(al);
434 moveDown.addActionListener(al);
435
436 // Put a selection listener on the list to enable buttons appropriately
437 classList.addListSelectionListener(new ListSelectionListener() {
438 public void valueChanged(ListSelectionEvent e) {
439 int [] indices = classList.getSelectedIndices();
440 switch (indices.length) {
441 case 0:
442 // Nothing selected; disable them all
443 delete.setEnabled(false);
444 moveUp.setEnabled(false);
445 moveDown.setEnabled(false);
446 break;
447 case 1:
448 delete.setEnabled(true);
449 // Can't move first one up
450 moveUp.setEnabled(indices[0] != 0);
451 // Can't move last one down
452 if (indices[0] == (classListModel.getSize() - 1)) {
453 moveDown.setEnabled(false);
454 } else {
455 moveDown.setEnabled(true);
456 }
457 break;
458 default:
459 // More than one; only delete is allowed
460 delete.setEnabled(true);
461 moveUp.setEnabled(false);
462 moveDown.setEnabled(false);
463 }
464 }
465 });
466 // Enable Add when class is not empty.
467 clientClass.getDocument().addDocumentListener(new DocumentListener() {
468 public void insertUpdate(DocumentEvent e) {
469 add.setEnabled(clientClass.getText().length() != 0);
470 }
471 public void changedUpdate(DocumentEvent e) {
472 insertUpdate(e);
473 }
474 public void removeUpdate(DocumentEvent e) {
475 insertUpdate(e);
476 }
477 });
478
479 mainPanel.add(classPanel, BorderLayout.CENTER);
480
481 signalBox = new JCheckBox(ResourceStrings.getString("signal_server"),
482 true);
483 signalBox.setToolTipText(
484 ResourceStrings.getString("signal_server"));
485 signalBox.setHorizontalAlignment(SwingConstants.CENTER);
486 JPanel signalPanel = new JPanel();
487 signalPanel.add(signalBox);
488 mainPanel.add(signalPanel, BorderLayout.SOUTH);
489
490 getContentPane().add(mainPanel);
491 getContentPane().add(new JSeparator());
492
493 buttonPanel = new ButtonPanel(true);
494 buttonPanel.addButtonPanelListener(this);
495 getContentPane().add(buttonPanel);
496
497 setOption(new Option());
498
499 if (mode == EDIT) {
500 buttonPanel.setOkEnabled(true);
501 }
502
503 // Enable OK when there is data in the name field
504 name.getDocument().addDocumentListener(new DocumentListener() {
505 public void insertUpdate(DocumentEvent e) {
506 buttonPanel.setOkEnabled(e.getDocument().getLength() != 0);
507 }
508 public void changedUpdate(DocumentEvent e) {
509 insertUpdate(e);
510 }
511 public void removeUpdate(DocumentEvent e) {
512 insertUpdate(e);
513 }
514 });
515
516 // If category != VENDOR you can't mess with the client class data
517 category.addItemListener(new ItemListener() {
518 public void itemStateChanged(ItemEvent e) {
519 OptionContext ctxt = categories[category.getSelectedIndex()];
520 boolean isVendor =
521 (ctxt.getCode() == Option.ctxts[Option.VENDOR].getCode());
522 if (!isVendor) {
523 option.clearVendors();
524 clientClass.setText("");
525 }
526 clientClass.setEnabled(isVendor);
527 classList.setEnabled(isVendor);
528 }
529 });
530
531 // Update state of granularity & maximum depending on data type selected
532 type.addItemListener(new ItemListener() {
533 public void itemStateChanged(ItemEvent e) {
534 OptionType stype = Option.types[type.getSelectedIndex()];
535 byte code = stype.getCode();
536 // Set granularity to correct minimum for type
537 if (code == Option.types[Option.BOOLEAN].getCode()) {
538 granularity.setText("0");
539 } else if ("0".equals(granularity.getText())) {
540 granularity.setText("1");
541 }
542 // Now set editability of the granularity and max fields
543 if (code == Option.types[Option.ASCII].getCode() ||
544 code == Option.types[Option.OCTET].getCode()) {
545 granularity.setEditable(false);
546 maximum.setEditable(true);
547 } else if (code == Option.types[Option.BOOLEAN].getCode()) {
548 granularity.setEditable(false);
549 // Also reset maximum value in this case
550 maximum.setText("0");
551 maximum.setEditable(false);
552 } else if (code == Option.types[Option.NUMBER].getCode() ||
553 code == Option.types[Option.UNUMBER8].getCode() ||
554 code == Option.types[Option.UNUMBER16].getCode() ||
555 code == Option.types[Option.UNUMBER32].getCode() ||
556 code == Option.types[Option.UNUMBER64].getCode() ||
557 code == Option.types[Option.SNUMBER8].getCode() ||
558 code == Option.types[Option.SNUMBER16].getCode() ||
559 code == Option.types[Option.SNUMBER32].getCode() ||
560 code == Option.types[Option.SNUMBER64].getCode() ||
561 code == Option.types[Option.IP].getCode()) {
562 granularity.setEditable(true);
563 maximum.setEditable(true);
564 }
565 }
566 });
567 }
568
569 public void setOption(Option o) {
570 originalOption = o; // Keep a copy so reset will work
571 option = (Option)o.clone();
572 resetValues();
573 }
574
575 private void resetValues() {
576 if (mode == DUPLICATE) {
577 name.setText("");
578 } else {
579 name.setText(option.getKey());
580 }
581 for (int i = 0; i < categories.length; i++) {
582 if (categories[i].getCode() == option.getContext()) {
583 category.setSelectedIndex(i);
584 break;
585 }
586 }
587
588 for (int i = 0; i < Option.types.length; i++) {
589 if (Option.types[i].getCode() == option.getType()) {
590 type.setSelectedIndex(i);
591 break;
592 }
593 }
594
595 code.setValue(option.getCode());
596 granularity.setValue(option.getGranularity());
597 maximum.setValue(option.getMaximum());
598 classListModel.reset();
599 signalBox.setSelected(true);
600 }
601
602 public void buttonPressed(int buttonId) {
603 switch (buttonId) {
604 case OK:
605 try {
606 OptionContext sctxt = categories[category.getSelectedIndex()];
607 OptionType stype = Option.types[type.getSelectedIndex()];
608 option.setKey(name.getText());
609 option.setContext(sctxt.getCode());
610 option.setCode((short)code.getValue());
611 option.setType(stype.getCode());
612 option.setGranularity(granularity.getValue());
613 option.setMaximum(maximum.getValue());
614 if (sctxt.getCode() == Option.ctxts[Option.VENDOR].getCode() &&
615 option.getVendorCount() == 0) {
616 JOptionPane.showMessageDialog(this,
617 ResourceStrings.getString("empty_vendor_error"),
618 ResourceStrings.getString("server_error_title"),
619 JOptionPane.ERROR_MESSAGE);
620 return;
621 }
622 DhcptabMgr server = DataManager.get().getDhcptabMgr();
623 if ((mode == CREATE) || (mode == DUPLICATE)) {
624 server.createRecord(option, signalBox.isSelected());
625 } else if (mode == EDIT) {
626 server.modifyRecord(originalOption, option,
627 signalBox.isSelected());
628 }
629 fireActionPerformed();
630 setVisible(false);
631 dispose();
632 } catch (NotRunningException e) {
633 // Server not running, put up a warning
634 JOptionPane.showMessageDialog(this,
635 ResourceStrings.getString("server_not_running"),
636 ResourceStrings.getString("warning"),
637 JOptionPane.WARNING_MESSAGE);
638 fireActionPerformed();
639 setVisible(false);
640 dispose();
641 } catch (Exception e) {
642 MessageFormat form = null;
643 Object [] args = new Object[2];
644 switch (mode) {
645 case CREATE:
646 case DUPLICATE:
647 form = new MessageFormat(
648 ResourceStrings.getString("create_option_error"));
649 args[0] = option.getKey();
650 break;
651 case EDIT:
652 form = new MessageFormat(
653 ResourceStrings.getString("edit_option_error"));
654 args[0] = originalOption.getKey();
655 break;
656 }
657 args[1] = e.getMessage();
658 JOptionPane.showMessageDialog(this, form.format(args),
659 ResourceStrings.getString("server_error_title"),
660 JOptionPane.ERROR_MESSAGE);
661 }
662 break;
663 case CANCEL:
664 setVisible(false);
665 dispose();
666 break;
667 case HELP:
668 String helpTag = null;
669 switch (mode) {
670 case CREATE:
671 helpTag = "create_option";
672 break;
673 case DUPLICATE:
674 helpTag = "duplicate_option";
675 break;
676 case EDIT:
677 helpTag = "modify_option";
678 break;
679 }
680 DhcpmgrApplet.showHelp(helpTag);
681 break;
682 case RESET:
683 setOption(originalOption);
684 break;
685 }
686 }
687
688 public void addActionListener(ActionListener l) {
689 listeners.addElement(l);
690 }
691
692 public void removeActionListener(ActionListener l) {
693 listeners.removeElement(l);
694 }
695
696 protected void fireActionPerformed() {
697 String command = null;
698 switch (mode) {
699 case CREATE:
700 command = DialogActions.CREATE;
701 case DUPLICATE:
702 command = DialogActions.DUPLICATE;
703 break;
704 case EDIT:
705 command = DialogActions.EDIT;
706 break;
707 }
708 ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
709 command);
710 Enumeration en = listeners.elements();
711 while (en.hasMoreElements()) {
712 ActionListener l = (ActionListener)en.nextElement();
713 l.actionPerformed(e);
714 }
715 }
716 }