You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Window.java 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
  1. /* *************************************************************************
  2. IT Mill Toolkit
  3. Development of Browser User Interfaces Made Easy
  4. Copyright (C) 2000-2006 IT Mill Ltd
  5. *************************************************************************
  6. This product is distributed under commercial license that can be found
  7. from the product package on license.pdf. Use of this product might
  8. require purchasing a commercial license from IT Mill Ltd. For guidelines
  9. on usage, see licensing-guidelines.html
  10. *************************************************************************
  11. For more information, contact:
  12. IT Mill Ltd phone: +358 2 4802 7180
  13. Ruukinkatu 2-4 fax: +358 2 4802 7181
  14. 20540, Turku email: info@itmill.com
  15. Finland company www: www.itmill.com
  16. Primary source for information and releases: www.itmill.com
  17. ********************************************************************** */
  18. package com.itmill.toolkit.ui;
  19. import com.itmill.toolkit.Application;
  20. import com.itmill.toolkit.terminal.DownloadStream;
  21. import com.itmill.toolkit.terminal.PaintException;
  22. import com.itmill.toolkit.terminal.PaintTarget;
  23. import com.itmill.toolkit.terminal.ParameterHandler;
  24. import com.itmill.toolkit.terminal.Resource;
  25. import com.itmill.toolkit.terminal.Sizeable;
  26. import com.itmill.toolkit.terminal.Terminal;
  27. import com.itmill.toolkit.terminal.URIHandler;
  28. import java.lang.ref.WeakReference;
  29. import java.net.MalformedURLException;
  30. import java.net.URL;
  31. import java.util.HashMap;
  32. import java.util.LinkedList;
  33. import java.util.Map;
  34. import java.util.Iterator;
  35. /**
  36. * Application window component.
  37. *
  38. * @author IT Mill Ltd.
  39. * @version
  40. * @VERSION@
  41. * @since 3.0
  42. */
  43. public class Window extends Panel implements URIHandler, ParameterHandler {
  44. /** Window with no border */
  45. public static final int BORDER_NONE = 0;
  46. /** Window with only minimal border */
  47. public static final int BORDER_MINIMAL = 1;
  48. /** Window with default borders */
  49. public static final int BORDER_DEFAULT = 2;
  50. /** The terminal this window is attached to */
  51. private Terminal terminal = null;
  52. /** The applicaiton this window is attached to */
  53. private Application application = null;
  54. /** List of URI handlers for this window */
  55. private LinkedList uriHandlerList = null;
  56. /** List of parameter handlers for this window */
  57. private LinkedList parameterHandlerList = null;
  58. /**
  59. * Explicitly specified theme of this window. If null, application theme is
  60. * used
  61. */
  62. private String theme = null;
  63. /** Resources to be opened automatically on next repaint */
  64. private LinkedList openList = new LinkedList();
  65. /** The name of the window */
  66. private String name = null;
  67. /** Window border mode */
  68. private int border = BORDER_DEFAULT;
  69. /** Focused component */
  70. private Focusable focusedComponent;
  71. /** Distance of Window top border in pixels from top border of the containing (main window) or -1 if unspecified */
  72. private int positionY = -1;
  73. /** Distance of Window left border in pixels from left border of the containing (main window) or -1 if unspecified */
  74. private int positionX = -1;
  75. /* ********************************************************************* */
  76. /**
  77. * Create new empty unnamed window with default layout.
  78. *
  79. * <p>
  80. * To show the window in application, it must be added to application with
  81. * <code>Application.addWindow()</code> method.
  82. * </p>
  83. *
  84. * <p>
  85. * The windows are scrollable by default.
  86. * </p>
  87. *
  88. * @param caption
  89. * Title of the window
  90. */
  91. public Window() {
  92. this("", null);
  93. }
  94. /**
  95. * Create new empty window with default layout.
  96. *
  97. * <p>
  98. * To show the window in application, it must be added to application with
  99. * <code>Application.addWindow()</code> method.
  100. * </p>
  101. *
  102. * <p>
  103. * The windows are scrollable by default.
  104. * </p>
  105. *
  106. * @param caption
  107. * Title of the window
  108. */
  109. public Window(String caption) {
  110. this(caption, null);
  111. }
  112. /**
  113. * Create new window.
  114. *
  115. * <p>
  116. * To show the window in application, it must be added to application with
  117. * <code>Application.addWindow()</code> method.
  118. * </p>
  119. *
  120. * <p>
  121. * The windows are scrollable by default.
  122. * </p>
  123. *
  124. * @param caption
  125. * Title of the window
  126. * @param layout
  127. * Layout of the window
  128. */
  129. public Window(String caption, Layout layout) {
  130. super(caption, layout);
  131. setScrollable(true);
  132. }
  133. /**
  134. * Get terminal type.
  135. *
  136. * @return Value of property terminal.
  137. */
  138. public Terminal getTerminal() {
  139. return this.terminal;
  140. }
  141. /* ********************************************************************* */
  142. /**
  143. * Get window of the component. Returns the window where this component
  144. * belongs to. If the component does not yet belong to a window the returns
  145. * null.
  146. *
  147. * @return parent window of the component.
  148. */
  149. public final Window getWindow() {
  150. return this;
  151. }
  152. /**
  153. * Get application instance of the component. Returns the application where
  154. * this component belongs to. If the component does not yet belong to a
  155. * application the returns null.
  156. *
  157. * @return parent application of the component.
  158. */
  159. public final Application getApplication() {
  160. return this.application;
  161. }
  162. /**
  163. * Getter for property parent. Parent is the visual parent of a component.
  164. * Each component can belong to only one ComponentContainer at time.
  165. *
  166. * @return Value of property parent.
  167. */
  168. public final Component getParent() {
  169. return null;
  170. }
  171. /**
  172. * Setter for property parent. Parent is the visual parent of a component.
  173. * This is mostly called by containers add method. Setting parent is not
  174. * allowed for the window, and thus this call should newer be called.
  175. *
  176. * @param parent
  177. * New value of property parent.
  178. */
  179. public void setParent(Component parent) {
  180. throw new RuntimeException("Setting parent for Window is not allowed");
  181. }
  182. /**
  183. * Get component UIDL tag.
  184. *
  185. * @return Component UIDL tag as string.
  186. */
  187. public String getTag() {
  188. return "window";
  189. }
  190. /* ********************************************************************* */
  191. /** Add new URI handler to this window */
  192. public void addURIHandler(URIHandler handler) {
  193. if (uriHandlerList == null)
  194. uriHandlerList = new LinkedList();
  195. synchronized (uriHandlerList) {
  196. if (!uriHandlerList.contains(handler))
  197. uriHandlerList.addLast(handler);
  198. }
  199. }
  200. /** Remove given URI handler from this window */
  201. public void removeURIHandler(URIHandler handler) {
  202. if (handler == null || uriHandlerList == null)
  203. return;
  204. synchronized (uriHandlerList) {
  205. uriHandlerList.remove(handler);
  206. if (uriHandlerList.isEmpty())
  207. uriHandlerList = null;
  208. }
  209. }
  210. /**
  211. * Handle uri recursively.
  212. */
  213. public DownloadStream handleURI(URL context, String relativeUri) {
  214. DownloadStream result = null;
  215. if (uriHandlerList != null) {
  216. Object[] handlers;
  217. synchronized (uriHandlerList) {
  218. handlers = uriHandlerList.toArray();
  219. }
  220. for (int i = 0; i < handlers.length; i++) {
  221. DownloadStream ds = ((URIHandler) handlers[i]).handleURI(
  222. context, relativeUri);
  223. if (ds != null) {
  224. if (result != null)
  225. throw new RuntimeException("handleURI for " + context
  226. + " uri: '" + relativeUri
  227. + "' returns ambigious result.");
  228. result = ds;
  229. }
  230. }
  231. }
  232. return result;
  233. }
  234. /* ********************************************************************* */
  235. /** Add new parameter handler to this window. */
  236. public void addParameterHandler(ParameterHandler handler) {
  237. if (parameterHandlerList == null)
  238. parameterHandlerList = new LinkedList();
  239. synchronized (parameterHandlerList) {
  240. if (!parameterHandlerList.contains(handler))
  241. parameterHandlerList.addLast(handler);
  242. }
  243. }
  244. /** Remove given URI handler from this window. */
  245. public void removeParameterHandler(ParameterHandler handler) {
  246. if (handler == null || parameterHandlerList == null)
  247. return;
  248. synchronized (parameterHandlerList) {
  249. parameterHandlerList.remove(handler);
  250. if (parameterHandlerList.isEmpty())
  251. parameterHandlerList = null;
  252. }
  253. }
  254. /* Documented by the interface */
  255. public void handleParameters(Map parameters) {
  256. if (parameterHandlerList != null) {
  257. Object[] handlers;
  258. synchronized (parameterHandlerList) {
  259. handlers = parameterHandlerList.toArray();
  260. }
  261. for (int i = 0; i < handlers.length; i++)
  262. ((ParameterHandler) handlers[i]).handleParameters(parameters);
  263. }
  264. }
  265. /* ********************************************************************* */
  266. /**
  267. * Get theme for this window.
  268. *
  269. * @return Name of the theme used in window. If the theme for this
  270. * individual window is not explicitly set, the application theme is
  271. * used instead. If application is not assigned the
  272. * terminal.getDefaultTheme is used. If terminal is not set, null is
  273. * returned
  274. */
  275. public String getTheme() {
  276. if (theme != null)
  277. return theme;
  278. if ((application != null) && (application.getTheme() != null))
  279. return application.getTheme();
  280. if (terminal != null)
  281. return terminal.getDefaultTheme();
  282. return null;
  283. }
  284. /**
  285. * Set theme for this window.
  286. *
  287. * @param theme
  288. * New theme for this window. Null implies the default theme.
  289. */
  290. public void setTheme(String theme) {
  291. this.theme = theme;
  292. requestRepaint();
  293. }
  294. /**
  295. * Paint the content of this component.
  296. *
  297. * @param event
  298. * PaintEvent.
  299. * @throws PaintException
  300. * The paint operation failed.
  301. */
  302. public synchronized void paintContent(PaintTarget target)
  303. throws PaintException {
  304. // Set the window name
  305. target.addAttribute("name", getName());
  306. // Set the window theme
  307. target.addAttribute("theme", getTheme());
  308. // Mark main window
  309. if (getApplication() != null
  310. && this == getApplication().getMainWindow())
  311. target.addAttribute("main", true);
  312. // Open requested resource
  313. synchronized (openList) {
  314. if (!openList.isEmpty()) {
  315. for (Iterator i = openList.iterator(); i.hasNext();)
  316. ((OpenResource) i.next()).paintContent(target);
  317. openList.clear();
  318. }
  319. }
  320. // Contents of the window panel is painted
  321. super.paintContent(target);
  322. // Window position
  323. target.addVariable(this, "positionx", getPositionX());
  324. target.addVariable(this, "positiony", getPositionY());
  325. // Window closing
  326. target.addVariable(this, "close", false);
  327. // Set focused component
  328. if (this.focusedComponent != null)
  329. target.addVariable(this, "focused", ""
  330. + this.focusedComponent.getFocusableId());
  331. else
  332. target.addVariable(this, "focused", "");
  333. }
  334. /* ********************************************************************* */
  335. /**
  336. * Open the given resource in this window.
  337. */
  338. public void open(Resource resource) {
  339. synchronized (openList) {
  340. if (!openList.contains(resource))
  341. openList.add(new OpenResource(resource, null, -1, -1,
  342. BORDER_DEFAULT));
  343. }
  344. requestRepaint();
  345. }
  346. /* ********************************************************************* */
  347. /**
  348. * Open the given resource in named terminal window. Empty or
  349. * <code>null</code> window name results the resource to be opened in this
  350. * window.
  351. */
  352. public void open(Resource resource, String windowName) {
  353. synchronized (openList) {
  354. if (!openList.contains(resource))
  355. openList.add(new OpenResource(resource, windowName, -1, -1,
  356. BORDER_DEFAULT));
  357. }
  358. requestRepaint();
  359. }
  360. /* ********************************************************************* */
  361. /**
  362. * Open the given resource in named terminal window with given size and
  363. * border properties. Empty or <code>null</code> window name results the
  364. * resource to be opened in this window.
  365. */
  366. public void open(Resource resource, String windowName, int width,
  367. int height, int border) {
  368. synchronized (openList) {
  369. if (!openList.contains(resource))
  370. openList.add(new OpenResource(resource, windowName, width,
  371. height, border));
  372. }
  373. requestRepaint();
  374. }
  375. /* ********************************************************************* */
  376. /**
  377. * Returns the full url of the window, this returns window specific url even
  378. * for the main window.
  379. *
  380. * @return String
  381. */
  382. public URL getURL() {
  383. if (application == null)
  384. return null;
  385. try {
  386. return new URL(application.getURL(), getName() + "/");
  387. } catch (MalformedURLException e) {
  388. throw new RuntimeException("Internal problem, please report");
  389. }
  390. }
  391. /**
  392. * Get the unique name of the window that indentifies it on the terminal.
  393. *
  394. * @return String
  395. */
  396. public String getName() {
  397. return name;
  398. }
  399. /**
  400. * Returns the border.
  401. *
  402. * @return int
  403. */
  404. public int getBorder() {
  405. return border;
  406. }
  407. /**
  408. * Sets the border.
  409. *
  410. * @param border
  411. * The border to set
  412. */
  413. public void setBorder(int border) {
  414. this.border = border;
  415. }
  416. /**
  417. * Sets the application this window is connected to.
  418. *
  419. * <p>
  420. * This method should not be invoked directly. Instead the
  421. * {@link com.itmill.toolkit.Application#addWindow(Window)} method should be
  422. * used to add the window to an application and
  423. * {@link com.itmill.toolkit.Application#removeWindow(Window)} method for
  424. * removing the window from the applicion. These methods call this method
  425. * implicitly.
  426. * </p>
  427. *
  428. * <p>
  429. * The method invokes {@link Component#attach()} and
  430. * {@link Component#detach()} methods when necessary.
  431. * <p>
  432. *
  433. * @param application
  434. * The application to set
  435. */
  436. public void setApplication(Application application) {
  437. // If the application is not changed, dont do nothing
  438. if (application == this.application)
  439. return;
  440. // Send detach event if the window is connected to application
  441. if (this.application != null) {
  442. detach();
  443. }
  444. // Connect to new parent
  445. this.application = application;
  446. // Send attach event if connected to a window
  447. if (application != null)
  448. attach();
  449. }
  450. /**
  451. * Sets the name.
  452. * <p>
  453. * The name of the window must be unique inside the application. Also the
  454. * name may only contain the following characters: a-z, A-Z and 0-9.
  455. * </p>
  456. *
  457. * <p>
  458. * If the name is null, the the window is given name automatically when it
  459. * is added to an application.
  460. * </p>
  461. *
  462. * @param name
  463. * The name to set
  464. */
  465. public void setName(String name) {
  466. // The name can not be changed in application
  467. if (getApplication() != null)
  468. throw new IllegalStateException(
  469. "Window name can not be changed while "
  470. + "the window is in application");
  471. // Check the name format
  472. if (name != null)
  473. for (int i = 0; i < name.length(); i++) {
  474. char c = name.charAt(i);
  475. if (!(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')))
  476. throw new IllegalArgumentException(
  477. "Window name can contain "
  478. + "only a-z, A-Z and 0-9 characters: '"
  479. + name + "' given.");
  480. }
  481. this.name = name;
  482. }
  483. /**
  484. * Set terminal type. The terminal type is set by the the terminal adapter
  485. * and may change from time to time.
  486. *
  487. * @param type
  488. * terminal type to set
  489. */
  490. public void setTerminal(Terminal type) {
  491. this.terminal = type;
  492. }
  493. /**
  494. * Window only supports pixels as unit.
  495. *
  496. * @see com.itmill.toolkit.terminal.Sizeable#getHeightUnits()
  497. */
  498. public void setHeightUnits(int units) {
  499. if (units != Sizeable.UNITS_PIXELS)
  500. throw new IllegalArgumentException("Only pixels are supported");
  501. }
  502. /**
  503. * Window only supports pixels as unit.
  504. *
  505. * @see com.itmill.toolkit.terminal.Sizeable#getWidthUnits()
  506. */
  507. public void setWidthUnits(int units) {
  508. if (units != Sizeable.UNITS_PIXELS)
  509. throw new IllegalArgumentException("Only pixels are supported");
  510. }
  511. /** Private data structure for storing opening window properties */
  512. private class OpenResource {
  513. private Resource resource;
  514. private String name;
  515. private int width;
  516. private int height;
  517. private int border;
  518. /** Create new open resource */
  519. private OpenResource(Resource resource, String name, int width,
  520. int height, int border) {
  521. this.resource = resource;
  522. this.name = name;
  523. this.width = width;
  524. this.height = height;
  525. this.border = border;
  526. }
  527. /** Paint the open-tag inside the window. */
  528. private void paintContent(PaintTarget target) throws PaintException {
  529. target.startTag("open");
  530. target.addAttribute("src", resource);
  531. if (name != null && name.length() > 0)
  532. target.addAttribute("name", name);
  533. if (width >= 0)
  534. target.addAttribute("width", width);
  535. if (height >= 0)
  536. target.addAttribute("height", height);
  537. switch (border) {
  538. case Window.BORDER_MINIMAL:
  539. target.addAttribute("border", "minimal");
  540. break;
  541. case Window.BORDER_NONE:
  542. target.addAttribute("border", "none");
  543. break;
  544. }
  545. target.endTag("open");
  546. }
  547. }
  548. /**
  549. * @see com.itmill.toolkit.terminal.VariableOwner#changeVariables(java.lang.Object,
  550. * java.util.Map)
  551. */
  552. public void changeVariables(Object source, Map variables) {
  553. super.changeVariables(source, variables);
  554. // Get focused component
  555. String focusedId = (String) variables.get("focused");
  556. if (focusedId != null) {
  557. try {
  558. long id = Long.parseLong(focusedId);
  559. this.focusedComponent = Window.getFocusableById(id);
  560. } catch (NumberFormatException ignored) {
  561. // We ignore invalid focusable ids
  562. }
  563. }
  564. // Positioning
  565. Integer positionx = (Integer) variables.get("positionx");
  566. if (positionx != null) {
  567. int x = positionx.intValue();
  568. setPositionX(x<0?-1:x);
  569. }
  570. Integer positiony = (Integer) variables.get("positiony");
  571. if (positiony != null) {
  572. int y = positiony.intValue();
  573. setPositionY(y<0?-1:y);
  574. }
  575. // Closing
  576. Boolean close = (Boolean) variables.get("close");
  577. if (close != null && close.booleanValue()) {
  578. // TODO We should also throw an event to listeners
  579. this.setVisible(false);
  580. }
  581. }
  582. /**
  583. * Get currently focused component in this window.
  584. *
  585. * @return Focused component or null if none is focused.
  586. */
  587. public Component.Focusable getFocusedComponent() {
  588. return this.focusedComponent;
  589. }
  590. /**
  591. * Set currently focused component in this window.
  592. *
  593. * @param focusable
  594. * Focused component or null if none is focused.
  595. */
  596. public void setFocusedComponent(Component.Focusable focusable) {
  597. this.focusedComponent = focusable;
  598. }
  599. /* Focusable id generator ****************************************** */
  600. private static long lastUsedFocusableId = 0;
  601. private static Map focusableComponents = new HashMap();
  602. /** Get an id for focusable component. */
  603. public static long getNewFocusableId(Component.Focusable focusable) {
  604. long newId = ++lastUsedFocusableId;
  605. WeakReference ref = new WeakReference(focusable);
  606. focusableComponents.put(new Long(newId), ref);
  607. return newId;
  608. }
  609. /** Map focusable id back to focusable component. */
  610. public static Component.Focusable getFocusableById(long focusableId) {
  611. WeakReference ref = (WeakReference) focusableComponents.get(new Long(
  612. focusableId));
  613. if (ref != null) {
  614. Object o = ref.get();
  615. if (o != null) {
  616. return (Component.Focusable) o;
  617. }
  618. }
  619. return null;
  620. }
  621. /** Release focusable component id when not used anymore. */
  622. public static void removeFocusableId(long focusableId) {
  623. Long id = new Long(focusableId);
  624. WeakReference ref = (WeakReference) focusableComponents.get(id);
  625. ref.clear();
  626. focusableComponents.remove(id);
  627. }
  628. /** Get the distance of Window left border in pixels from left border of the containing (main window).
  629. * @return Distance of Window left border in pixels from left border of the containing (main window). or -1 if unspecified.
  630. * @since 4.0.0
  631. */
  632. public int getPositionX() {
  633. return positionX;
  634. }
  635. /** Set the distance of Window left border in pixels from left border of the containing (main window).
  636. * @param positionX Distance of Window left border in pixels from left border of the containing (main window). or -1 if unspecified
  637. * @since 4.0.0
  638. */
  639. public void setPositionX(int positionX) {
  640. this.positionX = positionX;
  641. }
  642. /** Get the distance of Window top border in pixels from top border of the containing (main window).
  643. * @return Distance of Window top border in pixels from top border of the containing (main window). or -1 if unspecified
  644. *
  645. * @since 4.0.0
  646. */
  647. public int getPositionY() {
  648. return positionY;
  649. }
  650. /** Set the distance of Window top border in pixels from top border of the containing (main window).
  651. * @param positionY of Window top border in pixels from top border of the containing (main window). or -1 if unspecified
  652. *
  653. * @since 4.0.0
  654. */
  655. public void setPositionY(int positionY) {
  656. this.positionY = positionY;
  657. }
  658. }