All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rwt.widgets.Shell.js Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2002, 2016 Innoopract Informationssysteme GmbH and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Innoopract Informationssysteme GmbH - initial API and implementation
 *    EclipseSource - ongoing development
 ******************************************************************************/

rwt.qx.Class.define( "rwt.widgets.Shell", {

  extend : rwt.widgets.base.Window,

  include : rwt.animation.VisibilityAnimationMixin,

  construct : function( styles ) {
    this.base( arguments );
    this.setShowMinimize( styles.MIN === true );
    this.setAllowMinimize( styles.MIN === true  );
    this.setShowMaximize( styles.MAX === true  );
    this.setAllowMaximize(styles.MAX === true  );
    this.setShowClose( styles.CLOSE === true  );
    this.setAllowClose( styles.CLOSE === true  );
    this.setResizableWest( styles.RESIZE === true  );
    this.setResizableNorth( styles.RESIZE === true  );
    this.setResizableEast( styles.RESIZE === true  );
    this.setResizableSouth( styles.RESIZE === true  );
    this.setOverflow( "hidden" );
    // Note: This prevents a laoyut-glitch on the ipad:
    this.setRestrictToPageOnOpen( false );
    // TODO [rh] HACK to set mode on Label that shows the caption, _captionTitle
    //      is a 'protected' field on class Window
    this._captionTitle.setMode( "html" );
    this._activeControl = null;
    this._focusControl = null;
    this._parentShell = null;
    this._renderZIndex = true;
    this._sendBoundsTimer = new rwt.client.Timer( 0 );
    this._sendBoundsTimer.addEventListener( "interval", this._sendBounds, this );
    this._sendMoveFlag  = false;
    this._sendResizeFlag = false;
    this._sendResizeDelayed = false;
    this.addEventListener( "changeActiveChild", this._onChangeActiveChild );
    this.addEventListener( "changeFocusedChild", this._onChangeFocusedChild );
    this.addEventListener( "changeActive", this._onChangeActive );
    this.addEventListener( "changeLeft", this._onChangeLocation, this );
    this.addEventListener( "changeTop", this._onChangeLocation, this );
    this.addEventListener( "changeWidth", this._onChangeSize, this );
    this.addEventListener( "changeHeight", this._onChangeSize, this );
    this.addEventListener( "keydown", this._onKeydown );
    var req = rwt.remote.Connection.getInstance();
    req.addEventListener( "send", this._onSend, this );
    this.getCaptionBar().setWidth( "100%" );
    // [if] Listen for DOM event instead of qooxdoo event - see bug 294846.
    this.removeEventListener( "mousedown", this._onwindowmousedown );
    this.addEventListener( "create", this._onCreate, this );
    this.__onwindowmousedown = rwt.util.Functions.bind( this._onwindowmousedown, this );
    this.addToDocument();
  },

  statics : {
    TOP_LEFT : "topLeft",
    TOP_RIGHT : "topRight",
    BOTTOM_LEFT : "bottomLeft",
    BOTTOM_RIGHT : "bottomRight",
    CORNER_NAMES : [
      "topLeft",
      "topRight",
      "bottomLeft",
      "bottomRight"
    ],

    _onParentClose : function() {
      if( !rwt.remote.EventUtil.getSuspended() ) {
        this.doClose();
      }
    },

    reorderShells : function( vWindowManager ) {
      var shells = rwt.util.Objects.getValues( vWindowManager.getAll() );
      shells = shells.sort( rwt.widgets.Shell._compareShells );
      var vLength = shells.length;
      var upperModalShell = null;
      if( vLength > 0 ) {
        var vTop = shells[ 0 ].getTopLevelWidget();
        var vZIndex = rwt.widgets.Shell.MIN_ZINDEX;
        for( var i = 0; i < vLength; i++ ) {
          vZIndex += 10;
          shells[ i ].setZIndex( vZIndex );
          if( shells[ i ]._appModal && shells[ i ].getVisibility() && shells[ i ].getDisplay() ) {
            upperModalShell = shells[ i ];
          }
        }
        if( upperModalShell != null ) {
          this._copyStates( upperModalShell, vTop._getBlocker() );
          vTop._getBlocker().show();
          vTop._getBlocker().setZIndex( upperModalShell.getZIndex() - 1 );
        } else {
          vTop._getBlocker().hide();
        }
      }
      rwt.widgets.Shell._upperModalShell = upperModalShell;
    },

    _copyStates : function( source, target ) {
      target.__states = {};
      for( var state in source.__states ) {
        if( source._isRelevantState( state ) ) {
          target.addState( state );
        }
      }
      target._renderAppearance();
      rwt.widgets.base.Widget.removeFromGlobalLayoutQueue( target );
    },

    /*
     * Compares two Shells regarding their desired z-order.
     *
     * Result is
     * - positive if sh1 is higher
     * - negative if sh2 is higher
     * - zero if equal
     */
    _compareShells : function( sh1, sh2 ) {
      var result = 0;
      // check for dialog relationship
      if( sh1.isDialogOf( sh2 ) ) {
        result = 1;
      } else if( sh2.isDialogOf( sh1 ) ) {
        result = -1;
      }
      // compare by onTop property
      if( result === 0 ) {
        result = ( sh1._onTop ? 1 : 0 ) - ( sh2._onTop ? 1 : 0 );
      }
      // compare by appModal property
      if( result === 0 ) {
        result = ( sh1._appModal ? 1 : 0 ) - ( sh2._appModal ? 1 : 0 );
      }
      // compare by top-level parent's z-order
      if( result === 0 ) {
        var top1 = sh1.getTopLevelShell();
        var top2 = sh2.getTopLevelShell();
        result = top1.getZIndex() - top2.getZIndex();
      }
      // compare by actual z-order
      if( result === 0 ) {
        result = sh1.getZIndex() - sh2.getZIndex();
      }
      return result;
    },

    MIN_ZINDEX : 1e5,

    MAX_ZINDEX : 1e7
  },

  destruct : function() {
    this.setParentShell( null );
    var connection = rwt.remote.Connection.getInstance();
    connection.removeEventListener( "send", this._onSend, this );
    var document = rwt.widgets.base.ClientDocument.getInstance();
    document.removeEventListener( "windowresize", this._onWindowResize, this );
    if( this.isCreated() ) {
      this.getElement().removeEventListener( "mousedown", this.__onwindowmousedown, false );
    }
    this._disposeObjects( "_sendBoundsTimer" );
  },

  members : {

    destroy : function() {
      this.doClose();
      this.getWindowManager().remove( this );
      this.base( arguments );
    },

    _onCreate : function() {
      this.getElement().addEventListener( "mousedown", this.__onwindowmousedown, false );
      this.removeEventListener( "create", this._onCreate, this );
    },

    // [if] Override to prevent the new open shell to automaticaly become
    // an active shell (see bug 297167).
    _beforeAppear : function() {
      rwt.widgets.base.Parent.prototype._beforeAppear.call( this );
      rwt.widgets.util.PopupManager.getInstance().update();
      var activeWindow = this.getWindowManager().getActiveWindow();
      this.getWindowManager().add( this );
      this.getWindowManager().setActiveWindow( activeWindow );
    },

    setDefaultButton : function( value ) {
      this._defaultButton = value;
      this._renderHighlightButton();
    },

    _renderHighlightButton : function() {
      var button = this._defaultButton;
      var focused = this.getFocusedChild();
      if ( focused instanceof rwt.widgets.Button && focused.hasState( "push" ) ) {
        button = focused;
      }
      if( this._highlightButton != null ) {
        this._highlightButton.removeState( "default" );
      }
      this._highlightButton = button;
      if( this._highlightButton != null ) {
        this._highlightButton.addState( "default" );
      }
    },

    getDefaultButton : function() {
      return this._defaultButton;
    },

    setParentShell : function( parentShell ) {
      var oldParentShell = this._parentShell;
      this._parentShell = parentShell;
      var listener = rwt.widgets.Shell._onParentClose;
      if( oldParentShell != null ) {
        oldParentShell.removeEventListener( "close", listener, this );
      }
      if( parentShell != null ) {
        parentShell.addEventListener( "close", listener, this );
      }
      this.dispatchSimpleEvent( "parentShellChanged" );
    },

    isDisableResize : function() {
      return this._disableResize ? true : false;
    },

    setActiveControl : function( control ) {
      this._activeControl = control;
    },

    /** To be called after rwt_XXX states are set */
    initialize : function() {
      this.setShowCaption( this.hasState( "rwt_TITLE" ) );
      this._onTop = ( this._parentShell != null && this._parentShell._onTop )
                    || this.hasState( "rwt_ON_TOP" );
      this._appModal =    this.hasState( "rwt_APPLICATION_MODAL" )
                       || this.hasState( "rwt_PRIMARY_MODAL" )
                       || this.hasState( "rwt_SYSTEM_MODAL" );
      if( this._appModal ) {
        this.setStyleProperty( "position", "fixed" );
      }
    },

    // TODO [rst] Find a generic solution for state inheritance
    addState : function( state ) {
      this.base( arguments, state );
      if( this._isRelevantState( state ) ) {
        this._captionBar.addState( state );
        this._captionTitle.addState( state );
        this._minimizeButton.addState( state );
        this._maximizeButton.addState( state );
        this._restoreButton.addState( state );
        this._closeButton.addState( state );
        var blocker = this._getClientDocumentBlocker();
        if( blocker != null ) {
          blocker.addState( state );
        }
      }
    },

    removeState : function( state ) {
      this.base( arguments, state );
      if( this._isRelevantState( state ) ) {
        this._captionBar.removeState( state );
        this._captionTitle.removeState( state );
        this._minimizeButton.removeState( state );
        this._maximizeButton.removeState( state );
        this._restoreButton.removeState( state );
        this._closeButton.removeState( state );
        var blocker = this._getClientDocumentBlocker();
        if( blocker != null ) {
          blocker.removeState( state );
        }
      }
    },

    _getClientDocumentBlocker : function() {
      var result = null;
      if(    this._appModal
          && rwt.widgets.Shell._upperModalShell == this )
      {
        result = this.getTopLevelWidget()._getBlocker();
      }
      return result;
    },

    _isRelevantState : function( state ) {
      return    state == "active"
             || state == "maximized"
             || state == "minimized"
             || state.substr( 0, 8 ) == "variant_"
             || state.substr( 0, 4 ) == "rwt_";
    },

    /**
     * Overrides rwt.widgets.base.Window#close()
     *
     * Called when user tries to close the shell.
     */
    close : function() {
      if( !rwt.remote.EventUtil.getSuspended() ) {
        rwt.remote.Connection.getInstance().getRemoteObject( this ).notify( "Close" );
      }
    },

    /**
     * Really closes the shell.
     */
    doClose : function() {
      // Note [rst]: Fixes bug 232977
      // Background: There are situations where a shell is disposed twice, thus
      // doClose is called on an already disposed shell at the second time
      if( !this.isDisposed() ) {
        this.hide();
        if( this.hasEventListeners( "close" ) ) {
          var event = new rwt.event.DataEvent( "close", this );
          this.dispatchEvent( event, true );
        }
        var wm = this.getWindowManager();
        rwt.widgets.Shell.reorderShells( wm );
      }
    },

    _onChangeActiveChild : function( evt ) {
      // Work around qooxdoo bug #254: the changeActiveChild is fired twice when
      // a widget was activated by keyboard (getData() is null in this case)
      var widget = this._getParentControl( evt.getValue() );
      if(    !rwt.remote.EventUtil.getSuspended()
          && widget != null
          && widget !== this._activeControl )
      {
        this._notifyDeactivate( this._activeControl, widget );
        var id = rwt.remote.WidgetManager.getInstance().findIdByWidget( widget );
        var remoteObject = rwt.remote.Connection.getInstance().getRemoteObject( this );
        remoteObject.set( "activeControl", id );
        this._notifyActivate( this._activeControl, widget );
        this._activeControl = widget;
      }
    },

    _notifyDeactivate : function( oldActive, newActive ) {
      var target = oldActive;
      while( target != null && !this._hasDeactivateListener( target ) ) {
        if( target.getParent ) {
          target = target.getParent();
        } else {
          target = null;
        }
      }
      if( target != null && !target.contains( newActive ) ) {
        var remoteObject = rwt.remote.Connection.getInstance().getRemoteObject( target );
        remoteObject.notify( "Deactivate" );
      }
    },

    _notifyActivate : function( oldActive, newActive ) {
      var target = newActive;
      while( target != null && !this._hasActivateListener( target ) ) {
        if( target.getParent ) {
          target = target.getParent();
        } else {
          target = null;
        }
      }
      if( target != null && !target.contains( oldActive ) ) {
        var remoteObject = rwt.remote.Connection.getInstance().getRemoteObject( target );
        remoteObject.notify( "Activate" );
      }
    },

    _hasDeactivateListener : function( widget ) {
      return widget.getUserData( "deactivateListener" ) === true;
    },

    _hasActivateListener : function( widget ) {
      return widget.getUserData( "activateListener" ) === true;
    },

    _onChangeFocusedChild : function() {
      if( rwt.remote.EventUtil.getSuspended() ) {
        this._focusControl = this.getFocusedChild();
      }
      this._renderHighlightButton();
    },

    _onChangeActive : function( evt ) {
      // TODO [rst] This hack is a workaround for bug 345 in qooxdoo, remove this
      //      block as soon as the bug is fixed.
      //      See http://bugzilla.qooxdoo.org/show_bug.cgi?id=345
      if( !this.getActive() && !isFinite( this.getZIndex() ) ) {
        this.setZIndex( 1e8 );
      }
      // end of workaround
      if( !rwt.remote.EventUtil.getSuspended() && this.getActive() ) {
        rwt.remote.Connection.getInstance().getRemoteObject( this ).notify( "Activate" );
      }
      var active = evt.getValue();
      if( active ) {
        // workaround: Do not activate Shells that are blocked by a modal Shell
        var modalShell = rwt.widgets.Shell._upperModalShell;
        if( modalShell != null && modalShell.getZIndex() > this.getZIndex() ) {
          this.setActive( false );
          modalShell.setActive( true );
        }
        // end of workaround
      }
    },

    _applyMode : function( value, oldValue ) {
      var mode = value == null ? "normal" : value;
      rwt.remote.Connection.getInstance().getRemoteObject( this ).set( "mode", mode );
      var document = rwt.widgets.base.ClientDocument.getInstance();
      if( value == "maximized" ) {
        // User may change browser window size during long running request (before windowresize
        // listener is attached). Sync current shell bounds back to server - see bug 440948.
        this._sendBoundsTimer.start();
        document.addEventListener( "windowresize", this._onWindowResize, this );
      } else {
        document.removeEventListener( "windowresize", this._onWindowResize, this );
      }
      this.base( arguments, value, oldValue );
    },

    _onWindowResize : function() {
      this._sendResizeDelayed = true;
      this._sendResizeFlag = true;
      this._sendBounds();
    },

    _onChangeSize : function() {
      if( !rwt.remote.EventUtil.getSuspended() ) {
        this._sendResizeFlag = true;
        this._sendBoundsTimer.start();
      }
    },

    _onChangeLocation : function() {
      if( !rwt.remote.EventUtil.getSuspended() ) {
        this._sendMoveFlag = true;
        this._sendBoundsTimer.start();
      }
    },

    _sendBounds : function() {
      this._sendBoundsTimer.stop();
      var left = this._parseNumber( this.getLeft() );
      var top = this._parseNumber( this.getTop() );
      var height = this._parseNumber( this.getHeightValue() );
      var width = this._parseNumber( this.getWidthValue() );
      var remoteObject = rwt.remote.Connection.getInstance().getRemoteObject( this );
      remoteObject.set( "bounds", [ left, top, width, height ] );
      if( this._sendMoveFlag ) {
        remoteObject.notify( "Move", {} );
      }
      if( this._sendResizeFlag ) {
        remoteObject.notify( "Resize", {}, this._sendResizeDelayed ? 500 : undefined );
      }
      this._sendMoveFlag  = false;
      this._sendResizeFlag = false;
      this._sendResizeDelayed = false;
    },

    _parseNumber : function( value ) {
      var result = parseInt( value, 10 );
      return isNaN( result ) ? 0 : result;
    },

    _onKeydown : function( evt ) {
      var keyId = evt.getKeyIdentifier();
      if(    keyId == "Enter"
          && !evt.isShiftPressed()
          && !evt.isAltPressed()
          && !evt.isCtrlPressed()
          && !evt.isMetaPressed() )
      {
        var defButton = this.getDefaultButton();
        if( defButton != null && defButton.isSeeable() && defButton.getEnabled() ) {
          defButton.setFocused( true );
          defButton.execute();
        }
      } else if( keyId == "Escape" && this._parentShell != null ) {
        this.close();
      }
    },

    _onSend : function() {
      if( this.getActive() ) {
        var focusedChild = this.getFocusedChild();
        if( focusedChild != null && focusedChild != this._focusControl ) {
          this._focusControl = focusedChild;
          var widgetManager = rwt.remote.WidgetManager.getInstance();
          var focusedChildId = widgetManager.findIdByWidget( focusedChild );
          var server = rwt.remote.Connection.getInstance();
          var serverDisplay = server.getRemoteObject( rwt.widgets.Display.getCurrent() );
          serverDisplay.set( "focusControl", focusedChildId );
        }
      }
    },

    /**
     * Returns the parent Control for the given widget. If widget is a Control
     * itself, the widget is returned. Otherwise its parent is returned or null
     * if there is no parent
     */
    _getParentControl : function( widget ) {
      var widgetMgr = rwt.remote.WidgetManager.getInstance();
      var result = widget;
      while( result != null && !widgetMgr.isControl( result ) ) {
        if( result.getParent ) {
          result = result.getParent();
        } else {
          result = null;
        }
      }
      return result;
    },

    /**
     * Returns true if the receiver is a dialog shell of the given parent shell,
     * directly or indirectly.
     */
    isDialogOf : function( shell ) {
      var result = false;
      var parentShell = this._parentShell;
      while( !result && parentShell != null ) {
        result = shell === parentShell;
        parentShell = parentShell._parentShell;
      }
      return result;
    },

    /**
     * Returns the top-level shell if the receiver is a dialog or the shell
     * itself if it is a top-level shell.
     */
    getTopLevelShell : function() {
      var result = this;
      while( result._parentShell != null ) {
        result = result._parentShell;
      }
      return result;
    },

    /* TODO [rst] Revise when upgrading: overrides the _sendTo() function in
     *      superclass Window to allow for always-on-top.
     *      --> http://bugzilla.qooxdoo.org/show_bug.cgi?id=367
     */
    _sendTo : function() {
      rwt.widgets.Shell.reorderShells( this.getWindowManager() );
    },

    /*
     * Overwrites Popup#bringToFront
     */
    bringToFront : function() {
      var targetShell = this;
      while( targetShell._parentShell != null ) {
        targetShell = targetShell._parentShell;
      }
      this._setRenderZIndex( false );
      this.setZIndex( rwt.widgets.Shell.MAX_ZINDEX + 1 );
      targetShell.setZIndex( rwt.widgets.Shell.MAX_ZINDEX + 1 );
      rwt.widgets.Shell.reorderShells( this.getWindowManager() );
      this._setRenderZIndex( true );
    },

    _applyZIndex : function( newValue, oldValue ) {
      if( this._renderZIndex ) {
        this.base( arguments, newValue, oldValue );
      }
    },

    _setRenderZIndex : function( value ) {
       // Needed to prevent flickering during display-overlay animations.
      this._renderZIndex = value;
      if( value ) {
        this._applyZIndex( this.getZIndex() );
      }
    },

    _applyDirection : function( value ) {
      this.base( arguments, value );
      var isRTL = value === "rtl";
      this.getCaptionBar().setReverseChildrenOrder( isRTL );
      this.getCaptionBar().setHorizontalChildrenAlign( isRTL ? "right" : "left" );
      this.getLayoutImpl().setMirror( isRTL );
    },

    /*
     * E X P E R I M E N T A L
     * (for future PRIMARY_MODAL support)
     */
    setBlocked : function( blocked ) {
      if( blocked ) {
        if( !this._blocker ) {
          this._blocker = new rwt.widgets.base.Parent();
          this._blocker.setAppearance( "client-document-blocker" );
          this.add( this._blocker );
        }
        this._blocker.setSpace( 0, 0, 10000, 10000 );
        this._blocker.setZIndex( 1000 );
      } else {
        if( this._blocker ) {
          this.remove( this._blocker );
          this._blocker.destroy();
          this._blocker = null;
        }
      }
    },

    setFullScreen : function( fullScreen ) {
      if( fullScreen ) {
        this._captionBar.setDisplay( false );
      } else {
        this._captionBar.setDisplay( this.hasState( "rwt_TITLE" ) );
      }
    }

  }
} );




© 2015 - 2025 Weber Informatics LLC | Privacy Policy