/****************************************************************************** * Copyright (C) 2016 Logilite Technologies LLP * * 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.dashboard; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Set; import java.util.logging.Level; import org.adempiere.exceptions.DBException; import org.adempiere.webui.ClientInfo; import org.adempiere.webui.LayoutUtils; import org.adempiere.webui.component.Button; import org.adempiere.webui.component.ConfirmPanel; import org.adempiere.webui.component.Label; import org.adempiere.webui.component.ListHead; import org.adempiere.webui.component.ListHeader; import org.adempiere.webui.component.ListItem; import org.adempiere.webui.component.Listbox; import org.adempiere.webui.component.Panel; import org.adempiere.webui.component.SimpleListModel; import org.adempiere.webui.component.Window; import org.adempiere.webui.factory.ButtonFactory; import org.adempiere.webui.util.ZKUpdateUtil; import org.compiere.model.MMenu; import org.compiere.model.MRole; import org.compiere.model.MTable; import org.compiere.model.MTreeFavorite; import org.compiere.model.MTreeFavoriteNode; import org.compiere.model.PO; import org.compiere.model.Query; import org.compiere.util.CLogger; import org.compiere.util.Env; import org.compiere.util.Msg; import org.compiere.util.NamePair; import org.zkoss.zk.au.out.AuFocus; import org.zkoss.zk.ui.event.DropEvent; 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.Borderlayout; import org.zkoss.zul.Center; import org.zkoss.zul.Hlayout; import org.zkoss.zul.South; /** * Dialog to manage auto launch favourite items. * * @author Logilite Technologies */ public class LoginOpenSequenceDialog extends Window { /** * generated serial id */ private static final long serialVersionUID = -6200912526954948898L; /** Logger */ protected static final CLogger log = CLogger.getCLogger(LoginOpenSequenceDialog.class); private Button bAdd = ButtonFactory.createNamedButton("Next", false, true); private Button bRemove = ButtonFactory.createNamedButton("Previous", false, true); private Button bUp = ButtonFactory.createNamedButton("Parent", false, true); private Button bDown = ButtonFactory.createNamedButton("Detail", false, true); private ConfirmPanel confirmPanel = new ConfirmPanel(true, false, true, false, false, false); /** model for {@link #noList} */ private SimpleListModel noModel = new SimpleListModel(); /** model for {@link #yesList} */ private SimpleListModel yesModel = new SimpleListModel(); private Listbox noList = new Listbox(); private Listbox yesList = new Listbox(); private Label noLabel = new Label(); private Label yesLabel = new Label(); /** List for auto launch sequence */ private ArrayList autoOpenSeqs = new ArrayList(); private int m_AD_User_ID; /** * Favorite node arrange login open sequence constructor * * @param AD_User_ID */ public LoginOpenSequenceDialog(int AD_User_ID) { m_AD_User_ID = AD_User_ID; this.setTitle(Msg.translate(Env.getCtx(), "LoginOpenSeqNo")); this.setBorder("normal"); this.setPosition("center"); this.setShadow(true); this.setSizable(true); this.setClosable(true); this.setMaximizable(true); this.setSclass("popup-dialog fav-login-open-seq-dialog"); this.setStyle("position: relative; margin: none; border: none; padding: none;"); ZKUpdateUtil.setWindowWidthX(this, 650); ZKUpdateUtil.setWindowHeightX(this, 450); // init(); } // /** * Layout design */ private void init() { // Center Part Hlayout hlayout = new Hlayout(); hlayout.appendChild(noList); hlayout.appendChild(yesList); ZKUpdateUtil.setVflex(hlayout, "1"); ZKUpdateUtil.setHflex(hlayout, "1"); Center center = new Center(); center.setSclass("dialog-content"); center.appendChild(hlayout); // South part Panel southPanel = new Panel(); southPanel.appendChild(confirmPanel); South south = new South(); south.setSclass("dialog-footer"); south.appendChild(southPanel); // Main Layout Borderlayout layout = new Borderlayout(); ZKUpdateUtil.setHflex(layout, "1"); ZKUpdateUtil.setVflex(layout, "1"); layout.appendChild(center); layout.appendChild(south); this.appendChild(layout); // Yes or No list items yesModel.setMultiple(true); noModel.setMultiple(true); noLabel.setValue(Msg.getMsg(Env.getCtx(), "Available")); yesLabel.setValue(Msg.getMsg(Env.getCtx(), "Selected")); ZKUpdateUtil.setHflex(noList, "1"); ZKUpdateUtil.setVflex(noList, true); ZKUpdateUtil.setHflex(yesList, "1"); ZKUpdateUtil.setVflex(yesList, true); EventListener mouseListener = new EventListener() { @Override public void onEvent(Event event) throws Exception { if (Events.ON_DOUBLE_CLICK.equals(event.getName())) { migrateValueAcrossLists(event); } } }; yesList.addDoubleClickListener(mouseListener); noList.addDoubleClickListener(mouseListener); // EventListener actionListener = new EventListener() { @Override public void onEvent(Event event) throws Exception { migrateValueAcrossLists(event); } }; LayoutUtils.addSclass("btn-small", bAdd); LayoutUtils.addSclass("btn-sorttab small-img-btn", bAdd); bAdd.addEventListener(Events.ON_CLICK, actionListener); LayoutUtils.addSclass("btn-small", bRemove); LayoutUtils.addSclass("btn-sorttab small-img-btn", bRemove); bRemove.addEventListener(Events.ON_CLICK, actionListener); EventListener crossListMouseListener = new DragListener(); yesList.addOnDropListener(crossListMouseListener); noList.addOnDropListener(crossListMouseListener); if (!ClientInfo.isMobile()) { yesList.setItemDraggable(true); noList.setItemDraggable(true); } actionListener = new EventListener() { @Override public void onEvent(Event event) throws Exception { migrateValueWithinYesList(event); } }; LayoutUtils.addSclass("btn-small", bUp); LayoutUtils.addSclass("btn-sorttab small-img-btn", bUp); bUp.addEventListener(Events.ON_CLICK, actionListener); LayoutUtils.addSclass("btn-small", bDown); LayoutUtils.addSclass("btn-sorttab small-img-btn", bDown); bDown.addEventListener(Events.ON_CLICK, actionListener); // ListHead listHead = new ListHead(); listHead.setParent(yesList); ListHeader listHeader = new ListHeader(); listHeader.appendChild(yesLabel); Hlayout yesButtonLayout = new Hlayout(); yesButtonLayout.appendChild(bUp); yesButtonLayout.appendChild(bDown); yesButtonLayout.setStyle("display: inline-block; float: right;"); listHeader.appendChild(yesButtonLayout); listHeader.setParent(listHead); // listHead = new ListHead(); listHead.setParent(noList); listHeader = new ListHeader(); listHeader.appendChild(noLabel); listHeader.setSort("auto"); listHeader.setSortDirection("ascending"); // http://books.zkoss.org/wiki/ZK%20Configuration%20Reference/zk.xml/The%20Library%20Properties/org.zkoss.zul.listbox.autoSort noList.setAttribute("org.zkoss.zul.listbox.autoSort", "true"); Hlayout noButtonLayout = new Hlayout(); noButtonLayout.appendChild(bRemove); noButtonLayout.appendChild(bAdd); noButtonLayout.setStyle("display: inline-block; float: right;"); listHeader.appendChild(noButtonLayout); listHeader.setParent(listHead); // Confirm Panel listener EventListener onClickListener = new EventListener() { @Override public void onEvent(Event event) throws Exception { if (event.getTarget().equals(confirmPanel.getButton(ConfirmPanel.A_OK))) { saveData(); } else if (event.getTarget().equals(confirmPanel.getButton(ConfirmPanel.A_CANCEL))) { detach(); } else if (event.getTarget().equals(confirmPanel.getButton(ConfirmPanel.A_RESET))) { loadData(); } } }; confirmPanel.addActionListener(onClickListener); } // init /** * Load data into model */ public void loadData() { autoOpenSeqs.clear(); yesModel.removeAllElements(); noModel.removeAllElements(); int favTreeID = MTreeFavorite.getFavoriteTreeID(m_AD_User_ID); MRole role = MRole.get(Env.getCtx(), Env.getAD_Role_ID(Env.getCtx())); // get login open seq no nodes Query query = new Query(Env.getCtx(), MTreeFavoriteNode.Table_Name, "AD_Tree_Favorite_ID=? AND AD_Menu_ID>0 AND LoginOpenSeqNo>=0", null); query.setOrderBy(MTreeFavoriteNode.COLUMNNAME_LoginOpenSeqNo); query.setOnlyActiveRecords(true); query.setParameters(new Object[] { favTreeID }); for (int favNodeID : query.getIDs()) { autoOpenSeqs.add(favNodeID); } // get all menu nodes query = new Query(Env.getCtx(), MTreeFavoriteNode.Table_Name, "AD_Tree_Favorite_ID=? AND AD_Menu_ID>0 ", null); query.setOrderBy(MTreeFavoriteNode.COLUMNNAME_LoginOpenSeqNo); query.setOnlyActiveRecords(true); query.setParameters(new Object[] { favTreeID }); List lsFavNode = null; try { PO.setCrossTenantSafe(); lsFavNode = query.list(); }finally { PO.clearCrossTenantSafe(); } try { HashMap currSel = new HashMap(); for (MTreeFavoriteNode favNode : lsFavNode) { int key = favNode.getAD_Tree_Favorite_Node_ID(); MMenu menu = (MMenu) MTable.get(Env.getCtx(), MMenu.Table_ID).getPO(favNode.getAD_Menu_ID(), null); Boolean access = MTreeFavorite.getAccessForMenuItem(role, menu); if (access != null && access.booleanValue()) { ListElement pp = new ListElement(key, menu.get_Translation(MMenu.COLUMNNAME_Name)); if (autoOpenSeqs != null && autoOpenSeqs.size() > 0) { if (autoOpenSeqs.contains(key)) { currSel.put(key, pp); } else { noModel.addElement(pp); } } else { noModel.addElement(pp); } } } if (autoOpenSeqs != null) { for (int key : autoOpenSeqs) { if (currSel.get(key) != null) { yesModel.addElement(currSel.get(key)); } } } } catch (DBException e) { log.log(Level.SEVERE, e.getMessage(), e); } bAdd.setEnabled(true); bRemove.setEnabled(true); bUp.setEnabled(true); bDown.setEnabled(true); yesList.setEnabled(true); noList.setEnabled(true); yesList.setItemRenderer(yesModel); yesList.setModel(yesModel); noList.setItemRenderer(noModel); noList.setModel(noModel); } // loadData /** * @param event - Event */ protected void migrateValueAcrossLists(Event event) { Object source = event.getTarget(); if (source instanceof ListItem) { source = ((ListItem) source).getListbox(); } Listbox listFrom = (source == bAdd || source == noList) ? noList : yesList; Listbox listTo = (source == bAdd || source == noList) ? yesList : noList; int endIndex = yesList.getIndexOfItem(listTo.getSelectedItem()); // List to is empty. if (endIndex < 0) endIndex = 0; migrateLists(listFrom, listTo, endIndex); } // migrateValueAcrossLists /** * @param listFrom * @param listTo * @param endIndex */ protected void migrateLists(Listbox listFrom, Listbox listTo, int endIndex) { int index = 0; SimpleListModel lmFrom = (SimpleListModel) listFrom.getModel(); SimpleListModel lmTo = (SimpleListModel) listTo.getModel(); Set selectedItems = listFrom.getSelectedItems(); List selObjects = new ArrayList(); for (Object obj : selectedItems) { ListItem listItem = (ListItem) obj; index = listFrom.getIndexOfItem(listItem); ListElement selObject = (ListElement) lmFrom.getElementAt(index); selObjects.add(selObject); } for (ListElement selObject : selObjects) { if (selObject == null || !selObject.isUpdateable()) continue; lmFrom.removeElement(selObject); lmTo.add(endIndex, selObject); endIndex++; index = lmTo.indexOf(selObject); listTo.setSelectedIndex(index); } } // migrateLists /** * Move within Yes List with Drag Event and Multiple Choice * * @param endIndex * @param selObjects */ protected void migrateValueWithinYesList(int endIndex, List selObjects) { int iniIndex = 0; Arrays.sort(selObjects.toArray()); ListElement endObject = (ListElement) yesModel.getElementAt(endIndex); for (ListElement selected : selObjects) { iniIndex = yesModel.indexOf(selected); ListElement selObject = (ListElement) yesModel.getElementAt(iniIndex); yesModel.removeElement(selObject); endIndex = yesModel.indexOf(endObject); yesModel.add(endIndex, selObject); } } // migrateValueWithinYesList /** * Move within Yes List * * @param event - Event */ protected void migrateValueWithinYesList(Event event) { Object[] selObjects = yesList.getSelectedItems().toArray(); if (selObjects == null) return; int length = selObjects.length; if (length == 0) return; // int[] indices = yesList.getSelectedIndices(); Arrays.sort(indices); // boolean change = false; // Object source = event.getTarget(); if (source == bUp) { for (int i = 0; i < length; i++) { int index = indices[i]; if (index == 0) break; ListElement selObject = (ListElement) yesModel.getElementAt(index); ListElement newObject = (ListElement) yesModel.getElementAt(index - 1); if (!selObject.isUpdateable() || !newObject.isUpdateable()) break; yesModel.setElementAt(newObject, index); yesModel.setElementAt(selObject, index - 1); indices[i] = index - 1; change = true; } } // up else if (source == bDown) { for (int i = length - 1; i >= 0; i--) { int index = indices[i]; if (index >= yesModel.getSize() - 1) break; ListElement selObject = (ListElement) yesModel.getElementAt(index); ListElement newObject = (ListElement) yesModel.getElementAt(index + 1); if (!selObject.isUpdateable() || !newObject.isUpdateable()) break; yesModel.setElementAt(newObject, index); yesModel.setElementAt(selObject, index + 1); yesList.setSelectedIndex(index + 1); indices[i] = index + 1; change = true; } } // down // if (change) { yesList.setSelectedIndices(indices); if (yesList.getSelectedItem() != null) { AuFocus focus = new AuFocus(yesList.getSelectedItem()); Clients.response(focus); } } } // migrateValueWithinYesList /** * Save auto launch configuration to DB */ public void saveData() { try { // For service users, needs to persist data in system tenant PO.setCrossTenantSafe(); // yesList for (int i = 0; i < yesModel.getSize(); i++) { ListElement pp = (ListElement) yesModel.getElementAt(i); if (!pp.isUpdateable()) continue; // Set seq no MTreeFavoriteNode favNode = (MTreeFavoriteNode) MTable.get(MTreeFavoriteNode.Table_ID).getPO(pp.getKey(), null); favNode.setLoginOpenSeqNo(i); favNode.saveEx(); } // noList for (int i = 0; i < noModel.getSize(); i++) { ListElement pp = (ListElement) noModel.getElementAt(i); if (!pp.isUpdateable()) continue; // remove seq no if exists MTreeFavoriteNode favNode = (MTreeFavoriteNode) MTable.get(MTreeFavoriteNode.Table_ID).getPO(pp.getKey(), null); favNode.set_ValueNoCheck(MTreeFavoriteNode.COLUMNNAME_LoginOpenSeqNo, null); favNode.saveEx(); } } finally { PO.clearCrossTenantSafe(); } // detach(); } // saveData /** * List Item */ private static class ListElement extends NamePair { /** * generated serial id */ private static final long serialVersionUID = -1717531470895073281L; private int m_key; private boolean m_updateable; public ListElement(int key, String name) { super(name); this.m_key = key; this.m_updateable = true; } public int getKey() { return m_key; } public boolean isUpdateable() { return m_updateable; } @Override public String getID() { return m_key != -1 ? String.valueOf(m_key) : null; } @Override public int hashCode() { return m_key; } @Override public boolean equals(Object obj) { if (obj instanceof ListElement) { ListElement li = (ListElement) obj; return li.getKey() == m_key && li.getName() != null && li.getName().equals(getName()); } return false; } // equals @Override public String toString() { String s = super.toString(); if (s == null || s.trim().length() == 0) s = "<" + getKey() + ">"; return s; } } // ListElement /** * Listener for onDrop event */ private class DragListener implements EventListener { /** * Default constructor */ public DragListener() { } @Override public void onEvent(Event event) throws Exception { if (event instanceof DropEvent) { int endIndex = 0; DropEvent me = (DropEvent) event; ListItem endItem = (ListItem) me.getTarget(); ListItem startItem = (ListItem) me.getDragged(); if (!startItem.isSelected()) startItem.setSelected(true); if (!(startItem.getListbox() == endItem.getListbox())) { Listbox listFrom = (Listbox) startItem.getListbox(); Listbox listTo = (Listbox) endItem.getListbox(); endIndex = yesList.getIndexOfItem(endItem); migrateLists(listFrom, listTo, endIndex); } else if (startItem.getListbox() == endItem.getListbox() && startItem.getListbox() == yesList) { List selObjects = new ArrayList(); endIndex = yesList.getIndexOfItem(endItem); for (Object obj : yesList.getSelectedItems()) { ListItem listItem = (ListItem) obj; int index = yesList.getIndexOfItem(listItem); ListElement selObject = (ListElement) yesModel.getElementAt(index); selObjects.add(selObject); } migrateValueWithinYesList(endIndex, selObjects); yesList.clearSelection(); } } } } // DragListener }