blob: 53c8c7f2c607cc0321efd6252d720a6a0bb821d0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.update.ui.forms.internal.engine;
import java.io.InputStream;
import java.util.Hashtable;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.action.*;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.*;
import org.eclipse.swt.accessibility.Accessible;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.dnd.*;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.update.ui.forms.internal.*;
public class FormEngine extends Canvas {
public static final String URL_HANDLER_ID = "urlHandler";
boolean hasFocus;
boolean paragraphsSeparated = true;
TextModel model;
Hashtable objectTable = new Hashtable();
public int marginWidth = 0;
public int marginHeight = 1;
IHyperlinkSegment entered;
boolean mouseDown = false;
Point dragOrigin;
private Action openAction;
private Action copyShortcutAction;
private boolean loading=true;
private String loadingText="Loading...";
public boolean getFocus() {
return hasFocus;
}
public boolean isLoading() {
return loading;
}
public String getLoadingText() {
return loadingText;
}
public void setLoadingText(String loadingText) {
this.loadingText = loadingText;
}
public int getParagraphSpacing(int lineHeight) {
return lineHeight / 2;
}
public void setParagraphsSeparated(boolean value) {
paragraphsSeparated = value;
}
/**
* Constructor for SelectableFormLabel
*/
public FormEngine(Composite parent, int style) {
super(parent, style);
setLayout(new FormEngineLayout());
model = new TextModel();
addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
model.dispose();
}
});
addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
paint(e);
}
});
addListener(SWT.KeyDown, new Listener() {
public void handleEvent(Event e) {
if (e.character == '\r') {
activateSelectedLink();
return;
}
}
});
addListener(SWT.Traverse, new Listener() {
public void handleEvent(Event e) {
switch (e.detail) {
case SWT.TRAVERSE_PAGE_NEXT :
case SWT.TRAVERSE_PAGE_PREVIOUS :
case SWT.TRAVERSE_ARROW_NEXT :
case SWT.TRAVERSE_ARROW_PREVIOUS :
e.doit = false;
return;
}
if (!model.hasFocusSegments()) {
e.doit = true;
return;
}
if (e.detail == SWT.TRAVERSE_TAB_NEXT)
e.doit = advance(true);
else if (e.detail == SWT.TRAVERSE_TAB_PREVIOUS)
e.doit = advance(false);
else if (e.detail != SWT.TRAVERSE_RETURN)
e.doit = true;
}
});
addFocusListener(new FocusListener() {
public void focusGained(FocusEvent e) {
if (!hasFocus) {
hasFocus = true;
handleFocusChange();
}
}
public void focusLost(FocusEvent e) {
if (hasFocus) {
hasFocus = false;
handleFocusChange();
}
}
});
addMouseListener(new MouseListener() {
public void mouseDoubleClick(MouseEvent e) {
}
public void mouseDown(MouseEvent e) {
// select a link
handleMouseClick(e, true);
}
public void mouseUp(MouseEvent e) {
// activate a link
handleMouseClick(e, false);
}
});
addMouseTrackListener(new MouseTrackListener() {
public void mouseEnter(MouseEvent e) {
handleMouseMove(e);
}
public void mouseExit(MouseEvent e) {
if (entered != null) {
exitLink(entered);
paintLinkHover(entered, false);
entered = null;
setCursor(null);
}
}
public void mouseHover(MouseEvent e) {
handleMouseHover(e);
}
});
addMouseMoveListener(new MouseMoveListener() {
public void mouseMove(MouseEvent e) {
handleMouseMove(e);
}
});
initAccessible();
makeActions();
}
private void makeActions() {
openAction = new Action() {
public void run() {
activateSelectedLink();
}
};
openAction.setText(
FormsPlugin.getResourceString("FormEgine.linkPopup.open"));
copyShortcutAction = new Action() {
public void run() {
copyShortcut(getSelectedLink());
}
};
copyShortcutAction.setText(
FormsPlugin.getResourceString("FormEgine.linkPopup.copyShortcut"));
}
private String getAcessibleText() {
return model.getAccessibleText();
}
private void initAccessible() {
Accessible accessible = getAccessible();
accessible.addAccessibleListener(new AccessibleAdapter() {
public void getName(AccessibleEvent e) {
e.result = getAcessibleText();
}
public void getHelp(AccessibleEvent e) {
e.result = getToolTipText();
}
});
accessible
.addAccessibleControlListener(new AccessibleControlAdapter() {
public void getChildAtPoint(AccessibleControlEvent e) {
Point pt = toControl(new Point(e.x, e.y));
e.childID =
(getBounds().contains(pt))
? ACC.CHILDID_SELF
: ACC.CHILDID_NONE;
}
public void getLocation(AccessibleControlEvent e) {
Rectangle location = getBounds();
Point pt = toDisplay(new Point(location.x, location.y));
e.x = pt.x;
e.y = pt.y;
e.width = location.width;
e.height = location.height;
}
public void getChildCount(AccessibleControlEvent e) {
e.detail = 0;
}
public void getRole(AccessibleControlEvent e) {
e.detail = ACC.ROLE_TEXT;
}
public void getState(AccessibleControlEvent e) {
e.detail = ACC.STATE_READONLY;
}
});
}
private void handleMouseClick(MouseEvent e, boolean down) {
if (down) {
// select a hyperlink
IHyperlinkSegment segmentUnder = model.findHyperlinkAt(e.x, e.y);
if (segmentUnder != null) {
IHyperlinkSegment oldLink = model.getSelectedLink();
model.selectLink(segmentUnder);
enterLink(segmentUnder);
paintFocusTransfer(oldLink, segmentUnder);
}
mouseDown = true;
dragOrigin = new Point(e.x, e.y);
} else {
if (e.button == 1) {
IHyperlinkSegment segmentUnder =
model.findHyperlinkAt(e.x, e.y);
if (segmentUnder != null) {
activateLink(segmentUnder);
}
}
mouseDown = false;
}
}
private void handleMouseHover(MouseEvent e) {
}
private void handleMouseMove(MouseEvent e) {
if (mouseDown) {
handleDrag(e);
return;
}
ITextSegment segmentUnder = model.findSegmentAt(e.x, e.y);
if (segmentUnder == null) {
if (entered != null) {
exitLink(entered);
paintLinkHover(entered, false);
entered = null;
}
setCursor(null);
} else {
if (segmentUnder instanceof IHyperlinkSegment) {
IHyperlinkSegment linkUnder = (IHyperlinkSegment) segmentUnder;
if (entered == null) {
entered = linkUnder;
enterLink(linkUnder);
paintLinkHover(entered, true);
setCursor(
model.getHyperlinkSettings().getHyperlinkCursor());
}
} else {
if (entered != null) {
exitLink(entered);
paintLinkHover(entered, false);
entered = null;
}
setCursor(model.getHyperlinkSettings().getTextCursor());
}
}
}
private void handleDrag(MouseEvent e) {
}
public HyperlinkSettings getHyperlinkSettings() {
return model.getHyperlinkSettings();
}
public void setHyperlinkSettings(HyperlinkSettings settings) {
model.setHyperlinkSettings(settings);
}
private boolean advance(boolean next) {
IHyperlinkSegment current = model.getSelectedLink();
if (current != null)
exitLink(current);
boolean valid = model.traverseLinks(next);
IHyperlinkSegment newLink = model.getSelectedLink();
if (valid)
enterLink(newLink);
paintFocusTransfer(current, newLink);
if (newLink!=null) ensureVisible(newLink);
return !valid;
}
public IHyperlinkSegment getSelectedLink() {
return model.getSelectedLink();
}
private void handleFocusChange() {
if (hasFocus) {
model.traverseLinks(true);
enterLink(model.getSelectedLink());
paintFocusTransfer(null, model.getSelectedLink());
} else {
paintFocusTransfer(model.getSelectedLink(), null);
model.selectLink(null);
}
}
private void enterLink(IHyperlinkSegment link) {
if (link == null)
return;
HyperlinkAction action = link.getAction(objectTable);
if (action != null)
action.linkEntered(link);
}
private void exitLink(IHyperlinkSegment link) {
if (link == null)
return;
HyperlinkAction action = link.getAction(objectTable);
if (action != null)
action.linkExited(link);
}
private void paintLinkHover(IHyperlinkSegment link, boolean hover) {
GC gc = new GC(this);
HyperlinkSettings settings = getHyperlinkSettings();
gc.setForeground(
hover ? settings.getActiveForeground() : settings.getForeground());
gc.setBackground(getBackground());
gc.setFont(getFont());
boolean selected = (link == getSelectedLink());
link.repaint(gc, hover);
if (selected) {
link.paintFocus(gc, getBackground(), getForeground(), false);
link.paintFocus(gc, getBackground(), getForeground(), true);
}
gc.dispose();
}
private void activateSelectedLink() {
IHyperlinkSegment link = model.getSelectedLink();
if (link != null)
activateLink(link);
}
private void activateLink(IHyperlinkSegment link) {
setCursor(model.getHyperlinkSettings().getBusyCursor());
HyperlinkAction action = link.getAction(objectTable);
if (action != null)
action.linkActivated(link);
if (!isDisposed())
setCursor(model.getHyperlinkSettings().getHyperlinkCursor());
}
protected void paint(PaintEvent e) {
int width = getClientArea().width;
GC gc = e.gc;
gc.setFont(getFont());
gc.setForeground(getForeground());
gc.setBackground(getBackground());
Locator loc = new Locator();
loc.marginWidth = marginWidth;
loc.marginHeight = marginHeight;
loc.x = marginWidth;
loc.y = marginHeight;
FontMetrics fm = gc.getFontMetrics();
int lineHeight = fm.getHeight();
if (loading) {
int textWidth = gc.textExtent(loadingText).x;
gc.drawText(loadingText, width/2-textWidth/2, getClientArea().height/2-lineHeight/2);
return;
}
IParagraph[] paragraphs = model.getParagraphs();
IHyperlinkSegment selectedLink = model.getSelectedLink();
for (int i = 0; i < paragraphs.length; i++) {
IParagraph p = paragraphs[i];
if (i > 0 && paragraphsSeparated && p.getAddVerticalSpace())
loc.y += getParagraphSpacing(lineHeight);
loc.indent = p.getIndent();
loc.resetCaret();
loc.rowHeight = 0;
p.paint(gc, width, loc, lineHeight, objectTable, selectedLink);
}
}
public void registerTextObject(String key, Object value) {
objectTable.put(key, value);
}
public void load(String text, boolean parseTags, boolean expandURLs) {
try {
if (parseTags)
model.parseTaggedText(text, expandURLs);
else
model.parseRegularText(text, expandURLs);
} catch (CoreException e) {
FormsPlugin.logException(e);
}
finally {
loading = false;
}
}
public void load(InputStream is, boolean expandURLs) {
try {
model.parseInputStream(is, expandURLs);
} catch (CoreException e) {
FormsPlugin.logException(e);
}
finally {
loading = false;
}
}
public boolean setFocus() {
/*
if (!model.hasFocusSegments())
return false;
*/
return super.setFocus();
}
private void paintFocusTransfer(
IHyperlinkSegment oldLink,
IHyperlinkSegment newLink) {
GC gc = new GC(this);
Color bg = getBackground();
Color fg = getForeground();
gc.setFont(getFont());
if (oldLink != null) {
gc.setBackground(bg);
gc.setForeground(fg);
oldLink.paintFocus(gc, bg, fg, false);
}
if (newLink != null) {
//ensureVisible(newLink);
gc.setBackground(bg);
gc.setForeground(fg);
newLink.paintFocus(gc, bg, fg, true);
}
gc.dispose();
}
/**
* Gets the marginWidth.
* @return Returns a int
*/
public int getMarginWidth() {
return marginWidth;
}
/**
* Sets the marginWidth.
* @param marginWidth The marginWidth to set
*/
public void setMarginWidth(int marginWidth) {
this.marginWidth = marginWidth;
}
/**
* Gets the marginHeight.
* @return Returns a int
*/
public int getMarginHeight() {
return marginHeight;
}
/**
* Sets the marginHeight.
* @param marginHeight The marginHeight to set
*/
public void setMarginHeight(int marginHeight) {
this.marginHeight = marginHeight;
}
public void contextMenuAboutToShow(IMenuManager manager) {
IHyperlinkSegment link = getSelectedLink();
if (link != null)
contributeLinkActions(manager, link);
}
private void contributeLinkActions(
IMenuManager manager,
IHyperlinkSegment link) {
manager.add(openAction);
manager.add(copyShortcutAction);
manager.add(new Separator());
}
private void copyShortcut(IHyperlinkSegment link) {
String text = link.getText();
Clipboard clipboard = new Clipboard(getDisplay());
clipboard.setContents(
new Object[] { text },
new Transfer[] { TextTransfer.getInstance()});
}
private void ensureVisible(IHyperlinkSegment segment) {
Rectangle bounds = segment.getBounds();
ScrolledComposite scomp = getScrolledComposite();
if (scomp == null)
return;
Point origin = AbstractSectionForm.getControlLocation(scomp, this);
origin.x += bounds.x;
origin.y += bounds.y;
AbstractSectionForm.ensureVisible(
scomp,
origin,
new Point(bounds.width, bounds.height));
}
ScrolledComposite getScrolledComposite() {
Composite parent = getParent();
while (parent != null) {
if (parent instanceof ScrolledComposite)
return (ScrolledComposite) parent;
parent = parent.getParent();
}
return null;
}
}