1 /* Window.java --
2    Copyright (C) 1999, 2000, 2002, 2003 Free Software Foundation
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package java.awt;
40 
41 import java.awt.event.WindowEvent;
42 import java.awt.event.WindowFocusListener;
43 import java.awt.event.WindowListener;
44 import java.awt.event.WindowStateListener;
45 import java.awt.peer.WindowPeer;
46 import java.lang.ref.Reference;
47 import java.lang.ref.WeakReference;
48 import java.util.Iterator;
49 import java.util.EventListener;
50 import java.util.Locale;
51 import java.util.ResourceBundle;
52 import java.util.Vector;
53 import javax.accessibility.Accessible;
54 import javax.accessibility.AccessibleContext;
55 
56 /**
57  * This class represents a top-level window with no decorations.
58  *
59  * @author Aaron M. Renn <arenn@urbanophile.com>
60  * @author Warren Levy  <warrenl@cygnus.com>
61  */
62 public class Window extends Container implements Accessible
63 {
64   private static final long serialVersionUID = 4497834738069338734L;
65 
66   // Serialized fields, from Sun's serialization spec.
67   private String warningString = null;
68   private int windowSerializedDataVersion = 0; // FIXME
69   /** @since 1.2 */
70   // private FocusManager focusMgr;  // FIXME: what is this?
71   /** @since 1.2 */
72   private int state = 0;
73   /** @since 1.4 */
74   private boolean focusableWindowState = true;
75 
76   // A list of other top-level windows owned by this window.
77   private transient Vector ownedWindows = new Vector();
78 
79   private transient WindowListener windowListener;
80   private transient WindowFocusListener windowFocusListener;
81   private transient WindowStateListener windowStateListener;
82   private transient GraphicsConfiguration graphicsConfiguration;
83   private transient AccessibleContext accessibleContext;
84 
85   /**
86    * This (package access) constructor is used by subclasses that want
87    * to build windows that do not have parents.  Eg. toplevel
88    * application frames.  Subclasses cannot call super(null), since
89    * null is an illegal argument.
90    */
Window()91   Window()
92   {
93     visible = false;
94     setLayout(new BorderLayout());
95   }
96 
Window(GraphicsConfiguration gc)97   Window(GraphicsConfiguration gc)
98   {
99     this();
100     graphicsConfiguration = gc;
101   }
102 
103   /**
104    * Initializes a new instance of <code>Window</code> with the specified
105    * parent.  The window will initially be invisible.
106    *
107    * @param parent The owning <code>Frame</code> of this window.
108    *
109    * @exception IllegalArgumentException If the owner's GraphicsConfiguration
110    * is not from a screen device, or if owner is null; this exception is always
111    * thrown when GraphicsEnvironment.isHeadless returns true.
112    */
Window(Frame owner)113   public Window(Frame owner)
114   {
115     this (owner, owner.getGraphicsConfiguration ());
116   }
117 
118   /**
119    * Initializes a new instance of <code>Window</code> with the specified
120    * parent.  The window will initially be invisible.
121    *
122    * @exception IllegalArgumentException If the owner's GraphicsConfiguration
123    * is not from a screen device, or if owner is null; this exception is always
124    * thrown when GraphicsEnvironment.isHeadless returns true.
125    *
126    * @since 1.2
127    */
Window(Window owner)128   public Window(Window owner)
129   {
130     this (owner, owner.getGraphicsConfiguration ());
131   }
132 
133   /**
134    * Initializes a new instance of <code>Window</code> with the specified
135    * parent.  The window will initially be invisible.
136    *
137    * @exception IllegalArgumentException If owner is null or if gc is not from a
138    * screen device; this exception is always thrown when
139    * GraphicsEnvironment.isHeadless returns true.
140    *
141    * @since 1.3
142    */
Window(Window owner, GraphicsConfiguration gc)143   public Window(Window owner, GraphicsConfiguration gc)
144   {
145     this ();
146 
147     synchronized (getTreeLock())
148       {
149 	if (owner == null)
150 	  throw new IllegalArgumentException ("owner must not be null");
151 
152 	parent = owner;
153         owner.ownedWindows.add(new WeakReference(this));
154       }
155 
156     // FIXME: make this text visible in the window.
157     SecurityManager s = System.getSecurityManager();
158     if (s != null && ! s.checkTopLevelWindow(this))
159       warningString = System.getProperty("awt.appletWarning");
160 
161     if (gc != null
162         && gc.getDevice().getType() != GraphicsDevice.TYPE_RASTER_SCREEN)
163       throw new IllegalArgumentException ("gc must be from a screen device");
164 
165     // FIXME: until we implement this, it just causes AWT to crash.
166 //     if (gc == null)
167 //       graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment()
168 //         .getDefaultScreenDevice()
169 //         .getDefaultConfiguration();
170 //     else
171       graphicsConfiguration = gc;
172   }
173 
getGraphicsConfigurationImpl()174   GraphicsConfiguration getGraphicsConfigurationImpl()
175   {
176     if (graphicsConfiguration != null)
177 	return graphicsConfiguration;
178 
179     return super.getGraphicsConfigurationImpl();
180   }
181 
182   /**
183    * Creates the native peer for this window.
184    */
addNotify()185   public void addNotify()
186   {
187     if (peer == null)
188       peer = getToolkit().createWindow(this);
189     super.addNotify();
190   }
191 
192   /**
193    * Relays out this window's child components at their preferred size.
194    *
195    * @specnote pack() doesn't appear to be called internally by show(), so
196    *             we duplicate some of the functionality.
197    */
pack()198   public void pack()
199   {
200     if (parent != null && !parent.isDisplayable())
201       parent.addNotify();
202     if (peer == null)
203       addNotify();
204 
205     setSize(getPreferredSize());
206 
207     validate();
208   }
209 
210   /**
211    * Shows on-screen this window and any of its owned windows for whom
212    * isVisible returns true.
213    */
show()214   public void show()
215   {
216     if (parent != null && !parent.isDisplayable())
217       parent.addNotify();
218     if (peer == null)
219       addNotify();
220 
221     // Show visible owned windows.
222     synchronized (getTreeLock())
223       {
224 	Iterator e = ownedWindows.iterator();
225 	while(e.hasNext())
226 	  {
227 	    Window w = (Window)(((Reference) e.next()).get());
228 	    if (w != null)
229 	      {
230 		if (w.isVisible())
231 		  w.getPeer().setVisible(true);
232 	      }
233      	    else
234 	      // Remove null weak reference from ownedWindows.
235 	      // Unfortunately this can't be done in the Window's
236 	      // finalize method because there is no way to guarantee
237 	      // synchronous access to ownedWindows there.
238 	      e.remove();
239 	  }
240       }
241     validate();
242     super.show();
243     toFront();
244   }
245 
hide()246   public void hide()
247   {
248     // Hide visible owned windows.
249     synchronized (getTreeLock ())
250       {
251 	Iterator e = ownedWindows.iterator();
252 	while(e.hasNext())
253 	  {
254 	    Window w = (Window)(((Reference) e.next()).get());
255 	    if (w != null)
256 	      {
257 		if (w.isVisible() && w.getPeer() != null)
258 		  w.getPeer().setVisible(false);
259 	      }
260      	    else
261 	      e.remove();
262 	  }
263       }
264     super.hide();
265   }
266 
isDisplayable()267   public boolean isDisplayable()
268   {
269     if (super.isDisplayable())
270       return true;
271     return peer != null;
272   }
273 
274   /**
275    * Destroys any resources associated with this window.  This includes
276    * all components in the window and all owned top-level windows.
277    */
dispose()278   public void dispose()
279   {
280     hide();
281 
282     synchronized (getTreeLock ())
283       {
284 	Iterator e = ownedWindows.iterator();
285 	while(e.hasNext())
286 	  {
287 	    Window w = (Window)(((Reference) e.next()).get());
288 	    if (w != null)
289 	      w.dispose();
290 	    else
291 	      // Remove null weak reference from ownedWindows.
292 	      e.remove();
293 	  }
294 
295 	for (int i = 0; i < ncomponents; ++i)
296 	  component[i].removeNotify();
297 	this.removeNotify();
298 
299         // Post a WINDOW_CLOSED event.
300         WindowEvent we = new WindowEvent(this, WindowEvent.WINDOW_CLOSED);
301         getToolkit().getSystemEventQueue().postEvent(we);
302       }
303   }
304 
305   /**
306    * Sends this window to the back so that all other windows display in
307    * front of it.
308    */
toBack()309   public void toBack()
310   {
311     if (peer != null)
312       {
313 	WindowPeer wp = (WindowPeer) peer;
314 	wp.toBack();
315       }
316   }
317 
318   /**
319    * Brings this window to the front so that it displays in front of
320    * any other windows.
321    */
toFront()322   public void toFront()
323   {
324     if (peer != null)
325       {
326         WindowPeer wp = (WindowPeer) peer;
327         wp.toFront();
328       }
329   }
330 
331   /**
332    * Returns the toolkit used to create this window.
333    *
334    * @return The toolkit used to create this window.
335    *
336    * @specnote Unlike Component.getToolkit, this implementation always
337    *           returns the value of Toolkit.getDefaultToolkit().
338    */
getToolkit()339   public Toolkit getToolkit()
340   {
341     return Toolkit.getDefaultToolkit();
342   }
343 
344   /**
345    * Returns the warning string that will be displayed if this window is
346    * popped up by an unsecure applet or application.
347    *
348    * @return The unsecure window warning message.
349    */
getWarningString()350   public final String getWarningString()
351   {
352     return warningString;
353   }
354 
355   /**
356    * Returns the locale that this window is configured for.
357    *
358    * @return The locale this window is configured for.
359    */
getLocale()360   public Locale getLocale()
361   {
362     return locale == null ? Locale.getDefault() : locale;
363   }
364 
365   /*
366   /** @since 1.2
367   public InputContext getInputContext()
368   {
369     // FIXME
370   }
371   */
372 
373   /**
374    * Sets the cursor for this window to the specifiec cursor.
375    *
376    * @param cursor The new cursor for this window.
377    */
setCursor(Cursor cursor)378   public void setCursor(Cursor cursor)
379   {
380     super.setCursor(cursor);
381   }
382 
getOwner()383   public Window getOwner()
384   {
385     return (Window) parent;
386   }
387 
388   /** @since 1.2 */
getOwnedWindows()389   public Window[] getOwnedWindows()
390   {
391     Window [] trimmedList;
392     synchronized (getTreeLock ())
393       {
394 	// Windows with non-null weak references in ownedWindows.
395 	Window [] validList = new Window [ownedWindows.size()];
396 
397 	Iterator e = ownedWindows.iterator();
398 	int numValid = 0;
399 	while (e.hasNext())
400 	  {
401 	    Window w = (Window)(((Reference) e.next()).get());
402 	    if (w != null)
403 	      validList[numValid++] = w;
404 	    else
405 	      // Remove null weak reference from ownedWindows.
406 	      e.remove();
407 	  }
408 
409 	if (numValid != validList.length)
410 	  {
411 	    trimmedList = new Window [numValid];
412 	    System.arraycopy (validList, 0, trimmedList, 0, numValid);
413 	  }
414 	else
415 	  trimmedList = validList;
416       }
417     return trimmedList;
418   }
419 
420   /**
421    * Adds the specified listener to the list of <code>WindowListeners</code>
422    * that will receive events for this window.
423    *
424    * @param listener The <code>WindowListener</code> to add.
425    */
addWindowListener(WindowListener listener)426   public synchronized void addWindowListener(WindowListener listener)
427   {
428     windowListener = AWTEventMulticaster.add(windowListener, listener);
429   }
430 
431   /**
432    * Removes the specified listener from the list of
433    * <code>WindowListeners</code> that will receive events for this window.
434    *
435    * @param listener The <code>WindowListener</code> to remove.
436    */
removeWindowListener(WindowListener listener)437   public synchronized void removeWindowListener(WindowListener listener)
438   {
439     windowListener = AWTEventMulticaster.remove(windowListener, listener);
440   }
441 
442   /**
443    * Returns an array of all the window listeners registered on this window.
444    *
445    * @since 1.4
446    */
getWindowListeners()447   public synchronized WindowListener[] getWindowListeners()
448   {
449     return (WindowListener[])
450       AWTEventMulticaster.getListeners(windowListener,
451                                        WindowListener.class);
452   }
453 
454   /**
455    * Returns an array of all the window focus listeners registered on this
456    * window.
457    *
458    * @since 1.4
459    */
getWindowFocusListeners()460   public synchronized WindowFocusListener[] getWindowFocusListeners()
461   {
462     return (WindowFocusListener[])
463       AWTEventMulticaster.getListeners(windowFocusListener,
464                                        WindowFocusListener.class);
465   }
466 
467   /**
468    * Returns an array of all the window state listeners registered on this
469    * window.
470    *
471    * @since 1.4
472    */
getWindowStateListeners()473   public synchronized WindowStateListener[] getWindowStateListeners()
474   {
475     return (WindowStateListener[])
476       AWTEventMulticaster.getListeners(windowStateListener,
477                                        WindowStateListener.class);
478   }
479 
480   /**
481    * Adds the specified listener to this window.
482    */
addWindowFocusListener(WindowFocusListener wfl)483   public void addWindowFocusListener (WindowFocusListener wfl)
484   {
485     windowFocusListener = AWTEventMulticaster.add (windowFocusListener, wfl);
486   }
487 
488   /**
489    * Adds the specified listener to this window.
490    *
491    * @since 1.4
492    */
addWindowStateListener(WindowStateListener wsl)493   public void addWindowStateListener (WindowStateListener wsl)
494   {
495     windowStateListener = AWTEventMulticaster.add (windowStateListener, wsl);
496   }
497 
498   /**
499    * Removes the specified listener from this window.
500    */
removeWindowFocusListener(WindowFocusListener wfl)501   public void removeWindowFocusListener (WindowFocusListener wfl)
502   {
503     windowFocusListener = AWTEventMulticaster.remove (windowFocusListener, wfl);
504   }
505 
506   /**
507    * Removes the specified listener from this window.
508    *
509    * @since 1.4
510    */
removeWindowStateListener(WindowStateListener wsl)511   public void removeWindowStateListener (WindowStateListener wsl)
512   {
513     windowStateListener = AWTEventMulticaster.remove (windowStateListener, wsl);
514   }
515 
516   /**
517    * Returns an array of all the objects currently registered as FooListeners
518    * upon this Window. FooListeners are registered using the addFooListener
519    * method.
520    *
521    * @exception ClassCastException If listenerType doesn't specify a class or
522    * interface that implements java.util.EventListener.
523    *
524    * @since 1.3
525    */
getListeners(Class listenerType)526   public EventListener[] getListeners(Class listenerType)
527   {
528     if (listenerType == WindowListener.class)
529       return getWindowListeners();
530     return super.getListeners(listenerType);
531   }
532 
dispatchEventImpl(AWTEvent e)533   void dispatchEventImpl(AWTEvent e)
534   {
535     // Make use of event id's in order to avoid multiple instanceof tests.
536     if (e.id <= WindowEvent.WINDOW_LAST
537         && e.id >= WindowEvent.WINDOW_FIRST
538         && (windowListener != null
539 	    || windowFocusListener != null
540 	    || windowStateListener != null
541 	    || (eventMask & AWTEvent.WINDOW_EVENT_MASK) != 0))
542       processEvent(e);
543     else
544       super.dispatchEventImpl(e);
545   }
546 
547   /**
548    * Processes the specified event for this window.  If the event is an
549    * instance of <code>WindowEvent</code>, then
550    * <code>processWindowEvent()</code> is called to process the event,
551    * otherwise the superclass version of this method is invoked.
552    *
553    * @param event The event to process.
554    */
processEvent(AWTEvent evt)555   protected void processEvent(AWTEvent evt)
556   {
557     if (evt instanceof WindowEvent)
558       processWindowEvent((WindowEvent) evt);
559     else
560       super.processEvent(evt);
561   }
562 
563   /**
564    * Dispatches this event to any listeners that are listening for
565    * <code>WindowEvents</code> on this window.  This method only gets
566    * invoked if it is enabled via <code>enableEvents()</code> or if
567    * a listener has been added.
568    *
569    * @param event The event to process.
570    */
processWindowEvent(WindowEvent evt)571   protected void processWindowEvent(WindowEvent evt)
572   {
573     int id = evt.getID();
574 
575     if (id == WindowEvent.WINDOW_GAINED_FOCUS
576 	|| id == WindowEvent.WINDOW_LOST_FOCUS)
577       processWindowFocusEvent (evt);
578     else if (id == WindowEvent.WINDOW_STATE_CHANGED)
579       processWindowStateEvent (evt);
580     else
581       {
582 	if (windowListener != null)
583 	  {
584 	    switch (evt.getID())
585 	      {
586 	      case WindowEvent.WINDOW_ACTIVATED:
587 		windowListener.windowActivated(evt);
588 		break;
589 
590 	      case WindowEvent.WINDOW_CLOSED:
591 		windowListener.windowClosed(evt);
592 		break;
593 
594 	      case WindowEvent.WINDOW_CLOSING:
595 		windowListener.windowClosing(evt);
596 		break;
597 
598 	      case WindowEvent.WINDOW_DEACTIVATED:
599 		windowListener.windowDeactivated(evt);
600 		break;
601 
602 	      case WindowEvent.WINDOW_DEICONIFIED:
603 		windowListener.windowDeiconified(evt);
604 		break;
605 
606 	      case WindowEvent.WINDOW_ICONIFIED:
607 		windowListener.windowIconified(evt);
608 		break;
609 
610 	      case WindowEvent.WINDOW_OPENED:
611 		windowListener.windowOpened(evt);
612 		break;
613 
614 	      default:
615 		break;
616 	      }
617 	  }
618       }
619   }
620 
621   /**
622    * Returns the child window that has focus if this window is active.
623    * This method returns <code>null</code> if this window is not active
624    * or no children have focus.
625    *
626    * @return The component that has focus, or <code>null</code> if no
627    * component has focus.
628    */
getFocusOwner()629   public Component getFocusOwner()
630   {
631     // FIXME
632     return null;
633   }
634 
635   /**
636    * Post a Java 1.0 event to the event queue.
637    *
638    * @param event The event to post.
639    *
640    * @deprecated
641    */
postEvent(Event e)642   public boolean postEvent(Event e)
643   {
644     // FIXME
645     return false;
646   }
647 
648   /**
649    * Tests whether or not this window is visible on the screen.
650    *
651    * @return <code>true</code> if this window is visible, <code>false</code>
652    * otherwise.
653    */
isShowing()654   public boolean isShowing()
655   {
656     return super.isShowing();
657   }
658 
659   /**
660    * @since 1.2
661    *
662    * @deprecated
663    */
applyResourceBundle(ResourceBundle rb)664   public void applyResourceBundle(ResourceBundle rb)
665   {
666     throw new Error ("Not implemented");
667   }
668 
669   /**
670    * @since 1.2
671    *
672    * @deprecated
673    */
applyResourceBundle(String rbName)674   public void applyResourceBundle(String rbName)
675   {
676     ResourceBundle rb = ResourceBundle.getBundle(rbName);
677     if (rb != null)
678       applyResourceBundle(rb);
679   }
680 
getAccessibleContext()681   public AccessibleContext getAccessibleContext()
682   {
683     // FIXME
684     //return null;
685     throw new Error ("Not implemented");
686   }
687 
688   /**
689    * Get graphics configuration.  The implementation for Window will
690    * not ask any parent containers, since Window is a toplevel
691    * window and not actually embedded in the parent component.
692    */
getGraphicsConfiguration()693   public GraphicsConfiguration getGraphicsConfiguration()
694   {
695     if (graphicsConfiguration != null) return graphicsConfiguration;
696     if (peer != null) return peer.getGraphicsConfiguration();
697     return null;
698   }
699 
processWindowFocusEvent(WindowEvent event)700   protected void processWindowFocusEvent(WindowEvent event)
701   {
702     if (windowFocusListener != null)
703       {
704         switch (event.getID ())
705           {
706           case WindowEvent.WINDOW_GAINED_FOCUS:
707             windowFocusListener.windowGainedFocus (event);
708             break;
709 
710           case WindowEvent.WINDOW_LOST_FOCUS:
711             windowFocusListener.windowLostFocus (event);
712             break;
713 
714           default:
715             break;
716           }
717       }
718   }
719 
720   /**
721    * @since 1.4
722    */
processWindowStateEvent(WindowEvent event)723   protected void processWindowStateEvent(WindowEvent event)
724   {
725     if (windowStateListener != null
726         && event.getID () == WindowEvent.WINDOW_STATE_CHANGED)
727       windowStateListener.windowStateChanged (event);
728   }
729 
730   /**
731    * Returns whether this <code>Window</code> can get the focus or not.
732    *
733    * @since 1.4
734    */
isFocusableWindow()735   public final boolean isFocusableWindow ()
736   {
737     if (getFocusableWindowState () == false)
738       return false;
739 
740     if (this instanceof Dialog
741         || this instanceof Frame)
742       return true;
743 
744     // FIXME: Implement more possible cases for returning true.
745 
746     return false;
747   }
748 
749   /**
750    * Returns the value of the focusableWindowState property.
751    *
752    * @since 1.4
753    */
getFocusableWindowState()754   public boolean getFocusableWindowState ()
755   {
756     return focusableWindowState;
757   }
758 
759   /**
760    * Sets the value of the focusableWindowState property.
761    *
762    * @since 1.4
763    */
setFocusableWindowState(boolean focusableWindowState)764   public void setFocusableWindowState (boolean focusableWindowState)
765   {
766     this.focusableWindowState = focusableWindowState;
767   }
768 
769   // setBoundsCallback is needed so that when a user moves a window,
770   // the Window's location can be updated without calling the peer's
771   // setBounds method.  When a user moves a window the peer window's
772   // location is updated automatically and the windowing system sends
773   // a message back to the application informing it of its updated
774   // dimensions.  We must update the AWT Window class with these new
775   // dimensions.  But we don't want to call the peer's setBounds
776   // method, because the peer's dimensions have already been updated.
777   // (Under X, having this method prevents Configure event loops when
778   // moving windows: Component.setBounds -> peer.setBounds ->
779   // postConfigureEvent -> Component.setBounds -> ...  In some cases
780   // Configure event loops cause windows to jitter back and forth
781   // continuously).
setBoundsCallback(int x, int y, int w, int h)782   void setBoundsCallback (int x, int y, int w, int h)
783   {
784     if (this.x == x && this.y == y && width == w && height == h)
785       return;
786     invalidate();
787     this.x = x;
788     this.y = y;
789     width = w;
790     height = h;
791   }
792 }
793