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

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

Go to download

The Rich Ajax Platform lets you build rich, Ajax-enabled Web applications.

There is a newer version: 3.29.0
Show newest version
/*******************************************************************************
 * Copyright (c) 2009, 2012 EclipseSource 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:
 *    EclipseSource - initial API and implementation
 ******************************************************************************/

rwt.qx.Class.define( "rwt.widgets.Menu", {
  extend : rwt.widgets.base.Popup,
  include : rwt.animation.VisibilityAnimationMixin,

  construct : function() {
    this.base( arguments );
    this._layout = null;
    this._preItem = null;
    this._hasShowListener = false;
    this._hasHideListener = false;
    this._maxCellWidths = null;
    this._menuLayoutScheduled = false;
    this._opener = null;
    this._hoverItem = null;
    this._openTimer = null;
    this._closeTimer = null;
    this._openItem = null;
    this._itemsHiddenFlag = false;
    this._hoverFirstItemFlag = false;
    this.setHeight( "auto" );
    this.setWidth( "auto" );
    this._maxCellWidths = [ null, null, null, null ];
    this._layout = new rwt.widgets.base.VerticalBoxLayout();
    this._layout.set( {
      top : 0,
      right : 0,
      bottom : 0,
      left : 0,
      anonymous : true
    } );
    this.add( this._layout );
    this.addEventListener( "mousedown", this._unhoverSubMenu );
    this.addEventListener( "mouseout", this._onMouseOut );
    this.addEventListener( "mouseover", this._onMouseOver );
    this.addEventListener( "keypress", this._onKeyPress );
    this._openTimer = new rwt.client.Timer( 250 );
    this._openTimer.addEventListener( "interval", this._onopentimer, this );
    this._closeTimer = new rwt.client.Timer( 250 );
    this._closeTimer.addEventListener( "interval", this._onclosetimer, this );
    this.addToDocument();
  },

  destruct : function() {
    this._disposeObjects( "_openTimer", "_closeTimer", "_preItem", "_animation" );
    this._disposeFields( "_lastActive",
                         "_lastFocus",
                         "_layout",
                         "_opener",
                         "_hoverItem",
                         "_openItem" );
  },

  statics : {

    menuDetectedByKey : function( evt ) {
      if( evt.getKeyIdentifier() === "Apps" ) {
        rwt.widgets.Menu.contextMenuHandler( evt );
      }
    },

    menuDetectedByMouse : function( evt ) {
      if( evt.getButton() === rwt.event.MouseEvent.C_BUTTON_RIGHT ) {
        rwt.widgets.Menu.contextMenuHandler( evt );
      }
    },

    contextMenuHandler : function( event ) {
      var control = rwt.widgets.util.WidgetUtil.getControl( event.getTarget() );
      var contextMenu = control ? control.getContextMenu() : null;
      if( contextMenu != null ) {
        event.stopPropagation();
        event.preventDefault();
        var pageX = rwt.event.MouseEvent.getPageX();
        var pageY = rwt.event.MouseEvent.getPageY();
        contextMenu.setLocation( pageX, pageY );
        contextMenu.setOpener( control );
        contextMenu.show();
      }
    },

    getAllowContextMenu : function( target, domTarget ) {
      var result = false;
      switch( target.classname ) {
        case "rwt.widgets.Text":
        case "rwt.widgets.base.BasicText":
        case "qx.ui.form.TextArea":
          // NOTE: "enabled" can be "inherit", so it is not always a boolean
          if( target.getEnabled() !== false ) {
            if( rwt.widgets.Menu._hasNativeMenu( domTarget ) ) {
              result = target.getContextMenu() == null;
            }
          }
        break;
      }
      return result;
    },

    _hasNativeMenu : function( element ) {
      var tagName = typeof element.tagName == "string" ? element.tagName.toUpperCase() : "";
      return tagName === "INPUT" || tagName === "TEXTAREA";
    }

  },

  properties :  {

    appearance : {
      refine : true,
      init : "menu"
    }

  },

  events : {
    "changeHoverItem" : "rwt.event.Event"
  },

  members : {

    /////////
    // Opener

    setOpener : function( value ) {
      this._opener = value;
    },

    getOpener : function( value ) {
      return this._opener;
    },

    // Overwritten:
    getFocusRoot : function() {
      var root = null;
      if ( this._opener ) {
        root = this._opener.getFocusRoot();
      } else if( this._hasParent ) {
        root = this.getParent().getFocusRoot();
      }
      return root;
    },

    /////////
    // Layout

    addMenuItemAt : function( menuItem, index ) {
      // seperator does not have this function:
      if( menuItem.setParentMenu ) {
        // it is essential that this happens before the menuItem is added
        menuItem.setParentMenu( this );
      }
      this._layout.addAt( menuItem, index );
    },

    scheduleMenuLayout : function() {
      if( this._menuLayoutScheduled !== true ) {
        this._menuLayoutScheduled = true;
        var children = this._layout.getChildren();
        var length = children.length;
        for( var i = 0; i < length; i++ ) {
          children[ i ]._invalidatePreferredInnerWidth();
          children[ i ].addToQueue( "layoutX" );
        }
        this.addToQueue( "menuLayout" );
      }
    },

    _layoutPost : function( changes ) {
      this.base( arguments, changes );
      if( changes.menuLayout ) {
        this._menuLayoutScheduled = false;
        if( this.isSeeable() ) {
          this._afterAppear(); // recomputes the location
        }
      }
    },

    getMaxCellWidth : function( cell ) {
      if( this._maxCellWidths[ cell ] == null ) {
        var max = 0;
        var children = this._layout.getChildren();
        var length = children.length;
        for( var i = 0; i < length; i++ ) {
          if( children[ i ].getPreferredCellWidth ) {
            max = Math.max( max, children[ i ].getPreferredCellWidth( cell ) );
          }
        }
        this._maxCellWidths[ cell ] = max;
      }
      if( cell === 0 && this._maxCellWidths[ 0 ] === 0 && this.getMaxCellWidth( 1 ) === 0 ) {
        this._maxCellWidths[ cell ] = 13;
      }
      return this._maxCellWidths[ cell ];
    },

    invalidateMaxCellWidth : function( cell ) {
      this._maxCellWidths[ cell ] = null;
    },

    invalidateAllMaxCellWidths : function() {
      for( var i = 0; i < 4; i++ ) {
        this._maxCellWidths[ i ] = null;
      }
    },

    // needed for the menu-manager:
    isSubElement : function( vElement, vButtonsOnly ) {
      var ret = false;
      if (    ( vElement.getParent() === this._layout )
           || ( ( !vButtonsOnly ) && ( vElement === this ) ) ) {
        ret = true;
      }
      if( !ret ) {
        var a = this._layout.getChildren(), l=a.length;
        for ( var i = 0; i < l; i++ ) {
          if (    this.hasSubmenu( a[ i ] )
               && a[ i ].getMenu().isSubElement( vElement, vButtonsOnly ) )
          {
            ret = true;
          }
        }
      }
      return ret;
    },

    ////////
    // Hover

    setHoverItem : function( value, fromKeyEvent ) {
      var newHover = value ? value : this._openItem;
      if( this._hoverItem && this._hoverItem != newHover ) {
        this._hoverItem.removeState( "over" );
      }
      if( newHover ) {
        newHover.addState( "over" );
      }
      this._hoverItem = newHover;
      if( !fromKeyEvent ) {
        // handle open timer
        this._openTimer.setEnabled( false );
        if( this.hasSubmenu( newHover ) && ( this._openItem != newHover ) ) {
          this._openTimer.setEnabled( true );
        }
        // handle close tiemr
        if( this._openItem ) {
          if( this._openItem == newHover || newHover == null ) {
            this._closeTimer.setEnabled( false );
          } else if( newHover != null ) {
            this._closeTimer.setEnabled( true );
          }
        }
      }
      this.dispatchSimpleEvent( "changeHoverItem" );
    },

    getHoverItem : function( value ) {
      return this._hoverItem;
    },

    hoverFirstItem : function() {
      if( this._isDisplayable && !this._itemsHiddenFlag ) {
        this.setHoverItem( null, true );
        this._hoverNextItem();
        this.removeState( "hoverFristItem" );
      } else {
        this.addState( "hoverFristItem" );
      }
    },

    _hoverNextItem : function() {
      // About _hoverNext/Previous:
      // the index used for the array of visible children can have
      // "-1" as a valid value (as returned by indexOf), meaning a position
      // between the last and the first item. This is value is needed when no
      // item is hovered or the index-position is wrapping around.
      var current;
      var next = null;
      var children = this._layout.getVisibleChildren();
      var index = children.indexOf( this._hoverItem );
      var startIndex = index;
      do {
        index++;
        if( index > children.length ) {
          index = -1;
        }
        current = index >= 0 ? children[ index ] : null;
        if(   current
           && current.isEnabled()
           && current.classname == "rwt.widgets.MenuItem" )
        {
          next = current;
        }
      } while( !next && ( index != startIndex ) );
      this.setHoverItem( next, true );
    },

    _hoverPreviousItem : function() {
      var current;
      var prev = null;
      var children = this._layout.getVisibleChildren();
      var index = children.indexOf( this._hoverItem );
      var startIndex = index;
      do {
        index--;
        if( index < -1 ) {
          index = children.length;
        }
        current = index >= 0 ? children[ index ] : null;
        if(   current
           && current.isEnabled()
           && current.classname == "rwt.widgets.MenuItem" )
        {
          prev = current;
        }
      } while( !prev && ( index != startIndex ) );
      this.setHoverItem( prev, true );
    },

    //////////////////
    // Pop-Up handling

    // overwritten:
    _makeActive : function() {
      this._lastActive = this.getFocusRoot().getActiveChild();
      this._lastFocus = this.getFocusRoot().getFocusedChild();
      this.getFocusRoot().setActiveChild(this);
    },

    // overwritten:
    _makeInactive : function() {
      var vRoot = this.getFocusRoot();
      vRoot.setActiveChild( this._lastActive );
      vRoot.setFocusedChild( this._lastFocus );
    },


    _beforeAppear : function() {
      // original qooxdoo code: (1 line)
      rwt.widgets.base.Parent.prototype._beforeAppear.call( this );
      rwt.widgets.util.MenuManager.getInstance().add( this );
      this.bringToFront();
      this._makeActive();
      this._menuShown();
    },

    _beforeDisappear : function() {
      // original qooxdoo code: (1 line)
      rwt.widgets.base.Parent.prototype._beforeDisappear.call( this );
      rwt.widgets.util.MenuManager.getInstance().remove( this );
      if( this.getFocusRoot() ) {
        // if the menu is disposed while visible, it might not have a focusRoot
        this._makeInactive();
      }
      this.setOpenItem( null );
      this.setHoverItem( null );
      if( this._opener instanceof rwt.widgets.MenuItem ) {
        var parentMenu = this._opener.getParentMenu();
        if( parentMenu instanceof rwt.widgets.MenuBar ) {
          this._opener.removeState( "pressed" );
          if( parentMenu.getOpenItem() == this._opener ) {
            parentMenu.setOpenItem( null );
          }
        }
      }
      this._menuHidden();
    },


    //////////
    // Submenu

    hasSubmenu : function( item ) {
      return item && item.getMenu && item.getMenu();
    },

   _onopentimer : function( event ) {
      this._openTimer.stop();
      this.setOpenItem( this._hoverItem );
      // fix for bug 299350
      this._closeTimer.stop();
    },

    _onclosetimer : function( event ) {
      this._closeTimer.stop();
      this.setOpenItem( null );
    },

    setOpenItem : function( item ) {
      if( this._openItem && this._openItem.getMenu() ) {
        this._openItem.setSubMenuOpen( false );
        var oldMenu = this._openItem.getMenu();
        oldMenu.hide();
        this._makeActive();
      }
      this._openItem = item;
      // in theory an item could have lost it's assigned menu (by eval-code)
      // since the timer has been started/the item opend, so check for it
      if( item && item.getMenu() ) {
        var subMenu = item.getMenu();
        item.setSubMenuOpen( true );
        subMenu.setOpener( item );
        var itemNode = item.getElement();
        var thisNode = this.getElement();
        // the position is relative to the document, therefore we need helper
        subMenu.setTop( rwt.html.Location.getTop( itemNode ) - 2 );
        subMenu.setLeft(   rwt.html.Location.getLeft( thisNode )
                         + thisNode.offsetWidth
                         - 3 );
        subMenu.show();
      }
    },

    /////////////////
    // Event-handling

    _onMouseOut : function( event ) {
      var target = event.getTarget();
      var related = event.getRelatedTarget();
      if ( target == this || ( related != this && !this.contains( related ) ) )
      {
        this.setHoverItem( null );
      }
    },

   _onMouseOver : function( event ) {
     var target = event.getTarget();
     if( target != this ) {
       this.setHoverItem( target );
     }
     this._unhoverSubMenu();
   },

   _unhoverSubMenu : function() {
     if( this._openItem ) {
       var subMenu = this._openItem.getMenu();
       subMenu.setOpenItem( null );
       subMenu.setHoverItem( null );
     }
   },

    _onKeyPress : function( event ) {
      switch( event.getKeyIdentifier() ) {
        case "Up":
          this._handleKeyUp( event );
        break;
        case "Down":
          this._handleKeyDown( event );
        break;
        case "Left":
          this._handleKeyLeft( event );
        break;
        case "Right":
          this._handleKeyRight( event );
        break;
        case "Enter":
          this._handleKeyEnter( event );
        break;
      }
    },

    _handleKeyUp : function( event ) {
      if( this._openItem ) {
       this._openItem.getMenu()._hoverPreviousItem();
      } else {
        this._hoverPreviousItem();
      }
      event.preventDefault();
      event.stopPropagation();
    },

    _handleKeyDown : function( event ) {
      if( this._openItem ) {
       this._openItem.getMenu()._hoverNextItem();
      } else {
        this._hoverNextItem();
      }
      event.preventDefault();
      event.stopPropagation();
    },

    _handleKeyLeft : function( event ) {
      var parentMenu = this._opener ? this._opener.getParentMenu() : null;
      if( parentMenu instanceof rwt.widgets.Menu ) {
        var hover = this._opener;
        parentMenu.setOpenItem( null );
        parentMenu.setHoverItem( hover, true );
        event.preventDefault();
        event.stopPropagation();
      }
    },

    _handleKeyRight : function( event ) {
      if( this.hasSubmenu( this._hoverItem ) ) {
        this._onopentimer();
        this.setHoverItem( null, true );
        this._openItem.getMenu().hoverFirstItem();
        event.preventDefault();
        event.stopPropagation();
      }
    },

    _handleKeyEnter : function( event ) {
      if( this.hasSubmenu( this._hoverItem ) ) {
        this._onopentimer();
        this.setHoverItem( null, true );
        this._openItem.getMenu().hoverFirstItem();
      } else if( this._hoverItem ){
        this._hoverItem.execute();
        rwt.widgets.util.MenuManager.getInstance().update();
      }
      event.preventDefault();
      event.stopPropagation();
    },

   ////////////////
   // Client-Server

    setHasShowListener : function( value ) {
      if( !this.hasState( "rwt_BAR" ) ) {
        this._hasShowListener = value;
      }
    },

    setHasHideListener : function( value ) {
      if( !this.hasState( "rwt_BAR" ) ) {
        this._hasHideListener = value;
      }
    },

   _menuShown : function() {
      if( !rwt.remote.EventUtil.getSuspended() ) {
        if( this._hasShowListener ) {
          // create preliminary item
          if( this._preItem == null ) {
            this._preItem = new rwt.widgets.MenuItem( "push" );
            this._preItem.setText( "..." );
            this._preItem.setEnabled( false );
            this.addMenuItemAt( this._preItem, 0 );
          }
          // hide all but the preliminary item
          var items = this._layout.getChildren();
          for( var i = 0; i < items.length; i++ ) {
            var item = items[ i ];
            item.setDisplay( false );
          }
          this._preItem.setDisplay( true );
          this._itemsHiddenFlag = true;
          if( this.getWidth() < 60 ) {
            this.setWidth( 60 );
          }
          //this.setDisplay( true ); //wouldn't be called if display was false
          // send event
          rwt.remote.Server.getInstance().getRemoteObject( this ).notify( "Show" );
        } else {
          var display = this._layout.getChildren().length !== 0;
          //no items and no listener to add some:
          this.setDisplay( display );
          if( display ) {
            if( this._hoverFirstItemFlag ) {
              this.hoverFirstItem();
            }
          }
        }
      }
    },

    _menuHidden : function() {
      if( !rwt.remote.EventUtil.getSuspended() ) {
        if( this._hasHideListener ) {
          rwt.remote.Server.getInstance().getRemoteObject( this ).notify( "Hide" );
        }
      }
    },

    unhideItems : function( reveal ) {
      if( reveal ) {
        var items = this._layout.getChildren();
        for( var i = 0; i < items.length; i++ ) {
          items[ i ].setDisplay( true );
        }
        if( this._preItem ) {
          this._preItem.setDisplay( false );
        }
        this._itemsHiddenFlag = false;
        if( this._hoverFirstItemFlag ) {
          this.hoverFirstItem();
        }
      } else {
        this.hide();
      }
      this._hoverFirstItemFlag = false;
    },

    // Called to open a popup menu from server side
    showMenu : function( menu, x, y ) {
      if( menu != null ) {
        menu._renderAppearance();
        menu.setLocation( x, y );
        menu.show();
      }
    }

  }
} );




© 2015 - 2024 Weber Informatics LLC | Privacy Policy