001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.options;
043:
044: import java.awt.Color;
045: import java.awt.Cursor;
046: import java.awt.Dimension;
047: import java.awt.Font;
048: import java.awt.Frame;
049: import java.awt.GridBagConstraints;
050: import java.awt.GridBagLayout;
051: import java.awt.BorderLayout;
052: import java.awt.CardLayout;
053: import java.awt.Rectangle;
054: import java.awt.Window;
055: import java.awt.event.ActionEvent;
056: import java.awt.event.KeyEvent;
057: import java.awt.event.MouseEvent;
058: import java.awt.event.MouseListener;
059: import java.beans.PropertyChangeEvent;
060: import java.beans.PropertyChangeListener;
061: import java.util.ArrayList;
062: import java.util.Iterator;
063: import java.util.LinkedHashMap;
064: import java.util.List;
065: import java.util.Map;
066: import javax.swing.AbstractAction;
067: import javax.swing.BorderFactory;
068: import javax.swing.InputMap;
069: import javax.swing.JComponent;
070: import javax.swing.JLabel;
071: import javax.swing.JPanel;
072: import javax.swing.KeyStroke;
073: import javax.swing.SwingUtilities;
074: import javax.swing.UIManager;
075: import javax.swing.border.CompoundBorder;
076: import javax.swing.border.EmptyBorder;
077: import javax.swing.border.LineBorder;
078: import org.netbeans.modules.options.ui.VariableBorder;
079: import org.netbeans.spi.options.OptionsPanelController;
080: import org.openide.awt.Mnemonics;
081: import org.openide.util.HelpCtx;
082: import org.openide.util.NbBundle;
083: import org.openide.util.NbPreferences;
084: import org.openide.util.RequestProcessor;
085: import org.openide.util.Utilities;
086:
087: public class OptionsPanel extends JPanel {
088: private JPanel pCategories;
089: private JPanel pCategories2;
090: private JPanel pOptions;
091: private CardLayout cLayout;
092:
093: private Map<String, CategoryButton> buttons = new LinkedHashMap<String, CategoryButton>();
094: private final boolean isMac = UIManager.getLookAndFeel().getID()
095: .equals("Aqua");
096: private Color selected = isMac ? new Color(221, 221, 221)
097: : new Color(193, 210, 238);
098: private Color selectedB = isMac ? new Color(183, 183, 183)
099: : new Color(149, 106, 197);
100: private Color highlighted = isMac ? new Color(221, 221, 221)
101: : new Color(224, 232, 246);
102: private Color highlightedB = new Color(152, 180, 226);
103: private Color iconViewBorder = new Color(127, 157, 185);
104: private ControllerListener controllerListener = new ControllerListener();
105:
106: private final Color borderMac = new Color(141, 141, 141);
107: private final Font labelFontMac = new Font("Lucida Grande", 0, 10);
108:
109: private static String loc(String key) {
110: return NbBundle.getMessage(OptionsPanel.class, key);
111: }
112:
113: /** Creates new form OptionsPanel */
114: public OptionsPanel() {
115: this (null);
116: }
117:
118: public OptionsPanel(String categoryID) {
119: // init UI components, layout and actions, and add some default values
120: initUI(categoryID);
121: }
122:
123: private String getCategoryID(String categoryID) {
124: return categoryID == null ? CategoryModel.getInstance()
125: .getCurrentCategoryID() : categoryID;
126: }
127:
128: void initCurrentCategory(final String categoryID) {
129: //generalpanel should be moved to core/options and then could be implemented better
130: //generalpanel doesn't need lookup
131: boolean isGeneralPanel = "General"
132: .equals(getCategoryID(categoryID));//NOI18N
133: if (CategoryModel.getInstance().isLookupInitialized()
134: || isGeneralPanel) {
135: setCurrentCategory(CategoryModel.getInstance().getCategory(
136: getCategoryID(categoryID)));
137: initActions();
138: } else {
139: RequestProcessor.getDefault().post(new Runnable() {
140: public void run() {
141: SwingUtilities.invokeLater(new Runnable() {
142: public void run() {
143: // change cursor
144: Frame[] all = Frame.getFrames();
145: if (all == null || all.length == 0) {
146: return;
147: }
148: final Frame frame = all[0];
149: final Cursor cursor = frame.getCursor();
150: frame
151: .setCursor(Cursor
152: .getPredefinedCursor(Cursor.WAIT_CURSOR));
153: setCursor(Cursor
154: .getPredefinedCursor(Cursor.WAIT_CURSOR));
155: setCurrentCategory(CategoryModel
156: .getInstance().getCategory(
157: getCategoryID(categoryID)));
158: initActions();
159: // reset cursor
160: frame.setCursor(cursor);
161: setCursor(cursor);
162: }
163: });
164: }
165: }, 500);
166: }
167: }
168:
169: private void setCurrentCategory(
170: final CategoryModel.Category category) {
171: CategoryModel.Category oldCategory = CategoryModel
172: .getInstance().getCurrent();
173: if (oldCategory != null) {
174: (buttons.get(oldCategory.getID())).setNormal();
175: }
176: if (category != null) {
177: (buttons.get(category.getID())).setSelected();
178: }
179:
180: CategoryModel.getInstance().setCurrent(category);
181: JComponent component = category.getComponent();
182: category.update(controllerListener, false);
183: final Dimension size = component.getSize();
184: pOptions.add(component, category.getCategoryName());
185: cLayout.show(pOptions, category.getCategoryName());
186: checkSize(size);
187: /*if (CategoryModel.getInstance().getCurrent() != null) {
188: ((CategoryButton) buttons.get (CategoryModel.getInstance().getCurrentCategoryID())).requestFocus();
189: } */
190: firePropertyChange("buran"
191: + OptionsPanelController.PROP_HELP_CTX, null, null);
192: }
193:
194: HelpCtx getHelpCtx() {
195: return CategoryModel.getInstance().getHelpCtx();
196: }
197:
198: void update() {
199: CategoryModel.getInstance().update(controllerListener, true);
200: }
201:
202: void save() {
203: CategoryModel.getInstance().save();
204: }
205:
206: void cancel() {
207: CategoryModel.getInstance().cancel();
208: }
209:
210: boolean dataValid() {
211: return CategoryModel.getInstance().dataValid();
212: }
213:
214: boolean isChanged() {
215: return CategoryModel.getInstance().isChanged();
216: }
217:
218: boolean needsReinit() {
219: return CategoryModel.getInstance().needsReinit();
220: }
221:
222: // private methods .........................................................
223:
224: private void initUI(String categoryName) {
225: this .getAccessibleContext().setAccessibleDescription(
226: loc("ACS_OptionsPanel"));//NOI18N
227: // central panel
228: pOptions = new JPanel();
229: cLayout = new CardLayout();
230: pOptions.setLayout(cLayout);
231: pOptions.setPreferredSize(getUserSize());
232: JLabel label = new JLabel(loc("CTL_Loading_Options"));
233: label.setHorizontalAlignment(label.CENTER);
234: pOptions.add(label, label.getText());//NOI18N
235:
236: // icon view
237: pCategories2 = new JPanel(new GridBagLayout());
238: pCategories2.setBackground(Color.white);
239: pCategories2.setBorder(null);
240: addCategoryButtons();
241:
242: pCategories = new JPanel(new BorderLayout());
243: pCategories.setBorder(BorderFactory.createMatteBorder(0, 0, 1,
244: 0, Color.lightGray));
245: pCategories.setBackground(Color.white);
246: pCategories.add("Center", pCategories2);
247:
248: // layout
249: setLayout(new BorderLayout(10, 10));
250:
251: pOptions.setBorder(new CompoundBorder(new VariableBorder(null,
252: null, borderMac, null), BorderFactory
253: .createEmptyBorder(0, 5, 5, 5)));
254: add(pCategories, BorderLayout.NORTH);
255: add(pOptions, BorderLayout.CENTER);
256:
257: categoryName = getCategoryID(categoryName);
258: if (categoryName != null) {
259: CategoryModel.Category c = CategoryModel.getInstance()
260: .getCategory(getCategoryID(categoryName));
261:
262: CategoryButton b = buttons.get(categoryName);
263: if (b != null) {
264: b.setSelected();
265: }
266: }
267: }
268:
269: private void initActions() {
270: if (getActionMap().get("PREVIOUS") == null) {//NOI18N
271: InputMap inputMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
272:
273: inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0),
274: "PREVIOUS");//NOI18N
275: getActionMap().put("PREVIOUS", new PreviousAction());//NOI18N
276:
277: inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0),
278: "NEXT");//NOI18N
279: getActionMap().put("NEXT", new NextAction());//NOI18N
280: }
281: }
282:
283: private void addCategoryButtons() {
284: // remove old buttons
285: Iterator<CategoryButton> it = buttons.values().iterator();
286: while (it.hasNext()) {
287: removeButton((CategoryButton) it.next());
288: }
289: pCategories2.removeAll();
290: buttons = new LinkedHashMap<String, CategoryButton>();
291:
292: // add new buttons
293: Dimension maxSize = new Dimension(0, 0);
294: String[] names = CategoryModel.getInstance().getCategoryIDs();
295: for (int i = 0; i < names.length; i++) {
296: CategoryModel.Category category = CategoryModel
297: .getInstance().getCategory(names[i]);
298: CategoryButton button = addButton(category);
299: Dimension d = button.getPreferredSize();
300: maxSize.setSize(Math.max(maxSize.getWidth(), d.getWidth()),
301: Math.max(maxSize.getHeight(), d.getHeight()));
302:
303: }
304: it = buttons.values().iterator();
305: while (it.hasNext()) {
306: ((CategoryButton) it.next()).setPreferredSize(maxSize);
307: }
308:
309: addFakeButton();
310: }
311:
312: private CategoryButton addButton(CategoryModel.Category category) {
313: int index = buttons.size();
314: CategoryButton button = new CategoryButton(category);
315:
316: // add shortcut
317: KeyStroke keyStroke = KeyStroke.getKeyStroke(button
318: .getDisplayedMnemonic(), KeyEvent.ALT_MASK);
319: getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(keyStroke,
320: button);
321: getActionMap().put(button, new SelectAction(category));
322:
323: GridBagConstraints gbc = new GridBagConstraints();
324: gbc.anchor = GridBagConstraints.NORTHWEST;
325: gbc.fill = GridBagConstraints.BOTH;
326: gbc.weightx = 0.0;
327: gbc.weighty = 0.0;
328: gbc.gridx = index;
329: gbc.gridy = 0;
330: pCategories2.add(button, gbc);
331: buttons.put(category.getID(), button);
332: return button;
333: }
334:
335: private void removeButton(CategoryButton button) {
336: KeyStroke keyStroke = KeyStroke.getKeyStroke(button
337: .getDisplayedMnemonic(), KeyEvent.ALT_MASK);
338: getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
339: .remove(keyStroke);
340: getActionMap().remove(button);
341: }
342:
343: private void addFakeButton() {
344: /* i don't know a better workaround */
345: GridBagConstraints gbc = new GridBagConstraints();
346: gbc.fill = GridBagConstraints.BOTH;
347: gbc.weightx = 1.0;
348: gbc.weighty = 1.0;
349: gbc.gridy = 0;
350: pCategories2.add(new javax.swing.JLabel(""), gbc);
351: }
352:
353: private Dimension getInitSize() {
354: //if necessary init size could be chosen for individual resolutions differently
355: //DisplayMode dm = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode();
356: return new Dimension(750, 500);
357: }
358:
359: Dimension getUserSize() {
360: int w = NbPreferences.forModule(OptionsPanel.class).getInt(
361: "OptionsWidth", getInitSize().width);//NOI18N
362: int h = NbPreferences.forModule(OptionsPanel.class).getInt(
363: "OptionsHeight", getInitSize().height);//NOI18N
364: return new Dimension(w, h);
365: }
366:
367: @Override
368: public Dimension getPreferredSize() {
369: //#108865 Scrollbars appear on Options dialog - preferredSize mustn't exceed screenBounds.? - 100
370: //else NbPresenter will show up scrollbars
371: Dimension d = super .getPreferredSize();
372: final Rectangle screenBounds = Utilities
373: .getUsableScreenBounds();
374: return new Dimension(Math
375: .min(d.width, screenBounds.width - 101), Math.min(
376: d.height, screenBounds.height - 101));
377: }
378:
379: void storeUserSize() {
380: Dimension d = pOptions.getSize();
381: NbPreferences.forModule(OptionsPanel.class).putInt(
382: "OptionsWidth", d.width);//NOI18N
383: NbPreferences.forModule(OptionsPanel.class).putInt(
384: "OptionsHeight", d.height);//NOI18N
385: pOptions.setPreferredSize(d);
386: }
387:
388: private boolean checkSize(Dimension componentSize) {
389: boolean retval = false;
390: Dimension prefSize = pOptions.getPreferredSize();
391: Dimension userSize = getUserSize();
392: componentSize = new Dimension(Math.max(componentSize.width,
393: userSize.width), Math.max(componentSize.height,
394: userSize.height));
395: if (prefSize.width < componentSize.width
396: || prefSize.height < componentSize.height) {
397: Dimension newSize = new Dimension(Math.max(prefSize.width,
398: componentSize.width), Math.max(prefSize.height,
399: componentSize.height));
400: pOptions.setPreferredSize(newSize);
401: Window w = (Window) SwingUtilities.getAncestorOfClass(
402: Window.class, this );
403: invalidate();
404: if (w != null)
405: w.pack();
406: retval = true;
407: }
408: return retval;
409: }
410:
411: // innerclasses ............................................................
412:
413: private class SelectAction extends AbstractAction {
414: private CategoryModel.Category category;
415:
416: SelectAction(CategoryModel.Category category) {
417: this .category = category;
418: }
419:
420: public void actionPerformed(ActionEvent e) {
421: setCurrentCategory(category);
422: }
423: }
424:
425: private class SelectCurrentAction extends AbstractAction {
426: public void actionPerformed(ActionEvent e) {
427: CategoryModel.Category highlightedB = CategoryModel
428: .getInstance().getCategory(
429: CategoryModel.getInstance()
430: .getHighlitedCategoryID());
431: if (highlightedB != null) {
432: setCurrentCategory(highlightedB);
433: }
434: }
435: }
436:
437: private class PreviousAction extends AbstractAction {
438: public void actionPerformed(ActionEvent e) {
439: setCurrentCategory(CategoryModel.getInstance()
440: .getPreviousCategory());
441: }
442: }
443:
444: private class NextAction extends AbstractAction {
445: public void actionPerformed(ActionEvent e) {
446: setCurrentCategory(CategoryModel.getInstance()
447: .getNextCategory());
448: }
449: }
450:
451: class ControllerListener implements PropertyChangeListener {
452: public void propertyChange(PropertyChangeEvent evt) {
453: OptionsPanel.this .firePropertyChange("buran"
454: + evt.getPropertyName(), null, null);
455: }
456: }
457:
458: class CategoryButton extends JLabel implements MouseListener {
459: private final CategoryModel.Category category;
460:
461: CategoryButton(final CategoryModel.Category category) {
462: super (category.getIcon());
463: this .category = category;
464: Mnemonics
465: .setLocalizedText(this , category.getCategoryName());
466: setDisplayedMnemonic(0);
467: setOpaque(true);
468: setVerticalTextPosition(BOTTOM);
469: setHorizontalTextPosition(CENTER);
470: setHorizontalAlignment(CENTER);
471: addMouseListener(this );
472: setFocusable(false);
473: setFocusTraversalKeysEnabled(false);
474: setForeground(Color.black);
475:
476: if (isMac) {
477: setFont(labelFontMac);
478: setIconTextGap(2);
479: }
480:
481: setNormal();
482: }
483:
484: void setNormal() {
485: if (isMac) {
486: setBorder(new EmptyBorder(5, 6, 3, 6));
487: } else {
488: setBorder(new EmptyBorder(2, 4, 2, 4));
489: }
490: setBackground(Color.white);
491: }
492:
493: void setSelected() {
494: if (isMac) {
495: setBorder(new CompoundBorder(new VariableBorder(null,
496: selectedB, null, selectedB), BorderFactory
497: .createEmptyBorder(5, 5, 3, 5)));
498: } else {
499: setBorder(new CompoundBorder(new CompoundBorder(
500: new LineBorder(Color.white), new LineBorder(
501: selectedB)),
502: new EmptyBorder(0, 2, 0, 2)));
503: }
504: setBackground(selected);
505: }
506:
507: void setHighlighted() {
508: if (!isMac) {
509: setBorder(new CompoundBorder(new CompoundBorder(
510: new LineBorder(Color.white), new LineBorder(
511: highlightedB)), new EmptyBorder(0, 2,
512: 0, 2)));
513: setBackground(highlighted);
514: }
515: if (!category.isHighlited()) {
516: if (CategoryModel.getInstance()
517: .getHighlitedCategoryID() != null) {
518: CategoryButton b = buttons.get(CategoryModel
519: .getInstance().getHighlitedCategoryID());
520: if (b != null && !b.category.isCurrent()) {
521: b.setNormal();
522: }
523: }
524: CategoryModel.getInstance()
525: .setHighlited(category, true);
526: }
527: }
528:
529: public void mouseClicked(MouseEvent e) {
530: }
531:
532: public void mousePressed(MouseEvent e) {
533: if (!isMac
534: && CategoryModel.getInstance().getCurrent() != null) {
535: setSelected();
536: }
537: }
538:
539: public void mouseReleased(MouseEvent e) {
540: if (!category.isCurrent() && category.isHighlited()
541: && CategoryModel.getInstance().getCurrent() != null) {
542: setCurrentCategory(category);
543: }
544: }
545:
546: public void mouseEntered(MouseEvent e) {
547: if (!category.isCurrent()
548: && CategoryModel.getInstance().getCurrent() != null) {
549: setHighlighted();
550: } else {
551: CategoryModel.getInstance().setHighlited(
552: CategoryModel.getInstance().getCategory(
553: CategoryModel.getInstance()
554: .getHighlitedCategoryID()),
555: false);
556: }
557: }
558:
559: public void mouseExited(MouseEvent e) {
560: if (!category.isCurrent() && !isMac
561: && CategoryModel.getInstance().getCurrent() != null) {
562: setNormal();
563: }
564: }
565: }
566: }
|