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.spi.palette;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyChangeListener;
046: import java.beans.PropertyChangeSupport;
047: import java.lang.reflect.Method;
048: import java.util.ArrayList;
049: import java.util.Iterator;
050: import java.util.Set;
051: import java.util.logging.Level;
052: import java.util.logging.Logger;
053: import javax.swing.SwingUtilities;
054: import org.netbeans.api.editor.mimelookup.MimeLookup;
055: import org.netbeans.api.editor.mimelookup.MimePath;
056: import org.openide.filesystems.FileObject;
057: import org.openide.loaders.DataObject;
058: import org.openide.loaders.DataShadow;
059: import org.openide.nodes.Node;
060: import org.openide.util.Lookup;
061: import org.openide.util.LookupEvent;
062: import org.openide.util.LookupListener;
063: import org.openide.windows.TopComponent;
064: import org.openide.windows.TopComponentGroup;
065: import org.openide.windows.WindowManager;
066:
067: /**
068: * A class that listens to changes to the set of opened TopComponents and to the
069: * set of activated Nodes to show/hide the palette window when a TopComponent that
070: * supports the palette is activated/deactivated.
071: *
072: * @author S. Aubrecht
073: */
074: final class PaletteSwitch implements Runnable, LookupListener {
075:
076: static final String PROP_PALETTE_CONTENTS = "component_palette_contents"; //NOI18N
077:
078: private static PaletteSwitch theInstance;
079:
080: private PropertyChangeListener registryListener;
081:
082: private PropertyChangeSupport propertySupport;
083:
084: private PaletteController currentPalette;
085: // private boolean isGroupOpen = false;
086: private Lookup.Result lookupRes;
087:
088: /** Creates a new instance of PaletteSwitcher */
089: private PaletteSwitch() {
090:
091: propertySupport = new PropertyChangeSupport(this );
092: }
093:
094: public synchronized static PaletteSwitch getDefault() {
095: if (null == theInstance) {
096: theInstance = new PaletteSwitch();
097: }
098: return theInstance;
099: }
100:
101: public void startListening() {
102: synchronized (theInstance) {
103: if (null == registryListener) {
104: registryListener = createRegistryListener();
105: TopComponent.getRegistry().addPropertyChangeListener(
106: registryListener);
107: switchLookupListener();
108: run();
109: }
110: }
111: }
112:
113: public void stopListening() {
114: synchronized (theInstance) {
115: if (null != registryListener) {
116: TopComponent.getRegistry()
117: .removePropertyChangeListener(registryListener);
118: registryListener = null;
119: currentPalette = null;
120: }
121: }
122: }
123:
124: public void addPropertyChangeListener(PropertyChangeListener l) {
125: propertySupport.addPropertyChangeListener(l);
126: }
127:
128: public void removePropertyChangeListener(PropertyChangeListener l) {
129: propertySupport.removePropertyChangeListener(l);
130: }
131:
132: public PaletteController getCurrentPalette() {
133: return currentPalette;
134: }
135:
136: public void run() {
137: if (!SwingUtilities.isEventDispatchThread()) {
138: SwingUtilities.invokeLater(this );
139: return;
140: }
141: final PaletteController oldPalette = currentPalette;
142: currentPalette = findPalette();
143:
144: showHidePaletteTopComponent(oldPalette, currentPalette);
145:
146: propertySupport.firePropertyChange(PROP_PALETTE_CONTENTS,
147: oldPalette, currentPalette);
148: }
149:
150: private PaletteController findPalette() {
151: TopComponent.Registry registry = TopComponent.getRegistry();
152:
153: PaletteController palette;
154: TopComponent activeTc = registry.getActivated();
155: palette = getPaletteFromTopComponent(activeTc, true);
156:
157: ArrayList<PaletteController> availablePalettes = new ArrayList<PaletteController>(
158: 3);
159: if (null == palette) {
160: Set openedTcs = registry.getOpened();
161: for (Iterator i = openedTcs.iterator(); i.hasNext();) {
162: TopComponent tc = (TopComponent) i.next();
163:
164: palette = getPaletteFromTopComponent(tc, true);
165: if (null != palette) {
166: availablePalettes.add(palette);
167: }
168: }
169: if (null != currentPalette
170: && (availablePalettes.contains(currentPalette) || isPaletteMaximized()))
171: palette = currentPalette;
172: else if (availablePalettes.size() > 0)
173: palette = availablePalettes.get(0);
174: }
175: return palette;
176: }
177:
178: private boolean isPaletteMaximized() {
179: boolean isMaximized = true;
180: boolean currentPaletteStillAvailable = false;
181: TopComponent.Registry registry = TopComponent.getRegistry();
182: Set openedTcs = registry.getOpened();
183: for (Iterator i = openedTcs.iterator(); i.hasNext();) {
184: TopComponent tc = (TopComponent) i.next();
185:
186: if (tc.isShowing() && !(tc instanceof PaletteTopComponent)) {
187: //other window(s) than the Palette are showing
188: isMaximized = false;
189: break;
190: }
191: if (!currentPaletteStillAvailable) {
192: //check whether the window with the current palette controller wasn't closed
193: PaletteController palette = getPaletteFromTopComponent(
194: tc, false);
195: if (null != palette && palette == currentPalette) {
196: currentPaletteStillAvailable = true;
197: }
198: }
199: }
200: return isMaximized && currentPaletteStillAvailable;
201: }
202:
203: PaletteController getPaletteFromTopComponent(TopComponent tc,
204: boolean mustBeShowing) {
205: if (null == tc || (!tc.isShowing() && mustBeShowing))
206: return null;
207:
208: if (!WindowManager.getDefault().isEditorTopComponent(tc))
209: return null;
210:
211: PaletteController pc = (PaletteController) tc.getLookup()
212: .lookup(PaletteController.class);
213: if (null == pc
214: && WindowManager.getDefault()
215: .isOpenedEditorTopComponent(tc)) {
216: //check if there's any palette assigned to TopComponent's mime type
217: Node[] activeNodes = tc.getActivatedNodes();
218: if (null != activeNodes && activeNodes.length > 0) {
219: DataObject dob = activeNodes[0].getLookup().lookup(
220: DataObject.class);
221: if (null != dob) {
222: while (dob instanceof DataShadow) {
223: dob = ((DataShadow) dob).getOriginal();
224: }
225: FileObject fo = dob.getPrimaryFile();
226: if (!fo.isVirtual()) {
227: String mimeType = fo.getMIMEType();
228: pc = getPaletteFromMimeType(mimeType);
229: }
230: }
231: }
232: }
233: return pc;
234: }
235:
236: /**
237: * Finds appropriate PaletteController for given mime type.
238: *
239: * @param mimeType Mime type to check for associated palette content.
240: *
241: * @return PaletteController that is associated with the given mime type and that should
242: * be displayed in the Common Palette when an editor window with the given mime type is activated.
243: * @since 1.10
244: */
245: PaletteController getPaletteFromMimeType(String mimeType) {
246: MimePath path = MimePath.get(mimeType);
247: Lookup lkp = MimeLookup.getLookup(path);
248: return lkp.lookup(PaletteController.class);
249: }
250:
251: private void showHidePaletteTopComponent(
252: PaletteController prevPalette, PaletteController newPalette) {
253: if (prevPalette == newPalette && null != newPalette)
254: return;
255:
256: WindowManager wm = WindowManager.getDefault();
257: TopComponent palette = wm.findTopComponent("CommonPalette"); // NOI18N
258: if (null == palette) {
259: Logger.getLogger(getClass().getName()).log(Level.INFO,
260: "Cannot find CommonPalette component."); // NOI18N
261:
262: //for unit-testing
263: palette = PaletteTopComponent.getDefault();
264: }
265:
266: if (PaletteVisibility.isVisible(newPalette)
267: || PaletteVisibility.isVisible(null)) {
268: if (!palette.isOpened())
269: palette.open();
270: PaletteVisibility.setVisible(newPalette, true);
271: } else {
272: if (palette.isOpened())
273: palette.close();
274: }
275: }
276:
277: //the group might be already opened at startup time which we won't know about
278: //so let's make a 'real' check
279: private boolean isGroupOpenHack(TopComponentGroup group) {
280: try {
281: for (Method m : group.getClass().getDeclaredMethods()) {
282: if (m.getName().equals("isOpened")) {
283: m.setAccessible(true);
284: Object res = m.invoke(group, new Object[0]);
285: if (res instanceof Boolean) {
286: return ((Boolean) res).booleanValue();
287: }
288: }
289: }
290: } catch (Exception e) {
291: //ignore
292: }
293: return false;
294: }
295:
296: /**
297: * multiview components do not fire events when switching their inner tabs
298: * so we have to listen to changes in lookup contents
299: */
300: private void switchLookupListener() {
301: TopComponent active = TopComponent.getRegistry().getActivated();
302: if (null != lookupRes) {
303: lookupRes.removeLookupListener(PaletteSwitch.this );
304: lookupRes = null;
305: }
306: if (null != active) {
307: lookupRes = active.getLookup().lookup(
308: new Lookup.Template<PaletteController>(
309: PaletteController.class));
310: lookupRes.addLookupListener(PaletteSwitch.this );
311: lookupRes.allItems();
312: }
313: }
314:
315: private PropertyChangeListener createRegistryListener() {
316: return new PropertyChangeListener() {
317: public void propertyChange(PropertyChangeEvent evt) {
318: if (TopComponent.Registry.PROP_CURRENT_NODES.equals(evt
319: .getPropertyName())
320: || TopComponent.Registry.PROP_OPENED.equals(evt
321: .getPropertyName())
322: || TopComponent.Registry.PROP_ACTIVATED
323: .equals(evt.getPropertyName())) {
324:
325: if (TopComponent.Registry.PROP_ACTIVATED.equals(evt
326: .getPropertyName())) {
327: //switch lookup listener for the activated TC
328: switchLookupListener();
329: }
330: run();
331: }
332: }
333: };
334: }
335:
336: public void resultChanged(LookupEvent ev) {
337: run();
338: }
339: }
|