blob: 01a90f22e4711ac9ce6ca930fdd3aed62b7f65c6 [file] [log] [blame]
/*******************************************************************************
* 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
******************************************************************************/
/**
* This class provides the client-side implementation for
* rwt.widgets.Link
*/
qx.Class.define( "rwt.widgets.Link", {
extend : rwt.widgets.base.Parent,
construct : function() {
this.base( arguments );
this.setAppearance( "link" );
this._text = "";
this._hasSelectionListener = false;
this._hyperlinksHaveListeners = false;
this._readyToSendChanges = true;
this._focusedLinkIndex = -1;
this._linksCount = 0;
this._link = new rwt.widgets.base.HtmlEmbed();
this._link.setAppearance( "link-text" );
this.add( this._link );
this.setSelectable( false );
this.setHideFocus( true );
this.__onMouseDown = rwt.util.Function.bindEvent( this._onMouseDown, this );
this.__onMouseOver = rwt.util.Function.bindEvent( this._onMouseOver, this );
this.__onMouseOut = rwt.util.Function.bindEvent( this._onMouseOut, this );
this.__onKeyDown = rwt.util.Function.bindEvent( this._onKeyDown, this );
this.addEventListener( "appear", this._onAppear, this );
this.addEventListener( "changeEnabled", this._onChangeEnabled, this );
this.addEventListener( "keypress", this._onKeyPress );
this.addEventListener( "focusout", this._onFocusOut );
this._link.addEventListener( "changeHtml", this._onChangeHtml, this );
},
destruct : function() {
this._removeEventListeners();
delete this.__onMouseDown;
delete this.__onMouseOver;
delete this.__onMouseOut;
delete this.__onKeyDown;
this.removeEventListener( "appear", this._onAppear, this );
this.removeEventListener( "changeEnabled", this._onChangeEnabled, this );
this.removeEventListener( "keypress", this._onKeyPress );
this.removeEventListener( "focusout", this._onFocusOut );
this._link.removeEventListener( "changeHtml", this._onChangeHtml, this );
this._link.dispose();
},
members : {
_onAppear : function( evt ) {
this._link.setTabIndex( null );
this._link.setHideFocus( true );
this._applyHyperlinksStyleProperties();
this._addEventListeners();
},
_onChangeHtml : function( evt ) {
this._applyHyperlinksStyleProperties();
this._addEventListeners();
},
_applyTextColor : function( value, old ) {
this.base( arguments, value, old );
this._applyHyperlinksStyleProperties();
},
_onChangeEnabled : function( evt ) {
this._applyHyperlinksStyleProperties();
this._changeHyperlinksTabIndexProperty();
},
_getStates : function() {
if( !this.__states ) {
this.__states = {};
}
return this.__states;
},
addState : function( state ) {
this.base( arguments, state );
this._link.addState( state );
},
removeState : function( state ) {
this.base( arguments, state );
this._link.removeState( state );
},
setHasSelectionListener : function( value ) {
this._hasSelectionListener = value;
},
addText : function( text ) {
this._text += text;
},
addLink : function( text, index ) {
var style = this._getHyperlinkStyle( false );
var widgetManager = org.eclipse.swt.WidgetManager.getInstance();
var id = widgetManager.findIdByWidget( this ) + "#" + index;
this._text += "<span tabIndex=\"1\" ";
this._text += "style=\"";
this._text += "text-decoration:" + style.textDecoration + "; ";
this._text += "\" ";
this._text += "id=\"" + id + "\"";
this._text += ">";
this._text += text;
this._text += "</span>";
this._linksCount++;
},
applyText : function() {
this._link.setHtml( this._text );
if( this._linksCount === 0 ) {
this.setTabIndex( null );
} else {
this.setTabIndex( 1 );
}
},
clear : function() {
this._removeEventListeners();
this._text = "";
this._linksCount = 0;
this._focusedLinkIndex = -1;
},
_applyHyperlinksStyleProperties : function() {
var style = this._getHyperlinkStyle( false );
var hyperlinks = this._getHyperlinkElements();
for( var i = 0; i < hyperlinks.length; i++ ) {
org.eclipse.rwt.HtmlUtil.setStyleProperty( hyperlinks[ i ], "color", style.textColor );
org.eclipse.rwt.HtmlUtil.setTextShadow( hyperlinks[ i ], style.textShadow );
org.eclipse.rwt.HtmlUtil.setStyleProperty( hyperlinks[ i ], "cursor", style.cursor );
}
},
_changeHyperlinksTabIndexProperty : function() {
var hyperlinks = this._getHyperlinkElements();
for( var i = 0; i < hyperlinks.length; i++ ) {
if( this.isEnabled() ) {
hyperlinks[ i ].tabIndex = "1";
} else {
hyperlinks[ i ].tabIndex = "-1";
}
}
},
// TODO [tb] : This is way more complicated than it needs to be.
// There is no need to work on DOM-level except when handling the event.
_addEventListeners : function() {
var hyperlinks = this._getHyperlinkElements();
if( hyperlinks.length > 0 && !this._hyperlinksHaveListeners ) {
for( var i = 0; i < hyperlinks.length; i++ ) {
qx.html.EventRegistration.addEventListener( hyperlinks[ i ],
"mousedown",
this.__onMouseDown );
qx.html.EventRegistration.addEventListener( hyperlinks[ i ],
"mouseover",
this.__onMouseOver );
qx.html.EventRegistration.addEventListener( hyperlinks[ i ],
"mouseout",
this.__onMouseOut );
qx.html.EventRegistration.addEventListener( hyperlinks[ i ],
"keydown",
this.__onKeyDown );
}
this._hyperlinksHaveListeners = true;
}
},
_removeEventListeners : function() {
var hyperlinks = this._getHyperlinkElements();
if( hyperlinks.length > 0 && this._hyperlinksHaveListeners ) {
for( var i = 0; i < hyperlinks.length; i++ ) {
qx.html.EventRegistration.removeEventListener( hyperlinks[ i ],
"mousedown",
this.__onMouseDown );
qx.html.EventRegistration.removeEventListener( hyperlinks[ i ],
"mouseover",
this.__onMouseOver );
qx.html.EventRegistration.removeEventListener( hyperlinks[ i ],
"mouseout",
this.__onMouseOut );
qx.html.EventRegistration.removeEventListener( hyperlinks[ i ],
"keydown",
this.__onKeyDown );
}
this._hyperlinksHaveListeners = false;
}
},
_onMouseDown : function( evt ) {
if( this.isEnabled() && this._isLeftMouseButtonPressed( evt ) ) {
var target = this._getEventTarget( evt );
var index = this._getLinkIndex( target );
this._setFocusedLink( index );
if( this._readyToSendChanges ) {
// [if] Fix for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=252559
this._readyToSendChanges = false;
rwt.client.Timer.once( function() {
this._sendChanges( index );
}, this, org.eclipse.swt.EventUtil.DOUBLE_CLICK_TIME );
}
}
},
_onMouseOver : function( evt ) {
var target = this._getEventTarget( evt );
var style = this._getHyperlinkStyle( true );
org.eclipse.rwt.HtmlUtil.setStyleProperty( target, "textDecoration", style.textDecoration );
},
_onMouseOut : function( evt ) {
var target = this._getEventTarget( evt );
var style = this._getHyperlinkStyle( false );
org.eclipse.rwt.HtmlUtil.setStyleProperty( target, "textDecoration", style.textDecoration );
},
_isLeftMouseButtonPressed : function( evt ) {
var result = false;
if( evt.which ) {
result = ( evt.which === 1 );
} else if( evt.button ) {
if( rwt.client.Client.isMshtml() ) {
result = ( evt.button === 1 );
} else {
result = ( evt.button === 0 );
}
}
return result;
},
_onKeyDown : function( evt ) {
if( this.isEnabled() && evt.keyCode === 13 ) {
var target = this._getEventTarget( evt );
var index = this._getLinkIndex( target );
this._sendChanges( index );
}
},
_getLinkIndex : function( element ) {
var id = element.id;
var index = id.substr( id.lastIndexOf( "#" ) + 1 );
return parseInt( index, 10 );
},
_getHyperlinkStyle : function( hover ) {
var states = this._getStates();
if( hover ) {
states[ "over" ] = true;
} else {
delete states[ "over" ];
}
var manager = rwt.theme.AppearanceManager.getInstance();
return manager.styleFrom( "link-hyperlink", states );
},
_getEventTarget : function( evt ) {
var target;
if( rwt.client.Client.isMshtml() ) {
target = window.event.srcElement;
} else {
target = evt.target;
}
return target;
},
// Override of the _ontabfocus method from rwt.widgets.base.Widget
_ontabfocus : function() {
if( this._focusedLinkIndex === -1 && this._linksCount > 0 ) {
this._setFocusedLink( 0 );
}
},
_onKeyPress : function( evt ) {
if( this.isFocused() && evt.getKeyIdentifier() === "Tab" && this._linksCount > 0 ) {
var index = this._focusedLinkIndex;
if( !evt.isShiftPressed() && index >= 0 && index < this._linksCount - 1 ) {
evt.stopPropagation();
evt.preventDefault();
this._setFocusedLink( index + 1 );
} else if( !evt.isShiftPressed() && index === -1 ) {
evt.stopPropagation();
evt.preventDefault();
this._setFocusedLink( 0 );
} else if( evt.isShiftPressed() && index > 0 && index <= this._linksCount - 1 ) {
evt.stopPropagation();
evt.preventDefault();
this._setFocusedLink( index - 1 );
}
}
},
_onFocusOut : function( evt ) {
this._setFocusedLink( -1 );
},
_setFocusedLink : function( index ) {
var hyperlink = this._getFocusedHyperlinkElement();
if( hyperlink !== null ) {
hyperlink.blur();
hyperlink.style.outline = "none";
}
this._focusedLinkIndex = index;
hyperlink = this._getFocusedHyperlinkElement();
if( hyperlink !== null ) {
hyperlink.focus();
hyperlink.style.outline = "1px dotted";
}
},
_getFocusedHyperlinkElement : function() {
var result = null;
var hyperlinks = this._getHyperlinkElements();
var index = this._focusedLinkIndex;
if( index >= 0 && index < hyperlinks.length ) {
result = hyperlinks[ index ];
}
return result;
},
_getHyperlinkElements : function() {
var result;
var linkElement = this.getElement();
if( linkElement ) {
result = linkElement.getElementsByTagName( "span" );
} else {
result = [];
}
return result;
},
_sendChanges : function( index ) {
if( this._hasSelectionListener ) {
org.eclipse.swt.EventUtil.notifySelected( this, { "index" : index } );
}
this._readyToSendChanges = true;
}
}
} );