IDEMPIERE-2050 Improvement to Menu Lookup.

This commit is contained in:
Heng Sin Low 2014-07-10 16:50:05 +08:00
parent 7815708472
commit 7c390b1d25
6 changed files with 1102 additions and 6 deletions

View File

@ -0,0 +1,365 @@
/******************************************************************************
* Copyright (C) 2014 Low Heng Sin *
* Copyright (C) 2014 Trek Global *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.adempiere.webui.apps;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.adempiere.webui.component.Label;
import org.compiere.model.I_AD_SearchDefinition;
import org.compiere.model.MColumn;
import org.compiere.model.MLookup;
import org.compiere.model.MLookupFactory;
import org.compiere.model.MLookupInfo;
import org.compiere.model.MQuery;
import org.compiere.model.MRole;
import org.compiere.model.MSearchDefinition;
import org.compiere.model.MTable;
import org.compiere.model.MWindow;
import org.compiere.model.Query;
import org.compiere.util.DB;
import org.compiere.util.DisplayType;
import org.compiere.util.Env;
import org.compiere.util.Util;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.A;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.Vlayout;
/**
* @author hengsin
*
*/
public class DocumentSearchController implements EventListener<Event>{
private static final String SEARCH_RESULT = "search.result";
private static final String ON_SEARCH_DOCUMENTS = "onSearchDocuments";
private Vlayout layout;
/**
*
*/
public DocumentSearchController() {
}
public void create(Component parent) {
layout = new Vlayout();
layout.setStyle("padding: 3px;");
layout.setWidth("200px");
parent.appendChild(layout);
layout.addEventListener(ON_SEARCH_DOCUMENTS, this);
}
public void search(String value) {
layout.getChildren().clear();
Events.echoEvent(ON_SEARCH_DOCUMENTS, layout, value);
}
private void onSearchDocuments(String searchString) {
if (Util.isEmpty(searchString)) {
return;
}
List<SearchResult> list = doSearch(searchString);
if (list.size() > 0) {
Collections.sort(list, new Comparator<SearchResult>() {
@Override
public int compare(SearchResult o1, SearchResult o2) {
int r = o1.getWindowName().compareTo(o2.getWindowName());
if (r == 0)
r = o1.getLabel().compareTo(o2.getLabel());
return r;
}
});
String windowName = null;
for(SearchResult result : list) {
if (windowName == null || !windowName.equals(result.getWindowName())) {
windowName = result.getWindowName();
Label label = new Label(windowName);
label.setStyle("padding: 3px; font-weight: bold; display: inline-block;");
layout.appendChild(label);
}
A a = new A();
a.setAttribute(SEARCH_RESULT, result);
a.setLabel(result.getLabel());
layout.appendChild(a);
a.setStyle("padding-left: 3px; display: inline-block;");
a.addEventListener(Events.ON_CLICK, this);
}
layout.invalidate();
}
}
private List<SearchResult> doSearch(String searchString) {
final MRole role = MRole.get(Env.getCtx(), Env.getAD_Role_ID(Env.getCtx()), Env.getAD_User_ID(Env.getCtx()), true);
List<SearchResult> list = new ArrayList<SearchResult>();
Query query = new Query(Env.getCtx(), I_AD_SearchDefinition.Table_Name, "", null);
List<MSearchDefinition> definitions = query.setOnlyActiveRecords(true).list();
for(MSearchDefinition msd : definitions) {
MTable table = new MTable(Env.getCtx(), msd.getAD_Table_ID(), null);
StringBuilder sql = null;
MWindow window = msd.getAD_Window_ID() > 0 && role.getWindowAccess(msd.getAD_Window_ID()) != null ? MWindow.get(Env.getCtx(), msd.getAD_Window_ID()) : null;
MWindow powindow = msd.getPO_Window_ID() > 0 && role.getWindowAccess(msd.getPO_Window_ID()) != null ? MWindow.get(Env.getCtx(), msd.getPO_Window_ID()) : null;
if (window == null && powindow == null)
continue;
List<Object> params = new ArrayList<Object>();
// SearchDefinition with a given table and column
if (msd.getSearchType().equals(MSearchDefinition.SEARCHTYPE_TABLE)) {
MColumn column = new MColumn(Env.getCtx(), msd.getAD_Column_ID(), null);
sql = new StringBuilder("SELECT ").append(table.getTableName()).append("_ID, ")
.append(column.getColumnName());
sql.append(" FROM ")
.append(table.getTableName())
.append(" ");
// search for an Integer
if (msd.getDataType().equals(MSearchDefinition.DATATYPE_INTEGER)) {
sql.append("WHERE ").append(column.getColumnName()).append("=?");
// search for a String
} else {
sql.append("WHERE UPPER(").append(column.getColumnName()).append(") LIKE UPPER(?)");
}
// search for a Integer
if (msd.getDataType().equals(MSearchDefinition.DATATYPE_INTEGER)) {
params.add(Integer.valueOf(searchString.replaceAll("\\D", "")));
// search for a String
} else if (msd.getDataType().equals(MSearchDefinition.DATATYPE_STRING)) {
if (searchString.endsWith("%"))
params.add(searchString);
else
params.add(searchString+"%");
}
// SearchDefinition with a special query
} else if (msd.getSearchType().equals(MSearchDefinition.SEARCHTYPE_QUERY)) {
sql = new StringBuilder().append(msd.getQuery());
// count '?' in statement
int count = 1;
for (char c : sql.toString().toCharArray()) {
if (c == '?') {
count++;
}
}
for (int i = 1; i < count; i++) {
if (msd.getDataType().equals(MSearchDefinition.DATATYPE_INTEGER)) {
params.add(Integer.valueOf(searchString.replaceAll("\\D", "")));
} else if (msd.getDataType().equals(MSearchDefinition.DATATYPE_STRING)) {
if (searchString.endsWith("%"))
params.add(searchString);
else
params.add(searchString+"%");
}
}
}
MLookupInfo lookupInfo = MLookupFactory.getLookupInfo(Env.getCtx(), -1, -1, DisplayType.Search, Env.getLanguage(Env.getCtx()), table.getTableName() + "_ID", 0, false, null);
MLookup lookup = new MLookup(lookupInfo, -1);
if (sql != null) {
if (powindow != null) {
if (window != null) {
doRetrieval(msd, sql, params, lookup, window, table.getTableName(), " AND IsSOTrx='Y' ", list);
}
doRetrieval(msd, sql, params, lookup, powindow, table.getTableName(), " AND IsSOTrx='N' ", list);
} else if (window != null) {
doRetrieval(msd, sql, params, lookup, window, table.getTableName(), null, list);
}
}
}
return list;
}
private void doRetrieval(MSearchDefinition msd, StringBuilder builder, List<Object> params, MLookup lookup, MWindow window, String tableName,
String extraWhereClase, List<SearchResult> list) {
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
String sql = builder.toString();
if (!Util.isEmpty(extraWhereClase))
sql = sql + extraWhereClase;
pstmt = DB.prepareStatement(sql, (String)null);
if (params.size() > 0)
DB.setParameters(pstmt, params);
pstmt.setQueryTimeout(1);
rs = pstmt.executeQuery();
int count = 0;
while (rs.next() && count < 3) {
count++;
int id = rs.getInt(1);
SearchResult result = new SearchResult();
result.setLabel(lookup.getDisplay(id));
result.setRecordId(id);
result.setWindowName(window.get_Translation("Name"));
result.setWindowId(window.getAD_Window_ID());
result.setTableName(tableName);
if (rs.getMetaData().getColumnCount() > 1) {
result.setName(rs.getString(2));
}
list.add(result);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DB.close(rs, pstmt);
}
}
@Override
public void onEvent(Event event) throws Exception {
if (Events.ON_CLICK.equals(event.getName())) {
if (event.getTarget() instanceof A) {
SearchResult result = (SearchResult) event.getTarget().getAttribute(SEARCH_RESULT);
doZoom(result);
}
} else if (event.getName().equals(ON_SEARCH_DOCUMENTS)) {
onSearchDocuments((String)event.getData());
}
}
private void doZoom(SearchResult result) {
MQuery query = new MQuery();
query.addRestriction(result.getTableName()+"_ID", "=", result.getRecordId());
AEnv.zoom(result.getWindowId(), query);
}
private class SearchResult {
private String windowName;
private int windowId;
private String tableName;
private int recordId;
private String label;
private String name;
/**
* @return the windowId
*/
public int getWindowId() {
return windowId;
}
/**
* @param windowId the windowId to set
*/
public void setWindowId(int windowId) {
this.windowId = windowId;
}
/**
* @return the tableName
*/
public String getTableName() {
return tableName;
}
/**
* @param tableName the tableName to set
*/
public void setTableName(String tableName) {
this.tableName = tableName;
}
/**
* @return the windowName
*/
public String getWindowName() {
return windowName;
}
/**
* @param windowName the windowName to set
*/
public void setWindowName(String windowName) {
this.windowName = windowName;
}
/**
* @return the recordId
*/
public int getRecordId() {
return recordId;
}
/**
* @param recordId the recordId to set
*/
public void setRecordId(int recordId) {
this.recordId = recordId;
}
/**
* @return the label
*/
public String getLabel() {
return label;
}
/**
* @param label the label to set
*/
public void setLabel(String label) {
this.label = label;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
}
public boolean onOk(Textbox textbox) {
String text = textbox.getText();
if (Util.isEmpty(text))
return false;
text = text.toLowerCase();
int size = layout.getChildren().size();
A firstStart = null;
A exact = null;
for(int i = 0; i < size; i++) {
if (!(layout.getChildren().get(i) instanceof A)) continue;
A a = (A) layout.getChildren().get(i);
SearchResult result = (SearchResult) a.getAttribute(SEARCH_RESULT);
if (result.getLabel().equalsIgnoreCase(text)) {
exact = a;
break;
} else if (text.equalsIgnoreCase(result.getName())) {
exact = a;
break;
} else if (firstStart == null && result.getLabel().toLowerCase().startsWith(text) && text.length() >=3 ) {
firstStart = a;
}
}
SearchResult result = null;
if (exact != null)
result = (SearchResult) exact.getAttribute(SEARCH_RESULT);
else if (firstStart != null)
result = (SearchResult) firstStart.getAttribute(SEARCH_RESULT);
if (result != null) {
doZoom(result);
}
return false;
}
}

View File

@ -0,0 +1,147 @@
/******************************************************************************
* Copyright (C) 2014 Low Heng Sin *
* Copyright (C) 2014 Trek Global *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.adempiere.webui.apps;
import org.adempiere.webui.component.Bandbox;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.InputEvent;
import org.zkoss.zk.ui.event.KeyEvent;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.Bandpopup;
import org.zkoss.zul.Div;
import org.zkoss.zul.Hlayout;
import org.zkoss.zul.Separator;
/**
* @author hengsin
*
*/
public class GlobalSearch extends Div implements EventListener<Event> {
private static final String ON_ENTER_KEY = "onEnterKey";
private static final String ON_POST_ENTER_KEY = "onPostEnterKey";
private static final String ON_CREATE_ECHO = "onCreateEcho";
private static final String ON_SEARCH = "onSearch";
/**
* generated serial id
*/
private static final long serialVersionUID = -8793878697269469837L;
private Bandbox bandbox;
private MenuSearchController menuController;
private DocumentSearchController docController;
/**
*
*/
public GlobalSearch(MenuSearchController menuController) {
this.menuController = menuController;
docController = new DocumentSearchController();
init();
}
private void init() {
bandbox = new Bandbox();
appendChild(bandbox);
bandbox.setWidth("100%");
bandbox.setAutodrop(true);
bandbox.addEventListener(Events.ON_CHANGING, this);
bandbox.setCtrlKeys("#up#down");
bandbox.addEventListener(Events.ON_CTRL_KEY, this);
Bandpopup popup = new Bandpopup();
popup.setWidth("700px");
popup.setHeight("600px");
bandbox.appendChild(popup);
Hlayout hlayout = new Hlayout();
hlayout.setHflex("1");
popup.appendChild(hlayout);
menuController.create(hlayout);
Separator separator = new Separator();
separator.setHeight("100%");
separator.setBar(true);
separator.setHflex("0");
separator.setOrient("horizontal");
hlayout.appendChild(separator);
docController.create(hlayout);
addEventListener(ON_SEARCH, this);
addEventListener(ON_CREATE_ECHO, this);
bandbox.addEventListener(ON_ENTER_KEY, this);
addEventListener(ON_POST_ENTER_KEY, this);
Events.echoEvent(ON_CREATE_ECHO, this, null);
}
@Override
public void onEvent(Event event) throws Exception {
if (Events.ON_CHANGING.equals(event.getName())) {
InputEvent inputEvent = (InputEvent) event;
String value = inputEvent.getValue();
Events.postEvent(ON_SEARCH, this, value);
} else if (Events.ON_CTRL_KEY.equals(event.getName())) {
KeyEvent ke = (KeyEvent) event;
if (ke.getKeyCode() == KeyEvent.UP) {
if (bandbox.getFirstChild().isVisible()) {
MenuItem selected = menuController.selectPrior();
if (selected != null) {
bandbox.setText(selected.getLabel());
}
}
} else if (ke.getKeyCode() == KeyEvent.DOWN) {
if (bandbox.getFirstChild().isVisible()) {
MenuItem selected = menuController.selectNext();
if (selected != null && !"...".equals(selected.getType())) {
bandbox.setText(selected.getLabel());
}
}
}
} else if (event.getName().equals(ON_SEARCH)) {
String value = (String) event.getData();
menuController.search(value);
docController.search(value);
bandbox.focus();
} else if (event.getName().equals(ON_CREATE_ECHO)) {
StringBuilder script = new StringBuilder("jq('#")
.append(bandbox.getUuid())
.append("').bind('keydown', function(e) {var code=e.keyCode||e.which;console.log(code);if(code==13){")
.append("var widget=zk.Widget.$('#").append(bandbox.getUuid()).append("');")
.append("var event=new zk.Event(widget,'")
.append(ON_ENTER_KEY)
.append("',{},{toServer:true});")
.append("zAu.send(event);")
.append("}});");
Clients.evalJavaScript(script.toString());
} else if (event.getName().equals(ON_ENTER_KEY)) {
Clients.showBusy(bandbox, null);
Events.echoEvent(ON_POST_ENTER_KEY, this, null);
} else if (event.getName().equals(ON_POST_ENTER_KEY)) {
Clients.clearBusy(bandbox);
if (menuController.onOk(bandbox)) {
return;
} else {
docController.onOk(bandbox);
}
}
}
}

View File

@ -0,0 +1,87 @@
/**
*
*/
package org.adempiere.webui.apps;
/**
* @author hengsin
*
*/
public class MenuItem {
private String label;
private String description;
private String image;
private String type;
private Object data;
/**
*
*/
public MenuItem() {
}
/**
* @return the label
*/
public String getLabel() {
return label;
}
/**
* @param label the label to set
*/
public void setLabel(String label) {
this.label = label;
}
/**
* @return the description
*/
public String getDescription() {
return description;
}
/**
* @param description the description to set
*/
public void setDescription(String description) {
this.description = description;
}
/**
* @return the image
*/
public String getImage() {
return image;
}
/**
* @param image the image to set
*/
public void setImage(String image) {
this.image = image;
}
public void setData(Object data) {
this.data = data;
}
public Object getData() {
return data;
}
/**
* @return the type
*/
public String getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(String type) {
this.type = type;
}
}

View File

@ -0,0 +1,481 @@
/******************************************************************************
* Copyright (C) 2014 Low Heng Sin *
* Copyright (C) 2014 Trek Global *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.adempiere.webui.apps;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.adempiere.webui.component.Label;
import org.adempiere.webui.component.ListHead;
import org.adempiere.webui.component.ListItem;
import org.adempiere.webui.component.Listbox;
import org.adempiere.webui.theme.ThemeManager;
import org.adempiere.webui.util.TreeItemAction;
import org.adempiere.webui.util.TreeNodeAction;
import org.adempiere.webui.util.TreeUtils;
import org.compiere.model.MTreeNode;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.Util;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.A;
import org.zkoss.zul.DefaultTreeNode;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.ListModelList;
import org.zkoss.zul.ListModels;
import org.zkoss.zul.ListSubModel;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listheader;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.ListitemRenderer;
import org.zkoss.zul.ListitemRendererExt;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.Toolbarbutton;
import org.zkoss.zul.Tree;
import org.zkoss.zul.Treechildren;
import org.zkoss.zul.Treeitem;
import org.zkoss.zul.Vlayout;
import org.zkoss.zul.impl.LabelElement;
import org.zkoss.zul.impl.LabelImageElement;
/**
* @author hengsin
*
*/
public class MenuSearchController implements EventListener<Event>{
private static final String ON_SEARCH_ECHO = "onSearchEcho";
private static final String ON_LOAD_MORE = "onLoadMore";
private static final String ONSELECT_TIMESTAMP = "onselect.timestamp";
private Tree tree;
private Listbox listbox;
private ListModelList<MenuItem> model;
private Vlayout layout;
private ListModelList<MenuItem> fullModel;
private static final String ON_POST_SELECT_TREEITEM_EVENT = "onPostSelectTreeitem";
/**
*
*/
public MenuSearchController(Tree tree) {
this.tree = tree;
}
public void refreshModel() {
final List<MenuItem> list = new ArrayList<MenuItem>();
if (tree.getModel() == null) {
TreeUtils.traverse(tree, new TreeItemAction() {
public void run(Treeitem treeItem) {
if (treeItem.isVisible())
addTreeItem(list, treeItem);
}
});
} else {
TreeUtils.traverse(tree.getModel(), new TreeNodeAction() {
public void run(DefaultTreeNode<?> treeNode) {
addTreeItem(list, treeNode);
}
});
}
model = new ListModelList<MenuItem>(list, true);
model.sort(new Comparator<MenuItem>() {
@Override
public int compare(MenuItem o1, MenuItem o2) {
return o1.getLabel().compareTo(o2.getLabel());
}
}, true);
}
private void addTreeItem(List<MenuItem> list, DefaultTreeNode<?> treeNode) {
MTreeNode mNode = (MTreeNode) treeNode.getData();
if (!mNode.isLeaf())
return;
MenuItem item = new MenuItem();
item.setLabel(mNode.getName());
item.setDescription(mNode.getDescription());
item.setImage(mNode.getImagePath());
item.setData(treeNode);
list.add(item);
}
private boolean isFolder(Treeitem treeItem) {
List<Component> list = treeItem.getChildren();
for (Component c : list) {
if (c instanceof Treechildren && ((Treechildren)c).getChildren().size() > 0) {
return true;
}
}
return false;
}
private void addTreeItem(List<MenuItem> list, Treeitem treeItem) {
if (isFolder(treeItem))
return;
MenuItem item = new MenuItem();
item.setLabel(getLabel(treeItem));
item.setDescription(treeItem.getTooltiptext());
String image = getImage(treeItem);
if (image == null || image.length() == 0)
{
image = ThemeManager.getThemeResource("images/Folder16.png");
}
item.setImage(image);
item.setData(treeItem);
list.add(item);
item.setType((String) treeItem.getAttribute("menu.type"));
}
private String getLabel(Treeitem treeItem) {
String label = treeItem.getLabel();
if (label == null || label.trim().length() == 0)
{
if (treeItem.getTreerow().getFirstChild().getFirstChild() != null &&
treeItem.getTreerow().getFirstChild().getFirstChild() instanceof LabelElement)
{
LabelElement element = (LabelElement) treeItem.getTreerow().getFirstChild().getFirstChild();
label = element.getLabel();
}
}
return label;
}
private String getImage(Treeitem treeItem) {
String image = treeItem.getImage();
if (image == null || image.trim().length() == 0)
{
if (treeItem.getTreerow().getFirstChild().getFirstChild() != null &&
treeItem.getTreerow().getFirstChild().getFirstChild() instanceof LabelImageElement)
{
LabelImageElement element = (LabelImageElement) treeItem.getTreerow().getFirstChild().getFirstChild();
image = element.getImage();
}
}
return image != null ? image.intern() : null;
}
public void create(Component parent) {
refreshModel();
layout = new Vlayout();
layout.setHeight("100%");
parent.appendChild(layout);
Label label = new Label(Util.cleanAmp(Msg.getMsg(Env.getCtx(),"Menu")));
label.setStyle("padding: 3px; font-weight: bold; display: block;");
layout.appendChild(label);
listbox = new Listbox();
listbox.setEmptyMessage(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "FindZeroRecords")));
listbox.setStyle("border: none");
listbox.setWidth("500px");
layout.appendChild(listbox);
listbox.setItemRenderer(new MenuItemRenderer());
listbox.addEventListener(Events.ON_SELECT, this);
listbox.addEventListener(ON_POST_SELECT_TREEITEM_EVENT, this);
ListHead listhead = new ListHead();
listbox.appendChild(listhead);
Listheader listheader = new Listheader();
listhead.appendChild(listheader);
listheader = new Listheader();
listheader.setWidth("32px");
listhead.appendChild(listheader);
layout.addEventListener(ON_SEARCH_ECHO, this);
layout.addEventListener(ON_LOAD_MORE, this);
updateListboxModel(model);
}
@Override
public void onEvent(Event event) throws Exception {
if (Events.ON_SELECT.equals(event.getName())) {
ListItem selected = listbox.getSelectedItem();
if (selected == null) return;
onSelect(selected, Boolean.FALSE);
} else if (event.getName().equals(ON_POST_SELECT_TREEITEM_EVENT)) {
onPostSelectTreeitem((Boolean) event.getData());
} else if (Events.ON_CLICK.equals(event.getName())) {
if (event.getTarget() instanceof ListItem) {
ListItem item = (ListItem) event.getTarget();
Long onSelect = (Long) item.getAttribute(ONSELECT_TIMESTAMP);
if (onSelect == null) {
onSelect(item, Boolean.FALSE);
} else if (System.currentTimeMillis() - onSelect.longValue() > 1000) {
onSelect(item, Boolean.FALSE);
}
} else if (event.getTarget() instanceof Toolbarbutton) {
ListItem item = null;
Component parent = event.getTarget();
while (parent != null) {
if (parent instanceof ListItem) {
item = (ListItem) parent;
break;
}
parent = parent.getParent();
}
if (item != null) {
onSelect(item, Boolean.TRUE);
}
}
} else if (event.getName().equals(ON_SEARCH_ECHO)) {
onSearchEcho((String) event.getData());
} else if (event.getName().equals(ON_LOAD_MORE)) {
loadMore();
}
}
private void onSelect(ListItem selected, Boolean newRecord) {
MenuItem item = selected.getValue();
if (item == null) return;
if ("...".equals(item.getType())) {
selected.setAttribute(ONSELECT_TIMESTAMP, System.currentTimeMillis());
Clients.showBusy(selected, null);
Events.echoEvent(ON_LOAD_MORE, layout, null);
} else {
selectTreeitem(item.getData(), newRecord);
selected.setAttribute(ONSELECT_TIMESTAMP, System.currentTimeMillis());
}
}
private void loadMore() {
ListModel<MenuItem> listModel = listbox.getModel();
ListModelList<MenuItem> lml = (ListModelList<MenuItem>) listModel;
lml.remove(lml.size()-1);
List<MenuItem> subList = fullModel.subList(50, fullModel.size());
lml.addAll(subList);
fullModel = null;
listbox.setSelectedIndex(50);
Clients.scrollIntoView(listbox.getSelectedItem());
}
private void selectTreeitem(Object node, Boolean newRecord) {
if (Executions.getCurrent().getAttribute(listbox.getUuid()+".selectTreeitem") != null)
return;
Treeitem treeItem = null;
if (node == null) {
return;
} else if (node instanceof Treeitem) {
treeItem = (Treeitem) node;
} else {
DefaultTreeNode<?> sNode = (DefaultTreeNode<?>) node;
int[] path = tree.getModel().getPath(sNode);
treeItem = tree.renderItemByPath(path);
tree.setSelectedItem(treeItem);
}
if (treeItem != null) {
Executions.getCurrent().setAttribute(listbox.getUuid()+".selectTreeitem", Boolean.TRUE);
select(treeItem);
Events.postEvent(ON_POST_SELECT_TREEITEM_EVENT, listbox, newRecord);
}
}
private void select(Treeitem selectedItem) {
Treeitem parent = selectedItem.getParentItem();
while (parent != null) {
if (!parent.isOpen())
parent.setOpen(true);
parent = parent.getParentItem();
}
selectedItem.getTree().setSelectedItem(selectedItem);
}
private void onPostSelectTreeitem(Boolean newRecord) {
Event event = null;
if (tree.getSelectedItem().getTreerow().getFirstChild().getFirstChild() instanceof A)
{
event = new Event(Events.ON_CLICK, tree.getSelectedItem().getTreerow().getFirstChild().getFirstChild(), newRecord);
}
else
{
event = new Event(Events.ON_CLICK, tree.getSelectedItem().getTreerow(), newRecord);
}
Events.postEvent(event);
}
public void search(String value) {
listbox.setVisible(false);
Events.echoEvent(ON_SEARCH_ECHO, layout, value);
}
public void onSearchEcho(String value) {
ListModelList<MenuItem> newModel = null;
if (Util.isEmpty(value)) {
newModel = model;
} else {
@SuppressWarnings("unchecked")
ListSubModel<MenuItem> subModel = (ListSubModel<MenuItem>) ListModels.toListSubModel(model, new MenuListComparator(value), model.size());
newModel = (ListModelList<MenuItem>) subModel.getSubModel(null, -1);
}
updateListboxModel(newModel);
listbox.setVisible(true);
}
private void updateListboxModel(ListModelList<MenuItem> newModel) {
fullModel = null;
if (newModel.size() > 50) {
List<MenuItem> list = newModel.getInnerList();
List<MenuItem> subList = list.subList(0, 50);
fullModel = newModel;
newModel = new ListModelList<MenuItem>(subList.toArray(new MenuItem[0]));
MenuItem more = new MenuItem();
more.setLabel("...");
more.setType("...");
newModel.add(more);
}
listbox.setModel(newModel);
}
private class MenuListComparator implements Comparator<MenuItem> {
private String compare;
private MenuListComparator(String compare) {
this.compare = compare;
}
@Override
public int compare(MenuItem o1, MenuItem o2) {
compare = compare.toLowerCase().trim();
boolean match = false;
if (compare.length() < 3)
{
match = o2.getLabel().toLowerCase().startsWith(compare);
}
else
{
match = o2.getLabel().toLowerCase().contains(compare);
}
return match ? 0 : -1;
}
}
public MenuItem selectPrior() {
int i = listbox.getSelectedIndex();
if (i > 0) {
listbox.setSelectedIndex(i-1);
ListItem selected = listbox.getSelectedItem();
if (selected == null) return null;
Clients.scrollIntoView(selected);
MenuItem item = selected.getValue();
return item;
}
return null;
}
public MenuItem selectNext() {
int i = listbox.getSelectedIndex();
if (i < 0 && listbox.getItemCount() > 0) {
listbox.setSelectedIndex(0);
ListItem selected = listbox.getSelectedItem();
if (selected == null) return null;
MenuItem item = selected.getValue();
return item;
} else if (i+1 < listbox.getItemCount()) {
listbox.setSelectedIndex(i+1);
ListItem selected = listbox.getSelectedItem();
if (selected == null) return null;
MenuItem item = selected.getValue();
if (item == null) return null;
if ("...".equals(item.getType())) {
onSelect(selected, Boolean.FALSE);
}
Clients.scrollIntoView(selected);
return item;
}
return null;
}
public boolean onOk(Textbox textbox) {
String text = textbox.getText();
if (Util.isEmpty(text))
return false;
text = text.toLowerCase();
ListItem exact = null;
ListItem firstStart = null;
int count = listbox.getItemCount();
for(int i = 0; i < count; i++) {
ListItem item = listbox.getItemAtIndex(i);
String label = item.getLabel();
if (Util.isEmpty(label)) continue;
if (label.equalsIgnoreCase(text)) {
exact = item;
break;
} else if (firstStart == null && label.toLowerCase().startsWith(text) && text.length() >= 3) {
firstStart = item;
}
}
if (exact != null) {
textbox.setText(exact.getLabel());
onSelect(exact, false);
return true;
} else if (firstStart != null) {
textbox.setText(firstStart.getLabel());
onSelect(firstStart, false);
return true;
}
return false;
}
private class MenuItemRenderer implements ListitemRenderer<MenuItem>, ListitemRendererExt {
@Override
public Listitem newListitem(org.zkoss.zul.Listbox listbox) {
return new ListItem();
}
@Override
public Listcell newListcell(Listitem item) {
return new Listcell(item.getLabel());
}
@Override
public int getControls() {
return ListitemRendererExt.DETACH_ON_RENDER;
}
@Override
public void render(Listitem item, MenuItem data, int index)
throws Exception {
Listcell cell = new Listcell(data.getLabel(), data.getImage());
item.appendChild(cell);
cell.setTooltip(data.getDescription());
item.setValue(data);
item.addEventListener(Events.ON_CLICK, MenuSearchController.this);
cell = new Listcell();
item.appendChild(cell);
boolean isWindow = data.getType() != null && data.getType().equals("window");
if (isWindow) {
Toolbarbutton newBtn = new Toolbarbutton(null, ThemeManager.getThemeResource("images/New16.png"));
newBtn.addEventListener(Events.ON_CLICK, MenuSearchController.this);
newBtn.setSclass("fav-new-btn");
newBtn.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "New")));
cell.appendChild(newBtn);
}
}
}
}

View File

@ -17,6 +17,8 @@
package org.adempiere.webui.panel;
import org.adempiere.webui.apps.GlobalSearch;
import org.adempiere.webui.apps.MenuSearchController;
import org.adempiere.webui.component.Panel;
import org.adempiere.webui.theme.ThemeManager;
import org.adempiere.webui.window.AboutWindow;
@ -49,6 +51,8 @@ public class HeaderPanel extends Panel implements EventListener<Event>
protected LabelImageElement btnMenu;
protected Popup popMenu;
private MenuTreePanel menuTreePanel;
public HeaderPanel()
{
super();
@ -62,9 +66,9 @@ public class HeaderPanel extends Panel implements EventListener<Event>
image.addEventListener(Events.ON_CLICK, this);
image.setStyle("cursor: pointer;");
createSearchPanel();
createPopupMenu();
createSearchPanel();
btnMenu = (LabelImageElement) getFellow("menuButton");
btnMenu.setLabel(Util.cleanAmp(Msg.getMsg(Env.getCtx(),"Menu")));
@ -74,7 +78,7 @@ public class HeaderPanel extends Panel implements EventListener<Event>
protected void createPopupMenu() {
popMenu = new Popup();
popMenu.setId("menuTreePopup");
new MenuTreePanel(popMenu);
menuTreePanel = new MenuTreePanel(popMenu);
popMenu.setSclass("desktop-menu-popup");
popMenu.setHeight("90%");
popMenu.setWidth("600px");
@ -82,11 +86,11 @@ public class HeaderPanel extends Panel implements EventListener<Event>
}
protected void createSearchPanel() {
MenuSearchPanel menuSearchPanel = new MenuSearchPanel(this);
GlobalSearch globalSearch = new GlobalSearch(new MenuSearchController(menuTreePanel.getMenuTree()));
Component stub = getFellow("menuLookup");
stub.getParent().insertBefore(menuSearchPanel, stub);
stub.getParent().insertBefore(globalSearch, stub);
stub.detach();
menuSearchPanel.setId("menuLookup");
globalSearch.setId("menuLookup");
}
public void onEvent(Event event) throws Exception {

View File

@ -1376,6 +1376,18 @@ tbody.z-grid-empty-body td {
text-align: left;
}
tbody.z-listbox-empty-body td {
text-align: left;
}
div.z-listbox-body .z-listcell {
padding: 2px;
}
.z-listbox-autopaging .z-listcell-cnt {
height: 20px;
}
<%-- notification message --%>
.z-notification .z-notification-cl,
.z-notification .z-notification-cnt {