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:
parent
fa3a9fa428
commit
543153c407
|
@ -54,6 +54,8 @@ public class DocumentSearchController implements EventListener<Event>{
|
||||||
private static final String SEARCH_RESULT = "search.result";
|
private static final String SEARCH_RESULT = "search.result";
|
||||||
private static final String ON_SEARCH_DOCUMENTS = "onSearchDocuments";
|
private static final String ON_SEARCH_DOCUMENTS = "onSearchDocuments";
|
||||||
private Vlayout layout;
|
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) {
|
public void create(Component parent) {
|
||||||
layout = new Vlayout();
|
layout = new Vlayout();
|
||||||
layout.setStyle("padding: 3px;");
|
layout.setStyle("padding: 3px;");
|
||||||
layout.setWidth("200px");
|
layout.setWidth("100%");
|
||||||
|
layout.setVflex("true");
|
||||||
|
|
||||||
parent.appendChild(layout);
|
parent.appendChild(layout);
|
||||||
|
|
||||||
|
@ -77,6 +80,7 @@ public class DocumentSearchController implements EventListener<Event>{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onSearchDocuments(String searchString) {
|
private void onSearchDocuments(String searchString) {
|
||||||
|
list = new ArrayList<SearchResult>();
|
||||||
if (Util.isEmpty(searchString)) {
|
if (Util.isEmpty(searchString)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -113,8 +117,8 @@ public class DocumentSearchController implements EventListener<Event>{
|
||||||
|
|
||||||
private List<SearchResult> doSearch(String searchString) {
|
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);
|
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);
|
Query query = new Query(Env.getCtx(), I_AD_SearchDefinition.Table_Name, "", null);
|
||||||
List<MSearchDefinition> definitions = query.setOnlyActiveRecords(true).list();
|
List<MSearchDefinition> definitions = query.setOnlyActiveRecords(true).list();
|
||||||
for(MSearchDefinition msd : definitions) {
|
for(MSearchDefinition msd : definitions) {
|
||||||
|
@ -140,6 +144,7 @@ public class DocumentSearchController implements EventListener<Event>{
|
||||||
} else {
|
} else {
|
||||||
sql.append("WHERE UPPER(").append(column.getColumnName()).append(") LIKE UPPER(?)");
|
sql.append("WHERE UPPER(").append(column.getColumnName()).append(") LIKE UPPER(?)");
|
||||||
}
|
}
|
||||||
|
sql.append(" AND AD_Client_ID=@AD_Client_ID@ ");
|
||||||
|
|
||||||
// search for a Integer
|
// search for a Integer
|
||||||
if (msd.getDataType().equals(MSearchDefinition.DATATYPE_INTEGER)) {
|
if (msd.getDataType().equals(MSearchDefinition.DATATYPE_INTEGER)) {
|
||||||
|
@ -198,6 +203,13 @@ public class DocumentSearchController implements EventListener<Event>{
|
||||||
String sql = builder.toString();
|
String sql = builder.toString();
|
||||||
if (!Util.isEmpty(extraWhereClase))
|
if (!Util.isEmpty(extraWhereClase))
|
||||||
sql = sql + 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);
|
pstmt = DB.prepareStatement(sql, (String)null);
|
||||||
if (params.size() > 0)
|
if (params.size() > 0)
|
||||||
DB.setParameters(pstmt, params);
|
DB.setParameters(pstmt, params);
|
||||||
|
@ -245,7 +257,7 @@ public class DocumentSearchController implements EventListener<Event>{
|
||||||
AEnv.zoom(result.getWindowId(), query);
|
AEnv.zoom(result.getWindowId(), query);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SearchResult {
|
public static class SearchResult {
|
||||||
private String windowName;
|
private String windowName;
|
||||||
private int windowId;
|
private int windowId;
|
||||||
private String tableName;
|
private String tableName;
|
||||||
|
@ -362,4 +374,44 @@ public class DocumentSearchController implements EventListener<Event>{
|
||||||
|
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,17 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
package org.adempiere.webui.apps;
|
package org.adempiere.webui.apps;
|
||||||
|
|
||||||
|
import org.adempiere.webui.apps.DocumentSearchController.SearchResult;
|
||||||
import org.adempiere.webui.component.Bandbox;
|
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.Page;
|
||||||
import org.zkoss.zk.ui.event.Event;
|
import org.zkoss.zk.ui.event.Event;
|
||||||
import org.zkoss.zk.ui.event.EventListener;
|
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.zk.ui.util.Clients;
|
||||||
import org.zkoss.zul.Bandpopup;
|
import org.zkoss.zul.Bandpopup;
|
||||||
import org.zkoss.zul.Div;
|
import org.zkoss.zul.Div;
|
||||||
import org.zkoss.zul.Hlayout;
|
|
||||||
import org.zkoss.zul.Separator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author hengsin
|
* @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 ON_SEARCH = "onSearch";
|
||||||
|
|
||||||
|
private static final String PREFIX_DOCUMENT_SEARCH = "/";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* generated serial id
|
* generated serial id
|
||||||
*/
|
*/
|
||||||
|
@ -50,6 +60,8 @@ public class GlobalSearch extends Div implements EventListener<Event> {
|
||||||
private MenuSearchController menuController;
|
private MenuSearchController menuController;
|
||||||
private DocumentSearchController docController;
|
private DocumentSearchController docController;
|
||||||
|
|
||||||
|
private Tabbox tabbox;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -61,26 +73,43 @@ public class GlobalSearch extends Div implements EventListener<Event> {
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
bandbox = new Bandbox();
|
bandbox = new Bandbox();
|
||||||
|
bandbox.setSclass("global-search-box");
|
||||||
appendChild(bandbox);
|
appendChild(bandbox);
|
||||||
bandbox.setWidth("100%");
|
bandbox.setWidth("100%");
|
||||||
bandbox.setAutodrop(true);
|
bandbox.setAutodrop(true);
|
||||||
bandbox.addEventListener(Events.ON_CHANGING, this);
|
bandbox.addEventListener(Events.ON_CHANGING, this);
|
||||||
|
bandbox.addEventListener(Events.ON_CHANGE, this);
|
||||||
bandbox.setCtrlKeys("#up#down");
|
bandbox.setCtrlKeys("#up#down");
|
||||||
bandbox.addEventListener(Events.ON_CTRL_KEY, this);
|
bandbox.addEventListener(Events.ON_CTRL_KEY, this);
|
||||||
|
|
||||||
Bandpopup popup = new Bandpopup();
|
Bandpopup popup = new Bandpopup();
|
||||||
popup.setHeight("600px");
|
popup.setHeight("500px");
|
||||||
bandbox.appendChild(popup);
|
bandbox.appendChild(popup);
|
||||||
|
|
||||||
Hlayout hlayout = new Hlayout();
|
tabbox = new Tabbox();
|
||||||
popup.appendChild(hlayout);
|
tabbox.setVflex("true");
|
||||||
menuController.create(hlayout);
|
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();
|
tab = new Tab();
|
||||||
separator.setHflex("0");
|
tab.setLabel(Util.cleanAmp(Msg.getMsg(Env.getCtx(),"Document")));
|
||||||
separator.setOrient("vertical");
|
tabs.appendChild(tab);
|
||||||
hlayout.appendChild(separator);
|
tabPanel = new Tabpanel();
|
||||||
docController.create(hlayout);
|
tabPanel.setSclass("global-search-tabpanel");
|
||||||
|
tabPanels.appendChild(tabPanel);
|
||||||
|
docController.create(tabPanel);
|
||||||
|
|
||||||
addEventListener(ON_SEARCH, this);
|
addEventListener(ON_SEARCH, this);
|
||||||
addEventListener(ON_CREATE_ECHO, this);
|
addEventListener(ON_CREATE_ECHO, this);
|
||||||
|
@ -90,31 +119,50 @@ public class GlobalSearch extends Div implements EventListener<Event> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(Event event) throws Exception {
|
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;
|
InputEvent inputEvent = (InputEvent) event;
|
||||||
String value = inputEvent.getValue();
|
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())) {
|
} else if (Events.ON_CTRL_KEY.equals(event.getName())) {
|
||||||
KeyEvent ke = (KeyEvent) event;
|
KeyEvent ke = (KeyEvent) event;
|
||||||
if (ke.getKeyCode() == KeyEvent.UP) {
|
if (ke.getKeyCode() == KeyEvent.UP) {
|
||||||
if (bandbox.getFirstChild().isVisible()) {
|
if (bandbox.getFirstChild().isVisible()) {
|
||||||
MenuItem selected = menuController.selectPrior();
|
if (tabbox.getSelectedIndex()==0) {
|
||||||
if (selected != null) {
|
MenuItem selected = menuController.selectPrior();
|
||||||
bandbox.setText(selected.getLabel());
|
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) {
|
} else if (ke.getKeyCode() == KeyEvent.DOWN) {
|
||||||
if (bandbox.getFirstChild().isVisible()) {
|
if (bandbox.getFirstChild().isVisible()) {
|
||||||
MenuItem selected = menuController.selectNext();
|
if (tabbox.getSelectedIndex()==0) {
|
||||||
if (selected != null && !"...".equals(selected.getType())) {
|
MenuItem selected = menuController.selectNext();
|
||||||
bandbox.setText(selected.getLabel());
|
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)) {
|
} else if (event.getName().equals(ON_SEARCH)) {
|
||||||
String value = (String) event.getData();
|
String value = (String) event.getData();
|
||||||
menuController.search(value);
|
if (tabbox.getSelectedIndex()==0)
|
||||||
docController.search(value);
|
menuController.search(value);
|
||||||
|
else
|
||||||
|
docController.search(value);
|
||||||
bandbox.focus();
|
bandbox.focus();
|
||||||
} else if (event.getName().equals(ON_CREATE_ECHO)) {
|
} else if (event.getName().equals(ON_CREATE_ECHO)) {
|
||||||
StringBuilder script = new StringBuilder("jq('#")
|
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);
|
Events.echoEvent(ON_POST_ENTER_KEY, this, null);
|
||||||
} else if (event.getName().equals(ON_POST_ENTER_KEY)) {
|
} else if (event.getName().equals(ON_POST_ENTER_KEY)) {
|
||||||
Clients.clearBusy(bandbox);
|
Clients.clearBusy(bandbox);
|
||||||
if (menuController.onOk(bandbox)) {
|
if (bandbox.getValue() != null && bandbox.getValue().startsWith(PREFIX_DOCUMENT_SEARCH)) {
|
||||||
return;
|
DocumentSearch search = new DocumentSearch();
|
||||||
} else {
|
if (search.openDocumentsByDocumentNo(bandbox.getValue().substring(1)))
|
||||||
docController.onOk(bandbox);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.adempiere.webui.component.Label;
|
|
||||||
import org.adempiere.webui.component.ListHead;
|
import org.adempiere.webui.component.ListHead;
|
||||||
import org.adempiere.webui.component.ListItem;
|
import org.adempiere.webui.component.ListItem;
|
||||||
import org.adempiere.webui.component.Listbox;
|
import org.adempiere.webui.component.Listbox;
|
||||||
|
@ -182,13 +181,11 @@ public class MenuSearchController implements EventListener<Event>{
|
||||||
layout.setHeight("100%");
|
layout.setHeight("100%");
|
||||||
parent.appendChild(layout);
|
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 = new Listbox();
|
||||||
listbox.setEmptyMessage(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "FindZeroRecords")));
|
listbox.setEmptyMessage(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "FindZeroRecords")));
|
||||||
listbox.setStyle("border: none");
|
listbox.setStyle("border: none");
|
||||||
listbox.setWidth("500px");
|
listbox.setWidth("100%");
|
||||||
|
listbox.setVflex("true");
|
||||||
layout.appendChild(listbox);
|
layout.appendChild(listbox);
|
||||||
listbox.setItemRenderer(new MenuItemRenderer());
|
listbox.setItemRenderer(new MenuItemRenderer());
|
||||||
listbox.addEventListener(Events.ON_SELECT, this);
|
listbox.addEventListener(Events.ON_SELECT, this);
|
||||||
|
@ -316,7 +313,7 @@ public class MenuSearchController implements EventListener<Event>{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void search(String value) {
|
public void search(String value) {
|
||||||
listbox.setVisible(false);
|
listbox.setModel((ListModel)null);
|
||||||
Events.echoEvent(ON_SEARCH_ECHO, layout, value);
|
Events.echoEvent(ON_SEARCH_ECHO, layout, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,7 +327,6 @@ public class MenuSearchController implements EventListener<Event>{
|
||||||
newModel = (ListModelList<MenuItem>) subModel.getSubModel(null, -1);
|
newModel = (ListModelList<MenuItem>) subModel.getSubModel(null, -1);
|
||||||
}
|
}
|
||||||
updateListboxModel(newModel);
|
updateListboxModel(newModel);
|
||||||
listbox.setVisible(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateListboxModel(ListModelList<MenuItem> newModel) {
|
private void updateListboxModel(ListModelList<MenuItem> newModel) {
|
||||||
|
|
|
@ -1676,6 +1676,15 @@ font-size: 0;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
width: 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 --%>
|
<%-- workaround for http://jira.idempiere.com/browse/IDEMPIERE-692 --%>
|
||||||
.z-combobox-pp {
|
.z-combobox-pp {
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
|
|
Loading…
Reference in New Issue