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-2007 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: package org.netbeans.modules.sql.framework.ui.graph.view.impl;
042:
043: import java.awt.Color;
044: import java.awt.Component;
045: import java.awt.Dimension;
046: import java.awt.Graphics;
047: import java.awt.GridBagConstraints;
048: import java.awt.GridBagLayout;
049: import java.awt.Insets;
050: import java.awt.KeyboardFocusManager;
051: import java.awt.Point;
052: import java.awt.Polygon;
053: import java.awt.Rectangle;
054: import java.awt.Window;
055: import java.awt.event.ActionEvent;
056: import java.awt.event.ActionListener;
057: import java.awt.event.ComponentEvent;
058: import java.awt.event.ComponentListener;
059: import java.awt.event.FocusEvent;
060: import java.awt.event.FocusListener;
061: import java.awt.event.ItemEvent;
062: import java.awt.event.ItemListener;
063: import java.awt.event.KeyEvent;
064: import java.awt.event.MouseAdapter;
065: import java.awt.event.MouseEvent;
066: import java.awt.event.MouseMotionAdapter;
067: import java.awt.event.MouseMotionListener;
068: import java.util.Iterator;
069: import java.util.LinkedList;
070: import java.util.List;
071:
072: import javax.swing.AbstractAction;
073: import javax.swing.AbstractButton;
074: import javax.swing.Action;
075: import javax.swing.BorderFactory;
076: import javax.swing.Icon;
077: import javax.swing.JButton;
078: import javax.swing.JComponent;
079: import javax.swing.JPanel;
080: import javax.swing.JSeparator;
081: import javax.swing.JToggleButton;
082: import javax.swing.JWindow;
083: import javax.swing.KeyStroke;
084: import javax.swing.SwingConstants;
085: import javax.swing.SwingUtilities;
086: import javax.swing.UIManager;
087: import javax.swing.border.Border;
088: import javax.swing.border.MatteBorder;
089:
090: import net.java.hulp.i18n.Logger;
091: import org.netbeans.modules.etl.logger.Localizer;
092: import org.netbeans.modules.etl.logger.LogUtil;
093: import org.netbeans.modules.sql.framework.ui.graph.IOperatorManager;
094: import org.netbeans.modules.sql.framework.ui.graph.IOperatorXmlInfo;
095: import org.netbeans.modules.sql.framework.ui.graph.IOperatorXmlInfoCategory;
096: import org.openide.nodes.Node;
097:
098: /**
099: * ToolBar drop down Menu for displaying menu of Operators.
100: *
101: * @author Ritesh Adval
102: * @author Girish Patil
103: * @author Jonathan Giron
104: * @version $Revision$
105: */
106: public class SQLToolBarMenu extends JToggleButton {
107:
108: private static final Object _SEPARATOR = new Object();
109: private SQLToolBar mToolBar;
110: private IOperatorXmlInfoCategory mCategory;
111: private IOperatorManager mManager;
112: private List mMenuItemModelList;
113: private List mMenuItemList;
114: private ButtonPopupMenu mButtonMenu;
115: private static transient final Logger mLogger = LogUtil
116: .getLogger(SQLToolBarMenu.class.getName());
117: private static transient final Localizer mLoc = Localizer.get();
118:
119: public SQLToolBarMenu(IOperatorXmlInfoCategory catNode,
120: IOperatorManager manager, SQLToolBar toolbar) {
121: super ();
122: mCategory = catNode;
123: mManager = manager;
124: mToolBar = toolbar;
125: mMenuItemModelList = new LinkedList();
126: mMenuItemList = new LinkedList();
127: Icon paletteIcon = null;
128: paletteIcon = mCategory.getIcon();
129:
130: if (paletteIcon != null) {
131: setIcon(paletteIcon);
132: }
133:
134: addItemListener(new ItemListener() {
135:
136: public void itemStateChanged(ItemEvent e) {
137: if (isSelected()) {
138: showButtonMenu();
139: } else {
140: mButtonMenu.hide();
141: }
142: }
143: });
144:
145: setContentAreaFilled(false);
146:
147: setRolloverEnabled(false);
148: setMargin(new Insets(1, 5, 0, 12));
149: setIconTextGap(2);
150:
151: addMouseListener(new MouseAdapter() {
152:
153: public void mouseEntered(MouseEvent e) {
154: SQLToolBarMenu active = mToolBar.getActiveMenu();
155: if ((active != null) && (active != SQLToolBarMenu.this )) {
156: setSelected(true);
157: }
158: }
159: });
160: String nbBundle = mLoc.t("PRSR001: Operators");
161: this .setToolTipText(catNode.getDisplayName() + " "
162: + Localizer.parse(nbBundle));
163: }
164:
165: public void setToolBar(SQLToolBar group) {
166: mToolBar = group;
167: }
168:
169: public void addNotify() {
170: super .addNotify();
171: Window parent = SwingUtilities.getWindowAncestor(this );
172: if (mButtonMenu == null) {
173: createNewButtonPopupMenu(parent);
174: } else if (mButtonMenu.getOwner() != parent) {
175: createNewButtonPopupMenu(parent);
176: }
177: }
178:
179: private void createNewButtonPopupMenu(Window parent) {
180: if (mButtonMenu != null) {
181: mButtonMenu.cleanup();
182: mButtonMenu.dispose();
183: }
184: mButtonMenu = new ButtonPopupMenu(parent);
185: Iterator menuItemIter = mMenuItemList.iterator();
186: while (menuItemIter.hasNext()) {
187: Object menuItem = menuItemIter.next();
188: if (menuItem == _SEPARATOR) {
189: mButtonMenu.addSeparator();
190: } else {
191: mButtonMenu.addButton((AbstractButton) menuItem);
192: }
193: }
194: }
195:
196: public void showButtonMenu(Point location) {
197: if (mButtonMenu == null) {
198: throw new IllegalStateException(
199: "MenuButton has not been initialized yet");
200: }
201: mButtonMenu.setLocation(location);
202: mButtonMenu.pack();
203: mButtonMenu.show();
204: }
205:
206: public void hideButtonMenu() {
207: mButtonMenu.hide();
208: setSelected(false);
209: }
210:
211: public void showButtonMenu() {
212: Point pt = getLocationOnScreen();
213: pt.y += getSize().height;
214: showButtonMenu(pt);
215: }
216:
217: public SQLToolBarMenuItem addMenuItem(IOperatorXmlInfo item) {
218: if (mMenuItemModelList.contains(item)) {
219: return null;
220: }
221: mMenuItemModelList.add(item);
222: SQLToolBarMenuItem stbmi = createMenuItem(item);
223: mMenuItemList.add(stbmi);
224: if (mButtonMenu != null) {
225: mButtonMenu.addButton((AbstractButton) item);
226: }
227:
228: return stbmi;
229: }
230:
231: public int getMenuItemIndex(IOperatorXmlInfo item) {
232: return mMenuItemModelList.indexOf(item);
233: }
234:
235: public SQLToolBarMenuItem findMenuItem(IOperatorXmlInfo itemModel) {
236: SQLToolBarMenuItem stbmi = null;
237: int index = getMenuItemIndex(itemModel);
238: if (index >= 0) {
239: stbmi = (SQLToolBarMenuItem) mMenuItemList.get(index);
240: }
241:
242: return stbmi;
243: }
244:
245: public void addSeparator() {
246: mMenuItemList.add(_SEPARATOR);
247: if (mButtonMenu != null) {
248: mButtonMenu.addSeparator();
249: }
250: }
251:
252: public void setEnabled(boolean enable) {
253: if (this .isEnabled() == enable) {
254: return;
255: }
256: super .setEnabled(enable);
257: for (int i = 0; i < getComponentCount(); i++) {
258: Component comp = getComponent(i);
259: comp.setEnabled(enable);
260: }
261: }
262:
263: private class ButtonPopupMenu extends JWindow {
264:
265: private JPanel mButtonList;
266: private Border mButtonBorder;
267: private int mButtonHeight;
268: private ComponentListener mComponentListener;
269:
270: public ButtonPopupMenu(Window parent) {
271: super (parent);
272: mButtonList = new JPanel();
273: mButtonList.setBorder(BorderFactory
274: .createLineBorder(Color.black));
275: mButtonList.setLayout(new GridBagLayout());
276:
277: // The rollover border does not display correctly. It overlaps the
278: // button text, which looks bad. So, we expand the button size by
279: // giving it a large enough border. This border has a background color
280: // to match the button background color. This allows the rollover rectangle
281: // to size around the text correctly.
282: Color background = mButtonList.getBackground();
283: mButtonBorder = new MatteBorder(new Insets(2, 3, 3, 3),
284: background);
285:
286: mButtonList.addFocusListener(mFocusListener);
287: addFocusListener(mFocusListener);
288:
289: getContentPane().add(mButtonList);
290: registerDefaultKeyActionBindings();
291:
292: mComponentListener = new ComponentListener() {
293:
294: public void componentHidden(ComponentEvent e) {
295: SQLToolBarMenu.this .setSelected(false);
296: }
297:
298: public void componentMoved(ComponentEvent e) {
299: SQLToolBarMenu.this .setSelected(false);
300: }
301:
302: public void componentResized(ComponentEvent e) {
303: SQLToolBarMenu.this .setSelected(false);
304: }
305:
306: public void componentShown(ComponentEvent e) {
307: SQLToolBarMenu.this .setSelected(false);
308: }
309: };
310: getOwner().addComponentListener(mComponentListener);
311:
312: addSeparator();
313:
314: String nbBundle1 = mLoc.t("PRSR001: Settings...");
315: AbstractButton setupButton = new JButton(Localizer
316: .parse(nbBundle1));
317: setupButton.getAccessibleContext().setAccessibleName(
318: Localizer.parse(nbBundle1));
319: addButton(1, setupButton);
320: mButtonHeight = setupButton.getPreferredSize().height + 2;
321: setupButton.addActionListener(new ActionListener() {
322:
323: public void actionPerformed(ActionEvent e) {
324: mManager.show((Node) mCategory);
325: }
326: });
327: }
328:
329: @SuppressWarnings("deprecation")
330: @Override
331: public void hide() {
332: super .hide();
333: if (mToolBar.getActiveMenu() == SQLToolBarMenu.this ) {
334: mToolBar.setActiveMenu(null);
335: }
336: }
337:
338: private ActionListener mActionListener = new ActionListener() {
339:
340: public void actionPerformed(ActionEvent e) {
341: SQLToolBarMenu.this .setSelected(false);
342: }
343: };
344: private FocusListener mFocusListener = new FocusListener() {
345:
346: public void focusGained(FocusEvent e) {
347: }
348:
349: public void focusLost(FocusEvent e) {
350: SwingUtilities.invokeLater(new Runnable() {
351:
352: public void run() {
353: Component owner = KeyboardFocusManager
354: .getCurrentKeyboardFocusManager()
355: .getFocusOwner();
356:
357: Component win = SwingUtilities.getRoot(owner);
358: if (win != ButtonPopupMenu.this ) {
359: SQLToolBarMenu.this .setSelected(false);
360: }
361: }
362: });
363: }
364: };
365: private Action mNextFocusAction = new AbstractAction() {
366:
367: public void actionPerformed(ActionEvent e) {
368: KeyboardFocusManager fMgr = KeyboardFocusManager
369: .getCurrentKeyboardFocusManager();
370: Component owner = fMgr.getFocusOwner();
371: if (owner == null) {
372: return;
373: }
374: for (int i = 0; i < mButtonList.getComponentCount(); i++) {
375: if (owner == mButtonList.getComponent(i)) {
376: fMgr.focusNextComponent();
377: return;
378: }
379: }
380: }
381: };
382: private Action mPrevFocusAction = new AbstractAction() {
383:
384: public void actionPerformed(ActionEvent e) {
385: KeyboardFocusManager fMgr = KeyboardFocusManager
386: .getCurrentKeyboardFocusManager();
387: Component owner = fMgr.getFocusOwner();
388: if (owner == null) {
389: return;
390: }
391: for (int i = 0; i < mButtonList.getComponentCount(); i++) {
392: if (owner == mButtonList.getComponent(i)) {
393: fMgr.focusPreviousComponent();
394: return;
395: }
396: }
397: }
398: };
399: private Action mNextMenuAction = new AbstractAction() {
400:
401: public void actionPerformed(ActionEvent e) {
402: Component parent = SQLToolBarMenu.this .getParent();
403: if (parent instanceof SQLToolBar) {
404: SQLToolBar parentGroup = (SQLToolBar) parent;
405: SQLToolBarMenu next = parentGroup
406: .getNextMenu(SQLToolBarMenu.this );
407: if (next != null) {
408: setSelected(false);
409: next.setSelected(true);
410: }
411: }
412: }
413: };
414: private Action mPrevMenuAction = new AbstractAction() {
415:
416: public void actionPerformed(ActionEvent e) {
417: Component parent = SQLToolBarMenu.this .getParent();
418: if (parent instanceof SQLToolBar) {
419: SQLToolBar parentGroup = (SQLToolBar) parent;
420: SQLToolBarMenu prev = parentGroup
421: .getPreviousMenu(SQLToolBarMenu.this );
422: if (prev != null) {
423: setSelected(false);
424: prev.setSelected(true);
425: }
426: }
427: }
428: };
429: private Action mHideAction = new AbstractAction() {
430:
431: public void actionPerformed(ActionEvent e) {
432: SQLToolBarMenu.this .setSelected(false);
433: }
434: };
435: private Action mDefaultButtonAction = new AbstractAction() {
436:
437: public void actionPerformed(ActionEvent e) {
438: KeyboardFocusManager fMgr = KeyboardFocusManager
439: .getCurrentKeyboardFocusManager();
440: Component owner = fMgr.getFocusOwner();
441: if (owner == null) {
442: return;
443: }
444: for (int i = 0; i < mButtonList.getComponentCount(); i++) {
445: Component c = mButtonList.getComponent(i);
446: ;
447: if (owner == c) {
448: ((AbstractButton) c).doClick(0);
449: return;
450: }
451: }
452: }
453: };
454: private MouseMotionListener mButtonFocusListener = new MouseMotionAdapter() {
455:
456: @Override
457: public void mouseMoved(MouseEvent e) {
458: if (e.getSource() instanceof Component) {
459: ((Component) e.getSource()).requestFocus();
460: }
461: }
462: };
463:
464: @SuppressWarnings("deprecation")
465: @Override
466: public void show() {
467: super .show();
468: if (mButtonList.getComponentCount() > 0) {
469: mButtonList.getComponent(0).requestFocus();
470: }
471: mToolBar.setActiveMenu(SQLToolBarMenu.this );
472: }
473:
474: public void addButton(AbstractButton b) {
475: addButton(b, mButtonList.getComponentCount() - 2);
476: }
477:
478: public void addButton(AbstractButton b, int index) {
479: if (index < 0) {
480: index = 0;
481: } else if (index > mButtonList.getComponentCount() - 2) {
482: throw new IndexOutOfBoundsException("component count: "
483: + (mButtonList.getComponentCount() - 2)
484: + " insert index: " + index);
485: }
486: addButton(index, b);
487: }
488:
489: private void addButton(int y, AbstractButton b) {
490: initButton(b);
491: addComponent(y, b);
492: if (mButtonHeight > 0) {
493: // We size all buttons to have the same height.
494: // We also add an extra amount to the width so that the
495: // rollover border has enough room to draw itself.
496: Dimension bSize = new Dimension(
497: b.getPreferredSize().width + 2, mButtonHeight);
498: b.setPreferredSize(bSize);
499: b.setMinimumSize(bSize);
500: }
501: }
502:
503: public void removeButton(AbstractButton b) {
504: mButtonList.remove(b);
505: unInitButton(b);
506: }
507:
508: public void removeButton(int index) {
509: AbstractButton b = (AbstractButton) mButtonList
510: .getComponent(index);
511: mButtonList.remove(index);
512: unInitButton(b);
513: }
514:
515: public void addSeparator() {
516: Component sepa = new JSeparator(SwingConstants.HORIZONTAL);
517: addComponent(mButtonList.getComponentCount() - 1, sepa);
518: }
519:
520: public void removeAll() {
521: int lastComponentIndex = mButtonList.getComponentCount() - 1;
522: while (lastComponentIndex >= 0) {
523: if (mButtonList.getComponent(lastComponentIndex) instanceof AbstractButton) {
524: removeButton(lastComponentIndex--);
525: } else {
526: mButtonList.remove(lastComponentIndex--);
527: }
528: }
529: }
530:
531: private void addComponent(int index, Component b) {
532: GridBagConstraints c = new GridBagConstraints();
533: c.gridx = 0;
534: c.gridy = GridBagConstraints.RELATIVE;
535: c.weightx = 1.0;
536: c.weighty = 1.0;
537: c.fill = GridBagConstraints.BOTH;
538: c.insets = new Insets(0, 10, 0, 10);
539: mButtonList.add(b, c, index);
540: }
541:
542: private void initButton(AbstractButton b) {
543: b.setBorder(mButtonBorder);
544: b.setRolloverEnabled(false);
545: b.setBackground(mButtonList.getBackground());
546: b.addFocusListener(mFocusListener);
547: b.addMouseMotionListener(mButtonFocusListener);
548: b.addActionListener(mActionListener);
549: }
550:
551: private void unInitButton(AbstractButton b) {
552: b.removeFocusListener(mFocusListener);
553: b.removeMouseMotionListener(mButtonFocusListener);
554: b.removeActionListener(mActionListener);
555: }
556:
557: private void registerDefaultKeyActionBindings() {
558: KeyStroke arrowUp = KeyStroke.getKeyStroke(KeyEvent.VK_UP,
559: 0);
560: getRootPane().getActionMap().put("up", mPrevFocusAction);
561: getRootPane()
562: .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
563: .put(arrowUp, "up");
564:
565: KeyStroke arrowDown = KeyStroke.getKeyStroke(
566: KeyEvent.VK_DOWN, 0);
567: getRootPane().getActionMap().put("down", mNextFocusAction);
568: getRootPane()
569: .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
570: .put(arrowDown, "down");
571:
572: KeyStroke esc = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,
573: 0);
574: getRootPane().getActionMap().put("esc", mHideAction);
575: getRootPane()
576: .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
577: .put(esc, "esc");
578:
579: KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
580: 0);
581: getRootPane().getActionMap().put("enter",
582: mDefaultButtonAction);
583: getRootPane()
584: .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
585: .put(enter, "enter");
586:
587: KeyStroke arrowRight = KeyStroke.getKeyStroke(
588: KeyEvent.VK_RIGHT, 0);
589: getRootPane().getActionMap().put("right", mNextMenuAction);
590: getRootPane()
591: .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
592: .put(arrowRight, "right");
593:
594: KeyStroke arrowLeft = KeyStroke.getKeyStroke(
595: KeyEvent.VK_LEFT, 0);
596: getRootPane().getActionMap().put("left", mPrevMenuAction);
597: getRootPane()
598: .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
599: .put(arrowLeft, "left");
600:
601: }
602:
603: public void cleanup() {
604: if (mButtonList != null) {
605: removeAll();
606: getOwner().removeComponentListener(mComponentListener);
607: mButtonList.removeFocusListener(mFocusListener);
608: mButtonList = null;
609: mPrevFocusAction = null;
610: mNextFocusAction = null;
611: mHideAction = null;
612: mDefaultButtonAction = null;
613: mNextMenuAction = null;
614: mPrevMenuAction = null;
615: }
616: }
617: }
618:
619: public void close() {
620: if (mButtonMenu != null) {
621: mButtonMenu.cleanup();
622: mButtonMenu.dispose();
623: mButtonMenu = null;
624: mToolBar = null;
625: }
626: }
627:
628: Color mDisabledTextColor = null;
629:
630: // Draw a small down arrow over the right margin of the button
631: public void paint(Graphics g) {
632: super .paint(g);
633: AbstractButton b = this ;
634: Dimension size = b.getSize();
635: Rectangle viewRect = new Rectangle(size);
636:
637: /*
638: * XXXXX XVX X
639: */
640: int px = viewRect.x + viewRect.width - 10;
641: int py = viewRect.y + (viewRect.height) / 2;
642: Polygon p = new Polygon();
643: int cx = 4;
644: while (cx >= 0) {
645: p.addPoint(px, py);
646: p.addPoint(px + cx, py);
647: py++;
648: px++;
649: cx -= 2;
650: }
651: Color color = Color.black;
652: if (!b.isEnabled()) {
653: if (mDisabledTextColor == null) {
654: mDisabledTextColor = UIManager
655: .getColor("Button.disabledText");
656: }
657: color = mDisabledTextColor;
658: }
659: g.setColor(color);
660: g.drawPolygon(p);
661: g.fillPolygon(p);
662:
663: }
664:
665: /**
666: * Returns JButton item to be displayed on the toolbar.
667: *
668: * @param itemData
669: * @return
670: */
671: private SQLToolBarMenuItem createMenuItem(IOperatorXmlInfo itemData) {
672: SQLToolBarMenuItem menuItem = new SQLToolBarMenuItem(itemData);
673: return menuItem;
674: }
675: }
|