/**
* Copyright (c) 2005-2006 Aptana, Inc.
*
* 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. If redistributing this code,
* this entire header must remain intact.
*/
package org.radrails.rails.internal.ui.browser;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.core.commands.common.CommandException;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.util.Geometry;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IActionDelegate;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.internal.intro.impl.IntroPlugin;
import org.eclipse.ui.internal.intro.impl.model.AbstractIntroElement;
import org.eclipse.ui.internal.intro.impl.model.AbstractIntroPage;
import org.eclipse.ui.internal.intro.impl.model.IntroLaunchBarElement;
import org.eclipse.ui.internal.intro.impl.model.IntroModelRoot;
import org.eclipse.ui.internal.intro.impl.model.IntroPartPresentation;
import org.eclipse.ui.internal.intro.impl.model.IntroTheme;
import org.eclipse.ui.internal.intro.impl.model.loader.ExtensionPointManager;
import org.eclipse.ui.internal.intro.impl.model.loader.ModelLoaderUtil;
import org.eclipse.ui.internal.intro.impl.model.util.ModelUtil;
import org.eclipse.ui.internal.intro.impl.presentations.BrowserIntroPartImplementation;
import org.eclipse.ui.internal.intro.impl.presentations.IntroLaunchBar;
import org.eclipse.ui.internal.intro.impl.util.DialogUtil;
import org.eclipse.ui.internal.intro.impl.util.StringUtil;
import org.eclipse.ui.internal.intro.impl.util.Util;
import org.eclipse.ui.intro.IIntroPart;
import org.eclipse.ui.intro.config.CustomizableIntroPart;
import org.eclipse.ui.intro.config.IIntroAction;
import org.eclipse.ui.intro.config.IIntroURL;
import com.aptana.ide.core.ui.RectangleAnimation;
/**
* Core URL for internal Eclipse URLs
*
* @author Kevin Sawicki
*/
public class CoreURL implements IIntroURL
{
/**
* INTRO_PROTOCOL
*/
public static final String INTRO_PROTOCOL = "http"; //$NON-NLS-1$
/**
* INTRO_HOST_ID
*/
public static final String INTRO_HOST_ID = "org.eclipse.ui.intro"; //$NON-NLS-1$
/**
* Constants that represent Intro URL actions.
*/
public static final String SET_STANDBY_MODE = "setStandbyMode"; //$NON-NLS-1$
/**
* SHOW_STANDBY
*/
public static final String SHOW_STANDBY = "showStandby"; //$NON-NLS-1$
/**
* CLOSE
*/
public static final String CLOSE = "close"; //$NON-NLS-1$
/**
* SHOW_HELP_TOPIC
*/
public static final String SHOW_HELP_TOPIC = "showHelpTopic"; //$NON-NLS-1$
/**
* SHOW_HELP
*/
public static final String SHOW_HELP = "showHelp"; //$NON-NLS-1$
/**
* OPEN_BROWSER
*/
public static final String OPEN_BROWSER = "openBrowser"; //$NON-NLS-1$
/**
* OPEN_URL
*/
public static final String OPEN_URL = "openURL"; //$NON-NLS-1$
/**
* RUN_ACTION
*/
public static final String RUN_ACTION = "runAction"; //$NON-NLS-1$
/**
* SHOW_PAGE
*/
public static final String SHOW_PAGE = "showPage"; //$NON-NLS-1$
/**
* SHOW_MESSAGE
*/
public static final String SHOW_MESSAGE = "showMessage"; //$NON-NLS-1$
/**
* NAVIGATE
*/
public static final String NAVIGATE = "navigate"; //$NON-NLS-1$
/**
* SWITCH_TO_LAUNCH_BAR
*/
public static final String SWITCH_TO_LAUNCH_BAR = "switchToLaunchBar"; //$NON-NLS-1$
/**
* EXECUTE
*/
public static final String EXECUTE = "execute"; //$NON-NLS-1$
/**
* KEY_ID
*/
public static final String KEY_ID = "id"; //$NON-NLS-1$
/**
* KEY_PLUGIN_ID
*/
public static final String KEY_PLUGIN_ID = "pluginId"; //$NON-NLS-1$
/**
* KEY_CLASS
*/
public static final String KEY_CLASS = "class"; //$NON-NLS-1$
/**
* KEY_STANDBY
*/
public static final String KEY_STANDBY = "standby"; //$NON-NLS-1$
/**
* KEY_PART_ID
*/
public static final String KEY_PART_ID = "partId"; //$NON-NLS-1$
/**
* KEY_INPUT
*/
public static final String KEY_INPUT = "input"; //$NON-NLS-1$
/**
* KEY_MESSAGE
*/
public static final String KEY_MESSAGE = "message"; //$NON-NLS-1$
/**
* KEY_URL
*/
public static final String KEY_URL = "url"; //$NON-NLS-1$
/**
* KEY_DIRECTION
*/
public static final String KEY_DIRECTION = "direction"; //$NON-NLS-1$
/**
* KEY_EMBED
*/
public static final String KEY_EMBED = "embed"; //$NON-NLS-1$
/**
* KEY_EMBED_TARGET
*/
public static final String KEY_EMBED_TARGET = "embedTarget"; //$NON-NLS-1$
/**
* KEY_DECODE
*/
public static final String KEY_DECODE = "decode"; //$NON-NLS-1$
/**
* KEY_COMAND
*/
public static final String KEY_COMAND = "command"; //$NON-NLS-1$
/**
* VALUE_BACKWARD
*/
public static final String VALUE_BACKWARD = "backward"; //$NON-NLS-1$
/**
* VALUE_FORWARD
*/
public static final String VALUE_FORWARD = "forward"; //$NON-NLS-1$
/**
* VALUE_HOME
*/
public static final String VALUE_HOME = "home"; //$NON-NLS-1$
/**
* VALUE_TRUE
*/
public static final String VALUE_TRUE = "true"; //$NON-NLS-1$
/**
* VALUE_FALSE
*/
public static final String VALUE_FALSE = "false"; //$NON-NLS-1$
private String action = null;
private Properties parameters = null;
private Browser browser;
/**
* Prevent creation. Must be created through an IntroURLParser. This constructor assumed we have a valid intro url.
*
* @param url
*/
CoreURL(String action, Properties parameters)
{
this.action = action;
this.parameters = parameters;
}
/**
* Sets the browser to open urls in
*
* @param browser
*/
public void setBrowser(Browser browser)
{
this.browser = browser;
}
/**
* Executes whatever valid Intro action is embedded in this Intro URL.
*
* @return - true if executed
*/
public boolean execute()
{
final boolean[] result = new boolean[1];
Display display = Display.getCurrent();
BusyIndicator.showWhile(display, new Runnable()
{
public void run()
{
result[0] = doExecute();
}
});
return result[0];
}
/**
* Do execute
*
* @return - true if executed
*/
protected boolean doExecute()
{
if (action.equals(SHOW_HELP))
{
// display the full Help System.
return showHelp();
}
else if (action.equals(SHOW_HELP_TOPIC))
{
// display a Help System Topic. It can be displayed in the Help
// system window, or embedded as an intro page.
// return showHelpTopic(getParameter(KEY_ID));
return showHelpTopic(getParameter(KEY_ID), getParameter(KEY_EMBED), getParameter(KEY_EMBED_TARGET));
}
else if (action.equals(OPEN_BROWSER))
{
// display url in external browser
return openBrowser(getParameter(KEY_URL), getParameter(KEY_PLUGIN_ID));
}
if (action.equals(OPEN_URL))
{
// display url embedded in intro browser.
return openURL(getParameter(KEY_URL), getParameter(KEY_PLUGIN_ID));
}
else if (action.equals(RUN_ACTION))
{
// run an Intro action. Get the pluginId and the class keys. Pass
// the parameters and the standby state.
return runAction(getParameter(KEY_PLUGIN_ID), getParameter(KEY_CLASS), parameters,
getParameter(KEY_STANDBY));
}
else if (action.equals(EXECUTE))
{
// execute a serialized command
return executeCommand(getParameter(KEY_COMAND), getParameter(KEY_STANDBY));
}
else if (action.equals(SHOW_PAGE))
{
// display an Intro Page.
return showPage(getParameter(KEY_ID), getParameter(KEY_STANDBY));
}
else if (action.equals(SHOW_MESSAGE))
{
return showMessage(getParameter(KEY_MESSAGE));
}
else if (action.equals(NAVIGATE))
{
return navigate(getParameter(KEY_DIRECTION));
}
else if (action.equals(SWITCH_TO_LAUNCH_BAR))
{
return switchToLaunchBar();
}
else
{
return handleCustomAction();
}
}
/**
* Run an action
*/
private boolean runAction(String pluginId, String className, Properties parameters, String standbyState)
{
Object actionObject = ModelLoaderUtil.createClassInstance(pluginId, className);
try
{
if (actionObject instanceof IIntroAction)
{
IIntroAction introAction = (IIntroAction) actionObject;
introAction.run(null, parameters);
}
else if (actionObject instanceof IAction)
{
IAction action = (IAction) actionObject;
action.run();
}
else if (actionObject instanceof IActionDelegate)
{
final IActionDelegate delegate = (IActionDelegate) actionObject;
if (delegate instanceof IWorkbenchWindowActionDelegate)
{
((IWorkbenchWindowActionDelegate) delegate).init(PlatformUI.getWorkbench()
.getActiveWorkbenchWindow());
}
Action proxy = new Action(this.action)
{
public void run()
{
delegate.run(this);
}
};
proxy.run();
}
else
{
// we could not create the class.
return false;
}
}
catch (Exception e)
{
return false;
}
return true;
}
/**
* Executes a serialized <code>ParameterizedCommand</code>. Uses {@link ICommandService#deserialize(String)} to
* convert the <code>command</code> argument into the parameterized command.
*/
private boolean executeCommand(String command, String standbyState)
{
ICommandService commandService = getCommandService();
if (commandService == null)
{
return false;
}
try
{
ParameterizedCommand pCommand = commandService.deserialize(command);
pCommand.executeWithChecks(null, null);
}
catch (CommandException ex)
{
return false;
}
return true;
}
private ICommandService getCommandService()
{
IWorkbench wb = PlatformUI.getWorkbench();
if (wb != null)
{
Object serviceObject = wb.getAdapter(ICommandService.class);
if (serviceObject != null)
{
ICommandService service = (ICommandService) serviceObject;
return service;
}
}
return null;
}
/**
* Open a help topic. If embed="true", open the help href as an intro page. If false, open the href in the Help
* system window. If embedTarget is set, then the Help System topic is embedded instead of the content of the
* specified div.<br>
* In the case of SWT presentation, embedd flag is ignored and the topic is opened in the Help system window.
*/
private boolean showHelpTopic(String href, String embed, String embedTarget)
{
if (href == null)
{
return false;
}
boolean isEmbedded = (embed != null && embed.equals(VALUE_TRUE)) ? true : false;
if (isEmbedded == false)
{
// still false, check the embedTarget. If embedTarget is set, then
// we
// have embedded by default.
isEmbedded = (embedTarget != null) ? true : false;
}
if (isEmbedded)
{
href = PlatformUI.getWorkbench().getHelpSystem().resolve(href, true).toExternalForm();
if (embedTarget == null && browser != null)
{
return browser.setUrl(href);
}
// embedded in Div case.
IntroModelRoot model = IntroPlugin.getDefault().getIntroModelRoot();
return handleEmbedURLInDiv(href, embedTarget, model.getCurrentPage());
}
// show href in Help window. SWT presentation is handled here.
// WorkbenchHelp takes care of error handling.
PlatformUI.getWorkbench().getHelpSystem().displayHelpResource(href);
return true;
}
private boolean handleEmbedURLInDiv(String href, String embedTarget, AbstractIntroPage currentPage)
{
// re-use a runtime generated page, if found. Create the mangled id for
// the page and check if page exists first. If not, create one.
IntroModelRoot model = (IntroModelRoot) currentPage.getParentPage().getParent();
String currentPageId = null;
if (currentPage.isIFramePage())
{
currentPageId = currentPage.getUnmangledId();
}
else
{
currentPageId = currentPage.getId();
}
String mangledPageId = currentPageId + "_" + "WITH_IFRAME"; //$NON-NLS-1$ //$NON-NLS-2$
// get current standby state.
boolean standby = IntroPlugin.isIntroStandby();
String standbyAsString = standby ? CoreURL.VALUE_TRUE : "false"; //$NON-NLS-1$
AbstractIntroPage pageWithIFrame = (AbstractIntroPage) model.findChild(mangledPageId,
AbstractIntroElement.ABSTRACT_PAGE);
if (pageWithIFrame != null)
{
pageWithIFrame.setIFrameURL(href);
return this.showPage(mangledPageId, standbyAsString);
}
// Page never generated, clone and create.
AbstractIntroPage clonedPage = null;
try
{
clonedPage = (AbstractIntroPage) currentPage.clone();
}
catch (CloneNotSupportedException ex)
{
// should never be here.
return false;
}
// embed url as IFrame in target div. We need to find target div in
// cloned page not in the original page.
boolean canInjectFrame = clonedPage.injectIFrame(href, embedTarget);
if (!canInjectFrame)
{
// Called method handles error.
return false;
}
clonedPage.setId(mangledPageId);
model.addChild(clonedPage);
return this.showPage(clonedPage.getId(), standbyAsString);
}
/**
* Open the help system.
*/
private boolean showHelp()
{
PlatformUI.getWorkbench().getHelpSystem().displayHelp();
return true;
}
/**
* Launch external browser.
*/
private boolean openBrowser(String url, String pluginId)
{
// no need to decode url because we will create another url from this
// url anyway. Resolve the url just in case we are trying to load a
// plugin relative file.
url = ModelUtil.resolveURL(url, pluginId);
return Util.openBrowser(url);
}
/**
* Show a URL in an intro page. This is the embedded version of the intro action openBrowser(). It is useful when
* trying to show an html file relative to another plugin. When the presentation is UI forms presentation, this call
* behaves exactly as the openBrowser intro action.
*/
private boolean openURL(String url, String pluginId)
{
IntroModelRoot model = IntroPlugin.getDefault().getIntroModelRoot();
String presentationStyle = model.getPresentation().getImplementationKind();
if (presentationStyle.equals(IntroPartPresentation.BROWSER_IMPL_KIND))
{
// HTML presentation
url = ModelUtil.resolveURL(url, pluginId);
BrowserIntroPartImplementation impl = (BrowserIntroPartImplementation) IntroPlugin.getDefault()
.getIntroModelRoot().getPresentation().getIntroPartImplementation();
Browser browser = impl.getBrowser();
return browser.setUrl(url);
}
{
// SWT presentation.
return openBrowser(url, pluginId);
}
}
private boolean showMessage(String message)
{
if (message == null)
{
return false;
}
DialogUtil.displayInfoMessage(null, message);
return true;
}
/**
* Display an Intro Page.
* <p>
* INTRO: revisit picking first page.
*/
boolean showPage(String pageId, String standbyState)
{
// set the current page id in the model. This will triger appropriate
// listener event to the UI. If setting the page in the model fails (ie:
// the page was not found in the current model, look for it in loaded
// models. return false if failed.
// avoid flicker.
CustomizableIntroPart currentIntroPart = (CustomizableIntroPart) IntroPlugin.getIntro();
currentIntroPart.getControl().setRedraw(false);
IntroModelRoot modelRoot = IntroPlugin.getDefault().getIntroModelRoot();
boolean success = modelRoot.setCurrentPageId(pageId);
if (!success)
{
success = includePageToShow(modelRoot, pageId);
}
// we turned drawing off. Turn it on again.
currentIntroPart.getControl().setRedraw(true);
if (success)
{
// found page. Set the history
modelRoot.getPresentation().updateHistory(modelRoot.getCurrentPage());
}
// could not find referenced page.
return false;
}
/**
* Finds the target page and includes it in passed model.
*
* @param pageId
* @return - boolean
*/
private boolean includePageToShow(IntroModelRoot model, String pageId)
{
AbstractIntroPage page = findPageToShow(pageId);
if (page == null)
{
return false;
}
// now clone the target page because original model should be kept
// intact. Resolve target page first to resolve its includes
// properly. Insert presentation shared style at the top of the shared
// styles list because once reparented, the shared style is lost.
// Finally, add clone page to current model.
page.getChildren();
// current kind.
String currentPresentationKind = model.getPresentation().getImplementationKind();
// load shared style corresponding to same presentation kind from target
// model.
IntroPartPresentation targetPresentation = ((IntroModelRoot) page.getParent()).getPresentation();
String targetSharedStyle = targetPresentation.getSharedStyle(currentPresentationKind);
// clone.
AbstractIntroPage clonedPage = null;
try
{
clonedPage = (AbstractIntroPage) page.clone();
}
catch (CloneNotSupportedException ex)
{
// should never be here.
return false;
}
// reparent cloned target to current model.
clonedPage.setParent(model);
// REVISIT: SWT presentation does not support multiple shared
// styles.
if (targetSharedStyle != null)
{
// add target model shared style.
clonedPage.insertStyle(targetSharedStyle, 0);
}
model.addChild(clonedPage);
return model.setCurrentPageId(clonedPage.getId());
}
/**
* Searches all loaded models for the first page with the given id.
*
* @param pageId
* @return
*/
private AbstractIntroPage findPageToShow(String pageId)
{
// get all cached models.
Hashtable models = ExtensionPointManager.getInst().getIntroModels();
Enumeration values = models.elements();
while (values.hasMoreElements())
{
IntroModelRoot model = (IntroModelRoot) values.nextElement();
AbstractIntroPage page = (AbstractIntroPage) model.findChild(pageId, AbstractIntroElement.ABSTRACT_PAGE);
if (page != null)
{
return page;
}
}
// could not find page in any model.
return null;
}
/**
* Navigate foward in the presentation, whichever one it is.
*
* @return
*/
private boolean navigate(String direction)
{
// set intro to standby mode. we know we have a customizable part.
CustomizableIntroPart introPart = (CustomizableIntroPart) IntroPlugin.getIntro();
if (introPart == null)
{
// intro is closed. Do nothing.
return false;
}
IntroPartPresentation presentation = (IntroPartPresentation) introPart.getAdapter(IntroPartPresentation.class);
if (direction.equalsIgnoreCase(VALUE_BACKWARD))
{
return presentation.navigateBackward();
}
else if (direction.equalsIgnoreCase(VALUE_FORWARD))
{
return presentation.navigateForward();
}
else if (direction.equalsIgnoreCase(VALUE_HOME))
{
return presentation.navigateHome();
}
return false;
}
/**
* @return Returns the action imbedded in this URL.
*/
public String getAction()
{
return action;
}
/**
* Return a parameter defined in the Intro URL. Returns null if the parameter is not defined. If this intro url has
* a decode=true parameter, then all parameters are returned decoded using UTF-8.
*
* @param parameterId
* @return
*/
public String getParameter(String parameterId)
{
// make sure to decode only on return, since we may need to recreate the
// url when handling custom urls.
String value = parameters.getProperty(parameterId);
String decode = parameters.getProperty(KEY_DECODE);
if (value != null)
{
try
{
if (decode != null && decode.equalsIgnoreCase(VALUE_TRUE))
{
// we are told to decode the parameters of the url through
// the decode parameter. Assume that parameters are
// UTF-8 encoded.
return StringUtil.decode(value, "UTF-8"); //$NON-NLS-1$
}
return value;
}
catch (Exception e)
{
// should never be here.
}
}
return value;
}
private boolean handleCustomAction()
{
String replaces = null;
if (action.equals("installPlugin"))
{
replaces = "runAction?pluginId=com.aptana.ide.extras&class=com.aptana.ide.extras.plugins.InstallPlugin";
}
else if (action.equals("disablePlugin"))
{
replaces = "runAction?pluginId=com.aptana.ide.extras&class=com.aptana.ide.extras.plugins.DisablePlugin";
}
if (replaces != null)
{
// custom command. execute it.
StringBuffer url = new StringBuffer();
url.append("http://org.eclipse.ui.intro/"); //$NON-NLS-1$
url.append(replaces);
// command already has parameters.
url.append("&"); //$NON-NLS-1$
url.append(retrieveInitialQuery());
URLParser parser = new URLParser(url.toString());
parser.getIntroURL().doExecute();
}
return false;
}
/**
* Recreate the initial query passed to this URL.
*
* @return
*/
private String retrieveInitialQuery()
{
StringBuffer query = new StringBuffer();
Enumeration keys = parameters.keys();
while (keys.hasMoreElements())
{
String key = (String) keys.nextElement();
query.append(key);
query.append("="); //$NON-NLS-1$
query.append(parameters.get(key));
if (keys.hasMoreElements())
{
query.append("&"); //$NON-NLS-1$
}
}
return query.toString();
}
private boolean switchToLaunchBar()
{
IIntroPart intro = PlatformUI.getWorkbench().getIntroManager().getIntro();
if (intro == null)
{
return false;
}
CustomizableIntroPart cpart = (CustomizableIntroPart) intro;
IntroModelRoot modelRoot = IntroPlugin.getDefault().getIntroModelRoot();
String pageId = modelRoot.getCurrentPageId();
IntroTheme theme = modelRoot.getTheme();
Rectangle bounds = cpart.getControl().getBounds();
Rectangle startBounds = Geometry.toDisplay(cpart.getControl().getParent(), bounds);
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
IntroLaunchBarElement launchBarElement = modelRoot.getPresentation().getLaunchBarElement();
if (launchBarElement == null)
{
return true;
}
IntroLaunchBar launchBar = new IntroLaunchBar(launchBarElement.getOrientation(), pageId, launchBarElement,
theme);
launchBar.createInActiveWindow();
Rectangle endBounds = Geometry
.toDisplay(launchBar.getControl().getParent(), launchBar.getControl().getBounds());
RectangleAnimation animation = new RectangleAnimation(window.getShell(), startBounds, endBounds);
animation.schedule();
return true;
}
}