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
This commit is contained in:
Heng Sin Low 2014-08-21 00:05:50 +08:00
parent fa3a9fa428
commit 543153c407
4 changed files with 153 additions and 36 deletions

View File

@ -54,6 +54,8 @@ 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;
private ArrayList<SearchResult> list;
private int selected = -1;
/**
*
@ -64,7 +66,8 @@ public class DocumentSearchController implements EventListener<Event>{
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<Event>{
}
private void onSearchDocuments(String searchString) {
list = new ArrayList<SearchResult>();
if (Util.isEmpty(searchString)) {
return;
}
@ -113,8 +117,8 @@ public class DocumentSearchController implements EventListener<Event>{
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>();
selected = -1;
Query query = new Query(Env.getCtx(), I_AD_SearchDefinition.Table_Name, "", null);
List<MSearchDefinition> definitions = query.setOnlyActiveRecords(true).list();
for(MSearchDefinition msd : definitions) {
@ -140,6 +144,7 @@ public class DocumentSearchController implements EventListener<Event>{
} 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<Event>{
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<Event>{
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<Event>{
return false;
}
public SearchResult selectPrior() {
if (selected > 0) {
selected--;
SearchResult result = list.get(selected);
List<Component> 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<Component> 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;
}
}

View File

@ -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<Event> {
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<Event> {
private MenuSearchController menuController;
private DocumentSearchController docController;
private Tabbox tabbox;
/**
*
*/
@ -61,26 +73,43 @@ public class GlobalSearch extends Div implements EventListener<Event> {
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<Event> {
@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<Event> {
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);
}
}

View File

@ -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<Event>{
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<Event>{
}
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<Event>{
newModel = (ListModelList<MenuItem>) subModel.getSubModel(null, -1);
}
updateListboxModel(newModel);
listbox.setVisible(true);
}
private void updateListboxModel(ListModelList<MenuItem> newModel) {

View File

@ -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;