I am using Cell Table of GWT 2.2 version. I want to get the name of the header column on which I have clicked. I didn't get any click event on the same.
Is there any work around by which I can accomplish my task.
Something like this? ;)
public class CellTableExample implements EntryPoint, ClickHandler {
private static class SomeEntity {
/* ... */
}
private static class ClickableTextHeader extends TextHeader {
private ClickHandler handler;
public ClickableTextHeader(String text, ClickHandler handler) {
super(text);
this.handler = handler;
}
#Override
public void onBrowserEvent(Context context, final Element elem,
final NativeEvent event) {
//maybe hijack click event
if(handler != null) {
if(Event.ONCLICK == Event.getTypeInt(event.getType())) {
handler.onClick(new ClickEvent() {
{
setNativeEvent(event);
setRelativeElement(elem);
setSource(ClickableTextHeader.this);
}
});
}
}
//default dom event handler
super.onBrowserEvent(context, elem, event);
}
}
CellTable<SomeEntity> cellTable;
TextColumn<SomeEntity> firstColumn;
TextColumn<SomeEntity> secondColumn;
TextColumn<SomeEntity> thirdColumn;
#Override
public void onModuleLoad() {
/* somehow init columns - it's not the point for this example */
cellTable.addColumn(firstColumn, new ClickableTextHeader("First column header", this));
cellTable.addColumn(secondColumn, new ClickableTextHeader("Second column header", this));
cellTable.addColumn(thirdColumn, new ClickableTextHeader("Third column header", this));
}
#Override
public void onClick(ClickEvent event) {
ClickableTextHeader source = (ClickableTextHeader) event.getSource();
Window.alert(source.getValue());
}
}
Hijacking event could look simpler if we used "simple listener interface" - i just wanted to be "semanticaly compliant with out-of-the-box Handlers" :)
Related
I am creating an Eclipse RCP application with multiple views. One of my views is a multi-page editor view. Each of those pages has a a master/details block. I need to register all of those TableViewers as selection providers for my other view to listen to.
After much research online, I came across this article about multiple selection providers in a single view. I followed the instructions to create this selection provider for multiple viewers.
class MyMultipleSelectionProvider implements ISelectionProvider {
private final ListenerList selectionListeners = new ListenerList();
private ISelectionProvider delegate;
private final ISelectionChangedListener selectionListener = new ISelectionChangedListener() {
#Override
public void selectionChanged(final SelectionChangedEvent event) {
if (event.getSelectionProvider() == AdaptabilityProfileSelectionProvider.this.delegate) {
fireSelectionChanged( event.getSelection() );
}
}
};
/**
* Sets a new selection provider to delegate to. Selection listeners
* registered with the previous delegate are removed before.
*
* #param newDelegate new selection provider
*/
public void setSelectionProviderDelegate(final ISelectionProvider newDelegate) {
if (this.delegate == newDelegate) {
return;
}
if (this.delegate != null) {
this.delegate.removeSelectionChangedListener(this.selectionListener);
}
this.delegate = newDelegate;
if (newDelegate != null) {
newDelegate.addSelectionChangedListener(this.selectionListener);
fireSelectionChanged(newDelegate.getSelection());
}
}
#Override
public void addSelectionChangedListener(final ISelectionChangedListener listener) {
this.selectionListeners.add(listener);
}
#Override
public ISelection getSelection() {
return this.delegate == null ? null : this.delegate.getSelection();
}
#Override
public void removeSelectionChangedListener(final ISelectionChangedListener listener) {
this.selectionListeners.remove(listener);
}
#Override
public void setSelection(final ISelection selection) {
if (this.delegate != null) {
this.delegate.setSelection(selection);
}
}
protected void fireSelectionChanged(final ISelection selection) {
fireSelectionChanged(this.selectionListeners, selection);
}
private void fireSelectionChanged(final ListenerList list, final ISelection selection) {
final SelectionChangedEvent event = new SelectionChangedEvent(this.delegate, selection);
final Object[] listeners = list.getListeners();
for (int i = 0; i < listeners.length; i++) {
final ISelectionChangedListener listener = (ISelectionChangedListener) listeners[i];
listener.selectionChanged(event);
}
}
}
I added a focusListener on all of the edior's viewers so they become the delegate:
tree.addFocusListener(new FocusAdapter() {
#Override
public void focusGained(final FocusEvent e) {
editor.getSelectionProvider().setSelectionProviderDelegate(MyEditorPage.this.treeViewer);
}
});
And I registered this as the selection provider for my editor:
site.setSelectionProvider( this.selectionProvider );
Then, within my view that needs to hear about the selection, I registered a selection listener for this editor:
getSite().getPage().addSelectionListener(MyEditor.ID, this.selectionListener);
When I run the application, I see that the delegate is being changed and the selection events are being fired. However, the listener list is empty.
I am never calling addSelectionChangeListener() directly. I was under the impression that that was what the selection service is for. Am I wrong? Should I be calling it? If so, when? If not, who is supposed to be adding the listener, and why isn't it happening?
If your code is based on FormEditor (or MultiPageEditorPart) then the selection provider is set to MultiPageSelectionProvider at the end of the init method. This may be overriding your site.setSelectionProvider call.
Using:
#Override
public void init(IEditorSite site, IEditorInput input)
throws PartInitException {
super.init(site, input);
site.setSelectionProvider(this.selectionProvider);
}
should make sure your provider is the one used.
I would like to use a custom header with a TextBox (TextInputCell) in my CellTable.
I found this example and tried to adapt it:
GWT 2: how can I add Button to the CellTable's header?
public static class BtnHeader extends Header<String>{
public BtnHeader(ButtonCell cell) {
super(cell);
}
#Override
public void onBrowserEvent(Context context, Element elem, NativeEvent nativeEvent)
{
int eventType = Event.as(nativeEvent).getTypeInt();
if (eventType == Event.ONCLICK)
{
nativeEvent.preventDefault();
updateHeader();
}
}
#Override
public String getValue() {
return "Click!";
}
protected void updateHeader() {
// TODO to redefine in a defiant class
}
}
And in your code:
tnHeader header = new BtnHeader(new ButtonCell()){
#Override
protected void updateHeader(){
// Actions when clicking button
}
cTable.addColumn(column, header);
How can I adapt this to my use case ?
I tried the following:
public class TextBoxHeader extends Header<String> {
private String myCaption;
public TextBoxHeader(TextInputCell cell, String caption) {
super(cell);
myCaption = caption;
}
#Override
public String getValue() {
// TODO Auto-generated method stub
return myCaption;
}
protected void updateHeader() {
// TODO to redefine in a defiant class
}
}
The whole part with the onBrowserEvent is missing. How to implement it ?
The documentation (http://www.gwtproject.org/javadoc/latest/com/google/gwt/cell/client/TextInputCell.html) shows that onBrowserEvent is not protected. What to do now ?
Cheers,
Tim
Ok I found the solution. Instead of extending Header, I used a TextInputCell and put it in the Header. I then added a ValueUpdater to the header and I could react to the changes in the input field.
TextInputCell cell = new TextInputCell("Program");
com.google.gwt.user.cellview.client.Header<String> header = new com.google.gwt.user.cellview.client.Header<String>(cell) {
#Override
public String getValue() {
// TODO Auto-generated method stub
return "";
}
};
header.setUpdater(new ValueUpdater<String>() {
#Override
public void update(String value) {
}
});
Cheers,
Tim
I have two buttons(edit + delete) in one column.
ButtonCell functionButtonCell = new ButtonCell() {
#Override
public void render(final Context context, final SafeHtml data, final SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<button type='button' class='gwt-Button' style = 'width:60px;margin:1px;'>Edit</button>");
sb.appendHtmlConstant("<br/>");
sb.appendHtmlConstant("<button type='button' class='gwt-Button' style = 'width:60px;margin:1px;'>Delete</button>");
}
};
functionColumn = new Column<AdminModel, String>(functionButtonCell) {
public String getValue(final AdminModel object) {
return object.getSeq().toString();
}
};
Bind event for this column in Presenter as
.........
view.getFunctionColumn().setFieldUpdater(new FieldUpdater<AdminModel, String>() {
public void update(final int index, final AdminModel object, final String value) {
Window.alert(index + "-" + value);
}
});
After clicked on edit button , alert-box has appeared , but not on delete button. When I clicked on delete button , nothing has appeared. What would be the problem ?
Addition: How can I decide which button was clicked by user (edit or delete) from my presenter ?
I would really appreciate any of your suggestions because I am troubled on it for a long times. Thanks!
ButtonCell filters events on the first child element only: https://gwt.googlesource.com/gwt/+/2.6.1/user/src/com/google/gwt/cell/client/ButtonCell.java This is why you don't get an event when clicking the second button (note: the goal of that code is to make sure you clicked on the button, and not on blank space around the button; see https://gwt.googlesource.com/gwt/+/a0dc88c8be7408be9554f746eb1ec93798183a28)
The easiest way to implement a two-button cell is to use a CompositeCell; it requires that child cells are rendered into sibling elements though (uses <span>s by default, example below overrides the rendering to use <div>s so your buttons stack each on its own line).
new CompositeCell<AdminModel>(Arrays.asList(
// First button
new HasCell<AdminModel, String>() {
#Override public Cell<String> getCell() { return new ButtonCell(); }
#Override public FieldUpdated<AdminModel, String> getFieldUpdater() {
return new FieldUpdater<AdminModel, String>() {
#Override public void update(int index, AdminModel object, String value) {
Window.alert("Edit " + object.getId());
}
};
}
#Override public String getValue(AdminModel o) {
return "Edit";
}
},
// Second button
new HasCell<AdminModel, String>() {
#Override public Cell<String> getCell() { return new ButtonCell(); }
#Override public FieldUpdated<AdminModel, String> getFieldUpdater() {
return new FieldUpdater<AdminModel, String>() {
#Override public void update(int index, AdminModel object, String value) {
Window.alert("Delete " + object.getId());
}
};
}
#Override public String getValue(AdminModel o) {
return "Delete";
}
}) {
#Override protected <X> void render(Cell.Context context, AdminModel value, SafeHtmlBuilder sb, HasCell<String,X> hasCell) {
// use a <div> instead of the default <span>
Cell<X> cell = hasCell.getCell();
sb.appendHtmlConstant("<div>");
cell.render(context, hasCell.getValue(value), sb);
sb.appendHtmlConstant("</div>");
}
};
(note: in your case, because the button's text doesn't depend on the row object, maybe you should rather use an ActionCell; it would better fit "semantically" with what you're doing, but otherwise it's almost the same; with an ActionCell, you'd use HasCell<AdminModel, AdminModel>, ActionCell<AdminModel>, getFieldUpdater would return null, and thegetValueof theHasCellwould just return theAdminModel` argument as-is).
Otherwise, implement your Cell (or AbstractCell) entirely by yourself.
Ideally, a column should have only one type of cell be it ImageCell, ButtonCell etc. Because all this ImageCell and ButtonCell does not provide any in-built events. The events are handled by FieldUpdater itself which does not have differentiators to identify that which ButtonCell is clicked. Ideally on click of that column, the field-updater will be called.
You should rather create your own composite widget which extends HasCell. This composite widget will have two different buttons and those in built methods are called on click of respective button.
public void onModuleLoad() {
CellTable<Person> table = new CellTable<Person>();
List<HasCell<Person, ?>> cells = new LinkedList<HasCell<Person, ?>>();
cells.add(new ActionHasCell("Edit", new Delegate<Person>() {
#Override
public void execute(Person object) {
// EDIT CODE
}
}));
cells.add(new ActionHasCell("Delete", new Delegate<Person>() {
#Override
public void execute(Person object) {
// DELETE CODE
}
}));
CompositeCell<Person> cell = new CompositeCell<Person>(cells);
table.addColumn(new TextColumn<Person>() {
#Override
public String getValue(Person object) {
return object.getName()
}
}, "Name");
// ADD Cells for Age and Address
table.addColumn(new Column<Person, Person>(cell) {
#Override
public Person getValue(Person object) {
return object;
}
}, "Actions");
}
private class ActionHasCell implements HasCell<Person, Person> {
private ActionCell<Person> cell;
public ActionHasCell(String text, Delegate<Person> delegate) {
cell = new ActionCell<Person>(text, delegate);
}
#Override
public Cell<Person> getCell() {
return cell;
}
#Override
public FieldUpdater<Person, Person> getFieldUpdater() {
return null;
}
#Override
public Person getValue(Person object) {
return object;
}
}
Also, see the link below.
[GWT CellTable-Need to have two buttons in last single cell of each row
I am trying to figure out how to propagate events for components inside google maps InfoWindow.
I create anchor or a button and want to handle click event on any of those.
I have found solutions described here
and
here
but those both are using google maps wrappers for gwt.
I would like to avoid those libraries.
QUESTION:
Do you know any way how can I propagate those events from info window to some GWT panel which wraps google maps?
Based on code found here:
http://gwt-maps3.googlecode.com/svn/trunk/src/com/googlecode/maps3/client/
I have created this class that solves problem with using no external library (you have to take Only InfoWindowJSO source from link given)
And then instead passing InnerHtml as string to setContent... you just pass Widget element.
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Widget;
public class InfoWindow
{
static class FakePanel extends ComplexPanel
{
public FakePanel(Widget w)
{
w.removeFromParent();
getChildren().add(w);
adopt(w);
}
#Override
public boolean isAttached()
{
return true;
}
public void detachWidget()
{
this.remove(0);
}
}
/** */
InfoWindowJSO jso;
/** If we have a widget, this will exist so we can detach later */
FakePanel widgetAttacher;
/** Keep track of this so we can get it again later */
Widget widgetContent;
/** */
public InfoWindow()
{
this.jso = InfoWindowJSO.newInstance();
}
/** */
public InfoWindow(InfoWindowOptions opts)
{
this.jso = InfoWindowJSO.newInstance(opts);
}
/** Detaches the handler and closes */
public void close()
{
this.detachWidget();
this.jso.close();
}
/** Detaches the content widget, if it exists */
private void detachWidget()
{
if (this.widgetAttacher != null)
{
this.widgetAttacher.detachWidget();
this.widgetAttacher = null;
}
}
/** */
public void open(JavaScriptObject map)
{
this.jso.open(map);
}
public void open(JavaScriptObject map, JavaScriptObject marker)
{
this.jso.open(map, marker);
}
/** */
public void setOptions(InfoWindowOptions value)
{
this.jso.setOptions(value);
}
/** */
public void setContent(String value)
{
this.widgetContent = null;
this.detachWidget();
this.jso.setContent(value);
}
/** */
public void setContent(Element value)
{
this.widgetContent = null;
this.detachWidget();
this.jso.setContent(value);
}
/** */
public void setContent(Widget value)
{
this.widgetContent = value;
this.detachWidget();
this.jso.setContent(value.getElement());
if (this.widgetAttacher == null)
{
// Add a hook for the close button click
this.jso.addListener("closeclick", new Runnable() {
#Override
public void run()
{
detachWidget();
}
});
this.widgetAttacher = new FakePanel(value);
}
else if (this.widgetAttacher.getWidget(0) != value)
{
this.widgetAttacher.detachWidget();
this.widgetAttacher = new FakePanel(value);
}
}
/** #return the widget, if a widget was set */
public Widget getContentWidget()
{
return this.widgetContent;
}
/** */
public JavaScriptObject getPosition()
{
return this.jso.getPosition();
}
/** */
public void setPosition(JavaScriptObject value)
{
this.jso.setPosition(value);
}
/** */
public int getZIndex()
{
return this.jso.getZIndex();
}
/** */
public void setZIndex(int value)
{
this.jso.setZIndex(value);
}
/** */
public void addListener(String whichEvent, Runnable handler)
{
this.jso.addListener(whichEvent, handler);
}
}
A. Browser events bubble all the way to the top of the DOM tree. You can attach your click handlers to a widget that is parent to both the maps InfoWindow and your widget. Then, when a user clicks on your button, you need to check for the source of event to make sure it came from your button.
public void onClick(final ClickEvent event) {
Element e = Element.as(event.getNativeEvent().getEventTarget());
// check if e is your button
}
B. You can create a regular GWT button, attach a ClickHandler to it. Do not put it inside the InfoWindow: place it on top it using absolute positioning and a higher z-index.
I use the static value nextAnchorId to uniquely generate IDs for each InfoWindow, and when the InfoWindow is ready (usually when you call infoWindow.open(map);), I get the anchor by element ID and add my click handler to it. This is kind of what Manolo is doing, but this implementation doesn't require gwtquery, which means that I can run my code in Super Dev Mode.
private static int nextAnchorId = 1;
public InfoWindow makeInfo() {
InfoWindowOptions infoWindowOptions = InfoWindowOptions.create();
FlowPanel infoContentWidget = new FlowPanel();
final String theAnchorId_str = "theAnchor" + nextAnchorId;
HTML theAnchor = new HTML("<a id=\"" + theAnchorId_str + "\">Click me!</a>");
infoContentWidget.add(theAnchor);
infoWindowOptions.setContent(infoContentWidget.getElement());
InfoWindow infoWindow = InfoWindow.create(infoWindowOptions);
infoWindow.addDomReadyListenerOnce(new InfoWindow.DomReadyHandler() {
#Override
public void handle() {
com.google.gwt.user.client.Element muffinButton = (com.google.gwt.user.client.Element) Document.get().getElementById(theAnchorId_str);
DOM.sinkEvents(muffinButton, Event.ONCLICK);
DOM.setEventListener(muffinButton, new EventListener() {
#Override
public void onBrowserEvent(Event event) {
Window.alert("You clicked on the anchor!");
// This is where your click handling for the link goes.
}
});
}
});
nextAnchorId++;
return infoWindow
}
A very simple solution is to use gwtquery:
Identify the anchor in the map you want to add the click handler and define a css selector for that (for instance id=my_link)
Use gquery to locate it and to add the event.
$('#my_link').click(new Function() {
public boolean f(Event e) {
[...]
return false; //false means stop propagation and prevent default
}
});
Note that gwtquery is not a wrapper of jquery but an entire gwt implementation of its api, so including it in your project will not overload it, and the compiler will pick up just the stuff you use.
I have this button cell in my CellTable
ButtonCell reListCell = new ButtonCell();
reListColumn = new Column<EmployerJobs, String>(reListCell) {
#Override
public String getValue(EmployerJobs object) {
return "ReList";
}
};
ctJobs.addColumn(reListColumn,
EmployerDashBoardConstants.EMPTYHEADERCOLUMN);
but i only want this cell to be appear if the below condition pass
public void getDateDiff(final EmployerJobs object) {
rpcService.getDateDiff(object.getJobValidDate(), new AsyncCallback<Boolean>() {
public void onFailure(Throwable caught) {
}
public void onSuccess(Boolean jobExpired) {
if(jobExpired) {
// HERE I WANT TO SHOW MY RELISTCELL, means if the job is expired only then
// there will be a button showing relist would be appear in that row ,for
// the jobs which are not expired NO button should appear..
}
}
});
}
how can i achieve this?
thanks
I agree with DTing.
Quering the backend for each cell/row is not really efficient.
I would rather put the info (jobExpired) into your EmployerJobs class and transfer the info when you request the list of your EmployerJobs to be displayed in your CellTable.
You can update the list periodically to account for changes (see the expenses sample on how to do that).
But to your initial question (hiding the cell). There are two solutions:
Use an ActionCell and override the render method.
ActionCell:
ActionCell<EmployerJobs> reListCell = new ActionCell<EmployerJobs>("ReList",
new ActionCell.Delegate<EmployerJobs>() {
#Override
public void execute(EmployerJobs object) {
// code to be executed
}
})
{
#Override
public void render(Cell.Context context,EmployerJobs value,SafeHtmlBuilder sb) {
if (value.isJobExpired()) // isJobExpired returns the field jobExpired.
super.render(context,value,sb);
}
};
reListColumn = new Column<EmployerJobs, EmployerJobs>(reListCell) {
#Override
public String getValue(EmployerJobs object) {
return object;
}
};
ctJobs.addColumn(reListColumn,
EmployerDashBoardConstants.EMPTYHEADERCOLUMN);
Use a ButtonCell and override the render method of your Column.
ButtonCell:
ButtonCell reListCell = new ButtonCell();
reListColumn = new Column<EmployerJobs, String>(reListCell) {
#Override
public String getValue(EmployerJobs object) {
return "ReList";
}
#Override
public void render(Cell.Context context,EmployerJobs object,SafeHtmlBuilder sb) {
if (value.isJobExpired()) // isJobExpired returns the field jobExpired.
super.render(context,value,sb);
}
};
ctJobs.addColumn(reListColumn,
EmployerDashBoardConstants.EMPTYHEADERCOLUMN);
Just tried Umit solution #2 ButtonCell. It works!
To link an specific action to the button, reListColumn.setFieldUpdater(new FieldUpdater....
would be needed
I tried ButtonCell solution too. But if you click in a cell who as no button then an error on client side occur:
com.google.gwt.core.client.JavaScriptException: (TypeError) #com.google.gwt.core.client.impl.Impl::apply(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)([JavaScript object(445), JavaScript object(240), JavaScript object(637)]): parent is null
So I added this to override the Event and avoid Event if I want:
#Override
public void onBrowserEvent(com.google.gwt.cell.client.Cell.Context context,
Element parent, YourObject object, NativeEvent event) {
if (object.isCompleted())
super.onBrowserEvent( context, parent, object, event);
}
I don't know if it's the better way to do it but it works.