diff options
author | Matti Tahvonen <matti.tahvonen@itmill.com> | 2008-08-29 10:08:29 +0000 |
---|---|---|
committer | Matti Tahvonen <matti.tahvonen@itmill.com> | 2008-08-29 10:08:29 +0000 |
commit | 40cbef33e1c69f1c912d6a21968bcc8455476289 (patch) | |
tree | 714c3f9acdf85863c97b7d6821f618993e474463 | |
parent | e9d29d5cf3cb2f5ab3606692afc07be27f1810f4 (diff) | |
download | vaadin-framework-40cbef33e1c69f1c912d6a21968bcc8455476289.tar.gz vaadin-framework-40cbef33e1c69f1c912d6a21968bcc8455476289.zip |
#2009, ItemClickEvents
svn changeset:5298/svn branch:trunk
9 files changed, 639 insertions, 112 deletions
diff --git a/src/com/itmill/toolkit/event/ItemClickEvent.java b/src/com/itmill/toolkit/event/ItemClickEvent.java new file mode 100644 index 0000000000..782aa990ca --- /dev/null +++ b/src/com/itmill/toolkit/event/ItemClickEvent.java @@ -0,0 +1,158 @@ +package com.itmill.toolkit.event; + +import java.lang.reflect.Method; + +import com.itmill.toolkit.data.Container; +import com.itmill.toolkit.data.Item; +import com.itmill.toolkit.data.Property; +import com.itmill.toolkit.terminal.gwt.client.MouseEventDetails; +import com.itmill.toolkit.ui.Component; +import com.itmill.toolkit.ui.Component.Event; + +/** + * + * Click event fired by a {@link Component} implementing + * {@link com.itmill.toolkit.data.Container} interface. ItemClickEvents happens + * on an {@link Item} rendered somehow on terminal. Event may also contain a + * specific {@link Property} on which the click event happened. + * + * ClickEvents are rather terminal dependent events. Correct values in event + * details cannot be guaranteed. + * + * EXPERIMENTAL FEATURE, user input is welcome + * + * @since 5.3 + * + * TODO extract generic super class/interfaces if we implement some other click + * events. + */ +public class ItemClickEvent extends Event { + public static final int BUTTON_LEFT = MouseEventDetails.BUTTON_LEFT; + public static final int BUTTON_MIDDLE = MouseEventDetails.BUTTON_MIDDLE; + public static final int BUTTON_RIGHT = MouseEventDetails.BUTTON_RIGHT; + + private MouseEventDetails details; + private Item item; + private Object itemId; + private Object propertyId; + + public ItemClickEvent(Component source, Item item, Object itemId, + Object propertyId, MouseEventDetails details) { + super(source); + this.details = details; + this.item = item; + this.itemId = itemId; + this.propertyId = propertyId; + } + + /** + * Gets the item on which the click event occurred. + * + * @return item which was clicked + */ + public Item getItem() { + return item; + } + + /** + * Gets a possible identifier in source for clicked Item + * + * @return + */ + public Object getItemId() { + return itemId; + } + + /** + * Returns property on which click event occurred. Returns null if source + * cannot be resolved at property leve. For example if clicked a cell in + * table, the "column id" is returned. + * + * @return a property id of clicked property or null if click didn't occur + * on any distinct property. + */ + public Object getPropertyId() { + return propertyId; + } + + public int getButton() { + return details.getButton(); + } + + public int getClientX() { + return details.getClientX(); + } + + public int getClientY() { + return details.getClientY(); + } + + public boolean isDoubleClick() { + return details.isDoubleClick(); + } + + public boolean isAltKey() { + return details.isAltKey(); + } + + public boolean isCtrlKey() { + return details.isCtrlKey(); + } + + public boolean isMetaKey() { + return details.isMetaKey(); + } + + public boolean isShiftKey() { + return details.isShiftKey(); + } + + /** + * Serial generated by eclipse + */ + private static final long serialVersionUID = 3576399524236787971L; + + public static final Method ITEM_CLICK_METHOD; + + static { + try { + ITEM_CLICK_METHOD = ItemClickListener.class.getDeclaredMethod( + "itemClick", new Class[] { ItemClickEvent.class }); + } catch (final java.lang.NoSuchMethodException e) { + // This should never happen + throw new java.lang.RuntimeException(); + } + } + + public interface ItemClickListener { + public void itemClick(ItemClickEvent event); + } + + /** + * Components implementing + * + * @link {@link Container} interface may support emitting + * {@link ItemClickEvent}s. + */ + public interface ItemClickSource { + /** + * Register listener to handle ItemClickEvents. + * + * Note! Click listeners are rather terminal dependent features. + * + * This feature is EXPERIMENTAL + * + * @param listener + * ItemClickListener to be registered + */ + public void addListener(ItemClickListener listener); + + /** + * Removes ItemClickListener. + * + * @param listener + */ + public void removeListener(ItemClickListener listener); + } + +} diff --git a/src/com/itmill/toolkit/terminal/gwt/client/MouseEventDetails.java b/src/com/itmill/toolkit/terminal/gwt/client/MouseEventDetails.java new file mode 100644 index 0000000000..8d91863090 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/client/MouseEventDetails.java @@ -0,0 +1,92 @@ +package com.itmill.toolkit.terminal.gwt.client; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Event; + +/** + * Helper class to store and transfer mouse event details. + */ +public class MouseEventDetails { + public static final int BUTTON_LEFT = Event.BUTTON_LEFT; + public static final int BUTTON_MIDDLE = Event.BUTTON_MIDDLE; + public static final int BUTTON_RIGHT = Event.BUTTON_RIGHT; + + private static final char DELIM = ','; + + private int button; + private int clientX; + private int clientY; + private boolean altKey; + private boolean ctrlKey; + private boolean metaKey; + private boolean shiftKey; + private int type; + + public int getButton() { + return button; + } + + public int getClientX() { + return clientX; + } + + public int getClientY() { + return clientY; + } + + public boolean isAltKey() { + return altKey; + } + + public boolean isCtrlKey() { + return ctrlKey; + } + + public boolean isMetaKey() { + return metaKey; + } + + public boolean isShiftKey() { + return shiftKey; + } + + public MouseEventDetails(Event evt) { + button = DOM.eventGetButton(evt); + clientX = DOM.eventGetClientX(evt); + clientY = DOM.eventGetClientY(evt); + altKey = DOM.eventGetAltKey(evt); + ctrlKey = DOM.eventGetCtrlKey(evt); + metaKey = DOM.eventGetMetaKey(evt); + shiftKey = DOM.eventGetShiftKey(evt); + type = DOM.eventGetType(evt); + } + + private MouseEventDetails() { + } + + public String toString() { + return "" + button + DELIM + clientX + DELIM + clientY + DELIM + altKey + + DELIM + ctrlKey + DELIM + metaKey + DELIM + shiftKey + DELIM + + type; + } + + public static MouseEventDetails deSerialize(String serializedString) { + MouseEventDetails instance = new MouseEventDetails(); + String[] fields = serializedString.split(","); + + instance.button = Integer.parseInt(fields[0]); + instance.clientX = Integer.parseInt(fields[1]); + instance.clientY = Integer.parseInt(fields[2]); + instance.altKey = Boolean.valueOf(fields[3]).booleanValue(); + instance.ctrlKey = Boolean.valueOf(fields[4]).booleanValue(); + instance.metaKey = Boolean.valueOf(fields[5]).booleanValue(); + instance.shiftKey = Boolean.valueOf(fields[6]).booleanValue(); + instance.type = Integer.parseInt(fields[7]); + return instance; + } + + public boolean isDoubleClick() { + return type == Event.ONDBLCLICK; + } + +} diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IOrderedLayout.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IOrderedLayout.java index 71258c192e..b1e9f5d919 100644 --- a/src/com/itmill/toolkit/terminal/gwt/client/ui/IOrderedLayout.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/IOrderedLayout.java @@ -117,8 +117,8 @@ public class IOrderedLayout extends Panel implements Container, * <p> * There are two modes - vertical and horizontal. * <ul> - * <li>Vertical mode uses structure: div-root ( div-wrap ( child ) div-wrap - * ( child ))).</li> + * <li>Vertical mode uses structure: div-root ( div-wrap ( child ) div-wrap ( + * child ))).</li> * <li>Horizontal mode uses structure: table ( tbody ( tr-childcontainer ( * td-wrap ( child ) td-wrap ( child) )) )</li> * </ul> @@ -574,7 +574,7 @@ public class IOrderedLayout extends Panel implements Container, * without letting root element to affect the calculation. * * @param offset - * offsetWidth or offsetHeight + * offsetWidth or offsetHeight */ private int rootOffsetMeasure(String offset) { // TODO This method must be optimized! diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IPanel.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IPanel.java index 37fb6feca8..3493617722 100644 --- a/src/com/itmill/toolkit/terminal/gwt/client/ui/IPanel.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/IPanel.java @@ -302,8 +302,6 @@ public class IPanel extends SimplePanel implements Paintable, // Restore content to flow if (hasChildren) { - ApplicationConnection.getConsole().log( - "positioning:" + origPositioning); DOM.setStyleAttribute(contentEl, "position", origPositioning); } // restore scroll position diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IScrollTable.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IScrollTable.java index 25209a1431..6dab67ccd6 100644 --- a/src/com/itmill/toolkit/terminal/gwt/client/ui/IScrollTable.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/IScrollTable.java @@ -27,6 +27,7 @@ import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.Widget; import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection; import com.itmill.toolkit.terminal.gwt.client.ContainerResizedListener; +import com.itmill.toolkit.terminal.gwt.client.MouseEventDetails; import com.itmill.toolkit.terminal.gwt.client.Paintable; import com.itmill.toolkit.terminal.gwt.client.UIDL; import com.itmill.toolkit.terminal.gwt.client.Util; @@ -128,6 +129,7 @@ public class IScrollTable extends Composite implements Table, ScrollListener, * for container element. Then this value is used as a fallback. */ private int oldAvailPixels; + private boolean emitClickEvents; public IScrollTable() { @@ -154,6 +156,7 @@ public class IScrollTable extends Composite implements Table, ScrollListener, this.client = client; paintableId = uidl.getStringAttribute("id"); immediate = uidl.getBooleanAttribute("immediate"); + emitClickEvents = uidl.getBooleanAttribute("listenClicks"); final int newTotalRows = uidl.getIntAttribute("totalrows"); if (newTotalRows != totalRows) { if (tBody != null) { @@ -1942,7 +1945,7 @@ public class IScrollTable extends Composite implements Table, ScrollListener, private IScrollTableRow(int rowKey) { this.rowKey = rowKey; setElement(DOM.createElement("tr")); - DOM.sinkEvents(getElement(), Event.ONCLICK); + DOM.sinkEvents(getElement(), Event.ONCLICK | Event.ONDBLCLICK); attachContextMenuEvent(getElement()); } @@ -2084,26 +2087,55 @@ public class IScrollTable extends Composite implements Table, ScrollListener, return false; } + private void handleClickEvent(Event event) { + if (emitClickEvents) { + boolean dbl = DOM.eventGetType(event) == Event.ONDBLCLICK; + final Element tdOrTr = DOM.getParent(DOM + .eventGetTarget(event)); + client.updateVariable(paintableId, "clickedKey", "" + + rowKey, false); + if (DOM.compare(getElement(), DOM.getParent(tdOrTr))) { + int childIndex = DOM + .getChildIndex(getElement(), tdOrTr); + String colKey = null; + colKey = tHead.getHeaderCell(childIndex).getColKey(); + client.updateVariable(paintableId, "clickedColKey", + colKey, false); + } + MouseEventDetails details = new MouseEventDetails(event); + client + .updateVariable( + paintableId, + "clickEvent", + details.toString(), + !(!dbl + && selectMode > Table.SELECT_MODE_NONE && immediate)); + } + } + /* * React on click that occur on content cells only */ public void onBrowserEvent(Event event) { - switch (DOM.eventGetType(event)) { - case Event.ONCLICK: - final Element tdOrTr = DOM.getParent(DOM - .eventGetTarget(event)); - if (DOM.compare(getElement(), tdOrTr) - || DOM.compare(getElement(), DOM.getParent(tdOrTr))) { + final Element tdOrTr = DOM.getParent(DOM.eventGetTarget(event)); + if (DOM.compare(getElement(), tdOrTr) + || DOM.compare(getElement(), DOM.getParent(tdOrTr))) { + switch (DOM.eventGetType(event)) { + case Event.ONCLICK: + handleClickEvent(event); if (selectMode > Table.SELECT_MODE_NONE) { toggleSelection(); client.updateVariable(paintableId, "selected", selectedRowKeys.toArray(), immediate); } - } - break; + break; + case Event.ONDBLCLICK: + handleClickEvent(event); + break; - default: - break; + default: + break; + } } super.onBrowserEvent(event); } diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/ITree.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/ITree.java index 4615d3990f..08f3930799 100644 --- a/src/com/itmill/toolkit/terminal/gwt/client/ui/ITree.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/ITree.java @@ -16,6 +16,7 @@ import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.SimplePanel; import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection; +import com.itmill.toolkit.terminal.gwt.client.MouseEventDetails; import com.itmill.toolkit.terminal.gwt.client.Paintable; import com.itmill.toolkit.terminal.gwt.client.UIDL; import com.itmill.toolkit.terminal.gwt.client.Util; @@ -49,6 +50,8 @@ public class ITree extends FlowPanel implements Paintable { private boolean readonly; + private boolean emitClickEvents; + public ITree() { super(); setStyleName(CLASSNAME); @@ -97,6 +100,7 @@ public class ITree extends FlowPanel implements Paintable { disabled = uidl.getBooleanAttribute("disabled"); readonly = uidl.getBooleanAttribute("readonly"); + emitClickEvents = uidl.getBooleanAttribute("listenClicks"); isNullSelectionAllowed = uidl.getBooleanAttribute("nullselect"); @@ -189,7 +193,7 @@ public class ITree extends FlowPanel implements Paintable { public TreeNode() { constructDom(); - sinkEvents(Event.ONCLICK); + sinkEvents(Event.ONCLICK | Event.ONDBLCLICK | Event.ONMOUSEUP); } public void onBrowserEvent(Event event) { @@ -197,14 +201,19 @@ public class ITree extends FlowPanel implements Paintable { if (disabled) { return; } - if (DOM.eventGetType(event) == Event.ONCLICK) { - final Element target = DOM.eventGetTarget(event); + final int type = DOM.eventGetType(event); + final Element target = DOM.eventGetTarget(event); + if (emitClickEvents && DOM.compare(target, nodeCaptionSpan) + && (type == Event.ONDBLCLICK || type == Event.ONMOUSEUP)) { + fireClick(event); + } + if (type == Event.ONCLICK) { if (DOM.compare(getElement(), target) || DOM.compare(ie6compatnode, target)) { // state change toggleState(); } else if (!readonly && DOM.compare(target, nodeCaptionSpan)) { - // caption click = selection change + // caption click = selection change && possible click event toggleSelection(); } DOM.eventCancelBubble(event, true); @@ -215,6 +224,13 @@ public class ITree extends FlowPanel implements Paintable { } + private void fireClick(Event evt) { + MouseEventDetails details = new MouseEventDetails(evt); + client.updateVariable(paintableId, "clickedKey", key, false); + client.updateVariable(paintableId, "clickEvent", + details.toString(), !(selectable && immediate)); + } + private void toggleSelection() { if (selectable) { ITree.this.setSelected(this, !isSelected()); diff --git a/src/com/itmill/toolkit/tests/tickets/Ticket2009.java b/src/com/itmill/toolkit/tests/tickets/Ticket2009.java new file mode 100644 index 0000000000..f9eebb02b7 --- /dev/null +++ b/src/com/itmill/toolkit/tests/tickets/Ticket2009.java @@ -0,0 +1,125 @@ +package com.itmill.toolkit.tests.tickets;
+
+import com.itmill.toolkit.data.Container;
+import com.itmill.toolkit.event.ItemClickEvent;
+import com.itmill.toolkit.tests.TestForTablesInitialColumnWidthLogicRendering;
+import com.itmill.toolkit.ui.Button;
+import com.itmill.toolkit.ui.Label;
+import com.itmill.toolkit.ui.OrderedLayout;
+import com.itmill.toolkit.ui.Panel;
+import com.itmill.toolkit.ui.Table;
+import com.itmill.toolkit.ui.TextField;
+import com.itmill.toolkit.ui.Tree;
+import com.itmill.toolkit.ui.Window;
+import com.itmill.toolkit.ui.Button.ClickEvent;
+
+public class Ticket2009 extends com.itmill.toolkit.Application {
+
+ TextField f = new TextField();
+
+ public void init() {
+ final Window main = new Window(getClass().getName().substring(
+ getClass().getName().lastIndexOf(".") + 1));
+ setMainWindow(main);
+
+ OrderedLayout ol = new OrderedLayout(
+ OrderedLayout.ORIENTATION_HORIZONTAL);
+ main.setLayout(ol);
+ ol.setSizeFull();
+
+ Panel p = new Panel("Tree test");
+ p.setSizeFull();
+
+ Tree t = new Tree();
+
+ t.addItem("Foo");
+ t.addItem("Bar");
+
+ final OrderedLayout events = new OrderedLayout();
+
+ t.addListener(new ItemClickEvent.ItemClickListener() {
+ public void itemClick(ItemClickEvent event) {
+ events.addComponent(new Label(new Label("Click:"
+ + (event.isDoubleClick() ? "double" : "single")
+ + " button:" + event.getButton() + " propertyId:"
+ + event.getPropertyId() + " itemID:"
+ + event.getItemId() + " item:" + event.getItem())));
+
+ }
+ });
+
+ main.addComponent(p);
+ p.addComponent(t);
+ p.addComponent(events);
+
+ Panel p2 = new Panel("Table test (try dbl click also)");
+ p2.setSizeFull();
+
+ final OrderedLayout events2 = new OrderedLayout();
+ Table table = TestForTablesInitialColumnWidthLogicRendering
+ .getTestTable(5, 100);
+ table.setRowHeaderMode(Table.ROW_HEADER_MODE_ID);
+ table.addListener(new ItemClickEvent.ItemClickListener() {
+ public void itemClick(ItemClickEvent event) {
+ events2.addComponent(new Label("Click:"
+ + (event.isDoubleClick() ? "double" : "single")
+ + " button:" + event.getButton() + " propertyId:"
+ + event.getPropertyId() + " itemID:"
+ + event.getItemId() + " item:" + event.getItem()));
+ if (event.isDoubleClick()) {
+ new PropertyEditor(event);
+ }
+
+ }
+ });
+ p2.addComponent(table);
+ p2.addComponent(events2);
+
+ main.addComponent(p2);
+
+ }
+
+ class PropertyEditor extends Window {
+
+ private static final int W = 300;
+ private static final int H = 150;
+
+ private Container c;
+ private Object itemid;
+ private Object propertyid;
+
+ TextField editor = new TextField();
+ Button done = new Button("Done");
+
+ PropertyEditor(ItemClickEvent event) {
+ c = (Container) event.getSource();
+
+ propertyid = event.getPropertyId();
+ itemid = event.getItemId();
+
+ setCaption("Editing " + itemid + " : " + propertyid);
+
+ editor.setPropertyDataSource(c.getContainerProperty(itemid,
+ propertyid));
+ addComponent(editor);
+ addComponent(done);
+
+ setWidth(W);
+ setHeight(H);
+
+ setPositionX(event.getClientX() - W / 2);
+ setPositionY(event.getClientY() - H / 2);
+
+ getMainWindow().addWindow(this);
+
+ done.addListener(new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ getMainWindow().removeWindow(PropertyEditor.this);
+ }
+ });
+
+ }
+
+ }
+
+}
\ No newline at end of file diff --git a/src/com/itmill/toolkit/ui/Table.java b/src/com/itmill/toolkit/ui/Table.java index a8b660554f..1a5fb10a67 100644 --- a/src/com/itmill/toolkit/ui/Table.java +++ b/src/com/itmill/toolkit/ui/Table.java @@ -21,10 +21,14 @@ import com.itmill.toolkit.data.Property; import com.itmill.toolkit.data.util.ContainerOrderedWrapper; import com.itmill.toolkit.data.util.IndexedContainer; import com.itmill.toolkit.event.Action; +import com.itmill.toolkit.event.ItemClickEvent; +import com.itmill.toolkit.event.ItemClickEvent.ItemClickListener; +import com.itmill.toolkit.event.ItemClickEvent.ItemClickSource; import com.itmill.toolkit.terminal.KeyMapper; import com.itmill.toolkit.terminal.PaintException; import com.itmill.toolkit.terminal.PaintTarget; import com.itmill.toolkit.terminal.Resource; +import com.itmill.toolkit.terminal.gwt.client.MouseEventDetails; /** * <p> @@ -44,7 +48,7 @@ import com.itmill.toolkit.terminal.Resource; * @since 3.0 */ public class Table extends AbstractSelect implements Action.Container, - Container.Ordered, Container.Sortable { + Container.Ordered, Container.Sortable, ItemClickSource { private static final int CELL_KEY = 0; @@ -306,6 +310,8 @@ public class Table extends AbstractSelect implements Action.Container, */ private CellStyleGenerator cellStyleGenerator = null; + private int clickListenerCount; + /* Table constructors */ /** @@ -364,7 +370,7 @@ public class Table extends AbstractSelect implements Action.Container, * </p> * * @param visibleColumns - * the Array of shown property id:s. + * the Array of shown property id:s. */ public void setVisibleColumns(Object[] visibleColumns) { @@ -461,8 +467,8 @@ public class Table extends AbstractSelect implements Action.Container, * </p> * * @param columnHeaders - * the Array of column headers that match the - * <code>getVisibleColumns</code> method. + * the Array of column headers that match the + * <code>getVisibleColumns</code> method. */ public void setColumnHeaders(String[] columnHeaders) { @@ -521,8 +527,8 @@ public class Table extends AbstractSelect implements Action.Container, * </p> * * @param columnIcons - * the Array of icons that match the - * <code>getVisibleColumns</code>. + * the Array of icons that match the + * <code>getVisibleColumns</code>. */ public void setColumnIcons(Resource[] columnIcons) { @@ -548,8 +554,8 @@ public class Table extends AbstractSelect implements Action.Container, * * <p> * The items in the array must match the properties identified by - * <code>getVisibleColumns()</code>. The possible values for the alignments - * include: + * <code>getVisibleColumns()</code>. The possible values for the + * alignments include: * <ul> * <li><code>ALIGN_LEFT</code>: Left alignment</li> * <li><code>ALIGN_CENTER</code>: Centered</li> @@ -579,8 +585,8 @@ public class Table extends AbstractSelect implements Action.Container, * * <p> * The items in the array must match the properties identified by - * <code>getVisibleColumns()</code>. The possible values for the alignments - * include: + * <code>getVisibleColumns()</code>. The possible values for the + * alignments include: * <ul> * <li><code>ALIGN_LEFT</code>: Left alignment</li> * <li><code>ALIGN_CENTER</code>: Centered</li> @@ -590,7 +596,7 @@ public class Table extends AbstractSelect implements Action.Container, * </p> * * @param columnAlignments - * the Column alignments array. + * the Column alignments array. */ public void setColumnAlignments(String[] columnAlignments) { @@ -629,9 +635,9 @@ public class Table extends AbstractSelect implements Action.Container, * will make decision of width. * * @param columnId - * colunmns property id + * colunmns property id * @param width - * width to be reserved for colunmns content + * width to be reserved for colunmns content * @since 4.0.3 */ public void setColumnWidth(Object columnId, int width) { @@ -673,7 +679,7 @@ public class Table extends AbstractSelect implements Action.Container, * </p> * * @param pageLength - * the Length of one page. + * the Length of one page. */ public void setPageLength(int pageLength) { if (pageLength >= 0 && this.pageLength != pageLength) { @@ -715,7 +721,7 @@ public class Table extends AbstractSelect implements Action.Container, * Setter for property currentPageFirstItemId. * * @param currentPageFirstItemId - * the New value of property currentPageFirstItemId. + * the New value of property currentPageFirstItemId. */ public void setCurrentPageFirstItemId(Object currentPageFirstItemId) { @@ -753,7 +759,7 @@ public class Table extends AbstractSelect implements Action.Container, * Gets the icon Resource for the specified column. * * @param propertyId - * the propertyId indentifying the column. + * the propertyId indentifying the column. * @return the icon for the specified column; null if the column has no icon * set, or if the column is not visible. */ @@ -768,9 +774,9 @@ public class Table extends AbstractSelect implements Action.Container, * </p> * * @param propertyId - * the propertyId identifying the column. + * the propertyId identifying the column. * @param icon - * the icon Resource to set. + * the icon Resource to set. */ public void setColumnIcon(Object propertyId, Resource icon) { @@ -789,7 +795,7 @@ public class Table extends AbstractSelect implements Action.Container, * Gets the header for the specified column. * * @param propertyId - * the propertyId indentifying the column. + * the propertyId indentifying the column. * @return the header for the specifed column if it has one. */ public String getColumnHeader(Object propertyId) { @@ -810,9 +816,9 @@ public class Table extends AbstractSelect implements Action.Container, * Sets the column header for the specified column; * * @param propertyId - * the propertyId indentifying the column. + * the propertyId indentifying the column. * @param header - * the header to set. + * the header to set. */ public void setColumnHeader(Object propertyId, String header) { @@ -830,7 +836,7 @@ public class Table extends AbstractSelect implements Action.Container, * Gets the specified column's alignment. * * @param propertyId - * the propertyID identifying the column. + * the propertyID identifying the column. * @return the specified column's alignment if it as one; null otherwise. */ public String getColumnAlignment(Object propertyId) { @@ -847,9 +853,9 @@ public class Table extends AbstractSelect implements Action.Container, * </p> * * @param propertyId - * the propertyID identifying the column. + * the propertyID identifying the column. * @param alignment - * the desired alignment. + * the desired alignment. */ public void setColumnAlignment(Object propertyId, String alignment) { @@ -876,7 +882,7 @@ public class Table extends AbstractSelect implements Action.Container, * Checks if the specified column is collapsed. * * @param propertyId - * the propertyID identifying the column. + * the propertyID identifying the column. * @return true if the column is collapsed; false otherwise; */ public boolean isColumnCollapsed(Object propertyId) { @@ -889,9 +895,9 @@ public class Table extends AbstractSelect implements Action.Container, * * * @param propertyId - * the propertyID identifying the column. + * the propertyID identifying the column. * @param collapsed - * the desired collapsedness. + * the desired collapsedness. * @throws IllegalAccessException */ public void setColumnCollapsed(Object propertyId, boolean collapsed) @@ -924,7 +930,7 @@ public class Table extends AbstractSelect implements Action.Container, * Sets whether column collapsing is allowed or not. * * @param collapsingAllowed - * specifies whether column collapsing is allowed. + * specifies whether column collapsing is allowed. */ public void setColumnCollapsingAllowed(boolean collapsingAllowed) { columnCollapsingAllowed = collapsingAllowed; @@ -950,7 +956,7 @@ public class Table extends AbstractSelect implements Action.Container, * Sets whether column reordering is allowed or not. * * @param reorderingAllowed - * specifies whether column reordering is allowed. + * specifies whether column reordering is allowed. */ public void setColumnReorderingAllowed(boolean reorderingAllowed) { columnReorderingAllowed = reorderingAllowed; @@ -1080,7 +1086,7 @@ public class Table extends AbstractSelect implements Action.Container, * Setter for property currentPageFirstItem. * * @param newIndex - * the New value of property currentPageFirstItem. + * the New value of property currentPageFirstItem. */ public void setCurrentPageFirstItemIndex(int newIndex) { setCurrentPageFirstItemIndex(newIndex, true); @@ -1103,7 +1109,7 @@ public class Table extends AbstractSelect implements Action.Container, * @deprecated functionality is not needed in ajax rendering model * * @param pageBuffering - * the New value of property pageBuffering. + * the New value of property pageBuffering. */ public void setPageBufferingEnabled(boolean pageBuffering) { @@ -1130,7 +1136,7 @@ public class Table extends AbstractSelect implements Action.Container, * </p> * * @param selectable - * the New value of property selectable. + * the New value of property selectable. */ public void setSelectable(boolean selectable) { if (this.selectable != selectable) { @@ -1152,7 +1158,7 @@ public class Table extends AbstractSelect implements Action.Container, * Setter for property columnHeaderMode. * * @param columnHeaderMode - * the New value of property columnHeaderMode. + * the New value of property columnHeaderMode. */ public void setColumnHeaderMode(int columnHeaderMode) { if (columnHeaderMode >= COLUMN_HEADER_MODE_HIDDEN @@ -1392,20 +1398,20 @@ public class Table extends AbstractSelect implements Action.Container, * <code>toString()</code> is used as row caption. * <li><code>ROW_HEADER_MODE_PROPERTY</code>: Property set with * <code>setItemCaptionPropertyId()</code> is used as row header. - * <li><code>ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID</code>: Items Id-objects - * <code>toString()</code> is used as row header. If caption is explicitly - * specified, it overrides the id-caption. + * <li><code>ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID</code>: Items + * Id-objects <code>toString()</code> is used as row header. If caption is + * explicitly specified, it overrides the id-caption. * <li><code>ROW_HEADER_MODE_EXPLICIT</code>: The row headers must be * explicitly specified.</li> - * <li><code>ROW_HEADER_MODE_INDEX</code>: The index of the item is used as - * row caption. The index mode can only be used with the containers + * <li><code>ROW_HEADER_MODE_INDEX</code>: The index of the item is used + * as row caption. The index mode can only be used with the containers * implementing <code>Container.Indexed</code> interface.</li> * </ul> * The default value is <code>ROW_HEADER_MODE_HIDDEN</code> * </p> * * @param mode - * the One of the modes listed above. + * the One of the modes listed above. */ public void setRowHeaderMode(int mode) { if (ROW_HEADER_MODE_HIDDEN == mode) { @@ -1435,13 +1441,13 @@ public class Table extends AbstractSelect implements Action.Container, * columns) with given values. * * @param cells - * the Object array that is used for filling the visible cells - * new row. The types must be settable to visible column property - * types. + * the Object array that is used for filling the visible + * cells new row. The types must be settable to visible + * column property types. * @param itemId - * the Id the new row. If null, a new id is automatically - * assigned. If given, the table cant already have a item with - * given id. + * the Id the new row. If null, a new id is automatically + * assigned. If given, the table cant already have a item + * with given id. * @return Returns item id for the new row. Returns null if operation fails. */ public Object addItem(Object[] cells, Object itemId) @@ -1704,7 +1710,38 @@ public class Table extends AbstractSelect implements Action.Container, } } - enableContentRefreshing(clientNeedsContentRefresh); + // handle clicks before content refresh if content is refreshed anyway + if (clientNeedsContentRefresh) { + handleClickEvent(variables); + enableContentRefreshing(clientNeedsContentRefresh); + } else { + enableContentRefreshing(clientNeedsContentRefresh); + handleClickEvent(variables); + } + } + + /** + * Handles click event + * + * @param variables + */ + private void handleClickEvent(Map variables) { + if (clickListenerCount > 0) { + if (variables.containsKey("clickEvent")) { + String key = (String) variables.get("clickedKey"); + Object itemId = itemIdMapper.get(key); + Object propertyId = null; + String colkey = (String) variables.get("clickedColKey"); + // click is not necessary on a property + if (colkey != null) { + propertyId = columnIdMap.get(colkey); + } + MouseEventDetails evt = MouseEventDetails + .deSerialize((String) variables.get("clickEvent")); + fireEvent(new ItemClickEvent(this, getItem(itemId), itemId, + propertyId, evt)); + } + } } /** @@ -1724,7 +1761,7 @@ public class Table extends AbstractSelect implements Action.Container, * Go to mode where content content refreshing has effect. * * @param refreshContent - * true if content refresh needs to be done + * true if content refresh needs to be done */ protected void enableContentRefreshing(boolean refreshContent) { isContentRefreshesEnabled = true; @@ -1736,9 +1773,8 @@ public class Table extends AbstractSelect implements Action.Container, /* * (non-Javadoc) * - * @see - * com.itmill.toolkit.ui.AbstractSelect#paintContent(com.itmill.toolkit. - * terminal.PaintTarget) + * @see com.itmill.toolkit.ui.AbstractSelect#paintContent(com.itmill.toolkit. + * terminal.PaintTarget) */ public void paintContent(PaintTarget target) throws PaintException { @@ -1800,6 +1836,11 @@ public class Table extends AbstractSelect implements Action.Container, } else { target.addAttribute("selectmode", "none"); } + + if (clickListenerCount > 0) { + target.addAttribute("listenClicks", true); + } + target.addAttribute("cols", cols); target.addAttribute("rows", rows); @@ -2106,11 +2147,11 @@ public class Table extends AbstractSelect implements Action.Container, * the value representation. * * @param rowId - * the Id of the row (same as item Id). + * the Id of the row (same as item Id). * @param colId - * the Id of the column. + * the Id of the column. * @param property - * the Property to be presented. + * the Property to be presented. * @return Object Either formatted value or Component for field. * @see #setFieldFactory(FieldFactory) */ @@ -2133,11 +2174,11 @@ public class Table extends AbstractSelect implements Action.Container, * and return a empty string for null properties. * * @param rowId - * the Id of the row (same as item Id). + * the Id of the row (same as item Id). * @param colId - * the Id of the column. + * the Id of the column. * @param property - * the Property to be formatted. + * the Property to be formatted. * @return the String representation of property and its value. * @since 3.1 */ @@ -2305,11 +2346,11 @@ public class Table extends AbstractSelect implements Action.Container, * Adds a new property to the table and show it as a visible column. * * @param propertyId - * the Id of the proprty. + * the Id of the proprty. * @param type - * the class of the property. + * the class of the property. * @param defaultValue - * the default value given for all existing items. + * the default value given for all existing items. * @see com.itmill.toolkit.data.Container#addContainerProperty(Object, * Class, Object) */ @@ -2339,21 +2380,21 @@ public class Table extends AbstractSelect implements Action.Container, * Adds a new property to the table and show it as a visible column. * * @param propertyId - * the Id of the proprty + * the Id of the proprty * @param type - * the class of the property + * the class of the property * @param defaultValue - * the default value given for all existing items + * the default value given for all existing items * @param columnHeader - * the Explicit header of the column. If explicit header is not - * needed, this should be set null. + * the Explicit header of the column. If explicit header is + * not needed, this should be set null. * @param columnIcon - * the Icon of the column. If icon is not needed, this should be - * set null. + * the Icon of the column. If icon is not needed, this should + * be set null. * @param columnAlignment - * the Alignment of the column. Null implies align left. + * the Alignment of the column. Null implies align left. * @throws UnsupportedOperationException - * if the operation is not supported. + * if the operation is not supported. * @see com.itmill.toolkit.data.Container#addContainerProperty(Object, * Class, Object) */ @@ -2388,9 +2429,9 @@ public class Table extends AbstractSelect implements Action.Container, * </p> * * @param id - * the id of the column to be added + * the id of the column to be added * @param generatedColumn - * the {@link ColumnGenerator} to use for this column + * the {@link ColumnGenerator} to use for this column */ public void addGeneratedColumn(Object id, ColumnGenerator generatedColumn) { if (generatedColumn == null) { @@ -2412,7 +2453,7 @@ public class Table extends AbstractSelect implements Action.Container, * Removes a generated column previously added with addGeneratedColumn. * * @param id - * id of the generated column to remove + * id of the generated column to remove * @return true if the column could be removed (existed in the Table) */ public boolean removeGeneratedColumn(Object id) { @@ -2487,7 +2528,7 @@ public class Table extends AbstractSelect implements Action.Container, * Adding new items is not supported. * * @throws UnsupportedOperationException - * if set to true. + * if set to true. * @see com.itmill.toolkit.ui.Select#setNewItemsAllowed(boolean) */ public void setNewItemsAllowed(boolean allowNewOptions) @@ -2501,7 +2542,7 @@ public class Table extends AbstractSelect implements Action.Container, * Focusing to this component is not supported. * * @throws UnsupportedOperationException - * if invoked. + * if invoked. * @see com.itmill.toolkit.ui.AbstractField#focus() */ public void focus() throws UnsupportedOperationException { @@ -2617,7 +2658,7 @@ public class Table extends AbstractSelect implements Action.Container, * BaseFieldFactory is used. * * @param fieldFactory - * the field factory to set. + * the field factory to set. * @see #isEditable * @see BaseFieldFactory * @@ -2660,7 +2701,7 @@ public class Table extends AbstractSelect implements Action.Container, * property to true. * * @param editable - * true if table should be editable by user. + * true if table should be editable by user. * @see Field * @see FieldFactory * @@ -2677,8 +2718,8 @@ public class Table extends AbstractSelect implements Action.Container, * Sorts the table. * * @throws UnsupportedOperationException - * if the container data source does not implement - * Container.Sortable + * if the container data source does not implement + * Container.Sortable * @see com.itmill.toolkit.data.Container.Sortable#sort(java.lang.Object[], * boolean[]) * @@ -2703,8 +2744,8 @@ public class Table extends AbstractSelect implements Action.Container, * Sorts the table by currently selected sorting column. * * @throws UnsupportedOperationException - * if the container data source does not implement - * Container.Sortable + * if the container data source does not implement + * Container.Sortable */ public void sort() { if (getSortContainerPropertyId() == null) { @@ -2741,7 +2782,7 @@ public class Table extends AbstractSelect implements Action.Container, * Sets the currently sorted column property id. * * @param propertyId - * the Container property id of the currently sorted column. + * the Container property id of the currently sorted column. */ public void setSortContainerPropertyId(Object propertyId) { setSortContainerPropertyId(propertyId, true); @@ -2771,7 +2812,8 @@ public class Table extends AbstractSelect implements Action.Container, /** * Is the table currently sorted in ascending order. * - * @return <code>true</code> if ascending, <code>false</code> if descending. + * @return <code>true</code> if ascending, <code>false</code> if + * descending. */ public boolean isSortAscending() { return sortAscending; @@ -2781,8 +2823,8 @@ public class Table extends AbstractSelect implements Action.Container, * Sets the table in ascending order. * * @param ascending - * <code>true</code> if ascending, <code>false</code> if - * descending. + * <code>true</code> if ascending, <code>false</code> if + * descending. */ public void setSortAscending(boolean ascending) { setSortAscending(ascending, true); @@ -2825,7 +2867,7 @@ public class Table extends AbstractSelect implements Action.Container, * columns are given even in the case where datasource would support this. * * @param sortDisabled - * True iff sorting is disabled. + * True iff sorting is disabled. */ public void setSortDisabled(boolean sortDisabled) { if (this.sortDisabled != sortDisabled) { @@ -2870,12 +2912,13 @@ public class Table extends AbstractSelect implements Action.Container, * generated. * * @param source - * the source Table + * the source Table * @param itemId - * the itemId (aka rowId) for the of the cell to be generated + * the itemId (aka rowId) for the of the cell to be + * generated * @param columnId - * the id for the generated column (as specified in - * addGeneratedColumn) + * the id for the generated column (as specified in + * addGeneratedColumn) * @return */ public abstract Component generateCell(Table source, Object itemId, @@ -2886,7 +2929,7 @@ public class Table extends AbstractSelect implements Action.Container, * Set cell style generator for Table. * * @param cellStyleGenerator - * New cell style generator or null to remove generator. + * New cell style generator or null to remove generator. */ public void setCellStyleGenerator(CellStyleGenerator cellStyleGenerator) { this.cellStyleGenerator = cellStyleGenerator; @@ -2914,13 +2957,36 @@ public class Table extends AbstractSelect implements Action.Container, * Called by Table when a cell (and row) is painted. * * @param itemId - * The itemId of the painted cell + * The itemId of the painted cell * @param propertyId - * The propertyId of the cell, null when getting row style + * The propertyId of the cell, null when getting row + * style * @return The style name to add to this cell or row. (the CSS class * name will be i-table-cell-content-[style name], or * i-table-row-[style name] for rows) */ public abstract String getStyle(Object itemId, Object propertyId); } + + public void addListener(ItemClickListener listener) { + addListener(ItemClickEvent.class, listener, + ItemClickEvent.ITEM_CLICK_METHOD); + clickListenerCount++; + // repaint needed only if click listening became necessary + if (clickListenerCount == 1) { + requestRepaint(); + } + + } + + public void removeListener(ItemClickListener listener) { + removeListener(ItemClickEvent.class, listener, + ItemClickEvent.ITEM_CLICK_METHOD); + clickListenerCount++; + // repaint needed only if click listening is not needed in client + // anymore + if (clickListenerCount == 0) { + requestRepaint(); + } + } } diff --git a/src/com/itmill/toolkit/ui/Tree.java b/src/com/itmill/toolkit/ui/Tree.java index 67383ef51f..c6d07ebe65 100644 --- a/src/com/itmill/toolkit/ui/Tree.java +++ b/src/com/itmill/toolkit/ui/Tree.java @@ -21,10 +21,14 @@ import com.itmill.toolkit.data.Container; import com.itmill.toolkit.data.util.ContainerHierarchicalWrapper; import com.itmill.toolkit.data.util.IndexedContainer; import com.itmill.toolkit.event.Action; +import com.itmill.toolkit.event.ItemClickEvent; +import com.itmill.toolkit.event.ItemClickEvent.ItemClickListener; +import com.itmill.toolkit.event.ItemClickEvent.ItemClickSource; import com.itmill.toolkit.terminal.KeyMapper; import com.itmill.toolkit.terminal.PaintException; import com.itmill.toolkit.terminal.PaintTarget; import com.itmill.toolkit.terminal.Resource; +import com.itmill.toolkit.terminal.gwt.client.MouseEventDetails; /** * MenuTree component. MenuTree can be used to select an item (or multiple @@ -36,7 +40,7 @@ import com.itmill.toolkit.terminal.Resource; * @since 3.0 */ public class Tree extends AbstractSelect implements Container.Hierarchical, - Action.Container { + Action.Container, ItemClickSource { /* Static members */ @@ -327,6 +331,15 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, */ public void changeVariables(Object source, Map variables) { + if (clickListenerCount > 0 && variables.containsKey("clickedKey")) { + String key = (String) variables.get("clickedKey"); + + Object id = itemIdMapper.get(key); + MouseEventDetails details = MouseEventDetails.deSerialize((String) variables + .get("clickEvent")); + fireEvent(new ItemClickEvent(this, getItem(id), id, null, details)); + } + if (!isSelectable() && variables.containsKey("selected")) { // Not-selectable is a special case, AbstractSelect does not support // TODO could be optimized. @@ -418,6 +431,10 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, target.addAttribute("nullselect", true); } + if (clickListenerCount > 0) { + target.addAttribute("listenClicks", true); + } + } // Initialize variables @@ -996,4 +1013,27 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, } } + private int clickListenerCount = 0; + + public void addListener(ItemClickListener listener) { + addListener(ItemClickEvent.class, listener, + ItemClickEvent.ITEM_CLICK_METHOD); + clickListenerCount++; + // repaint needed only if click listening became necessary + if (clickListenerCount == 1) { + requestRepaint(); + } + } + + public void removeListener(ItemClickListener listener) { + removeListener(ItemClickEvent.class, listener, + ItemClickEvent.ITEM_CLICK_METHOD); + clickListenerCount++; + // repaint needed only if click listening is not needed in client + // anymore + if (clickListenerCount == 0) { + requestRepaint(); + } + } + } |