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.core.windows.view.ui;
043:
044: import java.text.MessageFormat;
045: import javax.swing.plaf.basic.BasicHTML;
046: import org.netbeans.core.windows.Constants;
047: import org.netbeans.core.windows.ModeImpl;
048: import org.netbeans.core.windows.WindowManagerImpl;
049: import org.netbeans.core.windows.view.ModeView;
050: import org.netbeans.core.windows.view.ViewElement;
051: import org.netbeans.core.windows.view.dnd.TopComponentDroppable;
052: import org.netbeans.core.windows.view.dnd.WindowDnDManager;
053: import org.netbeans.core.windows.view.dnd.ZOrderManager;
054: import org.netbeans.core.windows.view.ui.tabcontrol.TabbedAdapter;
055: import org.openide.util.NbBundle;
056: import org.openide.util.Utilities;
057: import org.openide.windows.TopComponent;
058:
059: import javax.swing.*;
060: import java.awt.*;
061: import java.awt.event.*;
062: import java.util.Set;
063: import java.util.logging.Level;
064: import java.util.logging.Logger;
065: import org.netbeans.core.windows.options.WinSysPrefs;
066: import org.openide.windows.WindowManager;
067:
068: /**
069: * Implementation of <code>ModeContainer</code> for separate mode kind.
070: *
071: * @author Peter Zavadsky
072: */
073: public final class DefaultSeparateContainer extends
074: AbstractModeContainer {
075:
076: /** Separate mode represented by JFrame or null if dialog is used */
077: private final ModeFrame modeFrame;
078: /** Separate mode represented by JDialog or null if frame is used */
079: private final ModeDialog modeDialog;
080:
081: /** Creates a DefaultSeparateContainer. */
082: public DefaultSeparateContainer(final ModeView modeView,
083: WindowDnDManager windowDnDManager, Rectangle bounds,
084: int kind) {
085: super (modeView, windowDnDManager, kind);
086: // JFrame or JDialog according to the mode kind
087: if (kind == Constants.MODE_KIND_EDITOR) {
088: modeFrame = new ModeFrame(this , modeView);
089: MainWindow.initFrameIcons(modeFrame);
090: modeDialog = null;
091: } else {
092: modeDialog = new ModeDialog(WindowManager.getDefault()
093: .getMainWindow(), this , modeView);
094: modeFrame = null;
095: }
096: Window w = getModeUIWindow();
097: ((RootPaneContainer) w).getContentPane().add(
098: tabbedHandler.getComponent());
099: w.setBounds(bounds);
100: }
101:
102: public void requestAttention(TopComponent tc) {
103: //not implemented
104: }
105:
106: public void cancelRequestAttention(TopComponent tc) {
107: //not implemented
108: }
109:
110: /** */
111: protected Component getModeComponent() {
112: return getModeUIWindow();
113: }
114:
115: protected Tabbed createTabbed() {
116: Tabbed tabbed;
117: if (getKind() == Constants.MODE_KIND_EDITOR) {
118: tabbed = new TabbedAdapter(Constants.MODE_KIND_EDITOR);
119: } else {
120: tabbed = new TabbedAdapter(Constants.MODE_KIND_VIEW);
121: }
122: return tabbed;
123: }
124:
125: protected void updateTitle(String title) {
126: getModeUIBase().updateTitle(title);
127: }
128:
129: protected void updateActive(boolean active) {
130: Window w = getModeUIWindow();
131: if (active) {
132: if (w.isVisible() && !w.isActive()) {
133: w.toFront();
134: }
135: }
136: }
137:
138: public boolean isActive() {
139: return getModeUIWindow().isActive();
140: }
141:
142: protected boolean isAttachingPossible() {
143: return false;
144: }
145:
146: protected TopComponentDroppable getModeDroppable() {
147: return getModeUIBase();
148: }
149:
150: private Window getModeUIWindow() {
151: return modeFrame != null ? modeFrame : modeDialog;
152: }
153:
154: private ModeUIBase getModeUIBase() {
155: return (ModeUIBase) getModeUIWindow();
156: }
157:
158: /** Separate mode UI backed by JFrame.
159: *
160: * [dafe] Whole DnD of window system expects that ModeComponent and
161: * TopComponentDroppable implementation must exist in AWT hierarchy,
162: * so I have to extend Swing class here, not just use it. That's why all this
163: * delegating stuff.
164: */
165: private static class ModeFrame extends JFrame implements ModeUIBase {
166:
167: /** Base helper to delegate to for common things */
168: private SharedModeUIBase modeBase;
169:
170: public ModeFrame(AbstractModeContainer abstractModeContainer,
171: ModeView view) {
172: super ();
173: // To be able to activate on mouse click.
174: enableEvents(java.awt.AWTEvent.MOUSE_EVENT_MASK);
175: modeBase = new SharedModeUIBaseImpl(abstractModeContainer,
176: view, this );
177: }
178:
179: public ModeView getModeView() {
180: return modeBase.getModeView();
181: }
182:
183: public int getKind() {
184: return modeBase.getKind();
185: }
186:
187: public Shape getIndicationForLocation(Point location) {
188: return modeBase.getIndicationForLocation(location);
189: }
190:
191: public Object getConstraintForLocation(Point location) {
192: return modeBase.getConstraintForLocation(location);
193: }
194:
195: public Component getDropComponent() {
196: return modeBase.getDropComponent();
197: }
198:
199: public ViewElement getDropViewElement() {
200: return modeBase.getDropViewElement();
201: }
202:
203: public boolean canDrop(TopComponent transfer, Point location) {
204: return modeBase.canDrop(transfer, location);
205: }
206:
207: public boolean supportsKind(int kind, TopComponent transfer) {
208: return modeBase.supportsKind(kind, transfer);
209: }
210:
211: /** Actually sets title for the frame
212: */
213: public void updateTitle(String title) {
214: // extract HTML from text - Output window (and soon others) uses it
215: if (BasicHTML.isHTMLString(title)) {
216: char[] c = title.toCharArray();
217: StringBuffer sb = new StringBuffer(title.length());
218: boolean inTag = false;
219: boolean inEntity = false;
220: for (int i = 0; i < c.length; i++) {
221: if (inTag && c[i] == '>') { //NOI18N
222: inTag = false;
223: continue;
224: }
225: if (!inTag && c[i] == '<') { //NOI18N
226: inTag = true;
227: continue;
228: }
229: if (!inTag) {
230: sb.append(c[i]);
231: }
232: }
233: //XXX, would be nicer to support the full complement of entities...
234: title = Utilities.replaceString(sb.toString(),
235: " ", " "); //NOI18N
236: }
237: String completeTitle = MessageFormat.format(NbBundle
238: .getMessage(DefaultSeparateContainer.class,
239: "CTL_SeparateEditorTitle"), title);
240: setTitle(completeTitle);
241: }
242:
243: } // end of ModeFrame
244:
245: /** Separate mode UI backed by JFrame.
246: *
247: * [dafe] Whole DnD of window system expects that ModeComponent and
248: * TopComponentDroppable implementation must exist in AWT hierarchy,
249: * so I have to extend Swing class here, not just use it. That's why all this
250: * delegating stuff.
251: */
252: private static class ModeDialog extends JDialog implements
253: ModeUIBase {
254:
255: /** Base helper to delegate to for common things */
256: private SharedModeUIBase modeBase;
257: private WindowSnapper snapper;
258: private boolean ignoreMovedEvents = false;
259:
260: public ModeDialog(Frame owner,
261: AbstractModeContainer abstractModeContainer,
262: ModeView view) {
263: super (owner);
264: // To be able to activate on mouse click.
265: enableEvents(java.awt.AWTEvent.MOUSE_EVENT_MASK);
266: modeBase = new SharedModeUIBaseImpl(abstractModeContainer,
267: view, this );
268:
269: try {
270: snapper = new WindowSnapper();
271: } catch (AWTException e) {
272: snapper = null;
273: Logger.getLogger(ModeDialog.class.getName()).log(
274: Level.INFO, null, e);
275: }
276: addComponentListener(new ComponentAdapter() {
277: @Override
278: public void componentMoved(ComponentEvent ce) {
279: if (ignoreMovedEvents
280: || null == snapper
281: || !WinSysPrefs.HANDLER.getBoolean(
282: WinSysPrefs.SNAPPING, true))
283: return;
284:
285: snapWindow();
286:
287: snapper.cursorMoved();
288: }
289: });
290: }
291:
292: private void snapWindow() {
293: Rectangle myBounds = getBounds();
294:
295: WindowManagerImpl wm = WindowManagerImpl.getInstance();
296: Set<? extends ModeImpl> modes = wm.getModes();
297: for (ModeImpl m : modes) {
298: if (m.getState() != Constants.MODE_STATE_SEPARATED)
299: continue;
300: TopComponent tc = m.getSelectedTopComponent();
301: if (null == tc)
302: continue;
303: Window w = SwingUtilities.getWindowAncestor(tc);
304: if (w == ModeDialog.this )
305: continue;
306: Rectangle targetBounds = w.getBounds();
307: if (snapper.snapTo(myBounds, targetBounds))
308: return;
309: }
310:
311: if (WinSysPrefs.HANDLER.getBoolean(
312: WinSysPrefs.SNAPPING_SCREENEDGES, true)) {
313: snapper.snapToScreenEdges(myBounds);
314: }
315: }
316:
317: public ModeView getModeView() {
318: return modeBase.getModeView();
319: }
320:
321: public int getKind() {
322: return modeBase.getKind();
323: }
324:
325: public Shape getIndicationForLocation(Point location) {
326: return modeBase.getIndicationForLocation(location);
327: }
328:
329: public Object getConstraintForLocation(Point location) {
330: return modeBase.getConstraintForLocation(location);
331: }
332:
333: public Component getDropComponent() {
334: return modeBase.getDropComponent();
335: }
336:
337: public ViewElement getDropViewElement() {
338: return modeBase.getDropViewElement();
339: }
340:
341: public boolean canDrop(TopComponent transfer, Point location) {
342: return modeBase.canDrop(transfer, location);
343: }
344:
345: public boolean supportsKind(int kind, TopComponent transfer) {
346: return modeBase.supportsKind(kind, transfer);
347: }
348:
349: public void updateTitle(String title) {
350: // noop - no title for dialogs
351: }
352:
353: @Override
354: public void setBounds(int x, int y, int w, int h) {
355: ignoreMovedEvents = true;
356: super .setBounds(x, y, w, h);
357: ignoreMovedEvents = false;
358: }
359:
360: @Override
361: public void setBounds(Rectangle r) {
362: ignoreMovedEvents = true;
363: super .setBounds(r);
364: ignoreMovedEvents = false;
365: }
366:
367: @Override
368: public void setLocation(Point p) {
369: ignoreMovedEvents = true;
370: super .setLocation(p);
371: ignoreMovedEvents = false;
372: }
373:
374: @Override
375: public void setLocation(int x, int y) {
376: ignoreMovedEvents = true;
377: super .setLocation(x, y);
378: ignoreMovedEvents = false;
379: }
380: } // end of ModeDialog
381:
382: /** Defines shared common attributes of UI element for separate mode. */
383: public interface SharedModeUIBase extends ModeComponent,
384: TopComponentDroppable {
385: }
386:
387: /** Defines base of UI element for separate mode, containing extras
388: * in which JDialog and JFrame separate mode differs
389: */
390: public interface ModeUIBase extends ModeComponent,
391: TopComponentDroppable {
392: public void updateTitle(String title);
393: }
394:
395: /** Base impl of separate UI element, used as delegatee for shared things.
396: */
397: private static class SharedModeUIBaseImpl implements
398: SharedModeUIBase {
399:
400: private final AbstractModeContainer abstractModeContainer;
401: private final ModeView modeView;
402: private long frametimestamp = 0;
403:
404: /** UI representation of separate window */
405: private Window window;
406:
407: public SharedModeUIBaseImpl(
408: AbstractModeContainer abstractModeContainer,
409: ModeView view, Window window) {
410: this .abstractModeContainer = abstractModeContainer;
411: this .modeView = view;
412: this .window = window;
413: initWindow(window);
414: attachListeners(window);
415: }
416:
417: /** Creates and returns window appropriate for type of dragged TC;
418: * either frame or dialog.
419: */
420: private void initWindow(Window w) {
421: // mark this as separate window, so that ShortcutAndMenuKeyEventProcessor
422: // allows normal shorcut processing like inside main window
423: ((RootPaneContainer) w).getRootPane().putClientProperty(
424: Constants.SEPARATE_WINDOW_PROPERTY, Boolean.TRUE);
425:
426: // register in z-order mng
427: ZOrderManager.getInstance().attachWindow(
428: (RootPaneContainer) w);
429: }
430:
431: private void attachListeners(Window w) {
432: w.addWindowListener(new WindowAdapter() {
433: @Override
434: public void windowClosing(WindowEvent evt) {
435: modeView.getController().userClosingMode(modeView);
436: ZOrderManager.getInstance().detachWindow(
437: (RootPaneContainer) window);
438: }
439:
440: @Override
441: public void windowClosed(WindowEvent evt) {
442: ZOrderManager.getInstance().detachWindow(
443: (RootPaneContainer) window);
444: }
445:
446: @Override
447: public void windowActivated(WindowEvent event) {
448: if (frametimestamp != 0
449: && System.currentTimeMillis() > frametimestamp + 500) {
450: modeView.getController()
451: .userActivatedModeWindow(modeView);
452: }
453: frametimestamp = System.currentTimeMillis();
454: }
455:
456: @Override
457: public void windowOpened(WindowEvent event) {
458: frametimestamp = System.currentTimeMillis();
459: }
460: }); // end of WindowListener
461:
462: w.addComponentListener(new ComponentAdapter() {
463: @Override
464: public void componentResized(ComponentEvent evt) {
465: /*if(DefaultSeparateContainer.this.frame.getExtendedState() == Frame.MAXIMIZED_BOTH) {
466: // Ignore changes when the frame is in maximized state.
467: return;
468: }*/
469:
470: modeView.getController().userResizedModeBounds(
471: modeView, window.getBounds());
472: }
473:
474: @Override
475: public void componentMoved(ComponentEvent evt) {
476: /*if(DefaultSeparateContainer.this.frame.getExtendedState() == Frame.MAXIMIZED_BOTH) {
477: // Ignore changes when the frame is in maximized state.
478: return;
479: }*/
480:
481: modeView.getController().userResizedModeBounds(
482: modeView, window.getBounds());
483: }
484:
485: }); // end of ComponentListener
486:
487: window.addWindowStateListener(new WindowStateListener() {
488: public void windowStateChanged(WindowEvent evt) {
489: if (!Constants.AUTO_ICONIFY) {
490: modeView.getController()
491: .userChangedFrameStateMode(modeView,
492: evt.getNewState());
493: } else {
494: // All the timestamping is a a workaround beause of buggy GNOME
495: // and of its kind who iconify the windows on leaving the desktop.
496: Component comp = modeView.getComponent();
497: if (comp instanceof Frame /*&& comp.isVisible() */) {
498: long currentStamp = System
499: .currentTimeMillis();
500: if (currentStamp > (modeView.getUserStamp() + 500)
501: && currentStamp > (modeView
502: .getMainWindowStamp() + 1000)) {
503: modeView.getController()
504: .userChangedFrameStateMode(
505: modeView,
506: evt.getNewState());
507: } else {
508: modeView.setUserStamp(0);
509: modeView.setMainWindowStamp(0);
510: modeView.updateFrameState();
511: }
512: long stamp = System.currentTimeMillis();
513: modeView.setUserStamp(stamp);
514: }
515: }
516: }
517: }); // end of WindowStateListener
518:
519: }
520:
521: public void setVisible(boolean visible) {
522: frametimestamp = System.currentTimeMillis();
523: window.setVisible(visible);
524: }
525:
526: public void toFront() {
527: frametimestamp = System.currentTimeMillis();
528: window.toFront();
529: }
530:
531: public ModeView getModeView() {
532: return abstractModeContainer.getModeView();
533: }
534:
535: public int getKind() {
536: return abstractModeContainer.getKind();
537: }
538:
539: // TopComponentDroppable>>
540: public Shape getIndicationForLocation(Point location) {
541: return abstractModeContainer
542: .getIndicationForLocation(location);
543: }
544:
545: public Object getConstraintForLocation(Point location) {
546: return abstractModeContainer
547: .getConstraintForLocation(location);
548: }
549:
550: public Component getDropComponent() {
551: return abstractModeContainer.getDropComponent();
552: }
553:
554: public ViewElement getDropViewElement() {
555: return abstractModeContainer.getDropModeView();
556: }
557:
558: public boolean canDrop(TopComponent transfer, Point location) {
559: return abstractModeContainer.canDrop(transfer);
560: }
561:
562: public boolean supportsKind(int kind, TopComponent transfer) {
563: // this is not a typo, yes it should be the same as canDrop
564: return abstractModeContainer.canDrop(transfer);
565: //return true;
566: /*
567: if(Constants.SWITCH_MODE_ADD_NO_RESTRICT
568: || WindowManagerImpl.getInstance().isTopComponentAllowedToMoveAnywhere(transfer)) {
569: return true;
570: }
571:
572: return kind == Constants.MODE_KIND_VIEW || kind == Constants.MODE_KIND_SLIDING;
573: */
574: }
575: // TopComponentDroppable<<
576:
577: } // End of ModeWindow.
578:
579: }
|