Layout problems in FieldEditorPreferencePage - eclipse

I have following problems with layout settings in the FieldEditorPreferencePage.
My code is something like this:
public void createFieldEditors () {
Group pv = new group(getfieldEditorParent(), SWT.SHADOW_OUT);
Group of = new group(getfieldEditorParent(), SWT.SHADOW_OUT);
pv.setText(“pv”);
of.setText(“of”);
GridLayout layout = new GridLayout(2,false);
pv.setLayout(layout);
of.setLayout(layout);
addField(new StringFieldEditor(“PreferenceStore name”,“Text:”, pv);
addField(new StringFieldEditor(“PreferenceStore name”,“Text:”, pv);
addField(new StringFieldEditor(“PreferenceStore name”,“Text:”, of);
addField(new StringFieldEditor(“PreferenceStore name”,“Text:”, of);
and so on.
}
The problem is that it does not work with GridLayout.
The StringFieldEditors are not parallel. The number of columns is always 1. Also when I try to change the size of StringFieldEditors in the groups, it doesn’t work too.
Anybody have any ideas?
Thanks.

The problem is that when you are using FieldEditorPreferencePage, you can use only FieldEditor subclasses as components. Here's a snippet from a documentation:
FieldEditorPreferencePage implements a
page that uses these field editors to
display and store the preference
values on the page. Instead of
creating SWT controls to fill its
contents, a FieldEditorPreferencePage
subclass creates field editors to
display the contents. All of the
fields on the page must be implemented
as field editors.
That means you have two options how to achieve what you want:
Implement your own subclass of FieldEditor, which would represent the Group widget.
Do not extend FieldEditorPreferencePage, but only a PreferencePage instead. Then you have to implement createContents method instead of createFieldEditors. You will also have to manage loading and saving of the properties.
I think the second way might be easier if you want to provide some complex layout. You may find some information more here

Another (easy) workaround:
You can also create new Composites to create more columns. The problem is that these FieldEditors communicate with their parent and mess up your layout. So, by creating an "empty" composite, they can communicate as much as they want :)
someGroup = new Group(..., SWT.NONE);
someGroup .setLayout(new GridLayout(16, false));
Composite myC1= new Composite(someGroup,SWT.NONE);
addField(new BooleanFieldEditor(...,C1);
Composite myC2= new Composite(someGroup,SWT.NONE);
addField(new BooleanFieldEditor(...,C2);

Two things to understand about FieldEditorPreferencePage (with GRID style):
The layout of field editor parents is always set to GridLayout even for "custom" components like Groups;
The number of columns in layout is adjusted according to the maximum number of components in any of the field editors (which is 2 in case of StringFieldEditor).
In the above example, the layout data of Groups should take this into account:
GridDataFactory.defaultsFor(pv).grab(true, false).span(2, 1).applyTo(pv);
GridDataFactory.defaultsFor(of).grab(true, false).span(2, 1).applyTo(of);

I implementer the Group-FieldEditor which can contain other FieldEditors and layout them as a Group.
import java.util.Collection;
import org.eclipse.jface.preference.FieldEditor;
import org.eclipse.jface.preference.FieldEditorPreferencePage;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
/**
* Class is intended to create a Group Widgets, inside of the {#link FieldEditorPreferencePage}
* objects.
* This class should be used as following:
*
* use the {#link #getFieldEditorParent()} to as a parent, while creating new Field Editors.
* use {#link #setFieldEditors(Collection)} to add the collection of FieldEditors to the
* {#link GroupFieldEditor}.
*
*
* #author alf
*
*/
public class GroupFieldEditor extends FieldEditor {
private String name;
private Collection members;
private int numcolumns;
private Group group;
private Composite parent;
/**
* The gap outside, between the group-frame and the widgets around the group
*/
private static final int GROUP_PADDING = 5; // px
/**
* The gap inside, between the group-frame and the content
*/
private static final int GROUP_VERTICAL_MARGIN = 5; // px
/**
* The inside-distance creates a new boolean field editor
*/
protected GroupFieldEditor() {
}
/**
* Creates a Group of {#link FieldEditor} objects
*
* #param name
* - name
* #param fieldEditorParent
* - parent
*/
public GroupFieldEditor(String name, Composite fieldEditorParent) {
this.name = name;
// the parent is a Composite, which is contained inside of the preference page. Initially it
// does not have any layout.
this.parent = fieldEditorParent;
FillLayout fillLayout = new FillLayout();
fillLayout.marginHeight = GROUP_VERTICAL_MARGIN;
this.parent.setLayout(fillLayout);
this.group = new Group(parent, SWT.DEFAULT);
this.group.setText(this.name);
}
/**
* The parent for all the FieldEditors inside of this Group.
*
* #return - the parent
*/
public Composite getFieldEditorParent() {
return group;
}
/**
* Sets the FieldeditorChildren for this {#link GroupFieldEditor}
*
* #param membersParam
*/
public void setFieldEditors(Collection membersParam) {
this.members = membersParam;
doFillIntoGrid(getFieldEditorParent(), numcolumns);
}
/*
* (non-Javadoc) Method declared on FieldEditor.
*/
#Override
protected void adjustForNumColumns(int numColumns) {
this.numcolumns = numColumns;
}
/*
* (non-Javadoc) Method declared on FieldEditor.
*/
#Override
protected void doFillIntoGrid(Composite parentParam, int numColumns) {
GridLayout gridLayout = new GridLayout();
gridLayout.marginLeft = GROUP_PADDING;
gridLayout.marginRight = GROUP_PADDING;
gridLayout.marginTop = GROUP_PADDING;
gridLayout.marginBottom = GROUP_PADDING;
this.group.setLayout(gridLayout);
this.parent.layout();
this.parent.redraw();
if (members != null) {
for (FieldEditor editor : members) {
editor.fillIntoGrid(getFieldEditorParent(), 1);
}
}
}
/*
* (non-Javadoc) Method declared on FieldEditor. Loads the value from the
* preference store and sets it to the check box.
*/
#Override
protected void doLoad() {
if (members != null) {
for (FieldEditor editor : members) {
editor.load();
}
}
}
/*
* (non-Javadoc) Method declared on FieldEditor. Loads the default value
* from the preference store and sets it to the check box.
*/
#Override
protected void doLoadDefault() {
if (members != null) {
for (FieldEditor editor : members) {
editor.loadDefault();
}
}
}
/*
* (non-Javadoc) Method declared on FieldEditor.
*/
#Override
protected void doStore() {
if (members != null) {
for (FieldEditor editor : members) {
editor.store();
}
}
}
#Override
public void store() {
super.store();
doStore();
}
/*
* (non-Javadoc) Method declared on FieldEditor.
*/
#Override
public int getNumberOfControls() {
return 1;
}
/*
* (non-Javadoc) Method declared on FieldEditor.
*/
#Override
public void setFocus() {
if (members != null && !members.isEmpty()) {
members.iterator().next().setFocus();
}
}
/*
* #see FieldEditor.setEnabled
*/
#Override
public void setEnabled(boolean enabled, Composite parentParam) {
if (members != null) {
for (FieldEditor editor : members) {
editor.setEnabled(enabled, parentParam);
}
}
}
#Override
public void setPreferenceStore(IPreferenceStore store) {
super.setPreferenceStore(store);
if (members != null) {
for (FieldEditor editor : members) {
editor.setPreferenceStore(store);
}
}
}
}

Yet another workaround:
Use Labels to separate groups of fields.
The following creates a vertical line separator and puts text directly beneath it:
new Label(getFieldEditorParent(), SWT.SEPARATOR | SWT.HORIZONTAL)
.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
new Label(getFieldEditorParent(), SWT.NONE).setText("My Group Title");

Related

Set compare editor as readonly

I'm developing an Eclipse plugin, where I need every editor of the application to be readonly, depending on the path of the contained file (relative to the project).
I've made my own editor class, and been able to override some method to get what I want:
public class MyTextEditor extends TextEditor {
/**
* Overridden to inhibit the replace action in find&replace dialog.
*/
#Override
public boolean isEditorInputModifiable() {
if (!super.isEditorInputModifiable()) {
return false;
}
return Utils.checkEditorInputModifiable(getEditorInput());
}
/**
* Overridden to inhibit any user edit in the editor input.
*/
#Override
public boolean isEditable() {
if (!super.isEditable()) {
return false;
}
return Utils.checkEditorInputModifiable(getEditorInput());
}
How to make read only editor in Eclipse (Eclipse Plugin Development)
But the question is: how to do that on the compare editor??
The editor used by eclipse in each compare dialog is a different editor, I can use my content ContentMergeViewer and set the compareConfiguration as readonly (on one side or both) but this is not sufficient. The compareInput on each side should be made readonly!
Or is there any other more convenient way to achive what I want??
Thank you!
I have a possible solution: you can extend the TextMergeViewer and override some method to check the file path "on the fly":
public abstract class AbstractMergeViewer extends TextMergeViewer {
/**
* Overridden to check if the editor input is modifiable.
*
* #see {#link TextMergeViewer#setEditable(ISourceViewer sourceViewer, boolean state)}
*/
#Override
protected void setEditable(ISourceViewer sourceViewer, boolean state) {
if (!Utils.checkEditorInputModifiable(getEditorInput(sourceViewer))) {
state = false;
}
super.setEditable(sourceViewer, state);
}
/**
* Return a custom implementation of a {#link MergeViewerContentProvider},
* to check if the content is modifiable.
*/
public IContentProvider getContentProvider() {
return new MASMergeViewerContentProvider(getCompareConfiguration());
}
}
The contentProvider has to be extended too:
public class MyMergeViewerContentProvider extends MergeViewerContentProvider {
/**
* Check if the left content is modifiable, with
* {#link Utils#checkResourceModifiable}.
*
* #see org.eclipse.compare.internal.MergeViewerContentProvider#isLeftEditable(java.lang.Object)
*/
#Override
public boolean isLeftEditable(Object element) {
if (element instanceof ICompareInput) {
Object left = ((ICompareInput) element).getLeft();
if (left instanceof LocalResourceTypedElement) {
LocalResourceTypedElement res = (LocalResourceTypedElement) left;
if (!Utils.checkResourceModifiable(res.getResource())) {
return false;
}
}
}
return super.isLeftEditable(element);
}
/**
* Check if the right content is modifiable, with
* {#link Utils#checkResourceModifiable}.
*
* #see org.eclipse.compare.internal.MergeViewerContentProvider#isRightEditable(java.lang.Object)
*/
#Override
public boolean isRightEditable(Object element) {
if (element instanceof ICompareInput) {
Object right = ((ICompareInput) element).getRight();
if (right instanceof LocalResourceTypedElement) {
LocalResourceTypedElement res = (LocalResourceTypedElement) right;
if (!Utils.checkResourceModifiable(res.getResource())) {
return false;
}
}
}
return super.isRightEditable(element);
}
This way all the graphic element in the compare dialog have the right state (disabled if the file is readonly).
MergeViewerContentProvider is an internal class, and should not be extended.. but I haven't found another solution.
The MergeViewer can be applied to a contentType via the extension point
org.eclipse.compare.contentMergeViewers
Now I have another problem: if the contentType is not one that I have defined, Eclipse uses its own implementation of the compare editor, and not mine.
Should I open a new question?

GWT: TextArea in Column

I've dabbled in GWT in the past and know just enough to burn myself. My current hurt can be described as follows:
I have a CellTable. This contains a number of Columns and TextColumns. The TextColumns contain variables from a report object, the Columns render radio buttons, check boxes etc.
I would like to insert another column which will provide a TextArea per table row, into which the user can enter some text.
My problem is I cannot figure out how to create the TextArea.
Could anyone possibly give me a code snippet to get me started?
Have a look at this showcase: http://gwt.google.com/samples/Showcase/Showcase.html#!CwCellSampler. You can use a TextInputCell for your purpose.
There's no built-in cell with a textarea as input, but it's easy to create one yourself. See http://code.google.com/webtoolkit/doc/latest/DevGuideUiCustomCells.html
(and have a look at either the TextInputCell or EditTextCell source code as a guide)
I've modeled my TextAreaInputCell after the TextInputCell that GWT ships with. It's basically the same code, except that the Template info is specific to a textarea (you can copy/paste this class, it's fully functional):
package com.xxx.scorecard.client.view.cell;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.AbstractInputCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.InputElement;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.client.SafeHtmlTemplates.Template;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
/**
* An {#link AbstractCell} used to render a text input.
*/
public class TextAreaInputCell extends AbstractInputCell<String, TextAreaInputCell.ViewData> {
interface Template extends SafeHtmlTemplates {
#Template("<textarea type=\"text\" cols=\"30\" rows=\"5\" tabindex=\"-1\">{0}</textarea>")
SafeHtml input(String value);
}
/**
* The {#code ViewData} for this cell.
*/
public static class ViewData {
/**
* The last value that was updated.
*/
private String lastValue;
/**
* The current value.
*/
private String curValue;
/**
* Construct a ViewData instance containing a given value.
*
* #param value
* a String value
*/
public ViewData(String value) {
this.lastValue = value;
this.curValue = value;
}
/**
* Return true if the last and current values of this ViewData object
* are equal to those of the other object.
*/
#Override
public boolean equals(Object other) {
if (!(other instanceof ViewData)) {
return false;
}
ViewData vd = (ViewData) other;
return equalsOrNull(lastValue, vd.lastValue) && equalsOrNull(curValue, vd.curValue);
}
/**
* Return the current value of the input element.
*
* #return the current value String
* #see #setCurrentValue(String)
*/
public String getCurrentValue() {
return curValue;
}
/**
* Return the last value sent to the {#link ValueUpdater}.
*
* #return the last value String
* #see #setLastValue(String)
*/
public String getLastValue() {
return lastValue;
}
/**
* Return a hash code based on the last and current values.
*/
#Override
public int hashCode() {
return (lastValue + "_*!#HASH_SEPARATOR#!*_" + curValue).hashCode();
}
/**
* Set the current value.
*
* #param curValue
* the current value
* #see #getCurrentValue()
*/
protected void setCurrentValue(String curValue) {
this.curValue = curValue;
}
/**
* Set the last value.
*
* #param lastValue
* the last value
* #see #getLastValue()
*/
protected void setLastValue(String lastValue) {
this.lastValue = lastValue;
}
private boolean equalsOrNull(Object a, Object b) {
return (a != null) ? a.equals(b) : ((b == null) ? true : false);
}
}
private static Template template;
/**
* Constructs a TextInputCell that renders its text without HTML markup.
*/
public TextAreaInputCell() {
super("change", "keyup");
if (template == null) {
template = GWT.create(Template.class);
}
}
#Override
public void onBrowserEvent(Context context, Element parent, String value, NativeEvent event,
ValueUpdater<String> valueUpdater) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
// Ignore events that don't target the input.
InputElement input = getInputElement(parent);
Element target = event.getEventTarget().cast();
if (!input.isOrHasChild(target)) {
return;
}
String eventType = event.getType();
Object key = context.getKey();
if ("change".equals(eventType)) {
finishEditing(parent, value, key, valueUpdater);
} else if ("keyup".equals(eventType)) {
// Record keys as they are typed.
ViewData vd = getViewData(key);
if (vd == null) {
vd = new ViewData(value);
setViewData(key, vd);
}
vd.setCurrentValue(input.getValue());
}
}
#Override
public void render(Context context, String value, SafeHtmlBuilder sb) {
// Get the view data.
Object key = context.getKey();
ViewData viewData = getViewData(key);
if (viewData != null && viewData.getCurrentValue().equals(value)) {
clearViewData(key);
viewData = null;
}
String s = (viewData != null) ? viewData.getCurrentValue() : value;
if (s != null) {
sb.append(template.input(s));
} else {
sb.appendHtmlConstant("<input type=\"text\" tabindex=\"-1\"></input>");
}
}
#Override
protected void finishEditing(Element parent, String value, Object key, ValueUpdater<String> valueUpdater) {
String newValue = getInputElement(parent).getValue();
// Get the view data.
ViewData vd = getViewData(key);
if (vd == null) {
vd = new ViewData(value);
setViewData(key, vd);
}
vd.setCurrentValue(newValue);
// Fire the value updater if the value has changed.
if (valueUpdater != null && !vd.getCurrentValue().equals(vd.getLastValue())) {
vd.setLastValue(newValue);
valueUpdater.update(newValue);
}
// Blur the element.
super.finishEditing(parent, newValue, key, valueUpdater);
}
#Override
protected InputElement getInputElement(Element parent) {
return super.getInputElement(parent).<InputElement> cast();
}
}
And here's how you use the cell:
Column<ScorecardEntryDTO, String> commentColumn = new Column<ScorecardEntryDTO, String>(new TextAreaInputCell()) {
#Override
public String getValue(ScorecardEntryDTO object) {
return object.getComment();
}
};
Try some like this:
TextArea outputArea = new TextArea();
outputArea.getElement().setAttribute("cols", "100");
outputArea.getElement().setAttribute("rows", "20");
locPanel.add(outputArea);

how to draw directed edges with "IGraphContentProvider"?

I am using ZEST and RCP to build a graph visalization tool. I used IGraphContentProvider and the LabelProvider for drawing the graph.
How can I draw a directed edge between two nodes using IGraphContentProvider?
Late answer, but I've just something similar:
You may do this, by providing a IEntityConnectionStyleProvider. It has a method called getConnectionStyle(src, dest). You can return the costant ZestStyles.CONNECTIONS_DIRECTED there, if all the connections are directed. Otherwise, you have to decide based on the source and destination, wether to draw a directed edge.
You may also want to look at IGraphEntityRelationshipContentProvider. With that content provider you can just add objects to the graph and later on decide which are connected.
Not a Zest expert, but a IGraphContentProvider seems limited to access the underlying obejct of a given relationship.
The getSource() and getDestination() methods will help a viewer like a Graphviewer, from AbstractStructuredGraphViewer view the edge defined by those "source-Destination" couples.
See this example for instance.
/*******************************************************************************
* Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC,
* Canada. 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: The Chisel Group, University of Victoria
******************************************************************************/
package org.eclipse.zest.core.examples.jface;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.zest.core.viewers.GraphViewer;
import org.eclipse.zest.core.viewers.IGraphContentProvider;
import org.eclipse.zest.layouts.LayoutStyles;
import org.eclipse.zest.layouts.algorithms.SpringLayoutAlgorithm;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/**
* This snippet shows how to use the IGraphContentProvider to create a graph with Zest.
* In this example, getElements returns 3 edges:
* * Rock2Paper
* * Paper2Scissors
* * Scissors2Rock
*
* And for each of these, the source and destination are returned in getSource and getDestination.
*
* A label provider is also used to create the text and icons for the graph.
*
* #author Ian Bull
*
*/
public class GraphJFaceSnippet2 {
static class MyContentProvider implements IGraphContentProvider {
public Object getSource(Object rel) {
if ("Rock2Paper".equals(rel)) {
return "Rock";
} else if ("Paper2Scissors".equals(rel)) {
return "Paper";
} else if ("Scissors2Rock".equals(rel)) {
return "Scissors";
}
return null;
}
public Object[] getElements(Object input) {
return new Object[] { "Rock2Paper", "Paper2Scissors", "Scissors2Rock" };
}
public Object getDestination(Object rel) {
if ("Rock2Paper".equals(rel)) {
return "Paper";
} else if ("Paper2Scissors".equals(rel)) {
return "Scissors";
} else if ("Scissors2Rock".equals(rel)) {
return "Rock";
}
return null;
}
public double getWeight(Object connection) {
return 0;
}
public void dispose() {
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
}
static class MyLabelProvider extends LabelProvider {
final Image image = Display.getDefault().getSystemImage(SWT.ICON_WARNING);
public Image getImage(Object element) {
if (element.equals("Rock") || element.equals("Paper") || element.equals("Scissors")) {
return image;
}
return null;
}
public String getText(Object element) {
return element.toString();
}
}
static GraphViewer viewer = null;
/**
* #param args
*/
public static void main(String[] args) {
Display d = new Display();
Shell shell = new Shell(d);
shell.setText("GraphJFaceSnippet2");
shell.setLayout(new FillLayout(SWT.VERTICAL));
shell.setSize(400, 400);
viewer = new GraphViewer(shell, SWT.NONE);
viewer.setContentProvider(new MyContentProvider());
viewer.setLabelProvider(new MyLabelProvider());
viewer.setLayoutAlgorithm(new SpringLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING));
viewer.setInput(new Object());
shell.open();
while (!shell.isDisposed()) {
while (!d.readAndDispatch()) {
d.sleep();
}
}
}
}

Multiple pages tutorial in Google Web Toolkit (GWT)

I just started learning Google Web Toolkit (GWT). How do I make different HTML pages in my GWT application?
For example, I want to create an application for a book store. In this application I'll have three pages:
Home pages where I will welcome the user and offer the user books
Page to browse books by categories and view details (use GWT widgets)
Check out books online.
Of course there could be other pages like the user's details, add new book, etc.
So, what is the best way of making different pages in GWT and how can I make navigation from page to page? Are there any examples or tutorials? Or do I even need to create different pages when I can create a whole application in one page?
What I usually do in situations like this is design the webpage framework first. I'll have a div for the header, side menu and footer. I'll also have a div in my HTML for the main content.
Example:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name='gwt:module' content='org.project.package.Core=org.project.package.Core'>
</head>
<body>
<!-- Load the JavaScript code for GWT -->
<script language="javascript" src="ui/org.project.package.ui.Core.nocache.js"></script>
<!-- For some unknown reason in Internet Explorer you have to have cellpadding/spacing ON THE ELEMENT and not on the STYLE if it is in the body tag like this -->
<table id="wrapper" cellpadding="0" cellspacing="0" style="width: 100%;height: 100%;">
<!-- Header row -->
<tr style="height: 25%;">
<td colspan="2" id="header"></td>
</tr>
<!-- Body row and left nav row -->
<tr style="height: 65%;">
<td id="leftnav"></td>
<td id="content"></td>
</tr>
<!-- Footer row -->
<tr style="height: 10%;">
<td colspan="2" id="footer"></td>
</tr>
</table>
<!-- This iframe handles history -->
<iframe id="__gwt_historyFrame" style="width:0;height:0;border:0"></iframe>
</body>
</html>
(If you like <div> based layouts, feel free to use those instead.)
Then you build your entry point (in my case Core.java) as you normally would, setting each of the elements as need be.
RootPanel.get("header").add(new Header());
RootPanel.get("leftnav").add(new NavigationMenu());
RootPanel.get("footer").add(new Footer());
It is, of course, possible to have a static footer and header, but that's neither here nor there.
I also have an abstract class called "Content". Content objects extend "Composite" and will have various methods for simplifying the creation and layout of a new page. Every page that I build for this application, be it a help screen, search screen, shopping cart, or anything else, is of type Content.
Now, what I do is create a class called "ContentContainer". This is a singleton that is responsible for managing the "content" element. It has one method "setContent" that accepts objects of type "Content". It then basically removes anything within the "content" <td> and replaces it with whatever widget (Composite) you assign via the "setContent" method. The setContent method also handles history and title bar management. Basically the ContentContainer serves to aggregate all the various points of binding that you might have to make if each page content had to "know" about all the functions it must perform.
Finally, you need a way to get to that page, right? That's simple:
ContentContainer.getInstance().setContent(new Search());
Put the above in an on-click event somewhere and you're golden.
The only things that your other widgets need to be bound to is the ContentContainer and the type of Content that they are adding.
The downsides that I can see to ChrisBo's approach are that you've got a list that has to be maintained of tokens -> pages. The other downside I can see is that I don't see how you can have an actual history system with this method.
One thing it does offer over my approach is that all page choices are pretty centralized. I'd use some sort of Enum or at least a static class with String values to prevent myself from mongling up links.
In either case, I think the point can be summed up as this: swap the content of some central page element based on what your user clicks actions your user(s) perform.
I would use the HyperLink and History class. The good thing about the Hyperlink class is, that it sets this token(e.g.#foobar), and all you have to do, is catch the event, that is fired when the value of the token is changed(ValueChangeEvent). In the eventHandler you would then replace the pages.
Example:
address of welcome Page: www.yourpage.com/#home
on this page would be a link to the "browse book"-page, when the link is clicked the new address would be something like this: www.yourpage.com/#browse
And here is the code:
public class MainEntryPoint implements EntryPoint, ValueChangeHandler {
VerticalPanel panel = new VerticalPanel();
Label label=new Label();
public void onModuleLoad() {
Hyperlink link1 = new Hyperlink("books", "browse");
Hyperlink link2 = new Hyperlink("user details", "details");
panel.add(link1);
panel.add(link2);
panel.add(label);
RootPanel.get().add(panel);
History.addValueChangeHandler(this);
//when there is no token, the "home" token is set else changePage() is called.
//this is useful if a user has bookmarked a site other than the homepage.
if(History.getToken().isEmpty()){
History.newItem("home");
} else {
changePage(History.getToken());
}
}
public void onValueChange(ValueChangeEvent event) {
changePage(History.getToken());
}
public void changePage(String token) {
if(History.getToken().equals("browse")) {
label.setText("Here would be some books");
} else if (History.getToken().equals("details")) {
label.setText("Here would be the user details");
} else {
label.setText("Welcome page");
}
}
}
Awesome! I combined Chris R.'s answer with Chris Boesing's to come up with this:
This is the 'index' start page
public class Index implements EntryPoint, ValueChangeHandler<String> {
public void onModuleLoad() {
History.addValueChangeHandler(this);
if (History.getToken().isEmpty()) History.newItem("index");
Composite c = new Login();
FlowControl.go(c);
}
public void onValueChange(ValueChangeEvent<String> e) {
FlowControl.go(History.getToken());
}
}
This is the controller, or ContentContainer according to Chris R.
public class FlowControl {
private static FlowControl instance;
private FlowControl() {}
public static void go(Composite c) {
if (instance == null) instance = new FlowControl(); // not sure why we need this yet since everything is static.
RootPanel.get("application").clear();
RootPanel.get("application").getElement().getStyle().setPosition(Position.RELATIVE); // not sure why, but GWT throws an exception without this. Adding to CSS doesn't work.
// add, determine height/width, center, then move. height/width are unknown until added to document. Catch-22!
RootPanel.get("application").add(c);
int left = Window.getClientWidth() / 2 - c.getOffsetWidth() / 2; // find center
int top = Window.getClientHeight() / 2 - c.getOffsetHeight() / 2;
RootPanel.get("application").setWidgetPosition(c, left, top);
History.newItem(c.getTitle()); // TODO: need to change and implement (or override) this method on each screen
}
public static void go(String token) {
if (token == null) go(new Login());
if (token.equals("cart")) go(new Cart());
if (token.equals("login")) go(new Login());
// Can probably make these constants in this class
}
Then you can pepper Hyperlinks and Buttons anywhere throughout your code. (Have not tried Hyperlinks yet.)
Button submit = new Button("Submit");
submit.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
FlowControl.go(new MyScreen());
}
});
I added a div to my HTML
<!-- This is where the application will reside within. It is controlled by FlowControl class. -->
<div id="application"></div>
And now all screens must call initWidget() in the constructor instead of adding to RootPanel, since it is a Composite class now, like
initWidget(myPanel); // all composites must call this in constructor
If you want it to be FULL AJAXified (like a desktop app) of course you'd only need one page. Then just change the contents of the body depending on the link.
Also, there is a google group for GWT that is very very active, and I know this has been asked before there, you just need to use the "search" feature.
GWT Multipage - simple framework for multi-page-GWT-applications.
You can use MVP patern.
Here is mi simple library https://code.google.com/p/gwt-simple-mvp/wiki/GettingStarted .
And you can split code to more js files. https://code.google.com/p/gwt-spliting/
I used Chloe S. answer (combining Chris R.'s answer with Chris Boesing's) to build this App Controller for a working GWT Web App. The version in production is tested (and working %100) but this redacted version below will need to be modified to integrate with your own app (start by renaming the page keys to your menu items).
AppController.java:
/**
* This App Controller utilizes two static inner-classes (Pages and External)
* to manage and server multiple pages with multiple sub-page (through their presenters)
* via String key constants which also serve as the literal text for the menu items.
*
* Pages are added as menu commands in their respective views:
* // Add menu items to the menu with commands:
* menuItems.put(Pages.PAGE1, mainMenu.addItem(Pages.PAGE1, new Command() {
* public void execute() {
* History.newItem(Pages.PAGE1);
* }
* }));
*
* Pages are fired as History tokens (from entry point java class):
*
* **
* * Receives history events and pushes them to the AppController using a deferred command.
* * Changes the cursor to show waiting.
* * #param the value change token
* *
* public void onValueChange(ValueChangeEvent<String> e) {
* // check token to cover first historical "back" navigation:
* if(!History.getToken().isEmpty()) {
* AppController.waitCursor.execute(); // cursor is reset in page attach method
* }
* Scheduler.get().scheduleDeferred(new ScheduledCommand() {
* public void execute() {
* AppController.go(History.getToken());
* }
* });
* }
*
* Wait cursors are implemented as CSS:
*
* body.wait, body.wait * {
* cursor: wait !important;
* }
*
* NOTE: This page swapping implementation technique (based on the StackOverflow solution
* found here: [http://stackoverflow.com/questions/1061705/multiple-pages-tutorial-in-google-web-toolkit-gwt][1])
* differs from the obtuse and ancient 2010 GWT framework documentation in that the App Controller manages / handles
* adding the widget to the container, and therefore all the Presenters must implement the
* "AppControlPresenter" or "AppControlContainerPresenter" interface to give it access to their containers.
* (thus eliminating "public void go(final HasWidgets container);" method in all presenter architecture except for 'MainAppPresenter')
* There is also no event bus; static method calls are used for any needed interactivity.
*
* Includes a popup for pages still under construction.
*/
package com.;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import java.util.HashMap;
import java.util.Map;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DecoratedPopupPanel;
import com.google.gwt.user.client.ui.Frame;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.LayoutPanel;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.UIObject;
/**
*
*/
public class AppController {
/** */
public final static String DEFAULT_INITIAL_PAGE1_SUB_PAGE = Pages.PAGE_1A;
/** Singleton instance for the AppController */
private static AppController instance = new AppController();
/** Presenter for the main app */
private static MainAppPresenter mainAppPresenter;
/** container for the different views */
private static LayoutPanel container;
/** sub-container for the different sub-views */
private static LayoutPanel page1Container;
/** */
private static DecoratedPopupPanel popup;
/** constant for Style-Dependent names for menu items (see menu-style.css) */
public final static String MENU_ACTIVE_STYLE = "active";
/** constant for Style-Dependent class name in css */
public final static String CURSOR_WAIT_CLASS = "wait";
/** */
public final static String POPUP_DEMO_ID = "popupDemo";
/** */
public final static int DEMOP_POPUP_VERTICAL_OFFSET = 0;
/** */
public final static String POPUP_DEMO_STATEMENT = "<span class='text'>This page is under construction</span>"
+ "<span class='char'>…</span>";
/** */
public static ScheduledCommand waitCursor = new ScheduledCommand() {
#Override
public void execute() {
AppController.waitCursor(true);
}
};
/** */
public static ScheduledCommand normalCursor = new ScheduledCommand() {
#Override
public void execute() {
AppController.waitCursor(false);
}
};
/** Flag for determining if the page was reloaded */
private static boolean reloaded = false;
private static final LoginServiceAsync loginRpcService = GWT.create(LoginService.class);
/**
* Called on the resize event to set the position of the demo popup
* window to be adjusted to the correct dimensions (size and positoin)
* regardless of screen size.
*/
private static ScheduledCommand resetPopupDimensions = new ScheduledCommand() {
#Override
public void execute() {
if(!UNDER_CONSTRUCTION || popup == null) {
return;
}
int demoWidth = Math.round(Window.getClientWidth() / MainApp.PHI),
demoYPosition = Window.getClientHeight() / 2 - Math.round(popup.getOffsetHeight() / 2);
popup.setWidth(String.valueOf(demoWidth) + "px");
if(popup.getOffsetWidth() >= Window.getClientWidth()) {
popup.setWidth("100%");
popup.setPopupPosition(0, demoYPosition);
} else {
popup.setPopupPosition(Window.getClientWidth() / 2 - (popup.getOffsetWidth() / 2), demoYPosition);
}
}
};
/** */
private static final String LOGIN_OBJECT_NAME = "Login Presenter Object";
/**
* static inner-class for external websites
*/
public static class External {
/** The frame to contain the website */
private static Frame frame;
/** */
public static final String EXTERNAL_URL_1 = "http://";
/** */
public static final String EXTERNAL_URL_2 = "http://";
/**
* #returns true if the name of the token is equal to one of the URLs
* #param token the name to check
*/
public static boolean has(String token) {
return token.equalsIgnoreCase(EXTERNAL_URL_1) ||
token.equalsIgnoreCase(EXTERNAL_URL_2);
}
/**
* Gets the external Frame object
* #param url
* #return Frame
*/
public static Frame get(String url) {
if(frame == null) {
frame = new Frame(url);
frame.addAttachHandler(new AttachEvent.Handler() {
#Override
public void onAttachOrDetach(AttachEvent event) {
// hide the popup:
showPopup(false);
Scheduler.get().scheduleFinally(resetPopupDimensions);
Scheduler.get().scheduleFinally(normalCursor);
}
});
}
else if(!frame.getUrl().equalsIgnoreCase(url)) {
frame.setUrl(url);
}
return frame;
}
}
/**
* static inner-class for holding pages activated by the app's main menu commands
*/
public static class Pages {
/** */
public static final String PAGE1 = "foo";
/** */
public static final String PAGE2 = "bar";
/** */
public static final String PAGE_1A = "baz";
/** */
public static final String PAGE_1B = "qux";
/** */
public static String lastPage;
/** */
public static String lastPage1SubPage;
/** */
public static String unsavedMessage;
/** */
private static HashMap<String, AppControlPresenter> pageMap;
/** */
private static AppControlPresenter presenter;
/** */
private static Composite view;
/**
* initializes the hashmap of pages
*/
public static void init() {
pageMap = new HashMap<String, AppControlPresenter>();
}
/**
* #returns true if the name of the token is equal to one of the pages
* #param token the name to check
*/
public static boolean has(String token) {
return token.equalsIgnoreCase(PAGE1) ||
token.equalsIgnoreCase(PAGE2) ||
token.equalsIgnoreCase(PAGE_1A);
}
/**
* Gets the correct page container to display as a Composite
* #param page the token name of the page
* #return Composite page
*/
public static Composite get(String page) {
view = null;
presenter = null;
if(page.equalsIgnoreCase(PAGE1)) {
if(pageMap.get(PAGE1) == null) {
pageMap.put(PAGE1, new Page1Presenter(PAGE1));
page1Container = ((AppControlContainerPresenter) pageMap.get(PAGE1)).getContentPane();
}
presenter = pageMap.get(PAGE1);
lastPage = page;
mainAppPresenter.setCurrentMenuItem(page);
}
else if(page.equalsIgnoreCase(PAGE_1A) ||
page.equalsIgnoreCase(PAGE_1B) {
if(pageMap.get(PAGE1) == null) {
pageMap.put(PAGE1, new Page1Presenter(PAGE1));
page1Container = ((AppControlContainerPresenter) pageMap.get(PAGE1)).getContentPane();
}
presenter = pageMap.get(PAGE1);
lastPage1SubPage = page;
view = ((AppControlContainerPresenter)presenter).setCurrentPage(page);
}
else if(page.equalsIgnoreCase(PAGE2)) {
if(pageMap.get(PAGE2) == null) {
pageMap.put(PAGE2, new Page2Presenter(PAGE2));
}
presenter = pageMap.get(PAGE2);
lastPage = PAGE2;
mainAppPresenter.setCurrentMenuItem(page);
}
else if(External.has(page)) {
throw new Error("App Controller Error -- Use 'External' inner-class for: " + page);
}
else {
throw new Error("App Controller Error -- Page name not found: " + page);
}
if(view == null) {
view = (Composite)presenter.view();
}
view.addAttachHandler(new AttachEvent.Handler() {
#Override
public void onAttachOrDetach(AttachEvent event) {
AppController.showPopup(false);
presenter.updateAttachOrDetach(event);
Scheduler.get().scheduleFinally(resetPopupDimensions);
Scheduler.get().scheduleFinally(normalCursor);
}
});
return view;
}
/**
* Gets the current AppControlPresenter for the last page.
* #returns the current AppControlPresenter
*/
public static AppControlPresenter getCurrentPresenter() {
return presenter;
}
/**
* Gets an AppControlPresenter from the pageMap.
* #param token the name of the presenter
* #returns the AppControlPresenter
*/
public static AppControlPresenter getPresenter(String token) {
return pageMap.get(token);
}
/**
* Returns true if the page is already loaded.
* #param token name of the page
*/
public static boolean alreadyLoaded(String token) {
MainApp.debug(1, "[already loaded: " + presenter.toString() + " (token: " + token + ")");
return presenter.toString().equalsIgnoreCase(token);
}
/**
* Returns true if the page is visible
* #param page the token name of the page
*/
public static boolean isVisible(String page) {
UIObject component = pageMap.get(page).view();
return !(component.getOffsetHeight() == 0 && component.getOffsetWidth() == 0);
}
/**
* Returns true if the page is visible
* #param presenter the AppControlPresenter instance
*/
public static boolean isVisible(AppControlPresenter presenter) {
UIObject component = presenter.view();
return !(component.getOffsetHeight() == 0 && component.getOffsetWidth() == 0);
}
/**
* Returns true if the application has unsaved data.
* Iterates through all the pages and checks each presenter.
*/
public static boolean unsavedData() {
if(pageMap.isEmpty()) return false;
boolean unsaved = false;
for(Map.Entry<String, AppControlPresenter> entry : pageMap.entrySet()) {
AppControlPresenter presenter = entry.getValue();
if(presenter != null && presenter.unsavedData()) {
MainApp.debug(1, "(!) " + presenter.toString() + " has unsaved data");
unsavedMessage = presenter.dataDescription();
unsaved = true;
break; // just need to know one exists for now (window closing event)
}
}
return unsaved;
}
/**
* Called on a resize event on the window. Iterates through all the pages
* and tells their presenters to resize their content.
*/
public static void resize() {
for(Map.Entry<String, AppControlPresenter> entry : pageMap.entrySet()) {
AppControlPresenter presenter = entry.getValue();
if(presenter != null && isVisible(presenter)) {
presenter.resize();
}
}
}
} //end class Pages
/**
* #returns true if the history token is equal to any of the pages in the app
*/
public static boolean hasHistory() {
String token = History.getToken();
return External.has(token) || Pages.has(token);
}
/**
* Starts the login view at the root layout level
*/
public static void goLoginScreen() {
//check for reload:
if(hasHistory()) {
MainApp.debug(1, "(!) AppController has History on Login");
reloaded = true;
}
else {
reloaded = false;
}
RootLayoutPanel.get().clear();
RootLayoutPanel.get().add(new LoginPresenter(LOGIN_OBJECT_NAME).view());
}
/**
* #returns the last "Page1" page
*/
public static String getLastPage1Page() {
if(Pages.lastPage1SubPage == null || Pages.lastPage1SubPage.isEmpty()) {
Pages.lastPage1SubPage = DEFAULT_INITIAL_PAGE1_SUB_PAGE;
}
return Pages.lastPage1SubPage;
}
/**
* Tells the app to start with the Page1 page.
* #param username the username of the person logged-in
*/
public static void goMainApp(String username) {
//hide the login background:
RootPanel.getBodyElement().getStyle().setProperty("background", "none");
mainAppPresenter = new MainAppPresenter(username);
RootLayoutPanel.get().clear();
mainAppPresenter.go(RootLayoutPanel.get());
//get the center panel:
container = mainAppPresenter.getContainer();
//check for reload:
//NOTE: the token will be empty if the user refreshes
// and navigates all the way back to the zero-state
// from the login screen.
//NOTE: this logic may change after user-persistence is implemented
if(hasHistory() || History.getToken().isEmpty()) {
// reset the reloaded flag:
reloaded = false;
if(History.getToken().isEmpty()) {
//land on the first page:
History.newItem(AppController.Pages.PAGE1);
}
else {
MainApp.debug(2, "(!) AppController has History on reload: " + History.getToken());
History.fireCurrentHistoryState();
}
}
else {
//land on the first page:
History.newItem(AppController.Pages.PAGE1);
}
}
/**
*
*/
public static void checkIfSessionActive() {
loginRpcService.loginFromSession(new AsyncCallback<LoginSummary>() {
#Override
public void onFailure(Throwable throwable) {
goLoginScreen();
}
#Override
public void onSuccess(LoginSummary loginSummary) {
if (loginSummary.getErrorString() != null)
goLoginScreen();
else
goMainApp(loginSummary.getUser().getName());
}
});
}
/**
*
*/
public static void sessionLogout() {
DialogBoxWidget.confirm(200,
"Logout",
"Are you sure you want to log out?",
new ConfirmDialogCallback() {
#Override
public void onAffirmative() {
loginRpcService.logout(new AsyncCallback<Void>() {
#Override
public void onFailure(Throwable throwable) {
goLoginScreen();
}
#Override
public void onSuccess(Void aVoid) {
goLoginScreen();
}
});
}
#Override
public void onCancel() {
}
});
}
/**
* Shows or hides the "Under Construction" popup if UNDER_CONSTRUCION is true.
* #param show true to show and false to hide
*/
public static void showPopup(boolean show) {
if(MainApp.UNDER_CONSTRUCTION && popup != null) {
if(show) {
popup.show();
}
else {
popup.hide();
}
}
}
/**
* Called by every history event fired (including the back and forward buttons).
* Ignores the login and empty index historically.
* #param token the name of the page to load
*/
public static void go(String token) {
if(reloaded) {
normalCursor.execute();
}
if(token == null || token.isEmpty() || reloaded == true) return;
MainApp.debug("<history changed> - AppController.go()-> " + token);
// build the popup message for all unfinished pages:
if(MainApp.UNDER_CONSTRUCTION) {
if(popup == null) {
popup = new DecoratedPopupPanel(false);
popup.ensureDebugId(POPUP_DEMO_ID);
popup.addStyleDependentName(POPUP_DEMO_ID);
popup.setWidget(new HTML(new Image("images/workingman.png") + POPUP_DEMO_STATEMENT + new Image("images/workingmanFLIP.png")));
}
}
// check token for which page to return:
if(token.equalsIgnoreCase(External.EXTERNAL_URL_1)) {
MainAppPresenter.clearActiveMenuItems();
setExternalContentURL(External.get(token));
}
else if(token.equalsIgnoreCase(External.EXTERNAL_URL_2)) {
MainAppPresenter.clearActiveMenuItems();
setExternalContentURL(External.get(token));
}
else if(token.equalsIgnoreCase(Pages.PAGE1)) {
setContent(Pages.get(Pages.PAGE1));
setPage1Content(Pages.get(getLastPage1Page()));
}
else if(token.equalsIgnoreCase(Pages.PAGE_1A) ||
token.equalsIgnoreCase(Pages.PAGE_1B)) {
setContent(Pages.get(Pages.PAGE1));
setPage1Content(Pages.get(token));
}
else if(token.equalsIgnoreCase(Pages.PAGE2)) {
setContent(Pages.get(Pages.PAGE2));
}
else { // default behavior for a page not described:
MainApp.debug(2, "(!) Unknown page: " + token);
setContent(Pages.get(token));
}
}
/**
* Called by MainApp on a window resize event.
* #param e the ResizeEvent
*/
public static void resize(ResizeEvent e) {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
#Override
public void execute() {
if(mainAppPresenter != null) {
mainAppPresenter.resize();
}
Pages.resize();
Scheduler.get().scheduleFinally(resetPopupDimensions);
}
});
}
/**
* Changes the cursor to "wait" or "auto" depending on the parameter
* #param wait true to set the cursor to waiting
*/
private static void waitCursor(Boolean wait) {
if(wait) {
RootPanel.getBodyElement().addClassName(CURSOR_WAIT_CLASS);
}
else {
RootPanel.getBodyElement().removeClassName(CURSOR_WAIT_CLASS);
}
}
/**
* Private Constructor which initializes the Pages object.
*/
private AppController() {
Pages.init();
}
/**
* Sets the content of the main app container to one of the "Pages."
* #param c the Composite widget to be added
*/
private static void setContent(Composite c) {
container.clear();
container.add(c.asWidget());
}
/**
* Sets the content of the main app container an external URL.
* #param f the Frame by which external web sites are added
*/
private static void setExternalContentURL(Frame f) {
container.clear();
container.add(f);
// must reset the width and height every time:
f.getElement().getStyle().setWidth(100, Unit.PCT);
f.getElement().getStyle().setHeight(100, Unit.PCT);
}
/**
* Sets the content of the Page1 container to one of the sub pages.
* #param c the Composite widget to be added
*/
private static void setPage1Content(Composite c) {
page1Container.clear();
page1Container.add(c.asWidget());
}
}
AppControlPresenter.java:
package com.*;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.user.client.ui.Composite;
/**
* Base interface for all 'Presenters' used by AppController.java
* NOTE: classes that implement this interface do not launch the presenter's view
* into the provided container; rather, the view is retrieved and used by the
* AppController instance by calling the 'view()' method
*/
public interface AppControlPresenter {
/**
* Gets the view (for use in AppController.java)
*/
public Composite view();
/**
* Indicates if current search data is present and unsaved.
* #returns true to if a search is still active
*/
public boolean unsavedData();
/**
* Called on resize event to notify presenters with visible
* components that need resizing for different screen sizes.
* #returns true if elements were resized
*/
public boolean resize();
/**
* Called on attachEvents to tell the presenter to update.
* #param event the AttachEvent
*/
public void updateAttachOrDetach(AttachEvent event);
/**
* Gets the message to display for unsaved data.
* #returns a message String describing the data
*/
public String dataDescription();
/**
* Gets a fully qualified name for use in comparisons
* #return the name of this presenter used by the <code>AppController</code>
*/
public String toString();
}
AppControlContainerPresenter.java:
package com.*;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.LayoutPanel;
/**
*/
public interface AppControlContainerPresenter extends AppControlPresenter {
/**
*
* #return
*/
public LayoutPanel getContentPane();
/**
*
* #param pageName
* #return
*/
public Composite setCurrentPage(String pageName);
}
Add a module for each page you have that needs the GWT functionality. Reuse your components.

Eclipse 3.3 Europa JDT TextHover

I want to show my own text hover in eclipse for some specific words? Please provide me some examples
You can start by looking at Koder examples.
E.g. this CEditorTextHoverDispatcher or this UCTextHover
package com.ut2003.uceditor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextHover;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Region;
import org.eclipse.swt.graphics.Point;
public class UCTextHover implements ITextHover
{
/* (non-Javadoc)
* Method declared on ITextHover
*/
public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion)
{
if (hoverRegion != null)
{
try
{
if (hoverRegion.getLength() > -1)
return textViewer.getDocument().get(hoverRegion.getOffset(), hoverRegion.getLength());
}
catch (BadLocationException x)
{
}
}
return "Empty Selection";
}
/* (non-Javadoc)
* Method declared on ITextHover
*/
public IRegion getHoverRegion(ITextViewer textViewer, int offset)
{
Point selection = textViewer.getSelectedRange();
if (selection.x <= offset && offset < selection.x + selection.y)
return new Region(selection.x, selection.y);
return new Region(offset, 0);
}
}
You would set a TextHover in a SourceViewerConfiguration like this GasSourceViewerConfiguration or this CalcSourceViewerConfiguration
package com.example.calc.ui.editors;
import org.eclipse.jface.text.ITextHover;
import org.eclipse.jface.text.reconciler.IReconciler;
import org.eclipse.jface.text.reconciler.MonoReconciler;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
/**
* #author cdaly
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public class CalcSourceViewerConfiguration extends SourceViewerConfiguration {
private CalcEditor _editor;
public CalcSourceViewerConfiguration(CalcEditor editor){
_editor = editor;
}
/* (non-Javadoc)
* #see org.eclipse.jface.text.source.SourceViewerConfiguration#getReconciler(org.eclipse.jface.text.source.ISourceViewer)
*/
public IReconciler getReconciler(ISourceViewer sourceViewer) {
return new MonoReconciler(_editor.getReconcilingStrategy(), false);
}
/* (non-Javadoc)
* #see org.eclipse.jface.text.source.SourceViewerConfiguration#getTextHover(org.eclipse.jface.text.source.ISourceViewer, java.lang.String)
*/
public ITextHover getTextHover(
ISourceViewer sourceViewer,
String contentType) {
ITextHover hover;
if (_editor != null && _editor instanceof CalcEditor) {
hover = new CalcTextHover((CalcEditor)_editor);
} else {
hover = null;
}
return hover;
}
}
Beyond that, I have not much more information: the examples I have found are more programmatic than declarative (i.e. "plugin.xml"), so you may want to explore some more code.
Another good example: Eclipse: Rich Hovers Redux (it is for eclipse3.4 though, but the full example can give you another hint at how a custom ITextHover is added to the current editor)
The best thing is to use the java editor plugin along with eclipse first. Take eclipse help ->welcome->Samples->Java Editor plugin