/*******************************************************************************
* Copyright (c) 2008
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the individual
* copyright holders listed below, as Initial Contributors under such license.
* The text of such license is available at
* http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Henrik Lindberg
*******************************************************************************/
package org.eclipse.equinox.p2.authoring.forms;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.forms.HyperlinkSettings;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.IMessage;
import org.eclipse.ui.forms.editor.FormEditor;
import org.eclipse.ui.forms.editor.FormPage;
import org.eclipse.ui.forms.events.ExpansionAdapter;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.events.IExpansionListener;
import org.eclipse.ui.forms.widgets.Form;
import org.eclipse.ui.forms.widgets.FormText;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.forms.widgets.TableWrapLayout;
import org.eclipse.ui.forms.AbstractFormPart;
/**
* A base class for Eclipse form pages that adds support for showing messages and sets wanted defaults.
*
* @author Henrik Lindberg
*
*/
public abstract class RichFormPage extends FormPage implements IGlobalActionPerformer
{
private IExpansionListener m_reflowOnExpansion;
/** The number of columns in the outermost TableWrapLayout - default is 2 */
protected int m_numColumns = 2;
/** The header string - default is "Undefined" */
protected String m_header = "Undefined";
protected boolean m_makeColumnsEqualWidth = false;
protected Image m_headerImage = null;
public RichFormPage(FormEditor editor, String id, String tabLabel)
{
super(editor, id, tabLabel);
}
/**
* Creates the form content:
* <ul>
* <li>The outermost ScrolledForm gets a TableWrapLayout</li>
* <li>The outermost table gets a default of 2 columns - change by setting {@link #m_numColumns }</li>
* <li>Left and right margins are set to 10 so the form contents does not kiss the edges</li>
* <li>The Eclipse 3.3. default header decoration is turned on</li>
* <li>Hyperlinks are set to underline on hover</li>
* <li>The header is set to the value of the protected {@link #m_header}</li>
* <li>A section expansion listener that reflows the layout is created, and can be used when setting up the contents
* of the form. See {@link #getReflowListener()}.</li>
* <li>A hyperlink listener is set up to display a list of formatted messages from the MessageManager.</li>
* </ul>
*
*/
@Override
protected final void createFormContent(IManagedForm managedForm)
{
final ScrolledForm scrolledForm = managedForm.getForm();
final FormToolkit toolkit = managedForm.getToolkit();
// Set header.
scrolledForm.setText(m_header);
if(m_headerImage != null)
scrolledForm.setImage(m_headerImage);
// Use default 3.3. form page header (old style required an image)
toolkit.decorateFormHeading(scrolledForm.getForm());
// underline hyperlinks on hover
toolkit.getHyperlinkGroup().setHyperlinkUnderlineMode(HyperlinkSettings.UNDERLINE_HOVER);
TableWrapLayout layout = new TableWrapLayout();
layout.leftMargin = 5;
layout.rightMargin = 5;
layout.numColumns = m_numColumns;
layout.makeColumnsEqualWidth = m_makeColumnsEqualWidth;
scrolledForm.getBody().setLayout(layout);
// Create the reflowing expansion listener
//
m_reflowOnExpansion = new ExpansionAdapter()
{
@Override
public void expansionStateChanged(ExpansionEvent e)
{
scrolledForm.reflow(true);
}
};
// Add a popup with formatted message to a hyperlink
// TODO: This sample code probably pops up an empty window for non message links
// Needs to be fixed if other hyperlinks are added to the form...
scrolledForm.getForm().addMessageHyperlinkListener(new HyperlinkAdapter()
{
@Override
public void linkActivated(HyperlinkEvent e)
{
String title = e.getLabel();
// String details = title;
Object href = e.getHref();
if(href instanceof IMessage[])
{
// details =
// managedForm.getMessageManager().createSummary((IMessage[])href);
}
// int type = form.getForm().getMessageType();
/*
* switch (type) { case IMessageProvider.NONE: case IMessageProvider.INFORMATION:
* MessageDialog.openInformation(form.getShell(), title, details); break; case IMessageProvider.WARNING:
* MessageDialog.openWarning(form.getShell(), title, details); break; case IMessageProvider.ERROR:
* MessageDialog.openError(form.getShell(), title, details); break; }
*/
Point hl = ((Control)e.widget).toDisplay(0, 0);
hl.x += 10;
hl.y += 10;
// Note, must be application modal, or it is possible to navigate the form and make changes that affects the
// message state. (And perhaps even worse, to open the popup again).
final Shell shell = new Shell(scrolledForm.getShell(), SWT.ON_TOP | SWT.TOOL | SWT.APPLICATION_MODAL);
shell.setImage(getImage(scrolledForm.getMessageType()));
shell.setText(title);
shell.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
FillLayout fillLayout = new FillLayout();
fillLayout.marginHeight = 5;
fillLayout.marginWidth = 5;
shell.setLayout(fillLayout);
// ScrolledFormText stext = new ScrolledFormText(shell, false);
// stext.setBackground(toolkit.getColors().getBackground());
FormText text = toolkit.createFormText(shell, false);
configureFormText(scrolledForm.getForm(), text);
// stext.setFormText(text);
if(href instanceof IMessage[])
text.setText(createFormTextContent((IMessage[])href), true, false);
shell.setLocation(hl);
// Point size = shell.computeSize(400, SWT.DEFAULT);
/*
* richToolTipMessageManager.setActive(false); shell.addDisposeListener(new DisposeListener() { public
* void widgetDisposed(DisposeEvent e) { richToolTipMessageManager.setActive(true); } });
*/
// must have a key listener to make it possible to close the popup shell on ESC key
shell.addKeyListener(new KeyAdapter(){
@Override
public void keyPressed(KeyEvent event)
{
// close shell on escape key
if(event.keyCode == SWT.ESC)
shell.dispose();
}
});
shell.pack();
shell.open();
shell.forceFocus();
}
});
scrolledForm.addTraverseListener(new TraverseListener(){
public void keyTraversed(TraverseEvent e)
{
System.err.print("Traverse Event Occured in RichFormPage");
}
});
addFormContent(managedForm);
}
/**
* Replacement for {@link #createFormContent(IManagedForm)} which has been made final. This method is called as the
* last step, and should complete the creation of the form content, but should not set up everything supported by
* the default implementation. See {@link #createFormContent(IManagedForm)} for all the things that are set up by
* default.
*
* @param managedForm
*/
abstract protected void addFormContent(IManagedForm managedForm);
/**
* Returns an IExpansionListener that reflows the form when expansion changes. Attach this listener to expandable
* sections in the form.
*
* @return null if createFormContent has not been called.
*/
protected IExpansionListener getReflowListener()
{
return m_reflowOnExpansion;
}
private void configureFormText(final Form form, FormText text)
{
text.addHyperlinkListener(new HyperlinkAdapter()
{
@Override
public void linkActivated(HyperlinkEvent e)
{
String is = (String)e.getHref();
try
{
int index = Integer.parseInt(is);
IMessage[] messages = form.getChildrenMessages();
IMessage message = messages[index];
Control c = message.getControl();
((FormText)e.widget).getShell().dispose();
if(c != null)
c.setFocus();
}
catch(ArrayIndexOutOfBoundsException ex)
{
// This can happens if message array changes while menu is up
((FormText)e.widget).getShell().dispose();
}
catch(NumberFormatException ex)
{
}
}
});
text.setImage("error", getImage(IMessageProvider.ERROR)); //$NON-NLS-1$
text.setImage("warning", getImage(IMessageProvider.WARNING)); //$NON-NLS-1$
text.setImage("info", getImage(IMessageProvider.INFORMATION)); //$NON-NLS-1$
}
/**
* Formats messages into a form string
*
* @param messages
* - messages to format
* @return a form string
*/
private String createFormTextContent(IMessage[] messages)
{
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.println("<form>"); //$NON-NLS-1$
for(int i = 0; i < messages.length; i++)
{
IMessage message = messages[i];
pw.print("<li vspace=\"false\" style=\"image\" indent=\"16\" value=\""); //$NON-NLS-1$
switch(message.getMessageType())
{
case IMessageProvider.ERROR:
pw.print("error"); //$NON-NLS-1$
break;
case IMessageProvider.WARNING:
pw.print("warning"); //$NON-NLS-1$
break;
case IMessageProvider.INFORMATION:
pw.print("info"); //$NON-NLS-1$
break;
}
pw.print("\"> <a href=\""); //$NON-NLS-1$
pw.print(i + ""); //$NON-NLS-1$
pw.print("\">");//$NON-NLS-1$
if(message.getPrefix() != null)
pw.print(message.getPrefix());
pw.print(message.getMessage());
pw.println("</a></li>"); //$NON-NLS-1$
}
pw.println("</form>"); //$NON-NLS-1$
pw.flush();
return sw.toString();
}
/**
* Convenience method to pick up platform images for error and warning.
*
* @param type
* @return
*/
private Image getImage(int type)
{
switch(type)
{
case IMessageProvider.ERROR:
return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_ERROR_TSK);
case IMessageProvider.WARNING:
return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_WARN_TSK);
case IMessageProvider.INFORMATION:
return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_INFO_TSK);
}
return null;
}
protected Control getFocusControl() {
IManagedForm form = getManagedForm();
if (form == null)
return null;
Control control = form.getForm();
if (control == null || control.isDisposed())
return null;
Display display = control.getDisplay();
Control focusControl = display.getFocusControl();
if (focusControl == null || focusControl.isDisposed())
return null;
return focusControl;
}
public boolean doGlobalAction(String actionId) {
Control focusControl = getFocusControl();
if (focusControl == null)
return false;
if (canPerformDirectly(actionId, focusControl))
return true;
AbstractFormPart focusPart = getFocusSection();
if (focusPart != null) {
if (focusPart instanceof IGlobalActionPerformer)
return ((IGlobalActionPerformer) focusPart).doGlobalAction(actionId);
}
return false;
}
public boolean canPaste(Clipboard clipboard) {
AbstractFormPart focusPart = getFocusSection();
if (focusPart != null) {
if (focusPart instanceof IGlobalActionPerformer) {
return ((IGlobalActionPerformer) focusPart).canPaste(clipboard);
}
}
// Otherwise, check if focus holder can handle action
Control focusControl = getFocusControl();
if (focusControl != null && focusControl instanceof Text && ((Text)focusControl).getEditable())
return true;
return false;
}
/**
* @param selection
* @return
*/
public boolean canCopy(ISelection selection) {
// If there is a focused part that wants to handle this, it should do so
AbstractFormPart focusPart = getFocusSection();
if (focusPart != null) {
if (focusPart instanceof IGlobalActionPerformer) {
return ((IGlobalActionPerformer) focusPart).canCopy(selection);
}
}
// Otherwise, check if focus holder can handle action
Control focusControl = getFocusControl();
if (focusControl != null && focusControl instanceof Text)
return true;
return false;
}
/**
* @param selection
* @return
*/
public boolean canCut(ISelection selection) {
AbstractFormPart focusPart = getFocusSection();
if (focusPart != null) {
if (focusPart instanceof IGlobalActionPerformer) {
return ((IGlobalActionPerformer) focusPart).canCut(selection);
}
}
// Otherwise, check if focus holder can handle action
Control focusControl = getFocusControl();
if (focusControl != null && focusControl instanceof Text && ((Text)focusControl).getEditable())
return true;
return false;
}
private AbstractFormPart getFocusSection() {
Control focusControl = getFocusControl();
if (focusControl == null)
return null;
Composite parent = focusControl.getParent();
AbstractFormPart targetPart = null;
while (parent != null) {
Object data = parent.getData("part"); //$NON-NLS-1$
if (data != null && data instanceof AbstractFormPart) {
targetPart = (AbstractFormPart) data;
break;
}
parent = parent.getParent();
}
return targetPart;
}
protected boolean canPerformDirectly(String id, Control control) {
if (control instanceof Text) {
Text text = (Text) control;
if (id.equals(ActionFactory.CUT.getId())) {
text.cut();
return true;
}
if (id.equals(ActionFactory.COPY.getId())) {
text.copy();
return true;
}
if (id.equals(ActionFactory.PASTE.getId())) {
text.paste();
return true;
}
if (id.equals(ActionFactory.SELECT_ALL.getId())) {
text.selectAll();
return true;
}
if (id.equals(ActionFactory.DELETE.getId())) {
int count = text.getSelectionCount();
if (count == 0) {
int caretPos = text.getCaretPosition();
text.setSelection(caretPos, caretPos + 1);
}
text.insert(""); //$NON-NLS-1$
return true;
}
}
return false;
}
}