1  /*
     2   Copyright aswing.org, see the LICENCE.txt.
     3  */
     4  
     5  import org.aswing.ASWingUtils;
     6  import org.aswing.BorderLayout;
     7  import org.aswing.Component;
     8  import org.aswing.Container;
     9  import org.aswing.Event;
    10  import org.aswing.geom.Dimension;
    11  import org.aswing.geom.Point;
    12  import org.aswing.LayoutManager;
    13  import org.aswing.MCPanel;
    14  import org.aswing.plaf.WindowUI;
    15  import org.aswing.UIManager;
    16  import org.aswing.utils.Delegate;
    17  import org.aswing.utils.DepthManager;
    18  import org.aswing.utils.MCUtils;
    19  import org.aswing.utils.Vector;
    20  import org.aswing.WindowLayout;
    21  
    22  /**
    23   * JWindow is a Container, but you should not add component to JWindow directly,
    24   * you should add component like this:<br>
    25   * <pre>
    26   * 		jwindow.getContentPane().append(child);
    27   * </pre>
    28   * <p>The same is true of setting LayoutManagers, removing components, listing children, etc.
    29   * All these methods should normally be sent to the contentPane instead of the JWindow itself. 
    30   * The contentPane will always be non-null. Attempting to set it to null will cause the JWindow to throw an Error. 
    31   * The default contentPane will have a BorderLayout manager set on it. 
    32   * 
    33   * <p>But if you really want to add child to JWindow like how JDialog and JFrame does,
    34   * just do it, normally if you want to extends JWindow to make a new type Window, you may
    35   * need to add child to JWindow, example a title bar on top, a menubar on top, a status bar on bottom, etc.
    36   * 
    37   * @author iiley
    38   */
    39  class org.aswing.JWindow extends Container{
    40  	/**
    41  	 * The window-activated event type.
    42  	 * onWindowActived Event{source:JWindow}
    43  	 */	
    44  	public static var ON_WINDOW_ACTIVATED:String = "onWindowActived";
    45  	/**
    46  	 * The window-deactivated event type.
    47  	 * onWindowDeactived Event{source:JWindow}
    48  	 */
    49  	public static var ON_WINDOW_DEACTIVATED:String = "onWindowDeactived";
    50  	/**
    51  	 * The window closed event means that When a window was opened.
    52  	 * onWindowOpened Event{source:JWindow}
    53  	 */	
    54  	public static var ON_WINDOW_OPENED:String = "onWindowOpened";		
    55  	/**
    56  	 * The window opened event means that When a window was disposed or hiden.
    57  	 * onWindowClosed Event{source:JWindow}
    58  	 */	
    59  	public static var ON_WINDOW_CLOSED:String = "onWindowClosed";	
    60  	/**
    61  	 * The "window is closing" event.
    62  	 * onWindowClosing Event{source:JWindow}
    63  	 */	
    64  	public static var ON_WINDOW_CLOSING:String = "onWindowClosing";
    65  	/**
    66  	 * The window iconified event.
    67  	 * onWindowIconified Event{source:JWindow}
    68  	 * @see org.aswing.JFrame#ICONIFIED
    69  	 * @see org.aswing.JFrame#setState()
    70  	 */	
    71  	public static var ON_WINDOW_ICONIFIED:String = "onWindowIconified";
    72  	/**
    73  	 * The window restored event, (the JFrame normal button pushed).
    74  	 * onWindowRestored Event{source:JWindow}
    75  	 * @see org.aswing.JFrame#NORMAL
    76  	 * @see org.aswing.JFrame#setState()
    77  	 */
    78  	public static var ON_WINDOW_RESTORED:String = "onWindowRestored";
    79  	/**
    80  	 * The window maximized event.
    81  	 * onWindowMaximized Event{source:JWindow}
    82  	 * @see org.aswing.JFrame#MAXIMIZED
    83  	 * @see org.aswing.JFrame#setState()
    84  	 */
    85  	public static var ON_WINDOW_MAXIMIZED:String = "onWindowMaximized";	
    86  	
    87  	private static var windows:Vector;
    88  	
    89  	private var ground_mc:MovieClip;
    90  	
    91  	private var contentPane:Container;
    92  	private var owner:Object;
    93  	private var modal:Boolean;
    94  	private var actived:Boolean;
    95  	private var modalMC:MovieClip;
    96  	
    97  	private var lootActiveFrom:JWindow;
    98  	private var listenerToOwner:Object;
    99  	
   100  	private var lastLAF:Object;
   101  	
   102  	/**
   103  	 * Create a JWindow
   104  	 * <br>
   105  	 * JWindow(owner:JWindow, modal:Boolean)<br>
   106  	 * JWindow(owner:MovieClip, modal:Boolean)<br>
   107  	 * JWindow(owner:JWindow)<br>
   108  	 * JWindow(owner:MovieClip)<br>
   109  	 * JWindow()<br>
   110  	 * 
   111  	 * @param owner the owner of this window, it can be a MovieClip or a JWindow, default it is default 
   112  	 * is <code>ASWingUtils.getRootMovieClip()</code>
   113  	 * @param modal true for a modal dialog, false for one that allows other windows to be active at the same time,
   114  	 *  default is false.
   115  	 * @see org.aswing.ASWingUtils#getRootMovieClip()
   116  	 */
   117  	public function JWindow(owner, modal:Boolean){
   118  		super();
   119  		setName("JWindow");
   120  		this.owner = (owner == undefined ? ASWingUtils.getRootMovieClip() : owner);
   121  		this.modal = (modal == undefined ? false : modal);
   122  		visible = false;
   123  		actived = false;
   124  		layout = new WindowLayout();
   125  		addEventListener(ON_MOVED, resetModalMC, this);
   126  		listenerToOwner = new Object();
   127  		listenerToOwner[ON_WINDOW_ICONIFIED] = Delegate.create(this, __ownerIconified);
   128  		listenerToOwner[ON_WINDOW_RESTORED] = Delegate.create(this, __ownerRestored);
   129  		listenerToOwner[ON_WINDOW_MAXIMIZED] = listenerToOwner[ON_WINDOW_RESTORED];
   130  		
   131  		updateUI();
   132  		lastLAF = UIManager.getLookAndFeel();
   133  	}
   134  	
   135      public function updateUI():Void{
   136      	setUI(WindowUI(UIManager.getUI(this)));
   137      }
   138      
   139      public function setUI(newUI:WindowUI):Void{
   140      	super.setUI(newUI);
   141      }
   142  	
   143  	public function getUIClassID():String{
   144  		return "WindowUI";
   145  	}
   146  	
   147  	/**
   148  	 * Sets the layout for the window.
   149  	 * @throws Error when you try to set a non-WindowLayout instance.
   150  	 */
   151  	public function setLayout(layout:LayoutManager):Void{
   152  		if(layout instanceof WindowLayout){
   153  			super.setLayout(layout);
   154  		}else{
   155  			trace(this + " Can not set a non-WindowLayout Layout to JWindow");
   156  			throw new Error(this + " Can not set a non-WindowLayout Layout to JWindow");
   157  		}
   158  	}
   159  		
   160  	/**
   161  	 * Check size first to make sure current size is not min than <code>getMinimumSize</code>, 
   162  	 */
   163  	public function paintImmediately():Void{
   164  		if(displayable && isVisible()){
   165  			var minimizSize:Dimension = getMinimumSize();
   166  			var needSize:Dimension = new Dimension(Math.max(getWidth(), minimizSize.width),
   167  													Math.max(getHeight(), minimizSize.height));
   168  			this.setSize(needSize);
   169  			super.paintImmediately();
   170  			revalidate();
   171  		}else{
   172  			super.paintImmediately();
   173  		}
   174  	}
   175  		
   176  	/**
   177  	 * @return true always here.
   178  	 */
   179  	public function isValidateRoot():Boolean{
   180  		return true;
   181  	}
   182  	
   183  	/**
   184  	 * Returns the content pane of this window.
   185  	 * @return the content pane
   186  	 */
   187  	public function getContentPane():Container{
   188  		if(contentPane == null){
   189  			var p:Container = new Container();
   190  			p.setLayout(new BorderLayout());
   191  			setContentPaneImp(p);
   192  		}
   193  		return contentPane;
   194  	}
   195  	
   196  	/**
   197  	 * Sets the window's content pane.
   198  	 * @param cp the content pane you want to set to the window.
   199  	 * @throws Error when cp is null or undefined
   200  	 */
   201  	public function setContentPane(cp:Container):Void{
   202  		if(cp != contentPane){
   203  			if(cp == null){
   204  				trace(this + " Can not set null to be JWindow's contentPane!");
   205  				throw new Error(this + " Can not set null to be JWindow's contentPane!");
   206  			}else{
   207  				setContentPaneImp(cp);
   208  			}
   209  		}
   210  	}
   211  	
   212  	private function setContentPaneImp(cp:Container):Void{
   213  		contentPane.removeFromContainer();
   214  		contentPane = cp;
   215  		append(contentPane, WindowLayout.CONTENT);
   216  	}
   217  	
   218  	/**
   219  	 * This will return the owner of this JWindow, it maybe a MovieClip maybe a JWindow.
   220  	 */
   221  	public function getOwner():Object{
   222  		return owner;
   223  	}
   224  	
   225  	/**
   226  	 * This will return the owner of this JWindow, it return a JWindow if
   227  	 * this window's owner is a JWindow, else return null;
   228  	 */
   229  	public function getWindowOwner():JWindow{
   230  		return JWindow(owner);
   231  	}
   232  	
   233  	/**
   234  	 * Specifies whether this dialog should be modal.
   235  	 */
   236  	public function setModal(m:Boolean):Void{
   237  		if(modal != m){
   238  			modal = m;
   239  			modalMC._visible = modal;
   240  		}
   241  	}
   242  	
   243  	/**
   244  	 * Returns is this dialog modal.
   245  	 */
   246  	public function isModal():Boolean{
   247  		return modal;
   248  	}	
   249  	
   250  	/**
   251  	 * Return an array containing all the windows this window currently owns.
   252  	 */
   253  	public function getOwnedWindows():Array{
   254  		return getOwnedWindowsWithOwner(this);
   255  	}
   256  			
   257  	/**
   258  	 * Shortcut of <code>setVisible(true)</code>
   259  	 */
   260  	public function show():Void{
   261  		setVisible(true);
   262  	}
   263  	
   264  	/**
   265  	 * Shows or hides the Window. 
   266  	 * <p>Shows the window when set visible true, If the Window and/or its owner are not yet displayable(and if Owner is a JWindow),
   267  	 * both are made displayable. The Window will be made visible and bring to top;
   268  	 * <p>Hides the window when set visible false, just hide the Window's MCs.
   269  	 * @param v true to show the window, false to hide the window.
   270  	 * @throws Error if the window has not a {@link JWindow} or <code>MovieClip</code> owner currently, 
   271  	 * generally this should be never occur since the default owner is <code>_root</code>.
   272  	 * @see #show()
   273  	 * @see #hide()
   274  	 */	
   275  	public function setVisible(v:Boolean):Void{
   276  		if(v != visible || (v && !MCUtils.isMovieClipExist(root_mc))){
   277  			super.setVisible(v);
   278  			
   279  			if(v){
   280  				if(!isDisplayable()){
   281  					createWindowContents();
   282  				}
   283  				resetModalMC();
   284  				dispatchEvent(ON_WINDOW_OPENED, createEventObj(ON_WINDOW_OPENED));
   285  			}else{
   286  				dispatchEvent(ON_WINDOW_CLOSED, createEventObj(ON_WINDOW_CLOSED));
   287  			}
   288  		}
   289  		if(v){
   290  			toFront();
   291  			setActive(true);
   292  		}else{
   293  			lostActiveAction();
   294  		}
   295  	}
   296  	
   297  	/**
   298  	 * Shortcut of <code>setVisible(false)</code>
   299  	 */
   300  	public function hide():Void{
   301  		setVisible(false);
   302  	}
   303  	
   304  	/**
   305  	 * Remove all of this window's source movieclips.(also the components in this window will be removed too)
   306  	 */
   307  	public function dispose():Void{
   308  		visible = false;
   309  		getWindowsVector().remove(this);
   310  		getWindowOwner().removeEventListener(listenerToOwner);
   311  		
   312  		//dispose owned windows
   313  		var owned:Array = getOwnedWindows();
   314  		for(var i:Number=0; i<owned.length; i++){
   315  			var w:JWindow = JWindow(owned[i]);
   316  			w.dispose();
   317  		}
   318  		
   319  		lostActiveAction();
   320  		removeFromContainer();
   321  		ground_mc.unloadMovie();
   322  		ground_mc.removeMovieClip();
   323  		ground_mc = null;
   324  		dispatchEvent(ON_WINDOW_CLOSED, createEventObj(ON_WINDOW_CLOSED));
   325  	}
   326  	
   327  	/**
   328  	 * Causes this Window to be sized to fit the preferred size and layouts of its subcomponents.
   329  	 */
   330  	public function pack():Void{
   331  		setSize(getPreferredSize());
   332  	}
   333  	
   334  	/**
   335  	 * If this Window is visible, sends this Window to the back and may cause it to lose 
   336  	 * focus or activation if it is the focused or active Window.
   337  	 * <p>Infact this sends this JWindow to the back of all the MCs in its owner's MC
   338  	 *  except it's owner's root_mc, it's owner is always below it.<br>
   339  	 * @see #toFront()
   340  	 */
   341  	public function toBack():Void{
   342  		if(displayable && visible){
   343  			if(!DepthManager.isBottom(ground_mc, getOwnerRootMC())){
   344  				DepthManager.bringToBottom(ground_mc, getOwnerRootMC());
   345  			}
   346  		}
   347  	}
   348  	
   349  	/**
   350  	 * If this Window is visible, brings this Window to the front and may make it the focused Window.
   351  	 * <p>Infact this brings this JWindow to the front in his owner, all owner's MovieClips' front.
   352  	 * @see #toBack()
   353  	 */
   354  	public function toFront():Void{
   355  		if(displayable && visible){
   356  			if(!DepthManager.isTop(ground_mc)){
   357  				DepthManager.bringToTop(ground_mc);	
   358  			}
   359  		}
   360  	}
   361  	
   362  	/**
   363  	 * Returns whether this Window is active. 
   364  	 * The active Window is always either the focused Window, 
   365  	 * or the first Frame or Dialog that is an owner of the focused Window. 
   366  	 */
   367  	public function isActive():Boolean{
   368  		return actived;
   369  	}
   370  	
   371  	/**
   372  	 * Sets the window to be actived or unactived.
   373  	 */
   374  	public function setActive(b:Boolean):Void{
   375  		if(actived != b){
   376  			if(b){
   377  				active();
   378  			}else{
   379  				deactive();
   380  			}
   381  		}
   382  	}
   383  	
   384  	/**
   385  	 * Returns the window's ancestor movieclip which it/it's owner is created on.
   386  	 * @return the ancestor movieclip of this window 
   387  	 */
   388  	public function getWindowAncestorMC():MovieClip{
   389  		var ow:JWindow = this;
   390  		while(ow.getWindowOwner() != null){
   391  			ow = ow.getWindowOwner();
   392  		}
   393  		return MovieClip(ow.getOwner());
   394  	}
   395  	
   396  	/**
   397  	 * This is just for WindowUI to draw modalMC face.
   398  	 * @return the modal mc
   399  	 */
   400  	public function getModalMC():MovieClip{
   401  		return modalMC;
   402  	}
   403  	
   404  	/**
   405  	 * Resets the modal mc to cover the hole screen
   406  	 */
   407  	public function resetModalMC():Void{
   408  		var p:Point = new Point(0, 0);
   409  		if(!isModal()){
   410  			p.y = - 100000;
   411  		}
   412  		modalMC._visible = isModal();
   413  		modalMC._parent.globalToLocal(p);
   414  		modalMC._width = Stage.width*3;
   415  		modalMC._height = Stage.height*3;
   416  		modalMC._x = p.x - Stage.width;
   417  		modalMC._y = p.y - Stage.width;
   418  	}
   419  	
   420  	private static function getWindowsVector():Vector{
   421  		if(windows == undefined){
   422  			windows = new Vector();
   423  		}
   424  		return windows;
   425  	}
   426  	
   427  	/**
   428  	 * Returns all displable windows currently. A window was disposed or destroied will not 
   429  	 * included by this array.
   430  	 * @return all displable windows currently.
   431  	 */
   432  	public static function getWindows():Array{
   433  		return getWindowsVector().toArray();
   434  	}
   435  	
   436  	/**
   437  	 * getOwnedWindowsWithOwner(owner:JWindow)<br>
   438  	 * getOwnedWindowsWithOwner(owner:MovieClip)
   439  	 * <p>
   440  	 * Returns owned windows of the specifid owner.
   441  	 * @return owned windows of the specifid owner.
   442  	 */
   443  	public static function getOwnedWindowsWithOwner(owner:Object):Array{
   444  		var ws:Array = new Array();
   445  		for(var i:Number=0; i<getWindowsVector().size(); i++){
   446  			var w:JWindow = JWindow(getWindowsVector().get(i));
   447  			if(w.getOwner() === owner){
   448  				ws.push(w);
   449  			}
   450  		}
   451  		return ws;
   452  	}
   453  	
   454  	private function initialize():Void{
   455  		super.initialize();
   456  		ground_mc._visible = isVisible();
   457  	}
   458  	
   459  	public function doLayout():Void{
   460  		super.doLayout();
   461  		ground_mc._visible = isVisible();
   462  	}
   463  		
   464  	/**
   465  	 * Returns the component's mc's depth
   466  	 */
   467  	public function getDepth():Number{
   468  		return ground_mc.getDepth();
   469  	}
   470  	
   471  	/**
   472  	 * Swap the component's mc's depth
   473  	 */
   474  	public function swapDepths(target):Void{
   475  		ground_mc.swapDepths(target);
   476  	}	
   477  	
   478  	
   479  	private var mouseMoveListener:Object;
   480  	public function startDrag():Void{
   481  		if(mouseMoveListener == null){
   482  			mouseMoveListener = new Object();
   483  			mouseMoveListener.onMouseMove = Delegate.create(this, __onDrag);
   484  		}
   485  		root_mc.startDrag(false);
   486  		Mouse.addListener(mouseMoveListener);
   487  	}
   488  	
   489  	private function __onDrag():Void{
   490  		var event:Event = createEventObj(ON_MOVED);
   491  		event.oldPos = new Point(bounds.x, bounds.y);
   492  		
   493  		bounds.x = root_mc._x;
   494  		bounds.y = root_mc._y;
   495  		
   496  		event.newPos = bounds.getLocation();
   497  		
   498  		dispatchEvent(ON_MOVED, event);
   499  		updateAfterEvent();
   500  	}
   501  	
   502  	public function stopDrag():Void{
   503  		root_mc.stopDrag();
   504  		__onDrag();
   505  		Mouse.removeListener(mouseMoveListener);
   506  	}
   507  	
   508  	//--------------------------------------------------------
   509  	private var visibleWhenOwnerIconing:Boolean;
   510  	private function __ownerIconified():Void{
   511  		visibleWhenOwnerIconing = isVisible();
   512  		if(visibleWhenOwnerIconing){
   513  			lostActiveAction();
   514  			ground_mc._visible = false;
   515  		}
   516  	}
   517  	private function __ownerRestored():Void{
   518  		if(visibleWhenOwnerIconing){
   519  			ground_mc._visible = true;
   520  		}
   521  	}
   522  		
   523  	private function lostActiveAction():Void{
   524  		if(isActive()){
   525  			deactive();
   526  			getLootActiveFrom().active();
   527  		}
   528  		setLootActiveFrom(null);
   529  	}
   530  	
   531  	private function createMCForOwnedWindow():MovieClip{
   532  		return creater.createMC(ground_mc, "ground_mc");
   533  	}
   534  	
   535  	/**
   536  	 * Return the root_mc of the window's owner window.
   537  	 * @return the root_mc of the window's owner window, undefined if 
   538  	 * it has not a window owner.
   539  	 */
   540  	private function getOwnerRootMC():MovieClip{
   541  		return getWindowOwner().root_mc;
   542  	}
   543  	
   544  	
   545  	private function createWindowContents():Void{
   546  		if(owner instanceof MovieClip){
   547  			var ownerMC:MovieClip = MovieClip(owner);
   548  			ground_mc = creater.createMC(ownerMC, "ground_mc");
   549  		}else if(owner instanceof JWindow){
   550  			var jwo:JWindow = JWindow(owner);
   551  			jwo.show();
   552  			ground_mc = jwo.createMCForOwnedWindow();
   553  			jwo.addEventListener(listenerToOwner);
   554  		}else{
   555  			trace(this + " JWindow's owner is not a mc or JWindow, owner is : " + owner);
   556  			throw new Error(this + " JWindow's owner is not a mc or JWindow, owner is : " + owner);
   557  		}
   558  		if(lastLAF != UIManager.getLookAndFeel()){
   559  			ASWingUtils.updateComponentTreeUI(this);
   560  			lastLAF = UIManager.getLookAndFeel();
   561  		}
   562  		var groundPanel:MCPanel = new MCPanel(ground_mc, 10000, 10000);
   563  		groundPanel.append(this); //MCPanel is just a tool to make JWindow created
   564  	}
   565  		
   566  	private function getLootActiveFrom():JWindow{
   567  		return lootActiveFrom;
   568  	}
   569  	private function setLootActiveFrom(activeOwner:JWindow):Void{
   570  		if(activeOwner.getLootActiveFrom() == this){
   571  			activeOwner.lootActiveFrom = lootActiveFrom;
   572  		}
   573  		lootActiveFrom = activeOwner;
   574  	}
   575  	
   576  	private function active():Void{
   577  		actived = true;
   578  		for(var i:Number=0; i<getWindowsVector().size(); i++){
   579  			var w:JWindow = JWindow(getWindowsVector().get(i));
   580  			if(w != this){
   581  				if(w.isActive()){
   582  					w.deactive();
   583  					setLootActiveFrom(w);
   584  				}
   585  			}
   586  
   587  		}
   588  		//trace("JWindow " +name + " Active " ,Log.INFO);
   589  
   590  		dispatchEvent(ON_WINDOW_ACTIVATED, createEventObj(ON_WINDOW_ACTIVATED));
   591  	}
   592  	
   593  	private function deactive():Void{
   594  		actived = false;
   595  		dispatchEvent(ON_WINDOW_DEACTIVATED, createEventObj(ON_WINDOW_DEACTIVATED));
   596  	}
   597  	
   598  	private function create():Void{
   599  		if(getWindowsVector().contains(this)){
   600  			getWindowsVector().remove(this);
   601  		}
   602  		getWindowsVector().append(this);
   603  		createModalMC();
   604  		super.create();
   605  	}
   606  		
   607  	private function __onPress():Void{
   608  		super.__onPress();
   609  		__activeWhenClicked();
   610  	}
   611  		
   612  	/**
   613  	 * Active and make this window to front.
   614  	 */
   615  	public function __onChildPressed(child:Component):Void{
   616  		super.__onChildPressed(child);
   617  		__activeWhenClicked();
   618  	}
   619  	
   620  	private function __activeWhenClicked():Void{
   621  		//getWindowOwner().__activeWhenClicked();
   622  		getWindowOwner().toFront();
   623  		if(!isActive()){
   624  			toFront();
   625  			active();
   626  		}
   627  	}
   628  	
   629  	private function createModalMC():Void{
   630  		modalMC = creater.createMC(root_mc, "modal_mc");
   631  		modalMC.tabEnabled = false;
   632  		modalMC.onPress = null;
   633  		modalMC.onRelease = null;
   634  		modalMC._visible = modal;
   635  	}
   636  }
   637