From 543153c40780f36f48cdb238d3c06ebacce2687c Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Thu, 21 Aug 2014 00:05:50 +0800 Subject: [PATCH] IDEMPIERE-2050 Improvement to Menu Lookup. Add back support for search definition transaction code. Separate search definition into a separate tab so there's no performance penalty for menu. Added keyboard navigation ( up and down arrow ) for the search definition result list. IDEMPIERE-2095 Menu Lookup return records from another client --- .../webui/apps/DocumentSearchController.java | 60 +++++++++- .../adempiere/webui/apps/GlobalSearch.java | 110 ++++++++++++++---- .../webui/apps/MenuSearchController.java | 10 +- .../theme/default/css/theme.css.dsp | 9 ++ 4 files changed, 153 insertions(+), 36 deletions(-) diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/DocumentSearchController.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/DocumentSearchController.java index e88cfaf869..13ca738621 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/DocumentSearchController.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/DocumentSearchController.java @@ -54,6 +54,8 @@ public class DocumentSearchController implements EventListener{ private static final String SEARCH_RESULT = "search.result"; private static final String ON_SEARCH_DOCUMENTS = "onSearchDocuments"; private Vlayout layout; + private ArrayList list; + private int selected = -1; /** * @@ -64,7 +66,8 @@ public class DocumentSearchController implements EventListener{ public void create(Component parent) { layout = new Vlayout(); layout.setStyle("padding: 3px;"); - layout.setWidth("200px"); + layout.setWidth("100%"); + layout.setVflex("true"); parent.appendChild(layout); @@ -77,6 +80,7 @@ public class DocumentSearchController implements EventListener{ } private void onSearchDocuments(String searchString) { + list = new ArrayList(); if (Util.isEmpty(searchString)) { return; } @@ -113,8 +117,8 @@ public class DocumentSearchController implements EventListener{ private List doSearch(String searchString) { final MRole role = MRole.get(Env.getCtx(), Env.getAD_Role_ID(Env.getCtx()), Env.getAD_User_ID(Env.getCtx()), true); - - List list = new ArrayList(); + + selected = -1; Query query = new Query(Env.getCtx(), I_AD_SearchDefinition.Table_Name, "", null); List definitions = query.setOnlyActiveRecords(true).list(); for(MSearchDefinition msd : definitions) { @@ -140,6 +144,7 @@ public class DocumentSearchController implements EventListener{ } else { sql.append("WHERE UPPER(").append(column.getColumnName()).append(") LIKE UPPER(?)"); } + sql.append(" AND AD_Client_ID=@AD_Client_ID@ "); // search for a Integer if (msd.getDataType().equals(MSearchDefinition.DATATYPE_INTEGER)) { @@ -198,6 +203,13 @@ public class DocumentSearchController implements EventListener{ String sql = builder.toString(); if (!Util.isEmpty(extraWhereClase)) sql = sql + extraWhereClase; + //@@ is full text search operator for postgresql + boolean hasFullTextOperator = sql.indexOf("@@") >= 0; + if (hasFullTextOperator) + sql = sql.replace("@@", "~!#$*"); + sql = Env.parseContext(Env.getCtx(), -1, sql, false, true); + if (hasFullTextOperator) + sql = sql.replace("~!#$*", "@@"); pstmt = DB.prepareStatement(sql, (String)null); if (params.size() > 0) DB.setParameters(pstmt, params); @@ -245,7 +257,7 @@ public class DocumentSearchController implements EventListener{ AEnv.zoom(result.getWindowId(), query); } - private class SearchResult { + public static class SearchResult { private String windowName; private int windowId; private String tableName; @@ -362,4 +374,44 @@ public class DocumentSearchController implements EventListener{ return false; } + + public SearchResult selectPrior() { + if (selected > 0) { + selected--; + SearchResult result = list.get(selected); + List links = layout.getChildren(); + for(Component link : links) { + if (link instanceof A) { + A a = (A) link; + if (result.getLabel().equals(a.getLabel())) { + a.setSclass("document-search-current-link"); + } else if ("document-search-current-link".equals(a.getSclass())) { + a.setSclass(null); + } + } + } + return result; + } + return null; + } + + public SearchResult selectNext() { + if (selected < (list.size()-1)) { + selected++; + SearchResult result = list.get(selected); + List links = layout.getChildren(); + for(Component link : links) { + if (link instanceof A) { + A a = (A) link; + if (result.getLabel().equals(a.getLabel())) { + a.setSclass("document-search-current-link"); + } else if ("document-search-current-link".equals(a.getSclass())) { + a.setSclass(null); + } + } + } + return result; + } + return null; + } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/GlobalSearch.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/GlobalSearch.java index e14eeb8c39..cc0b8f8b07 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/GlobalSearch.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/GlobalSearch.java @@ -13,7 +13,17 @@ *****************************************************************************/ package org.adempiere.webui.apps; +import org.adempiere.webui.apps.DocumentSearchController.SearchResult; import org.adempiere.webui.component.Bandbox; +import org.adempiere.webui.component.Tab; +import org.adempiere.webui.component.Tabbox; +import org.adempiere.webui.component.Tabpanel; +import org.adempiere.webui.component.Tabpanels; +import org.adempiere.webui.component.Tabs; +import org.adempiere.webui.util.DocumentSearch; +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.compiere.util.Util; import org.zkoss.zk.ui.Page; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; @@ -23,8 +33,6 @@ 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 @@ -40,6 +48,8 @@ public class GlobalSearch extends Div implements EventListener { private static final String ON_SEARCH = "onSearch"; + private static final String PREFIX_DOCUMENT_SEARCH = "/"; + /** * generated serial id */ @@ -50,6 +60,8 @@ public class GlobalSearch extends Div implements EventListener { private MenuSearchController menuController; private DocumentSearchController docController; + private Tabbox tabbox; + /** * */ @@ -61,26 +73,43 @@ public class GlobalSearch extends Div implements EventListener { private void init() { bandbox = new Bandbox(); + bandbox.setSclass("global-search-box"); appendChild(bandbox); bandbox.setWidth("100%"); bandbox.setAutodrop(true); bandbox.addEventListener(Events.ON_CHANGING, this); + bandbox.addEventListener(Events.ON_CHANGE, this); bandbox.setCtrlKeys("#up#down"); bandbox.addEventListener(Events.ON_CTRL_KEY, this); Bandpopup popup = new Bandpopup(); - popup.setHeight("600px"); + popup.setHeight("500px"); bandbox.appendChild(popup); - Hlayout hlayout = new Hlayout(); - popup.appendChild(hlayout); - menuController.create(hlayout); + tabbox = new Tabbox(); + tabbox.setVflex("true"); + tabbox.addEventListener(Events.ON_SELECT, this); + Tabs tabs = new Tabs(); + tabbox.appendChild(tabs); + Tab tab = new Tab(); + tab.setLabel(Util.cleanAmp(Msg.getMsg(Env.getCtx(),"Menu"))); + tabs.appendChild(tab); + Tabpanels tabPanels = new Tabpanels(); + tabbox.appendChild(tabPanels); + Tabpanel tabPanel = new Tabpanel(); + tabPanel.setVflex("true"); + tabPanel.setSclass("global-search-tabpanel"); + tabPanels.appendChild(tabPanel); + popup.appendChild(tabbox); + menuController.create(tabPanel); - Separator separator = new Separator(); - separator.setHflex("0"); - separator.setOrient("vertical"); - hlayout.appendChild(separator); - docController.create(hlayout); + tab = new Tab(); + tab.setLabel(Util.cleanAmp(Msg.getMsg(Env.getCtx(),"Document"))); + tabs.appendChild(tab); + tabPanel = new Tabpanel(); + tabPanel.setSclass("global-search-tabpanel"); + tabPanels.appendChild(tabPanel); + docController.create(tabPanel); addEventListener(ON_SEARCH, this); addEventListener(ON_CREATE_ECHO, this); @@ -90,31 +119,50 @@ public class GlobalSearch extends Div implements EventListener { @Override public void onEvent(Event event) throws Exception { - if (Events.ON_CHANGING.equals(event.getName())) { + if (Events.ON_CHANGING.equals(event.getName())) { InputEvent inputEvent = (InputEvent) event; String value = inputEvent.getValue(); - Events.postEvent(ON_SEARCH, this, value); + bandbox.setAttribute("last.onchanging", value); + Events.postEvent(ON_SEARCH, this, value); + } else if (Events.ON_CHANGE.equals(event.getName())) { + bandbox.removeAttribute("last.onchanging"); } 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()); + if (tabbox.getSelectedIndex()==0) { + MenuItem selected = menuController.selectPrior(); + if (selected != null) { + bandbox.setText(selected.getLabel()); + } + } else { + SearchResult selected = docController.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()); + if (tabbox.getSelectedIndex()==0) { + MenuItem selected = menuController.selectNext(); + if (selected != null && !"...".equals(selected.getType())) { + bandbox.setText(selected.getLabel()); + } + } else { + SearchResult selected = docController.selectNext(); + if (selected != null) { + bandbox.setText(selected.getLabel()); + } } } } } else if (event.getName().equals(ON_SEARCH)) { String value = (String) event.getData(); - menuController.search(value); - docController.search(value); + if (tabbox.getSelectedIndex()==0) + menuController.search(value); + else + docController.search(value); bandbox.focus(); } else if (event.getName().equals(ON_CREATE_ECHO)) { StringBuilder script = new StringBuilder("jq('#") @@ -132,11 +180,23 @@ public class GlobalSearch extends Div implements EventListener { 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); + if (bandbox.getValue() != null && bandbox.getValue().startsWith(PREFIX_DOCUMENT_SEARCH)) { + DocumentSearch search = new DocumentSearch(); + if (search.openDocumentsByDocumentNo(bandbox.getValue().substring(1))) + bandbox.setText(null); + } else { + if (tabbox.getSelectedIndex()==0) { + menuController.onOk(bandbox); + } else { + docController.onOk(bandbox); + } } + } else if (event.getName().equals(Events.ON_SELECT)) { + String value = (String) bandbox.getAttribute("last.onchanging"); + if (value == null) { + value = bandbox.getValue(); + } + Events.postEvent(ON_SEARCH, this, value); } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/MenuSearchController.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/MenuSearchController.java index d4ab059688..2e81062e73 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/MenuSearchController.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/MenuSearchController.java @@ -17,7 +17,6 @@ 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; @@ -182,13 +181,11 @@ public class MenuSearchController implements EventListener{ 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"); + listbox.setWidth("100%"); + listbox.setVflex("true"); layout.appendChild(listbox); listbox.setItemRenderer(new MenuItemRenderer()); listbox.addEventListener(Events.ON_SELECT, this); @@ -316,7 +313,7 @@ public class MenuSearchController implements EventListener{ } public void search(String value) { - listbox.setVisible(false); + listbox.setModel((ListModel)null); Events.echoEvent(ON_SEARCH_ECHO, layout, value); } @@ -330,7 +327,6 @@ public class MenuSearchController implements EventListener{ newModel = (ListModelList) subModel.getSubModel(null, -1); } updateListboxModel(newModel); - listbox.setVisible(true); } private void updateListboxModel(ListModelList newModel) { diff --git a/org.adempiere.ui.zk/theme/default/css/theme.css.dsp b/org.adempiere.ui.zk/theme/default/css/theme.css.dsp index f4687241b6..c59de64820 100644 --- a/org.adempiere.ui.zk/theme/default/css/theme.css.dsp +++ b/org.adempiere.ui.zk/theme/default/css/theme.css.dsp @@ -1676,6 +1676,15 @@ font-size: 0; height: 16px; width: 16px; } + +.document-search-current-link { + background-image:url(${c:encodeThemeURL('~./zul/img/tree/item-sel.gif')}); +} + +.global-search-tabpanel { + width: 500px; +} + <%-- workaround for http://jira.idempiere.com/browse/IDEMPIERE-692 --%> .z-combobox-pp { max-height: 200px;