From 46a4fb1adb15166c6a740b2fae6492f83c70ad53 Mon Sep 17 00:00:00 2001 From: Matti Tahvonen Date: Tue, 10 Jul 2007 10:59:10 +0000 Subject: [PATCH] Client side context menu implementation and context menus for scroll tables rows svn changeset:1827/svn branch:trunk --- .../toolkit/terminal/gwt/client/Client.java | 15 +++ .../toolkit/terminal/gwt/client/UIDL.java | 8 ++ .../terminal/gwt/client/ui/IAction.java | 97 +++++++++++++++ .../terminal/gwt/client/ui/IContextMenu.java | 75 ++++++++++++ .../terminal/gwt/client/ui/IScrollTable.java | 115 ++++++++++++++---- 5 files changed, 286 insertions(+), 24 deletions(-) create mode 100644 src/com/itmill/toolkit/terminal/gwt/client/ui/IAction.java create mode 100644 src/com/itmill/toolkit/terminal/gwt/client/ui/IContextMenu.java diff --git a/src/com/itmill/toolkit/terminal/gwt/client/Client.java b/src/com/itmill/toolkit/terminal/gwt/client/Client.java index b1151e0ac8..02a613fa46 100755 --- a/src/com/itmill/toolkit/terminal/gwt/client/Client.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/Client.java @@ -19,6 +19,7 @@ import com.google.gwt.json.client.JSONValue; import com.google.gwt.user.client.ui.FocusWidget; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.Widget; +import com.itmill.toolkit.terminal.gwt.client.ui.IContextMenu; /** * Entry point classes define onModuleLoad(). @@ -45,6 +46,8 @@ public class Client implements EntryPoint { private LocaleService locale; + private IContextMenu contextMenu = null; + /** * This is the entry point method. */ @@ -370,4 +373,16 @@ public class Client implements EntryPoint { public String getResource(String name) { return (String) resourcesMap.get(name); } + + /** + * Singleton method to get instance of app's context menu. + * + * @return IContextMenu object + */ + public IContextMenu getContextMenu() { + if(contextMenu == null) { + contextMenu = new IContextMenu(); + } + return contextMenu; + } } diff --git a/src/com/itmill/toolkit/terminal/gwt/client/UIDL.java b/src/com/itmill/toolkit/terminal/gwt/client/UIDL.java index cee8c8aa2e..ed8ca237a0 100644 --- a/src/com/itmill/toolkit/terminal/gwt/client/UIDL.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/UIDL.java @@ -68,6 +68,14 @@ public class UIDL { return false; return ((JSONBoolean) val).booleanValue(); } + + public String[] getStringArrayAttribute(String name) { + JSONArray a = (JSONArray) ((JSONObject) json.get(1)).get(name); + String[] s = new String[a.size()]; + for (int i = 0; i < a.size(); i++) + s[i] = ((JSONString) a.get(i)).stringValue(); + return s; + } /** * Get attributes value as string whateever the type is diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IAction.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IAction.java new file mode 100644 index 0000000000..af75a49491 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/IAction.java @@ -0,0 +1,97 @@ +package com.itmill.toolkit.terminal.gwt.client.ui; + +import com.google.gwt.user.client.Command; +import com.itmill.toolkit.terminal.gwt.client.Client; + +/** + * + */ +public class IAction implements Command { + + IActionOwner owner; + + String targetKey = ""; + String actionKey = ""; + + String iconUrl = null; + + String caption = ""; + + public IAction(IActionOwner owner) { + this.owner = owner; + } + + public IAction(IActionOwner owner, String target, String action) { + this(owner); + this.targetKey = target; + this.actionKey = action; + } + + + /** + * Sends message to server that this action has been fired. + * Messages are "standard" Toolkit messages whose value is comma + * separated pair of targetKey (row, treeNod ...) and actions id. + * + * Variablename is always "action". + * + * Actions are always sent immediatedly to server. + */ + public void execute() { + owner.getClient().updateVariable( + owner.getPaintableId(), + "action", + targetKey + "," + actionKey, + true); + owner.getClient().getContextMenu().hide(); + } + + public String getActionKey() { + return actionKey; + } + + public void setActionKey(String actionKey) { + this.actionKey = actionKey; + } + + public String getTargetKey() { + return targetKey; + } + + public void setTargetKey(String targetKey) { + this.targetKey = targetKey; + } + + public String getHTMLRepresentation() { + StringBuffer sb = new StringBuffer(); + if(iconUrl != null) { + sb.append("\"icon\""); + } + sb.append(caption); + return sb.toString(); + } + + public String getCaption() { + return caption; + } + + public void setCaption(String caption) { + this.caption = caption; + } +} + +/** + * Action owner must provide a set of actions for context menu + * and IAction objects. + */ +interface IActionOwner { + + /** + * @return Array of IActions + */ + public IAction[] getActions(); + + public Client getClient(); + + public String getPaintableId(); +} \ No newline at end of file diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IContextMenu.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IContextMenu.java new file mode 100644 index 0000000000..d18120f217 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/IContextMenu.java @@ -0,0 +1,75 @@ +package com.itmill.toolkit.terminal.gwt.client.ui; + +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.MenuBar; +import com.google.gwt.user.client.ui.MenuItem; +import com.google.gwt.user.client.ui.PopupPanel; + +public class IContextMenu extends PopupPanel { + + private IActionOwner actionOwner; + + private CMenuBar menu = new CMenuBar(); + + /** + * This method should be used only by Client object as + * only one per client should exists. Request an instance + * via client.getContextMenu(); + * + * @param cli to be set as an owner of menu + */ + public IContextMenu() { + super(true); + setWidget(menu); + setStyleName("i-contextmenu"); + } + + /** + * Sets the element from which to build menu + * @param ao + */ + public void setActionOwner(IActionOwner ao) { + this.actionOwner = ao; + } + + /** + * Shows context menu at given location. + * + * @param left + * @param top + */ + public void showAt(int left, int top) { + menu.clearItems(); + IAction[] actions = actionOwner.getActions(); + for (int i = 0; i < actions.length; i++) { + IAction a = actions[i]; + menu.addItem(new MenuItem(a.getHTMLRepresentation(), true, a)); + } + + setPopupPosition(left, top); + + show(); + } + + public void showAt(IActionOwner ao, int left, int top) { + setActionOwner(ao); + showAt(left, top); + + } + + /** + * Extend standard Gwt MenuBar to set proper settings and + * to override onPopupClosed method so that PopupPanel gets + * closed. + */ + class CMenuBar extends MenuBar { + public CMenuBar() { + super(true); + } + + public void onPopupClosed(PopupPanel sender, boolean autoClosed) { + super.onPopupClosed(sender, autoClosed); + IContextMenu.this.hide(); + } + } +} 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 ed4a4dc192..ec81c750c2 100644 --- a/src/com/itmill/toolkit/terminal/gwt/client/ui/IScrollTable.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/IScrollTable.java @@ -12,7 +12,6 @@ import com.google.gwt.user.client.DeferredCommand; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.ui.ClickListener; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.ScrollListener; @@ -45,7 +44,7 @@ public class IScrollTable extends Composite implements Paintable, ITable, Scroll private String[] columnOrder; private Client client; - private String id; + private String paintableId; private boolean immediate; @@ -74,6 +73,14 @@ public class IScrollTable extends Composite implements Paintable, ITable, Scroll private String sortColumn; private boolean columnReordering; + /** + * This map contains captions and icon urls for + * actions like: + * * "33_c" -> "Edit" + * * "33_i" -> "http://dom.com/edit.png" + */ + private HashMap actionMap = new HashMap(); + public IScrollTable() { headerContainer.setStyleName("iscrolltable-header"); headerContainer.add(tHead); @@ -95,7 +102,7 @@ public class IScrollTable extends Composite implements Paintable, ITable, Scroll return; this.client = client; - this.id = uidl.getStringAttribute("id"); + this.paintableId = uidl.getStringAttribute("id"); this.immediate = uidl.getBooleanAttribute("immediate"); this.totalRows = uidl.getIntAttribute("totalrows"); this.pageLength = uidl.getIntAttribute("pagelength"); @@ -169,9 +176,28 @@ public class IScrollTable extends Composite implements Paintable, ITable, Scroll } private void updateActionMap(UIDL c) { - // TODO Auto-generated method stub + Iterator it = c.getChildIterator(); + while(it.hasNext()) { + UIDL action = (UIDL) it.next(); + String key = action.getStringAttribute("key"); + String caption = action.getStringAttribute("caption"); + actionMap.put(key + "_c", caption); + if(action.hasAttribute("icon")) { + // TODO need some uri handling ?? + actionMap.put(key + "_i", action.getStringAttribute("icon")); + } + } } + + public String getActionCaption(String actionKey) { + return (String) actionMap.get(actionKey + "_c"); + } + + public String getActionIcon(String actionKey) { + return (String) actionMap.get(actionKey + "_i"); + } + private void updateHeader(UIDL uidl) { if(uidl == null) @@ -303,7 +329,7 @@ public class IScrollTable extends Composite implements Paintable, ITable, Scroll newOrder[index++] = ((HeaderCell) hCells.next()).getColKey(); } columnOrder = newOrder; - client.updateVariable(id, "columnorder", newOrder, false); + client.updateVariable(paintableId, "columnorder", newOrder, false); } @@ -333,7 +359,7 @@ public class IScrollTable extends Composite implements Paintable, ITable, Scroll int lastRendered = tBody.getLastRendered(); int firstRendered = tBody.getFirstRendered(); if( postLimit <= lastRendered && preLimit >= firstRendered ) { - client.updateVariable(this.id, "firstvisible", firstRowInViewPort, false); + client.updateVariable(this.paintableId, "firstvisible", firstRowInViewPort, false); return; // scrolled withing "non-react area" } @@ -455,9 +481,9 @@ public class IScrollTable extends Composite implements Paintable, ITable, Scroll public void run() { client.console.log("Getting " + reqRows + " rows from " + reqFirstRow); - client.updateVariable(id, "firstvisible", firstRowInViewPort, false); - client.updateVariable(id, "reqfirstrow", reqFirstRow, false); - client.updateVariable(id, "reqrows", reqRows, true); + client.updateVariable(paintableId, "firstvisible", firstRowInViewPort, false); + client.updateVariable(paintableId, "reqfirstrow", reqFirstRow, false); + client.updateVariable(paintableId, "reqrows", reqRows, true); } public int getReqFirstRow() { @@ -627,10 +653,10 @@ public class IScrollTable extends Composite implements Paintable, ITable, Scroll if(sortable) { if(sortColumn.equals(cid)) { // just toggle order - client.updateVariable(id, "sortascending", !sortAscending, false); + client.updateVariable(paintableId, "sortascending", !sortAscending, false); } else { // set table scrolled by this column - client.updateVariable(id, "sortcolumn", cid, false); + client.updateVariable(paintableId, "sortcolumn", cid, false); } // get also cache columns at the same request bodyContainer.setScrollPosition(0); @@ -1047,28 +1073,46 @@ public class IScrollTable extends Composite implements Paintable, ITable, Scroll } - public class IScrollTableRow extends Panel { + public class IScrollTableRow extends Panel implements IActionOwner { Vector childWidgets = new Vector(); private boolean selected = false; private int rowKey; + private String[] actionKeys = null; + private IScrollTableRow(int rowKey) { this.rowKey = rowKey; - this.selected = selected; setElement(DOM.createElement("tr")); - DOM.sinkEvents(getElement(), Event.BUTTON_RIGHT | Event.ONCLICK); + DOM.sinkEvents(getElement(), Event.ONCLICK); + disableContextMenu(getElement()); setStyleName("iscrolltable-row"); } + private native void disableContextMenu(Element el) /*-{ + var row = this; + el.oncontextmenu = function(e) { + if(!e) + e = window.event; + row.@com.itmill.toolkit.terminal.gwt.client.ui.IScrollTable.IScrollTableBody.IScrollTableRow::showContextMenu(Lcom/google/gwt/user/client/Event;)(e); + return false; + }; + }-*/; + public String getKey() { return String.valueOf(rowKey); } public IScrollTableRow(UIDL uidl) { this(uidl.getIntAttribute("key")); + + // row header if(uidl.hasAttribute("caption")) addCell(uidl.getStringAttribute("caption")); + + if(uidl.hasAttribute("al")) + actionKeys = uidl.getStringArrayAttribute("al"); + Iterator cells = uidl.getChildIterator(); while(cells.hasNext()) { Object cell = cells.next(); @@ -1079,7 +1123,7 @@ public class IScrollTable extends Composite implements Paintable, ITable, Scroll (( Paintable) cellContent).updateFromUIDL((UIDL) cell, client); } } - if(uidl.hasAttribute("selected")) + if(uidl.hasAttribute("selected") && !isSelected()) toggleSelection(); } @@ -1114,16 +1158,11 @@ public class IScrollTable extends Composite implements Paintable, ITable, Scroll public void onBrowserEvent(Event event) { switch (DOM.eventGetType(event)) { - case Event.BUTTON_RIGHT: - // TODO - System.out.println("Context menu"); - break; - case Event.ONCLICK: - System.out.println("Row click"); + client.console.log("Row click"); if(selectMode > ITable.SELECT_MODE_NONE) { toggleSelection(); - client.updateVariable(id, "selected", selectedRowKeys.toArray(), immediate); + client.updateVariable(paintableId, "selected", selectedRowKeys.toArray(), immediate); } break; @@ -1132,6 +1171,15 @@ public class IScrollTable extends Composite implements Paintable, ITable, Scroll } super.onBrowserEvent(event); } + + public void showContextMenu(Event event) { + client.console.log("Context menu"); + if(actionKeys != null) { + int left = DOM.eventGetClientX(event); + int top = DOM.eventGetClientY(event); + client.getContextMenu().showAt(this, left, top); + } + } public boolean isSelected() { return selected; @@ -1150,9 +1198,28 @@ public class IScrollTable extends Composite implements Paintable, ITable, Scroll setStyleName("iscrolltable-row"); } } - - } + public IAction[] getActions() { + if(actionKeys == null) + return new IAction[] {}; + IAction[] actions = new IAction[actionKeys.length]; + for (int i = 0; i < actions.length; i++) { + String actionKey = actionKeys[i]; + IAction a = new IAction(this, String.valueOf(rowKey), actionKey); + a.setCaption(getActionCaption(actionKey)); + actions[i] = a; + } + return actions; + } + + public Client getClient() { + return client; + } + + public String getPaintableId() { + return paintableId; + } + } } public void deselectAll() { -- 2.39.5