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.palette.ui;
043:
044: import java.awt.Component;
045: import java.awt.Container;
046: import java.awt.Cursor;
047: import java.awt.Dimension;
048: import java.awt.LayoutManager;
049: import java.awt.Point;
050: import java.awt.Rectangle;
051: import java.util.ArrayList;
052: import javax.swing.BorderFactory;
053: import org.netbeans.modules.palette.Category;
054: import org.netbeans.modules.palette.Item;
055: import org.netbeans.modules.palette.Model;
056: import org.netbeans.modules.palette.ModelListener;
057: import org.netbeans.modules.palette.Settings;
058: import org.netbeans.modules.palette.Utils;
059: import org.netbeans.spi.palette.PaletteController;
060: import org.openide.util.Utilities;
061:
062: import java.awt.event.MouseAdapter;
063: import java.awt.event.MouseListener;
064: import java.awt.event.MouseEvent;
065: import java.beans.PropertyChangeEvent;
066: import java.beans.PropertyChangeListener;
067: import javax.swing.JComponent;
068: import javax.swing.JPanel;
069: import javax.swing.JPopupMenu;
070: import javax.swing.JScrollPane;
071: import javax.swing.Scrollable;
072: import javax.swing.SwingUtilities;
073: import javax.swing.UIManager;
074: import org.openide.nodes.Node;
075: import org.openide.util.HelpCtx;
076:
077: /**
078: * Palette's visual component implementation.
079: *
080: * @author S. Aubrecht
081: */
082: public class PalettePanel extends JPanel implements Scrollable {
083:
084: private static PalettePanel theInstance;
085:
086: private PaletteController controller;
087: private Model model;
088: private Settings settings;
089:
090: private ModelListener modelListener;
091: private PropertyChangeListener settingsListener;
092:
093: private CategoryDescriptor[] descriptors = new CategoryDescriptor[0];
094: private Category selectedCategory;
095:
096: private Object lock = new Object();
097: private MouseListener mouseListener;
098:
099: private JScrollPane scrollPane;
100:
101: private DnDSupport dndSupport;
102:
103: private PalettePanel() {
104: setLayout(new PaletteLayoutManager());
105: addMouseListener(mouseListener());
106:
107: dndSupport = new DnDSupport(this );
108:
109: setBackground(UIManager.getColor("Panel.background"));
110: }
111:
112: public static synchronized PalettePanel getDefault() {
113: if (null == theInstance) {
114: theInstance = new PalettePanel();
115: }
116: return theInstance;
117: }
118:
119: public JScrollPane getScrollPane() {
120: if (null == scrollPane) {
121: scrollPane = new JScrollPane(this );
122: scrollPane.setBorder(BorderFactory.createEmptyBorder());
123: scrollPane.addMouseListener(mouseListener());
124: scrollPane.getViewport().setBackground(
125: UIManager.getColor("Panel.background"));
126: // GTK L&F paints extra border around viewport, get rid of it
127: scrollPane.setViewportBorder(null);
128: }
129: return scrollPane;
130: }
131:
132: private CategoryDescriptor getCategoryDescriptor(Category category) {
133: for (int i = 0; i < descriptors.length; i++) {
134: CategoryDescriptor descriptor = descriptors[i];
135: if (descriptor.getCategory() == category)
136: return descriptor;
137: }
138: return null;
139: }
140:
141: private CategoryDescriptor[] computeDescriptors(
142: Category[] categories) {
143: if (null == categories) {
144: return new CategoryDescriptor[0];
145: }
146: categories = getVisibleCategories(categories);
147: CategoryDescriptor[] descriptors = new CategoryDescriptor[categories.length];
148: for (int i = 0; i < categories.length; i++) {
149: Category category = categories[i];
150: CategoryDescriptor descriptor = getCategoryDescriptor(category);
151: if (descriptor == null) {
152: descriptor = new CategoryDescriptor(this , category);
153: descriptor.setShowNames(getSettings()
154: .getShowItemNames());
155: descriptor.setIconSize(getSettings().getIconSize());
156: } else {
157: descriptor.refresh();
158: }
159: descriptor.setWidth(getWidth());
160: descriptors[i] = descriptor;
161: }
162: return descriptors;
163: }
164:
165: private Category[] getVisibleCategories(Category[] cats) {
166: ArrayList<Category> tmp = new ArrayList<Category>(cats.length);
167: for (int i = 0; i < cats.length; i++) {
168: if (settings.isVisible(cats[i])) {
169: tmp.add(cats[i]);
170: }
171: }
172: return tmp.toArray(new Category[tmp.size()]);
173: }
174:
175: void computeHeights(Category openedCategory) {
176: computeHeights(descriptors, openedCategory);
177: }
178:
179: private void computeHeights(
180: CategoryDescriptor[] paletteCategoryDescriptors,
181: Category openedCategory) {
182: if (paletteCategoryDescriptors == null
183: || paletteCategoryDescriptors.length <= 0) {
184: return;
185: }
186: revalidate();
187: }
188:
189: private static boolean arrayContains(Object[] objects, Object object) {
190: if (objects == null || object == null)
191: return false;
192: for (int i = 0; i < objects.length; i++) {
193: if (objects[i] == object)
194: return true;
195: }
196: return false;
197: }
198:
199: private void setDescriptors(
200: CategoryDescriptor[] paletteCategoryDescriptors) {
201: for (int i = 0; i < descriptors.length; i++) {
202: CategoryDescriptor descriptor = descriptors[i];
203: if (!arrayContains(paletteCategoryDescriptors, descriptor)) {
204: remove(descriptor.getComponent());
205: dndSupport.remove(descriptor);
206: }
207: }
208: for (int i = 0; i < paletteCategoryDescriptors.length; i++) {
209: CategoryDescriptor paletteCategoryDescriptor = paletteCategoryDescriptors[i];
210: if (!arrayContains(descriptors, paletteCategoryDescriptor)) {
211: add(paletteCategoryDescriptor.getComponent());
212: dndSupport.add(paletteCategoryDescriptor);
213: }
214: }
215: if (descriptors.length == 0
216: && paletteCategoryDescriptors.length > 0) {
217: boolean isAnyCategoryOpened = false;
218: for (int i = 0; i < paletteCategoryDescriptors.length; i++) {
219: if (paletteCategoryDescriptors[i].isOpened()) {
220: isAnyCategoryOpened = true;
221: break;
222: }
223: }
224: if (!isAnyCategoryOpened) {
225: paletteCategoryDescriptors[0].setOpened(true);
226: }
227: }
228: descriptors = paletteCategoryDescriptors;
229: revalidate();
230: }
231:
232: public void doRefresh() {
233: if (null != controller)
234: controller.refresh();
235: }
236:
237: public void refresh() {
238: Runnable runnable = new Runnable() {
239: public void run() {
240: synchronized (lock) {
241: setCursor(Cursor
242: .getPredefinedCursor(Cursor.WAIT_CURSOR));
243: CategoryDescriptor[] paletteCategoryDescriptors = computeDescriptors(null != model ? model
244: .getCategories()
245: : null);
246: setDescriptors(paletteCategoryDescriptors);
247: if (null != settings) {
248: setIconSize(settings.getIconSize());
249: setShowItemNames(settings.getShowItemNames());
250: setItemWidth(settings.getShowItemNames() ? settings
251: .getItemWidth()
252: : -1);
253: }
254: if (null != model) {
255: Item item = model.getSelectedItem();
256: Category category = model.getSelectedCategory();
257: setSelectedItemFromModel(category, item);
258: }
259: setCursor(Cursor.getDefaultCursor());
260: }
261: }
262: };
263: if (SwingUtilities.isEventDispatchThread()) {
264: runnable.run();
265: } else {
266: SwingUtilities.invokeLater(runnable);
267: }
268: }
269:
270: public void propertyChange(PropertyChangeEvent evt) {
271: refresh();
272: }
273:
274: void select(Category category, Item item) {
275: if (category != selectedCategory) {
276: CategoryDescriptor selectedDescriptor = findDescriptorFor(selectedCategory);
277: if (selectedDescriptor != null) {
278: selectedDescriptor.setSelectedItem(null);
279: }
280: }
281: selectedCategory = category;
282: if (null != model) {
283: if (null == category || null == item)
284: model.clearSelection();
285: else
286: model.setSelectedItem(category.getLookup(), item
287: .getLookup());
288: }
289: }
290:
291: private void setSelectedItemFromModel(Category category, Item item) {
292: if (null != selectedCategory
293: && !selectedCategory.equals(category)) {
294: CategoryDescriptor selectedDescriptor = findDescriptorFor(selectedCategory);
295: if (selectedDescriptor != null) {
296: selectedDescriptor.setSelectedItem(null);
297: }
298: }
299: CategoryDescriptor descriptor = findDescriptorFor(category);
300: if (descriptor == null) {
301: return;
302: }
303: if (item != null) {
304: selectedCategory = category;
305: }
306: descriptor.setSelectedItem(item);
307: }
308:
309: private CategoryDescriptor findDescriptorFor(Category category) {
310: if (null != descriptors) {
311: for (int i = 0; i < descriptors.length; i++) {
312: CategoryDescriptor descriptor = descriptors[i];
313: if (descriptor.getCategory().equals(category))
314: return descriptor;
315: }
316: }
317: return null;
318: }
319:
320: private void scrollToCategory(final Category category) {
321: Runnable runnable = new Runnable() {
322: public void run() {
323: synchronized (lock) {
324: CategoryDescriptor descriptor = findDescriptorFor(category);
325: if (null != descriptor) {
326: scrollPane.validate();
327: Point loc = descriptor.getComponent()
328: .getLocation();
329: scrollPane.getViewport().setViewPosition(loc);
330: }
331: }
332: }
333: };
334: if (SwingUtilities.isEventDispatchThread()) {
335: runnable.run();
336: } else {
337: SwingUtilities.invokeLater(runnable);
338: }
339: }
340:
341: /**
342: * Set new palette model and settings.
343: */
344: public void setContent(PaletteController newController,
345: Model newModel, Settings newSettings) {
346: synchronized (lock) {
347: if (newModel == model && newSettings == settings) {
348: return;
349: }
350:
351: Model old = model;
352: if (model != null && null != modelListener) {
353: model.removeModelListener(modelListener);
354: }
355: if (settings != null && null != settingsListener) {
356: settings.removePropertyChangeListener(settingsListener);
357: }
358:
359: model = newModel;
360: settings = newSettings;
361: controller = newController;
362: selectedCategory = null;
363: if (model != null) {
364: model.addModelListener(getModelListener());
365: }
366: if (null != settings) {
367: settings
368: .addPropertyChangeListener(getSettingsListener());
369: }
370: refresh();
371: }
372: }
373:
374: private MouseListener mouseListener() {
375: if (null == mouseListener) {
376: mouseListener = new MouseAdapter() {
377: public void mouseClicked(MouseEvent event) {
378: if (SwingUtilities.isRightMouseButton(event)
379: && null != model) {
380: JPopupMenu popup = Utilities.actionsToPopup(
381: model.getActions(), PalettePanel.this );
382: Utils.addCustomizationMenuItems(popup,
383: getController(), getSettings());
384: popup.show((Component) event.getSource(), event
385: .getX(), event.getY());
386: }
387: }
388: };
389: }
390: return mouseListener;
391: }
392:
393: private void setShowItemNames(boolean showNames) {
394: for (int i = 0; i < descriptors.length; i++) {
395: descriptors[i].setShowNames(showNames);
396: }
397: repaint();
398: }
399:
400: private void setIconSize(int iconSize) {
401: for (int i = 0; i < descriptors.length; i++) {
402: descriptors[i].setIconSize(iconSize);
403: }
404: repaint();
405: }
406:
407: private void setItemWidth(int itemWidth) {
408: for (int i = 0; i < descriptors.length; i++) {
409: descriptors[i].setItemWidth(itemWidth);
410: }
411: repaint();
412: }
413:
414: public boolean getScrollableTracksViewportHeight() {
415: return false;
416: }
417:
418: public boolean getScrollableTracksViewportWidth() {
419: return true;
420: }
421:
422: public Dimension getPreferredScrollableViewportSize() {
423: return getPreferredSize();
424: }
425:
426: public int getScrollableBlockIncrement(Rectangle visibleRect,
427: int orientation, int direction) {
428: return 100;
429: }
430:
431: public int getScrollableUnitIncrement(Rectangle visibleRect,
432: int orientation, int direction) {
433: return 20;
434: }
435:
436: public HelpCtx getHelpCtx() {
437: HelpCtx ctx = null;
438: if (null != getModel()) {
439: Item selItem = getModel().getSelectedItem();
440: if (null != selItem) {
441: Node selNode = (Node) selItem.getLookup().lookup(
442: Node.class);
443: if (null != selNode)
444: ctx = selNode.getHelpCtx();
445: }
446: if (null == ctx || HelpCtx.DEFAULT_HELP.equals(ctx)) {
447: //find the selected category
448: CategoryDescriptor selCategory = null;
449: for (int i = 0; i < descriptors.length; i++) {
450: if (descriptors[i].isSelected()) {
451: selCategory = descriptors[i];
452: break;
453: }
454: }
455: if (null != selCategory) {
456: Node selNode = (Node) selCategory.getCategory()
457: .getLookup().lookup(Node.class);
458: if (null != selNode)
459: ctx = selNode.getHelpCtx();
460: }
461: }
462: if (null == ctx || HelpCtx.DEFAULT_HELP.equals(ctx)) {
463: Node selNode = (Node) getModel().getRoot().lookup(
464: Node.class);
465: if (null != selNode)
466: ctx = selNode.getHelpCtx();
467: }
468: }
469: if (null == ctx || HelpCtx.DEFAULT_HELP.equals(ctx)) {
470: ctx = new HelpCtx("CommonPalette"); // NOI18N
471: }
472: return ctx;
473: }
474:
475: private ModelListener getModelListener() {
476: if (null == modelListener) {
477: modelListener = new ModelListener() {
478: public void categoriesAdded(Category[] addedCategories) {
479: PalettePanel.this .refresh();
480: if (null != addedCategories
481: && addedCategories.length > 0) {
482: PalettePanel.this
483: .scrollToCategory(addedCategories[0]);
484: }
485: }
486:
487: public void categoriesRemoved(
488: Category[] removedCategories) {
489: PalettePanel.this .refresh();
490: }
491:
492: public void categoriesReordered() {
493: PalettePanel.this .refresh();
494: }
495:
496: public void propertyChange(PropertyChangeEvent evt) {
497: if (ModelListener.PROP_SELECTED_ITEM.equals(evt
498: .getPropertyName())) {
499: Item selectedItem = model.getSelectedItem();
500: Category selectedCategory = model
501: .getSelectedCategory();
502: setSelectedItemFromModel(selectedCategory,
503: selectedItem);
504: }
505: }
506:
507: };
508: }
509: return modelListener;
510: }
511:
512: private PropertyChangeListener getSettingsListener() {
513: if (null == settingsListener) {
514: settingsListener = new PropertyChangeListener() {
515: public void propertyChange(PropertyChangeEvent evt) {
516: if (PaletteController.ATTR_IS_VISIBLE.equals(evt
517: .getPropertyName())) {
518: PalettePanel.this .refresh();
519: for (int i = 0; null != descriptors
520: && i < descriptors.length; i++) {
521: descriptors[i].computeItems();
522: }
523: } else if (PaletteController.ATTR_ICON_SIZE
524: .equals(evt.getPropertyName())) {
525:
526: setIconSize(getSettings().getIconSize());
527:
528: } else if (PaletteController.ATTR_SHOW_ITEM_NAMES
529: .equals(evt.getPropertyName())) {
530:
531: setShowItemNames(getSettings()
532: .getShowItemNames());
533: setItemWidth(getSettings().getShowItemNames() ? getSettings()
534: .getItemWidth()
535: : -1);
536:
537: }
538: }
539:
540: };
541: }
542: return settingsListener;
543: }
544:
545: Model getModel() {
546: return model;
547: }
548:
549: Settings getSettings() {
550: return settings;
551: }
552:
553: PaletteController getController() {
554: return controller;
555: }
556:
557: public void updateUI() {
558: super .updateUI();
559: if (null != model)
560: model.refresh();
561: }
562:
563: private class PaletteLayoutManager implements LayoutManager {
564:
565: public void addLayoutComponent(String name, Component comp) {
566: }
567:
568: public void layoutContainer(Container parent) {
569: int width = getWidth();
570:
571: int height = 0;
572: for (int i = 0; i < descriptors.length; i++) {
573: CategoryDescriptor paletteCategoryDescriptor = descriptors[i];
574: paletteCategoryDescriptor.setPositionY(height);
575: JComponent comp = paletteCategoryDescriptor
576: .getComponent();
577: comp.setSize(width, comp.getPreferredSize().height);
578: height += paletteCategoryDescriptor.getComponent()
579: .getHeight();
580: }
581: }
582:
583: public Dimension minimumLayoutSize(Container parent) {
584: return new Dimension(0, 0);
585: }
586:
587: public Dimension preferredLayoutSize(Container parent) {
588: int height = 0;
589: int width = getWidth();
590: for (int i = 0; i < descriptors.length; i++) {
591: CategoryDescriptor descriptor = descriptors[i];
592: height += descriptor.getPreferredHeight(width) + 1;
593: }
594: return new Dimension(
595: 10 /* not used - tracks viewports width*/, height);
596: }
597:
598: public void removeLayoutComponent(Component comp) {
599: }
600: }
601: }
|