History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/plaf/basic/BasicOptionPaneUI.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/plaf/basic/BasicOptionPaneUI.java+1455−0
@@ -0,0 +1,1455 @@
1/*
2 * Copyright (c) 1997, 2008, 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
26package javax.swing.plaf.basic;
27
28import sun.swing.DefaultLookup;
29import sun.swing.UIAction;
30import javax.swing.border.Border;
31import javax.swing.border.EmptyBorder;
32import javax.swing.*;
33import javax.swing.event.*;
34import javax.swing.plaf.ActionMapUIResource;
35import javax.swing.plaf.ComponentUI;
36import javax.swing.plaf.OptionPaneUI;
37import java.awt.*;
38import java.awt.event.*;
39import java.beans.PropertyChangeEvent;
40import java.beans.PropertyChangeListener;
41import java.util.Locale;
42import java.security.AccessController;
43
44import sun.security.action.GetPropertyAction;
45
46
47/**
48 * Provides the basic look and feel for a <code>JOptionPane</code>.
49 * <code>BasicMessagePaneUI</code> provides a means to place an icon,
50 * message and buttons into a <code>Container</code>.
51 * Generally, the layout will look like:<p>
52 * <pre>
53 * ------------------
54 * | i | message |
55 * | c | message |
56 * | o | message |
57 * | n | message |
58 * ------------------
59 * | buttons |
60 * |________________|
61 * </pre>
62 * icon is an instance of <code>Icon</code> that is wrapped inside a
63 * <code>JLabel</code>. The message is an opaque object and is tested
64 * for the following: if the message is a <code>Component</code> it is
65 * added to the <code>Container</code>, if it is an <code>Icon</code>
66 * it is wrapped inside a <code>JLabel</code> and added to the
67 * <code>Container</code> otherwise it is wrapped inside a <code>JLabel</code>.
68 * <p>
69 * The above layout is used when the option pane's
70 * <code>ComponentOrientation</code> property is horizontal, left-to-right.
71 * The layout will be adjusted appropriately for other orientations.
72 * <p>
73 * The <code>Container</code>, message, icon, and buttons are all
74 * determined from abstract methods.
75 *
76 * @author James Gosling
77 * @author Scott Violet
78 * @author Amy Fowler
79 */
80public class BasicOptionPaneUI extends OptionPaneUI {
81
82 public static final int MinimumWidth = 262;
83 public static final int MinimumHeight = 90;
84
85 private static String newline;
86
87 /**
88 * <code>JOptionPane</code> that the receiver is providing the
89 * look and feel for.
90 */
91 protected JOptionPane optionPane;
92
93 protected Dimension minimumSize;
94
95 /** JComponent provide for input if optionPane.getWantsInput() returns
96 * true. */
97 protected JComponent inputComponent;
98
99 /** Component to receive focus when messaged with selectInitialValue. */
100 protected Component initialFocusComponent;
101
102 /** This is set to true in validateComponent if a Component is contained
103 * in either the message or the buttons. */
104 protected boolean hasCustomComponents;
105
106 protected PropertyChangeListener propertyChangeListener;
107
108 private Handler handler;
109
110
111 static {
112 newline = java.security.AccessController.doPrivileged(
113 new GetPropertyAction("line.separator"));
114 if (newline == null) {
115 newline = "\n";
116 }
117 }
118
119 static void loadActionMap(LazyActionMap map) {
120 map.put(new Actions(Actions.CLOSE));
121 BasicLookAndFeel.installAudioActionMap(map);
122 }
123
124
125
126 /**
127 * Creates a new BasicOptionPaneUI instance.
128 */
129 public static ComponentUI createUI(JComponent x) {
130 return new BasicOptionPaneUI();
131 }
132
133 /**
134 * Installs the receiver as the L&F for the passed in
135 * <code>JOptionPane</code>.
136 */
137 public void installUI(JComponent c) {
138 optionPane = (JOptionPane)c;
139 installDefaults();
140 optionPane.setLayout(createLayoutManager());
141 installComponents();
142 installListeners();
143 installKeyboardActions();
144 }
145
146 /**
147 * Removes the receiver from the L&F controller of the passed in split
148 * pane.
149 */
150 public void uninstallUI(JComponent c) {
151 uninstallComponents();
152 optionPane.setLayout(null);
153 uninstallKeyboardActions();
154 uninstallListeners();
155 uninstallDefaults();
156 optionPane = null;
157 }
158
159 protected void installDefaults() {
160 LookAndFeel.installColorsAndFont(optionPane, "OptionPane.background",
161 "OptionPane.foreground", "OptionPane.font");
162 LookAndFeel.installBorder(optionPane, "OptionPane.border");
163 minimumSize = UIManager.getDimension("OptionPane.minimumSize");
164 LookAndFeel.installProperty(optionPane, "opaque", Boolean.TRUE);
165 }
166
167 protected void uninstallDefaults() {
168 LookAndFeel.uninstallBorder(optionPane);
169 }
170
171 protected void installComponents() {
172 optionPane.add(createMessageArea());
173
174 Container separator = createSeparator();
175 if (separator != null) {
176 optionPane.add(separator);
177 }
178 optionPane.add(createButtonArea());
179 optionPane.applyComponentOrientation(optionPane.getComponentOrientation());
180 }
181
182 protected void uninstallComponents() {
183 hasCustomComponents = false;
184 inputComponent = null;
185 initialFocusComponent = null;
186 optionPane.removeAll();
187 }
188
189 protected LayoutManager createLayoutManager() {
190 return new BoxLayout(optionPane, BoxLayout.Y_AXIS);
191 }
192
193 protected void installListeners() {
194 if ((propertyChangeListener = createPropertyChangeListener()) != null) {
195 optionPane.addPropertyChangeListener(propertyChangeListener);
196 }
197 }
198
199 protected void uninstallListeners() {
200 if (propertyChangeListener != null) {
201 optionPane.removePropertyChangeListener(propertyChangeListener);
202 propertyChangeListener = null;
203 }
204 handler = null;
205 }
206
207 protected PropertyChangeListener createPropertyChangeListener() {
208 return getHandler();
209 }
210
211 private Handler getHandler() {
212 if (handler == null) {
213 handler = new Handler();
214 }
215 return handler;
216 }
217
218 protected void installKeyboardActions() {
219 InputMap map = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
220
221 SwingUtilities.replaceUIInputMap(optionPane, JComponent.
222 WHEN_IN_FOCUSED_WINDOW, map);
223
224 LazyActionMap.installLazyActionMap(optionPane, BasicOptionPaneUI.class,
225 "OptionPane.actionMap");
226 }
227
228 protected void uninstallKeyboardActions() {
229 SwingUtilities.replaceUIInputMap(optionPane, JComponent.
230 WHEN_IN_FOCUSED_WINDOW, null);
231 SwingUtilities.replaceUIActionMap(optionPane, null);
232 }
233
234 InputMap getInputMap(int condition) {
235 if (condition == JComponent.WHEN_IN_FOCUSED_WINDOW) {
236 Object[] bindings = (Object[])DefaultLookup.get(
237 optionPane, this, "OptionPane.windowBindings");
238 if (bindings != null) {
239 return LookAndFeel.makeComponentInputMap(optionPane, bindings);
240 }
241 }
242 return null;
243 }
244
245 /**
246 * Returns the minimum size the option pane should be. Primarily
247 * provided for subclassers wishing to offer a different minimum size.
248 */
249 public Dimension getMinimumOptionPaneSize() {
250 if (minimumSize == null) {
251 return new Dimension(MinimumWidth, MinimumHeight);
252 }
253 return new Dimension(minimumSize.width,
254 minimumSize.height);
255 }
256
257 /**
258 * If <code>c</code> is the <code>JOptionPane</code> the receiver
259 * is contained in, the preferred
260 * size that is returned is the maximum of the preferred size of
261 * the <code>LayoutManager</code> for the <code>JOptionPane</code>, and
262 * <code>getMinimumOptionPaneSize</code>.
263 */
264 public Dimension getPreferredSize(JComponent c) {
265 if (c == optionPane) {
266 Dimension ourMin = getMinimumOptionPaneSize();
267 LayoutManager lm = c.getLayout();
268
269 if (lm != null) {
270 Dimension lmSize = lm.preferredLayoutSize(c);
271
272 if (ourMin != null)
273 return new Dimension
274 (Math.max(lmSize.width, ourMin.width),
275 Math.max(lmSize.height, ourMin.height));
276 return lmSize;
277 }
278 return ourMin;
279 }
280 return null;
281 }
282
283 /**
284 * Messaged from installComponents to create a Container containing the
285 * body of the message. The icon is the created by calling
286 * <code>addIcon</code>.
287 */
288 protected Container createMessageArea() {
289 JPanel top = new JPanel();
290 Border topBorder = (Border)DefaultLookup.get(optionPane, this,
291 "OptionPane.messageAreaBorder");
292 if (topBorder != null) {
293 top.setBorder(topBorder);
294 }
295 top.setLayout(new BorderLayout());
296
297 /* Fill the body. */
298 Container body = new JPanel(new GridBagLayout());
299 Container realBody = new JPanel(new BorderLayout());
300
301 body.setName("OptionPane.body");
302 realBody.setName("OptionPane.realBody");
303
304 if (getIcon() != null) {
305 JPanel sep = new JPanel();
306 sep.setName("OptionPane.separator");
307 sep.setPreferredSize(new Dimension(15, 1));
308 realBody.add(sep, BorderLayout.BEFORE_LINE_BEGINS);
309 }
310 realBody.add(body, BorderLayout.CENTER);
311
312 GridBagConstraints cons = new GridBagConstraints();
313 cons.gridx = cons.gridy = 0;
314 cons.gridwidth = GridBagConstraints.REMAINDER;
315 cons.gridheight = 1;
316 cons.anchor = DefaultLookup.getInt(optionPane, this,
317 "OptionPane.messageAnchor", GridBagConstraints.CENTER);
318 cons.insets = new Insets(0,0,3,0);
319
320 addMessageComponents(body, cons, getMessage(),
321 getMaxCharactersPerLineCount(), false);
322 top.add(realBody, BorderLayout.CENTER);
323
324 addIcon(top);
325 return top;
326 }
327
328 /**
329 * Creates the appropriate object to represent <code>msg</code> and
330 * places it into <code>container</code>. If <code>msg</code> is an
331 * instance of Component, it is added directly, if it is an Icon,
332 * a JLabel is created to represent it, otherwise a JLabel is
333 * created for the string, if <code>d</code> is an Object[], this
334 * method will be recursively invoked for the children.
335 * <code>internallyCreated</code> is true if Objc is an instance
336 * of Component and was created internally by this method (this is
337 * used to correctly set hasCustomComponents only if !internallyCreated).
338 */
339 protected void addMessageComponents(Container container,
340 GridBagConstraints cons,
341 Object msg, int maxll,
342 boolean internallyCreated) {
343 if (msg == null) {
344 return;
345 }
346 if (msg instanceof Component) {
347 // To workaround problem where Gridbad will set child
348 // to its minimum size if its preferred size will not fit
349 // within allocated cells
350 if (msg instanceof JScrollPane || msg instanceof JPanel) {
351 cons.fill = GridBagConstraints.BOTH;
352 cons.weighty = 1;
353 } else {
354 cons.fill = GridBagConstraints.HORIZONTAL;
355 }
356 cons.weightx = 1;
357
358 container.add((Component) msg, cons);
359 cons.weightx = 0;
360 cons.weighty = 0;
361 cons.fill = GridBagConstraints.NONE;
362 cons.gridy++;
363 if (!internallyCreated) {
364 hasCustomComponents = true;
365 }
366
367 } else if (msg instanceof Object[]) {
368 Object [] msgs = (Object[]) msg;
369 for (Object o : msgs) {
370 addMessageComponents(container, cons, o, maxll, false);
371 }
372
373 } else if (msg instanceof Icon) {
374 JLabel label = new JLabel( (Icon)msg, SwingConstants.CENTER );
375 configureMessageLabel(label);
376 addMessageComponents(container, cons, label, maxll, true);
377
378 } else {
379 String s = msg.toString();
380 int len = s.length();
381 if (len <= 0) {
382 return;
383 }
384 int nl;
385 int nll = 0;
386
387 if ((nl = s.indexOf(newline)) >= 0) {
388 nll = newline.length();
389 } else if ((nl = s.indexOf("\r\n")) >= 0) {
390 nll = 2;
391 } else if ((nl = s.indexOf('\n')) >= 0) {
392 nll = 1;
393 }
394 if (nl >= 0) {
395 // break up newlines
396 if (nl == 0) {
397 JPanel breakPanel = new JPanel() {
398 public Dimension getPreferredSize() {
399 Font f = getFont();
400
401 if (f != null) {
402 return new Dimension(1, f.getSize() + 2);
403 }
404 return new Dimension(0, 0);
405 }
406 };
407 breakPanel.setName("OptionPane.break");
408 addMessageComponents(container, cons, breakPanel, maxll,
409 true);
410 } else {
411 addMessageComponents(container, cons, s.substring(0, nl),
412 maxll, false);
413 }
414 addMessageComponents(container, cons, s.substring(nl + nll), maxll,
415 false);
416
417 } else if (len > maxll) {
418 Container c = Box.createVerticalBox();
419 c.setName("OptionPane.verticalBox");
420 burstStringInto(c, s, maxll);
421 addMessageComponents(container, cons, c, maxll, true );
422
423 } else {
424 JLabel label;
425 label = new JLabel( s, JLabel.LEADING );
426 label.setName("OptionPane.label");
427 configureMessageLabel(label);
428 addMessageComponents(container, cons, label, maxll, true);
429 }
430 }
431 }
432
433 /**
434 * Returns the message to display from the JOptionPane the receiver is
435 * providing the look and feel for.
436 */
437 protected Object getMessage() {
438 inputComponent = null;
439 if (optionPane != null) {
440 if (optionPane.getWantsInput()) {
441 /* Create a user component to capture the input. If the
442 selectionValues are non null the component and there
443 are < 20 values it'll be a combobox, if non null and
444 >= 20, it'll be a list, otherwise it'll be a textfield. */
445 Object message = optionPane.getMessage();
446 Object[] sValues = optionPane.getSelectionValues();
447 Object inputValue = optionPane
448 .getInitialSelectionValue();
449 JComponent toAdd;
450
451 if (sValues != null) {
452 if (sValues.length < 20) {
453 JComboBox cBox = new JComboBox();
454
455 cBox.setName("OptionPane.comboBox");
456 for(int counter = 0, maxCounter = sValues.length;
457 counter < maxCounter; counter++) {
458 cBox.addItem(sValues[counter]);
459 }
460 if (inputValue != null) {
461 cBox.setSelectedItem(inputValue);
462 }
463 inputComponent = cBox;
464 toAdd = cBox;
465
466 } else {
467 JList list = new JList(sValues);
468 JScrollPane sp = new JScrollPane(list);
469
470 sp.setName("OptionPane.scrollPane");
471 list.setName("OptionPane.list");
472 list.setVisibleRowCount(10);
473 list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
474 if(inputValue != null)
475 list.setSelectedValue(inputValue, true);
476 list.addMouseListener(getHandler());
477 toAdd = sp;
478 inputComponent = list;
479 }
480
481 } else {
482 MultiplexingTextField tf = new MultiplexingTextField(20);
483
484 tf.setName("OptionPane.textField");
485 tf.setKeyStrokes(new KeyStroke[] {
486 KeyStroke.getKeyStroke("ENTER") } );
487 if (inputValue != null) {
488 String inputString = inputValue.toString();
489 tf.setText(inputString);
490 tf.setSelectionStart(0);
491 tf.setSelectionEnd(inputString.length());
492 }
493 tf.addActionListener(getHandler());
494 toAdd = inputComponent = tf;
495 }
496
497 Object[] newMessage;
498
499 if (message == null) {
500 newMessage = new Object[1];
501 newMessage[0] = toAdd;
502
503 } else {
504 newMessage = new Object[2];
505 newMessage[0] = message;
506 newMessage[1] = toAdd;
507 }
508 return newMessage;
509 }
510 return optionPane.getMessage();
511 }
512 return null;
513 }
514
515 /**
516 * Creates and adds a JLabel representing the icon returned from
517 * <code>getIcon</code> to <code>top</code>. This is messaged from
518 * <code>createMessageArea</code>
519 */
520 protected void addIcon(Container top) {
521 /* Create the icon. */
522 Icon sideIcon = getIcon();
523
524 if (sideIcon != null) {
525 JLabel iconLabel = new JLabel(sideIcon);
526
527 iconLabel.setName("OptionPane.iconLabel");
528 iconLabel.setVerticalAlignment(SwingConstants.TOP);
529 top.add(iconLabel, BorderLayout.BEFORE_LINE_BEGINS);
530 }
531 }
532
533 /**
534 * Returns the icon from the JOptionPane the receiver is providing
535 * the look and feel for, or the default icon as returned from
536 * <code>getDefaultIcon</code>.
537 */
538 protected Icon getIcon() {
539 Icon mIcon = (optionPane == null ? null : optionPane.getIcon());
540
541 if(mIcon == null && optionPane != null)
542 mIcon = getIconForType(optionPane.getMessageType());
543 return mIcon;
544 }
545
546 /**
547 * Returns the icon to use for the passed in type.
548 */
549 protected Icon getIconForType(int messageType) {
550 if(messageType < 0 || messageType > 3)
551 return null;
552 String propertyName = null;
553 switch(messageType) {
554 case 0:
555 propertyName = "OptionPane.errorIcon";
556 break;
557 case 1:
558 propertyName = "OptionPane.informationIcon";
559 break;
560 case 2:
561 propertyName = "OptionPane.warningIcon";
562 break;
563 case 3:
564 propertyName = "OptionPane.questionIcon";
565 break;
566 }
567 if (propertyName != null) {
568 return (Icon)DefaultLookup.get(optionPane, this, propertyName);
569 }
570 return null;
571 }
572
573 /**
574 * Returns the maximum number of characters to place on a line.
575 */
576 protected int getMaxCharactersPerLineCount() {
577 return optionPane.getMaxCharactersPerLineCount();
578 }
579
580 /**
581 * Recursively creates new JLabel instances to represent <code>d</code>.
582 * Each JLabel instance is added to <code>c</code>.
583 */
584 protected void burstStringInto(Container c, String d, int maxll) {
585 // Primitive line wrapping
586 int len = d.length();
587 if (len <= 0)
588 return;
589 if (len > maxll) {
590 int p = d.lastIndexOf(' ', maxll);
591 if (p <= 0)
592 p = d.indexOf(' ', maxll);
593 if (p > 0 && p < len) {
594 burstStringInto(c, d.substring(0, p), maxll);
595 burstStringInto(c, d.substring(p + 1), maxll);
596 return;
597 }
598 }
599 JLabel label = new JLabel(d, JLabel.LEFT);
600 label.setName("OptionPane.label");
601 configureMessageLabel(label);
602 c.add(label);
603 }
604
605 protected Container createSeparator() {
606 return null;
607 }
608
609 /**
610 * Creates and returns a Container containing the buttons. The buttons
611 * are created by calling <code>getButtons</code>.
612 */
613 protected Container createButtonArea() {
614 JPanel bottom = new JPanel();
615 Border border = (Border)DefaultLookup.get(optionPane, this,
616 "OptionPane.buttonAreaBorder");
617 bottom.setName("OptionPane.buttonArea");
618 if (border != null) {
619 bottom.setBorder(border);
620 }
621 bottom.setLayout(new ButtonAreaLayout(
622 DefaultLookup.getBoolean(optionPane, this,
623 "OptionPane.sameSizeButtons", true),
624 DefaultLookup.getInt(optionPane, this, "OptionPane.buttonPadding",
625 6),
626 DefaultLookup.getInt(optionPane, this,
627 "OptionPane.buttonOrientation", SwingConstants.CENTER),
628 DefaultLookup.getBoolean(optionPane, this, "OptionPane.isYesLast",
629 false)));
630 addButtonComponents(bottom, getButtons(), getInitialValueIndex());
631 return bottom;
632 }
633
634 /**
635 * Creates the appropriate object to represent each of the objects in
636 * <code>buttons</code> and adds it to <code>container</code>. This
637 * differs from addMessageComponents in that it will recurse on
638 * <code>buttons</code> and that if button is not a Component
639 * it will create an instance of JButton.
640 */
641 protected void addButtonComponents(Container container, Object[] buttons,
642 int initialIndex) {
643 if (buttons != null && buttons.length > 0) {
644 boolean sizeButtonsToSame = getSizeButtonsToSameWidth();
645 boolean createdAll = true;
646 int numButtons = buttons.length;
647 JButton[] createdButtons = null;
648 int maxWidth = 0;
649
650 if (sizeButtonsToSame) {
651 createdButtons = new JButton[numButtons];
652 }
653
654 for(int counter = 0; counter < numButtons; counter++) {
655 Object button = buttons[counter];
656 Component newComponent;
657
658 if (button instanceof Component) {
659 createdAll = false;
660 newComponent = (Component)button;
661 container.add(newComponent);
662 hasCustomComponents = true;
663
664 } else {
665 JButton aButton;
666
667 if (button instanceof ButtonFactory) {
668 aButton = ((ButtonFactory)button).createButton();
669 }
670 else if (button instanceof Icon)
671 aButton = new JButton((Icon)button);
672 else
673 aButton = new JButton(button.toString());
674
675 aButton.setName("OptionPane.button");
676 aButton.setMultiClickThreshhold(DefaultLookup.getInt(
677 optionPane, this, "OptionPane.buttonClickThreshhold",
678 0));
679 configureButton(aButton);
680
681 container.add(aButton);
682
683 ActionListener buttonListener = createButtonActionListener(counter);
684 if (buttonListener != null) {
685 aButton.addActionListener(buttonListener);
686 }
687 newComponent = aButton;
688 }
689 if (sizeButtonsToSame && createdAll &&
690 (newComponent instanceof JButton)) {
691 createdButtons[counter] = (JButton)newComponent;
692 maxWidth = Math.max(maxWidth,
693 newComponent.getMinimumSize().width);
694 }
695 if (counter == initialIndex) {
696 initialFocusComponent = newComponent;
697 if (initialFocusComponent instanceof JButton) {
698 JButton defaultB = (JButton)initialFocusComponent;
699 defaultB.addHierarchyListener(new HierarchyListener() {
700 public void hierarchyChanged(HierarchyEvent e) {
701 if ((e.getChangeFlags() &
702 HierarchyEvent.PARENT_CHANGED) != 0) {
703 JButton defaultButton = (JButton) e.getComponent();
704 JRootPane root =
705 SwingUtilities.getRootPane(defaultButton);
706 if (root != null) {
707 root.setDefaultButton(defaultButton);
708 }
709 }
710 }
711 });
712 }
713 }
714 }
715 ((ButtonAreaLayout)container.getLayout()).
716 setSyncAllWidths((sizeButtonsToSame && createdAll));
717 /* Set the padding, windows seems to use 8 if <= 2 components,
718 otherwise 4 is used. It may actually just be the size of the
719 buttons is always the same, not sure. */
720 if (DefaultLookup.getBoolean(optionPane, this,
721 "OptionPane.setButtonMargin", true) && sizeButtonsToSame &&
722 createdAll) {
723 JButton aButton;
724 int padSize;
725
726 padSize = (numButtons <= 2? 8 : 4);
727
728 for(int counter = 0; counter < numButtons; counter++) {
729 aButton = createdButtons[counter];
730 aButton.setMargin(new Insets(2, padSize, 2, padSize));
731 }
732 }
733 }
734 }
735
736 protected ActionListener createButtonActionListener(int buttonIndex) {
737 return new ButtonActionListener(buttonIndex);
738 }
739
740 /**
741 * Returns the buttons to display from the JOptionPane the receiver is
742 * providing the look and feel for. If the JOptionPane has options
743 * set, they will be provided, otherwise if the optionType is
744 * YES_NO_OPTION, yesNoOptions is returned, if the type is
745 * YES_NO_CANCEL_OPTION yesNoCancelOptions is returned, otherwise
746 * defaultButtons are returned.
747 */
748 protected Object[] getButtons() {
749 if (optionPane != null) {
750 Object[] suppliedOptions = optionPane.getOptions();
751
752 if (suppliedOptions == null) {
753 Object[] defaultOptions;
754 int type = optionPane.getOptionType();
755 Locale l = optionPane.getLocale();
756 int minimumWidth =
757 DefaultLookup.getInt(optionPane, this,
758 "OptionPane.buttonMinimumWidth",-1);
759 if (type == JOptionPane.YES_NO_OPTION) {
760 defaultOptions = new ButtonFactory[2];
761 defaultOptions[0] = new ButtonFactory(
762 UIManager.getString("OptionPane.yesButtonText", l),
763 getMnemonic("OptionPane.yesButtonMnemonic", l),
764 (Icon)DefaultLookup.get(optionPane, this,
765 "OptionPane.yesIcon"), minimumWidth);
766 defaultOptions[1] = new ButtonFactory(
767 UIManager.getString("OptionPane.noButtonText", l),
768 getMnemonic("OptionPane.noButtonMnemonic", l),
769 (Icon)DefaultLookup.get(optionPane, this,
770 "OptionPane.noIcon"), minimumWidth);
771 } else if (type == JOptionPane.YES_NO_CANCEL_OPTION) {
772 defaultOptions = new ButtonFactory[3];
773 defaultOptions[0] = new ButtonFactory(
774 UIManager.getString("OptionPane.yesButtonText", l),
775 getMnemonic("OptionPane.yesButtonMnemonic", l),
776 (Icon)DefaultLookup.get(optionPane, this,
777 "OptionPane.yesIcon"), minimumWidth);
778 defaultOptions[1] = new ButtonFactory(
779 UIManager.getString("OptionPane.noButtonText",l),
780 getMnemonic("OptionPane.noButtonMnemonic", l),
781 (Icon)DefaultLookup.get(optionPane, this,
782 "OptionPane.noIcon"), minimumWidth);
783 defaultOptions[2] = new ButtonFactory(
784 UIManager.getString("OptionPane.cancelButtonText",l),
785 getMnemonic("OptionPane.cancelButtonMnemonic", l),
786 (Icon)DefaultLookup.get(optionPane, this,
787 "OptionPane.cancelIcon"), minimumWidth);
788 } else if (type == JOptionPane.OK_CANCEL_OPTION) {
789 defaultOptions = new ButtonFactory[2];
790 defaultOptions[0] = new ButtonFactory(
791 UIManager.getString("OptionPane.okButtonText",l),
792 getMnemonic("OptionPane.okButtonMnemonic", l),
793 (Icon)DefaultLookup.get(optionPane, this,
794 "OptionPane.okIcon"), minimumWidth);
795 defaultOptions[1] = new ButtonFactory(
796 UIManager.getString("OptionPane.cancelButtonText",l),
797 getMnemonic("OptionPane.cancelButtonMnemonic", l),
798 (Icon)DefaultLookup.get(optionPane, this,
799 "OptionPane.cancelIcon"), minimumWidth);
800 } else {
801 defaultOptions = new ButtonFactory[1];
802 defaultOptions[0] = new ButtonFactory(
803 UIManager.getString("OptionPane.okButtonText",l),
804 getMnemonic("OptionPane.okButtonMnemonic", l),
805 (Icon)DefaultLookup.get(optionPane, this,
806 "OptionPane.okIcon"), minimumWidth);
807 }
808 return defaultOptions;
809
810 }
811 return suppliedOptions;
812 }
813 return null;
814 }
815
816 private int getMnemonic(String key, Locale l) {
817 String value = (String)UIManager.get(key, l);
818
819 if (value == null) {
820 return 0;
821 }
822 try {
823 return Integer.parseInt(value);
824 }
825 catch (NumberFormatException nfe) { }
826 return 0;
827 }
828
829 /**
830 * Returns true, basic L&F wants all the buttons to have the same
831 * width.
832 */
833 protected boolean getSizeButtonsToSameWidth() {
834 return true;
835 }
836
837 /**
838 * Returns the initial index into the buttons to select. The index
839 * is calculated from the initial value from the JOptionPane and
840 * options of the JOptionPane or 0.
841 */
842 protected int getInitialValueIndex() {
843 if (optionPane != null) {
844 Object iv = optionPane.getInitialValue();
845 Object[] options = optionPane.getOptions();
846
847 if(options == null) {
848 return 0;
849 }
850 else if(iv != null) {
851 for(int counter = options.length - 1; counter >= 0; counter--){
852 if(options[counter].equals(iv))
853 return counter;
854 }
855 }
856 }
857 return -1;
858 }
859
860 /**
861 * Sets the input value in the option pane the receiver is providing
862 * the look and feel for based on the value in the inputComponent.
863 */
864 protected void resetInputValue() {
865 if(inputComponent != null && (inputComponent instanceof JTextField)) {
866 optionPane.setInputValue(((JTextField)inputComponent).getText());
867
868 } else if(inputComponent != null &&
869 (inputComponent instanceof JComboBox)) {
870 optionPane.setInputValue(((JComboBox)inputComponent)
871 .getSelectedItem());
872 } else if(inputComponent != null) {
873 optionPane.setInputValue(((JList)inputComponent)
874 .getSelectedValue());
875 }
876 }
877
878
879 /**
880 * If inputComponent is non-null, the focus is requested on that,
881 * otherwise request focus on the default value
882 */
883 public void selectInitialValue(JOptionPane op) {
884 if (inputComponent != null)
885 inputComponent.requestFocus();
886 else {
887 if (initialFocusComponent != null)
888 initialFocusComponent.requestFocus();
889
890 if (initialFocusComponent instanceof JButton) {
891 JRootPane root = SwingUtilities.getRootPane(initialFocusComponent);
892 if (root != null) {
893 root.setDefaultButton((JButton)initialFocusComponent);
894 }
895 }
896 }
897 }
898
899 /**
900 * Returns true if in the last call to validateComponent the message
901 * or buttons contained a subclass of Component.
902 */
903 public boolean containsCustomComponents(JOptionPane op) {
904 return hasCustomComponents;
905 }
906
907
908 /**
909 * <code>ButtonAreaLayout</code> behaves in a similar manner to
910 * <code>FlowLayout</code>. It lays out all components from left to
911 * right. If <code>syncAllWidths</code> is true, the widths of each
912 * component will be set to the largest preferred size width.
913 *
914 * This class should be treated as a &quot;protected&quot; inner class.
915 * Instantiate it only within subclasses of {@code BasicOptionPaneUI}.
916 */
917 public static class ButtonAreaLayout implements LayoutManager {
918 protected boolean syncAllWidths;
919 protected int padding;
920 /** If true, children are lumped together in parent. */
921 protected boolean centersChildren;
922 private int orientation;
923 private boolean reverseButtons;
924 /**
925 * Indicates whether or not centersChildren should be used vs
926 * the orientation. This is done for backward compatability
927 * for subclassers.
928 */
929 private boolean useOrientation;
930
931 public ButtonAreaLayout(boolean syncAllWidths, int padding) {
932 this.syncAllWidths = syncAllWidths;
933 this.padding = padding;
934 centersChildren = true;
935 useOrientation = false;
936 }
937
938 ButtonAreaLayout(boolean syncAllSizes, int padding, int orientation,
939 boolean reverseButtons) {
940 this(syncAllSizes, padding);
941 useOrientation = true;
942 this.orientation = orientation;
943 this.reverseButtons = reverseButtons;
944 }
945
946 public void setSyncAllWidths(boolean newValue) {
947 syncAllWidths = newValue;
948 }
949
950 public boolean getSyncAllWidths() {
951 return syncAllWidths;
952 }
953
954 public void setPadding(int newPadding) {
955 this.padding = newPadding;
956 }
957
958 public int getPadding() {
959 return padding;
960 }
961
962 public void setCentersChildren(boolean newValue) {
963 centersChildren = newValue;
964 useOrientation = false;
965 }
966
967 public boolean getCentersChildren() {
968 return centersChildren;
969 }
970
971 private int getOrientation(Container container) {
972 if (!useOrientation) {
973 return SwingConstants.CENTER;
974 }
975 if (container.getComponentOrientation().isLeftToRight()) {
976 return orientation;
977 }
978 switch (orientation) {
979 case SwingConstants.LEFT:
980 return SwingConstants.RIGHT;
981 case SwingConstants.RIGHT:
982 return SwingConstants.LEFT;
983 case SwingConstants.CENTER:
984 return SwingConstants.CENTER;
985 }
986 return SwingConstants.LEFT;
987 }
988
989 public void addLayoutComponent(String string, Component comp) {
990 }
991
992 public void layoutContainer(Container container) {
993 Component[] children = container.getComponents();
994
995 if(children != null && children.length > 0) {
996 int numChildren = children.length;
997 Insets insets = container.getInsets();
998 int maxWidth = 0;
999 int maxHeight = 0;
1000 int totalButtonWidth = 0;
1001 int x = 0;
1002 int xOffset = 0;
1003 boolean ltr = container.getComponentOrientation().
1004 isLeftToRight();
1005 boolean reverse = (ltr) ? reverseButtons : !reverseButtons;
1006
1007 for(int counter = 0; counter < numChildren; counter++) {
1008 Dimension pref = children[counter].getPreferredSize();
1009 maxWidth = Math.max(maxWidth, pref.width);
1010 maxHeight = Math.max(maxHeight, pref.height);
1011 totalButtonWidth += pref.width;
1012 }
1013 if (getSyncAllWidths()) {
1014 totalButtonWidth = maxWidth * numChildren;
1015 }
1016 totalButtonWidth += (numChildren - 1) * padding;
1017
1018 switch (getOrientation(container)) {
1019 case SwingConstants.LEFT:
1020 x = insets.left;
1021 break;
1022 case SwingConstants.RIGHT:
1023 x = container.getWidth() - insets.right - totalButtonWidth;
1024 break;
1025 case SwingConstants.CENTER:
1026 if (getCentersChildren() || numChildren < 2) {
1027 x = (container.getWidth() - totalButtonWidth) / 2;
1028 }
1029 else {
1030 x = insets.left;
1031 if (getSyncAllWidths()) {
1032 xOffset = (container.getWidth() - insets.left -
1033 insets.right - totalButtonWidth) /
1034 (numChildren - 1) + maxWidth;
1035 }
1036 else {
1037 xOffset = (container.getWidth() - insets.left -
1038 insets.right - totalButtonWidth) /
1039 (numChildren - 1);
1040 }
1041 }
1042 break;
1043 }
1044
1045 for (int counter = 0; counter < numChildren; counter++) {
1046 int index = (reverse) ? numChildren - counter - 1 :
1047 counter;
1048 Dimension pref = children[index].getPreferredSize();
1049
1050 if (getSyncAllWidths()) {
1051 children[index].setBounds(x, insets.top,
1052 maxWidth, maxHeight);
1053 }
1054 else {
1055 children[index].setBounds(x, insets.top, pref.width,
1056 pref.height);
1057 }
1058 if (xOffset != 0) {
1059 x += xOffset;
1060 }
1061 else {
1062 x += children[index].getWidth() + padding;
1063 }
1064 }
1065 }
1066 }
1067
1068 public Dimension minimumLayoutSize(Container c) {
1069 if(c != null) {
1070 Component[] children = c.getComponents();
1071
1072 if(children != null && children.length > 0) {
1073 Dimension aSize;
1074 int numChildren = children.length;
1075 int height = 0;
1076 Insets cInsets = c.getInsets();
1077 int extraHeight = cInsets.top + cInsets.bottom;
1078 int extraWidth = cInsets.left + cInsets.right;
1079
1080 if (syncAllWidths) {
1081 int maxWidth = 0;
1082
1083 for(int counter = 0; counter < numChildren; counter++){
1084 aSize = children[counter].getPreferredSize();
1085 height = Math.max(height, aSize.height);
1086 maxWidth = Math.max(maxWidth, aSize.width);
1087 }
1088 return new Dimension(extraWidth + (maxWidth * numChildren) +
1089 (numChildren - 1) * padding,
1090 extraHeight + height);
1091 }
1092 else {
1093 int totalWidth = 0;
1094
1095 for(int counter = 0; counter < numChildren; counter++){
1096 aSize = children[counter].getPreferredSize();
1097 height = Math.max(height, aSize.height);
1098 totalWidth += aSize.width;
1099 }
1100 totalWidth += ((numChildren - 1) * padding);
1101 return new Dimension(extraWidth + totalWidth, extraHeight + height);
1102 }
1103 }
1104 }
1105 return new Dimension(0, 0);
1106 }
1107
1108 public Dimension preferredLayoutSize(Container c) {
1109 return minimumLayoutSize(c);
1110 }
1111
1112 public void removeLayoutComponent(Component c) { }
1113 }
1114
1115
1116 /**
1117 * This class should be treated as a &quot;protected&quot; inner class.
1118 * Instantiate it only within subclasses of {@code BasicOptionPaneUI}.
1119 */
1120 public class PropertyChangeHandler implements PropertyChangeListener {
1121 /**
1122 * If the source of the PropertyChangeEvent <code>e</code> equals the
1123 * optionPane and is one of the ICON_PROPERTY, MESSAGE_PROPERTY,
1124 * OPTIONS_PROPERTY or INITIAL_VALUE_PROPERTY,
1125 * validateComponent is invoked.
1126 */
1127 public void propertyChange(PropertyChangeEvent e) {
1128 getHandler().propertyChange(e);
1129 }
1130 }
1131
1132 /**
1133 * Configures any necessary colors/fonts for the specified label
1134 * used representing the message.
1135 */
1136 private void configureMessageLabel(JLabel label) {
1137 Color color = (Color)DefaultLookup.get(optionPane, this,
1138 "OptionPane.messageForeground");
1139 if (color != null) {
1140 label.setForeground(color);
1141 }
1142 Font messageFont = (Font)DefaultLookup.get(optionPane, this,
1143 "OptionPane.messageFont");
1144 if (messageFont != null) {
1145 label.setFont(messageFont);
1146 }
1147 }
1148
1149 /**
1150 * Configures any necessary colors/fonts for the specified button
1151 * used representing the button portion of the optionpane.
1152 */
1153 private void configureButton(JButton button) {
1154 Font buttonFont = (Font)DefaultLookup.get(optionPane, this,
1155 "OptionPane.buttonFont");
1156 if (buttonFont != null) {
1157 button.setFont(buttonFont);
1158 }
1159 }
1160
1161 /**
1162 * This class should be treated as a &quot;protected&quot; inner class.
1163 * Instantiate it only within subclasses of {@code BasicOptionPaneUI}.
1164 */
1165 public class ButtonActionListener implements ActionListener {
1166 protected int buttonIndex;
1167
1168 public ButtonActionListener(int buttonIndex) {
1169 this.buttonIndex = buttonIndex;
1170 }
1171
1172 public void actionPerformed(ActionEvent e) {
1173 if (optionPane != null) {
1174 int optionType = optionPane.getOptionType();
1175 Object[] options = optionPane.getOptions();
1176
1177 /* If the option pane takes input, then store the input value
1178 * if custom options were specified, if the option type is
1179 * DEFAULT_OPTION, OR if option type is set to a predefined
1180 * one and the user chose the affirmative answer.
1181 */
1182 if (inputComponent != null) {
1183 if (options != null ||
1184 optionType == JOptionPane.DEFAULT_OPTION ||
1185 ((optionType == JOptionPane.YES_NO_OPTION ||
1186 optionType == JOptionPane.YES_NO_CANCEL_OPTION ||
1187 optionType == JOptionPane.OK_CANCEL_OPTION) &&
1188 buttonIndex == 0)) {
1189 resetInputValue();
1190 }
1191 }
1192 if (options == null) {
1193 if (optionType == JOptionPane.OK_CANCEL_OPTION &&
1194 buttonIndex == 1) {
1195 optionPane.setValue(Integer.valueOf(2));
1196
1197 } else {
1198 optionPane.setValue(Integer.valueOf(buttonIndex));
1199 }
1200 } else {
1201 optionPane.setValue(options[buttonIndex]);
1202 }
1203 }
1204 }
1205 }
1206
1207
1208 private class Handler implements ActionListener, MouseListener,
1209 PropertyChangeListener {
1210 //
1211 // ActionListener
1212 //
1213 public void actionPerformed(ActionEvent e) {
1214 optionPane.setInputValue(((JTextField)e.getSource()).getText());
1215 }
1216
1217
1218 //
1219 // MouseListener
1220 //
1221 public void mouseClicked(MouseEvent e) {
1222 }
1223
1224 public void mouseReleased(MouseEvent e) {
1225 }
1226
1227 public void mouseEntered(MouseEvent e) {
1228 }
1229
1230 public void mouseExited(MouseEvent e) {
1231 }
1232
1233 public void mousePressed(MouseEvent e) {
1234 if (e.getClickCount() == 2) {
1235 JList list = (JList)e.getSource();
1236 int index = list.locationToIndex(e.getPoint());
1237
1238 optionPane.setInputValue(list.getModel().getElementAt(index));
1239 }
1240 }
1241
1242 //
1243 // PropertyChangeListener
1244 //
1245 public void propertyChange(PropertyChangeEvent e) {
1246 if(e.getSource() == optionPane) {
1247 // Option Pane Auditory Cue Activation
1248 // only respond to "ancestor" changes
1249 // the idea being that a JOptionPane gets a JDialog when it is
1250 // set to appear and loses it's JDialog when it is dismissed.
1251 if ("ancestor" == e.getPropertyName()) {
1252 JOptionPane op = (JOptionPane)e.getSource();
1253 boolean isComingUp;
1254
1255 // if the old value is null, then the JOptionPane is being
1256 // created since it didn't previously have an ancestor.
1257 if (e.getOldValue() == null) {
1258 isComingUp = true;
1259 } else {
1260 isComingUp = false;
1261 }
1262
1263 // figure out what to do based on the message type
1264 switch (op.getMessageType()) {
1265 case JOptionPane.PLAIN_MESSAGE:
1266 if (isComingUp) {
1267 BasicLookAndFeel.playSound(optionPane,
1268 "OptionPane.informationSound");
1269 }
1270 break;
1271 case JOptionPane.QUESTION_MESSAGE:
1272 if (isComingUp) {
1273 BasicLookAndFeel.playSound(optionPane,
1274 "OptionPane.questionSound");
1275 }
1276 break;
1277 case JOptionPane.INFORMATION_MESSAGE:
1278 if (isComingUp) {
1279 BasicLookAndFeel.playSound(optionPane,
1280 "OptionPane.informationSound");
1281 }
1282 break;
1283 case JOptionPane.WARNING_MESSAGE:
1284 if (isComingUp) {
1285 BasicLookAndFeel.playSound(optionPane,
1286 "OptionPane.warningSound");
1287 }
1288 break;
1289 case JOptionPane.ERROR_MESSAGE:
1290 if (isComingUp) {
1291 BasicLookAndFeel.playSound(optionPane,
1292 "OptionPane.errorSound");
1293 }
1294 break;
1295 default:
1296 System.err.println("Undefined JOptionPane type: " +
1297 op.getMessageType());
1298 break;
1299 }
1300 }
1301 // Visual activity
1302 String changeName = e.getPropertyName();
1303
1304 if(changeName == JOptionPane.OPTIONS_PROPERTY ||
1305 changeName == JOptionPane.INITIAL_VALUE_PROPERTY ||
1306 changeName == JOptionPane.ICON_PROPERTY ||
1307 changeName == JOptionPane.MESSAGE_TYPE_PROPERTY ||
1308 changeName == JOptionPane.OPTION_TYPE_PROPERTY ||
1309 changeName == JOptionPane.MESSAGE_PROPERTY ||
1310 changeName == JOptionPane.SELECTION_VALUES_PROPERTY ||
1311 changeName == JOptionPane.INITIAL_SELECTION_VALUE_PROPERTY ||
1312 changeName == JOptionPane.WANTS_INPUT_PROPERTY) {
1313 uninstallComponents();
1314 installComponents();
1315 optionPane.validate();
1316 }
1317 else if (changeName == "componentOrientation") {
1318 ComponentOrientation o = (ComponentOrientation)e.getNewValue();
1319 JOptionPane op = (JOptionPane)e.getSource();
1320 if (o != e.getOldValue()) {
1321 op.applyComponentOrientation(o);
1322 }
1323 }
1324 }
1325 }
1326 }
1327
1328
1329 //
1330 // Classes used when optionPane.getWantsInput returns true.
1331 //
1332
1333 /**
1334 * A JTextField that allows you to specify an array of KeyStrokes that
1335 * that will have their bindings processed regardless of whether or
1336 * not they are registered on the JTextField. This is used as we really
1337 * want the ActionListener to be notified so that we can push the
1338 * change to the JOptionPane, but we also want additional bindings
1339 * (those of the JRootPane) to be processed as well.
1340 */
1341 private static class MultiplexingTextField extends JTextField {
1342 private KeyStroke[] strokes;
1343
1344 MultiplexingTextField(int cols) {
1345 super(cols);
1346 }
1347
1348 /**
1349 * Sets the KeyStrokes that will be additional processed for
1350 * ancestor bindings.
1351 */
1352 void setKeyStrokes(KeyStroke[] strokes) {
1353 this.strokes = strokes;
1354 }
1355
1356 protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
1357 int condition, boolean pressed) {
1358 boolean processed = super.processKeyBinding(ks, e, condition,
1359 pressed);
1360
1361 if (processed && condition != JComponent.WHEN_IN_FOCUSED_WINDOW) {
1362 for (int counter = strokes.length - 1; counter >= 0;
1363 counter--) {
1364 if (strokes[counter].equals(ks)) {
1365 // Returning false will allow further processing
1366 // of the bindings, eg our parent Containers will get a
1367 // crack at them.
1368 return false;
1369 }
1370 }
1371 }
1372 return processed;
1373 }
1374 }
1375
1376
1377
1378 /**
1379 * Registered in the ActionMap. Sets the value of the option pane
1380 * to <code>JOptionPane.CLOSED_OPTION</code>.
1381 */
1382 private static class Actions extends UIAction {
1383 private static final String CLOSE = "close";
1384
1385 Actions(String key) {
1386 super(key);
1387 }
1388
1389 public void actionPerformed(ActionEvent e) {
1390 if (getName() == CLOSE) {
1391 JOptionPane optionPane = (JOptionPane)e.getSource();
1392
1393 optionPane.setValue(Integer.valueOf(JOptionPane.CLOSED_OPTION));
1394 }
1395 }
1396 }
1397
1398
1399 /**
1400 * This class is used to create the default buttons. This indirection is
1401 * used so that addButtonComponents can tell which Buttons were created
1402 * by us vs subclassers or from the JOptionPane itself.
1403 */
1404 private static class ButtonFactory {
1405 private String text;
1406 private int mnemonic;
1407 private Icon icon;
1408 private int minimumWidth = -1;
1409
1410 ButtonFactory(String text, int mnemonic, Icon icon, int minimumWidth) {
1411 this.text = text;
1412 this.mnemonic = mnemonic;
1413 this.icon = icon;
1414 this.minimumWidth = minimumWidth;
1415 }
1416
1417 JButton createButton() {
1418 JButton button;
1419
1420 if (minimumWidth > 0) {
1421 button = new ConstrainedButton(text, minimumWidth);
1422 } else {
1423 button = new JButton(text);
1424 }
1425 if (icon != null) {
1426 button.setIcon(icon);
1427 }
1428 if (mnemonic != 0) {
1429 button.setMnemonic(mnemonic);
1430 }
1431 return button;
1432 }
1433
1434 private static class ConstrainedButton extends JButton {
1435 int minimumWidth;
1436
1437 ConstrainedButton(String text, int minimumWidth) {
1438 super(text);
1439 this.minimumWidth = minimumWidth;
1440 }
1441
1442 public Dimension getMinimumSize() {
1443 Dimension min = super.getMinimumSize();
1444 min.width = Math.max(min.width, minimumWidth);
1445 return min;
1446 }
1447
1448 public Dimension getPreferredSize() {
1449 Dimension pref = super.getPreferredSize();
1450 pref.width = Math.max(pref.width, minimumWidth);
1451 return pref;
1452 }
1453 }
1454 }
1455}