📄 window.java
字号:
/* Window.java{{IS_NOTE Purpose: Description: History: Tue May 31 19:29:13 2005, Created by tomyeh}}IS_NOTECopyright (C) 2005 Potix Corporation. All Rights Reserved.{{IS_RIGHT This program is distributed under GPL Version 2.0 in the hope that it will be useful, but WITHOUT ANY WARRANTY.}}IS_RIGHT*/package org.zkoss.zul;import java.util.Collections;import java.util.Map;import java.util.HashMap;import java.util.Iterator;import java.util.Set;import java.util.LinkedHashSet;import org.zkoss.mesg.MCommon;import org.zkoss.lang.D;import org.zkoss.lang.Objects;import org.zkoss.util.logging.Log;import org.zkoss.xml.HTMLs;import org.zkoss.zk.ui.Desktop;import org.zkoss.zk.ui.Component;import org.zkoss.zk.ui.Page;import org.zkoss.zk.ui.Executions;import org.zkoss.zk.ui.UiException;import org.zkoss.zk.ui.WrongValueException;import org.zkoss.zk.ui.SuspendNotAllowedException;import org.zkoss.zk.ui.IdSpace;import org.zkoss.zk.ui.ext.render.MultiBranch;import org.zkoss.zk.ui.ext.client.Openable;import org.zkoss.zk.ui.event.Events;import org.zkoss.zul.impl.XulElement;/** * A generic window. * * <p>Unlike other elements, each {@link Window} is an independent ID space * (by implementing {@link IdSpace}). * It means a window and all its descendants forms a ID space and * the ID of each of them is unique in this space. * You could retrieve any of them in this space by calling {@link #getFellow}. * * <p>If a window X is a descendant of another window Y, X's descendants * are not visible in Y's space. To retrieve a descendant, say Z, of X, * you have to invoke Y.getFellow('X').getFellow('Z'). * * <p>Events:<br/> * onMove, onOpen, onClose, onOK, onCacnel and onCtrlKey.<br/> * Note: to have better performance, onOpen is sent only if a * non-deferrable event listener is registered * (see {@link org.zkoss.zk.ui.event.Deferrable}). * * <p><code>onClose</code> is sent when the close button is pressed * (if {@link #isClosable} is true). The window has to detach or hide * the window. By default, {@link #onClose} detaches the window. To prevent * it from detached, you have to call {@link org.zkoss.zk.ui.event.Event#stopPropagation} * to prevent {@link #onClose} is called. * * <p>On the other hand, <code>onOpen</code> is sent when a popup * window (i.e., {@link #getMode} is popup) is closed due to user's activity * (such as press ESC). This event is only a notification. * In other words, the popup is hidden before the event is sent to the server. * The application cannot prevent the window from being hidden. * * @author tomyeh */public class Window extends XulElement implements IdSpace { private static final Log log = Log.lookup(Window.class); private transient Caption _caption; private String _border = "none"; private String _title = ""; /** What control and function keys to intercepts. */ private String _ctrlKeys; /** The value passed to the client; parsed from _ctrlKeys. */ private String _ctkeys; /** One of MODAL, EMBEDDED, OVERLAPPED, HIGHLIGHTED, POPUP. */ private int _mode = EMBEDDED; /** Used for doModal. */ private transient Object _mutex; /** The style used for the content block. */ private String _cntStyle; /** The style class used for the content block. */ private String _cntscls; /** How to position the window. */ private String _pos; /** Whether to show a close button. */ private boolean _closable; /** Whether the window is sizable. */ private boolean _sizable; /** Embeds the window as normal component. */ private static final int EMBEDDED = 0; /** Makes the window as a modal dialog. once {@link #doModal} * is called, the execution of the event processing thread * is suspended until one of the following occurs. * <ol> * <li>{@link #setMode} is called with a mode other than MODAL.</li> * <li>Either {@link #doOverlapped}, {@link #doPopup}, * {@link #doEmbedded}, or {@link #doHighlighted} is called.</li> * <li>{@link #setVisible} is called with false.</li> * <li>The window is detached from the window.</li> * </ol> * * <p>Note: In the last two cases, the mode becomes {@link #OVERLAPPED}. * In other words, one might say a modal window is a special overlapped window. * * @see #HIGHLIGHTED */ private static final int MODAL = 1; /** Makes the window as overlapped other components. */ private static final int OVERLAPPED = 2; /** Makes the window as popup. * It is similar to {@link #OVERLAPPED}, except it is auto hidden * when user clicks outside of the window. */ private static final int POPUP = 3; /** Makes the window as hilighted. * Its visual effect is the same as {@link #MODAL}. * However, from the server side's viewpoint, it is similar to * {@link #OVERLAPPED}. The execution won't be suspended when * {@link #doHighlighted} is called. * * @see #MODAL * @see #OVERLAPPED */ private static final int HIGHLIGHTED = 4; public Window() { init(); } /** * @param title the window title (see {@link #setTitle}). * @param border the border (see {@link #setBorder}). * @param closable whether it is closable (see {@link #setClosable}). */ public Window(String title, String border, boolean closable) { this(); setTitle(title); setBorder(border); setClosable(closable); } private void init() { _mutex = new Object(); } /** Returns the caption of this window. */ public Caption getCaption() { return _caption; } /** Returns the border. * The border actually controls what the content style class is * is used. In fact, the name of the border (except "normal") * is generate as part of the style class used for the content block. * Refer to {@link #getContentSclass} for more details. * * <p>Default: "none". */ public String getBorder() { return _border; } /** Sets the border (either none or normal). * * @param border the border. If null or "0", "none" is assumed. * Since 2.4.1, We assume "0" to be "none". */ public void setBorder(String border) { if (border == null || "0".equals(border)) border = "none"; if (!Objects.equals(_border, border)) { _border = border; smartUpdate("class", getSclass()); } } /** Returns the title. * Besides this attribute, you could use {@link Caption} to define * a more sophiscated caption (aka., title). * <p>If a window has a caption whose label ({@link Caption#getLabel}) * is not empty, then this attribute is ignored. * <p>Default: empty. */ public String getTitle() { return _title; } /** Sets the title. */ public void setTitle(String title) { if (title == null) title = ""; if (!Objects.equals(_title, title)) { _title = title; if (_caption != null) _caption.invalidate(); else invalidate(); } } /** Returns what keystrokes to intercept. * <p>Default: null. */ public String getCtrlKeys() { return _ctrlKeys; } /** Sets what keystrokes to intercept. * * <p>The string could be a combination of the following: * <dl> * <dt>^k</dt> * <dd>A control key, i.e., Ctrl+k, where k could be a~z, 0~9, #n</dd> * <dt>@k</dt> * <dd>A alt key, i.e., Alt+k, where k could be a~z, 0~9, #n</dd> * <dt>$k</dt> * <dd>A shift key, i.e., Shift+k, where k could be #n</dd> * <dt>#home</dt> * <dd>Home</dd> * <dt>#end</dt> * <dd>End</dd> * <dt>#ins</dt> * <dd>Insert</dd> * <dt>#del</dt> * <dd>Delete</dd> * <dt>#left</dt> * <dd>Left arrow</dd> * <dt>#right</dt> * <dd>Right arrow</dd> * <dt>#up</dt> * <dd>Up arrow</dd> * <dt>#down</dt> * <dd>Down arrow</dd> * <dt>#pgup</dt> * <dd>PageUp</dd> * <dt>#pgdn</dt> * <dd>PageDn</dd> * <dt>#f1 #f2 ... #f12</dt> * <dd>Function keys representing F1, F2, ... F12</dd> * </dl> * * <p>For example, * <dl> * <dt>^a^d@c#f10#left#right</dt> * <dd>It means you want to intercept Ctrl+A, Ctrl+D, Alt+C, F10, * Left and Right.</dd> * <dt>^#left</dt> * <dd>It means Ctrl+Left.</dd> * <dt>^#f1</dt> * <dd>It means Ctrl+F1.</dd> * <dt>@#f3</dt> * <dd>It means Alt+F3.</dd> * </dl> * * <p>Note: it doesn't support Ctrl+Alt, Shift+Ctrl, Shift+Alt or Shift+Ctrl+Alt. */ public void setCtrlKeys(String ctrlKeys) throws UiException { if (ctrlKeys != null && ctrlKeys.length() == 0) ctrlKeys = null; if (!Objects.equals(_ctrlKeys, ctrlKeys)) { parseCtrlKeys(ctrlKeys); smartUpdate("z.ctkeys", _ctkeys); } } private void parseCtrlKeys(String keys) throws UiException { if (keys == null || keys.length() == 0) { _ctrlKeys = _ctkeys = null; return; } final StringBuffer sbctl = new StringBuffer(), sbsft = new StringBuffer(), sbalt = new StringBuffer(), sbext = new StringBuffer(); StringBuffer sbcur = null; for (int j = 0, len = keys.length(); j < len; ++j) { char cc = keys.charAt(j); switch (cc) { case '^': case '$': case '@': if (sbcur != null) throw new WrongValueException("Combination of Shift, Alt and Ctrl not supported: "+keys); sbcur = cc == '^' ? sbctl: cc == '@' ? sbalt: sbsft; break; case '#': { int k = j + 1; for (; k < len; ++k) { final char c2 = (char)keys.charAt(k); if ((c2 > 'Z' || c2 < 'A') && (c2 > 'z' || c2 < 'a') && (c2 > '9' || c2 < '0')) break; } if (k == j + 1) throw new WrongValueException(MCommon.UNEXPECTED_CHARACTER, new Object[] {new Character(cc), keys}); final String s = keys.substring(j+1, k).toLowerCase(); if ("pgup".equals(s)) cc = 'A'; else if ("pgdn".equals(s)) cc = 'B'; else if ("end".equals(s)) cc = 'C'; else if ("home".equals(s)) cc = 'D'; else if ("left".equals(s)) cc = 'E'; else if ("up".equals(s)) cc = 'F'; else if ("right".equals(s)) cc = 'G'; else if ("down".equals(s)) cc = 'H'; else if ("ins".equals(s)) cc = 'I'; else if ("del".equals(s)) cc = 'J'; else if (s.length() > 1 && s.charAt(0) == 'f') { final int v; try { v = Integer.parseInt(s.substring(1)); } catch (Throwable ex) { throw new WrongValueException("Unknown #"+s+" in "+keys); } if (v == 0 || v > 12) throw new WrongValueException("Unsupported function key: #f"+v); cc = (char)('O' + v); //'P': F1, 'Q': F2... 'Z': F12 } else throw new WrongValueException("Unknown #"+s+" in "+keys); if (sbcur == null) sbext.append(cc); else { sbcur.append(cc); sbcur = null; } j = k - 1; } break; default: if (sbcur == null || ((cc > 'Z' || cc < 'A') && (cc > 'z' || cc < 'a') && (cc > '9' || cc < '0'))) throw new WrongValueException(MCommon.UNEXPECTED_CHARACTER, new Object[] {new Character(cc), keys}); if (sbcur == sbsft) throw new WrongValueException("$"+cc+" not supported: "+keys); if (cc <= 'Z' && cc >= 'A') cc = (char)(cc + ('a' - 'A')); //to lower case sbcur.append(cc); sbcur = null; break; } } _ctkeys = new StringBuffer() .append('^').append(sbctl).append(';') .append('@').append(sbalt).append(';') .append('$').append(sbsft).append(';') .append('#').append(sbext).append(';').toString(); _ctrlKeys = keys; } /** Returns the current mode. * One of "modal", "embedded", "overlapped", "popup", and "highlighted". */ public String getMode() { return modeToString(_mode); } private static String modeToString(int mode) { switch (mode) { case MODAL: return "modal"; case POPUP: return "popup"; case OVERLAPPED: return "overlapped"; case HIGHLIGHTED: return "highlighted"; default: return "embedded"; } } /** Sets the mode to overlapped, popup, modal, embedded or highlighted. * * <p>Notice: {@link Events#ON_MODAL} is posted if you specify * "modal" to this method and in a thread other than an event * listener ({@link Events#inEventListener}). * In other words, if this method is called with modal and * <em>not</em> in any event listener, the mode won't be changed * immediately (until {@link Events#ON_MODAL} is processed later). * * @param name the mode which could be one of * "embedded", "overlapped", "popup", "modal", "highlighted". * Note: it cannot be "modal". Use {@link #doModal} instead. * * @exception InterruptedException thrown if "modal" is specified, * and one of the following conditions occurs: * 1) the desktop or the Web application is being destroyed, or * 2) {@link org.zkoss.zk.ui.sys.DesktopCtrl#ceaseSuspendedThread}. * To tell the difference, check the getMessage method of InterruptedException. */ public void setMode(String name) throws InterruptedException { if ("popup".equals(name)) doPopup(); else if ("overlapped".equals(name)) doOverlapped(); else if ("embedded".equals(name)) doEmbedded(); else if ("modal".equals(name)) doModal(); else if ("highlighted".equals(name)) doHighlighted(); else throw new WrongValueException("Uknown mode: "+name); } /** Sets the mode to overlapped, popup, modal, embedded or highlighted. * * @see #setMode(String) */ public void setMode(int mode) throws InterruptedException { switch (mode) { case POPUP: doPopup(); break; case OVERLAPPED: doOverlapped(); break; case EMBEDDED: doEmbedded(); break; case MODAL: doModal(); break; case HIGHLIGHTED: doHighlighted(); break; default: throw new WrongValueException("Unknown mode: "+mode); } } /** Returns whether this is a modal dialog. */ public boolean inModal() { return _mode == MODAL; } /** Returns whether this is embedded with other components (Default). * @see #doEmbedded */ public boolean inEmbedded() { return _mode == EMBEDDED; } /** Returns whether this is a overlapped window. */ public boolean inOverlapped() { return _mode == OVERLAPPED; } /** Returns whether this is a popup window.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -