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.

VDebugWindow.java 36KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client.debug.internal;
  17. import java.util.ArrayList;
  18. import java.util.Date;
  19. import com.google.gwt.core.client.Duration;
  20. import com.google.gwt.core.client.Scheduler;
  21. import com.google.gwt.core.client.Scheduler.ScheduledCommand;
  22. import com.google.gwt.core.shared.GWT;
  23. import com.google.gwt.dom.client.Element;
  24. import com.google.gwt.dom.client.NativeEvent;
  25. import com.google.gwt.dom.client.Style;
  26. import com.google.gwt.dom.client.Style.Cursor;
  27. import com.google.gwt.dom.client.Style.Overflow;
  28. import com.google.gwt.event.dom.client.ClickEvent;
  29. import com.google.gwt.event.dom.client.ClickHandler;
  30. import com.google.gwt.event.dom.client.MouseDownEvent;
  31. import com.google.gwt.event.dom.client.MouseDownHandler;
  32. import com.google.gwt.event.dom.client.MouseEvent;
  33. import com.google.gwt.event.dom.client.MouseMoveEvent;
  34. import com.google.gwt.event.dom.client.MouseMoveHandler;
  35. import com.google.gwt.event.logical.shared.ResizeEvent;
  36. import com.google.gwt.event.shared.HandlerRegistration;
  37. import com.google.gwt.http.client.UrlBuilder;
  38. import com.google.gwt.i18n.client.DateTimeFormat;
  39. import com.google.gwt.i18n.client.NumberFormat;
  40. import com.google.gwt.storage.client.Storage;
  41. import com.google.gwt.user.client.DOM;
  42. import com.google.gwt.user.client.Event;
  43. import com.google.gwt.user.client.Event.NativePreviewEvent;
  44. import com.google.gwt.user.client.Event.NativePreviewHandler;
  45. import com.google.gwt.user.client.Timer;
  46. import com.google.gwt.user.client.Window;
  47. import com.google.gwt.user.client.Window.Location;
  48. import com.google.gwt.user.client.ui.Button;
  49. import com.google.gwt.user.client.ui.FlowPanel;
  50. import com.google.gwt.user.client.ui.RootPanel;
  51. import com.google.gwt.user.client.ui.SimplePanel;
  52. import com.google.gwt.user.client.ui.Widget;
  53. import com.vaadin.client.ApplicationConnection;
  54. import com.vaadin.client.ValueMap;
  55. import com.vaadin.client.ui.VOverlay;
  56. /**
  57. * Debug window implementation.
  58. *
  59. * @since 7.1
  60. * @author Vaadin Ltd
  61. */
  62. public final class VDebugWindow extends VOverlay {
  63. // CSS classes
  64. static final String STYLENAME = "v-debugwindow";
  65. static final String STYLENAME_BUTTON = STYLENAME + "-button";
  66. static final String STYLENAME_ACTIVE = "active";
  67. protected static final String STYLENAME_HEAD = STYLENAME + "-head";
  68. protected static final String STYLENAME_TABS = STYLENAME + "-tabs";
  69. protected static final String STYLENAME_TAB = STYLENAME + "-tab";
  70. protected static final String STYLENAME_CONTROLS = STYLENAME + "-controls";
  71. protected static final String STYLENAME_SECTION_HEAD = STYLENAME
  72. + "-section-head";
  73. protected static final String STYLENAME_CONTENT = STYLENAME + "-content";
  74. protected static final String STYLENAME_SELECTED = "selected";
  75. // drag this far before actually moving window
  76. protected static final int MOVE_TRESHOLD = 5;
  77. // window minimum height, minimum width comes from tab+controls
  78. protected static final int MIN_HEIGHT = 40;
  79. // size of area to grab for resize; bottom corners size in CSS
  80. protected static final int HANDLE_SIZE = 7;
  81. // identifiers for localStorage
  82. private static final String STORAGE_PREFIX = "v-debug-";
  83. private static final String STORAGE_FULL_X = "x";
  84. private static final String STORAGE_FULL_Y = "y";
  85. private static final String STORAGE_FULL_W = "w";
  86. private static final String STORAGE_FULL_H = "h";
  87. private static final String STORAGE_MIN_X = "mx";
  88. private static final String STORAGE_MIN_Y = "my";
  89. private static final String STORAGE_ACTIVE_SECTION = "t";
  90. private static final String STORAGE_IS_MINIMIZED = "m";
  91. private static final String STORAGE_FONT_SIZE = "s";
  92. // state, these are persisted
  93. protected Section activeSection;
  94. protected boolean minimized = false;
  95. protected int fullX = -10;
  96. protected int fullY = -10;
  97. protected int fullW = 300;
  98. protected int fullH = 150;
  99. protected int minX = -10;
  100. protected int minY = 10;
  101. protected int fontSize = 1; // 0-2
  102. // Timers since application start, and last timer reset
  103. private static final Duration start = new Duration();
  104. private static Duration lastReset = start;
  105. // outer panel
  106. protected FlowPanel window = new FlowPanel();
  107. // top (tabs + controls)
  108. protected FlowPanel head = new FlowPanel();
  109. protected FlowPanel tabs = new FlowPanel();
  110. protected FlowPanel controls = new FlowPanel();
  111. protected Button minimize = new DebugButton(Icon.MINIMIZE, "Minimize");
  112. protected Button menu = new DebugButton(Icon.MENU, "Menu");
  113. protected Button close = new DebugButton(Icon.CLOSE, "Close");
  114. // menu
  115. protected Menu menuPopup = new Menu();
  116. // section specific area
  117. protected FlowPanel sectionHead = new FlowPanel();
  118. // content wrapper
  119. protected SimplePanel content = new SimplePanel();
  120. // sections
  121. protected ArrayList<Section> sections = new ArrayList<>();
  122. // handles resize/move
  123. protected HandlerRegistration mouseDownHandler = null;
  124. protected HandlerRegistration mouseMoveHandler = null;
  125. // TODO this class should really be a singleton.
  126. static VDebugWindow instance;
  127. /**
  128. * This class should only be instantiated by the framework, use
  129. * {@link #get()} instead to get the singleton instance.
  130. * <p>
  131. * {@link VDebugWindow} provides windowing functionality and shows
  132. * {@link Section}s added with {@link #addSection(Section)} as tabs.
  133. * </p>
  134. * <p>
  135. * {@link Section#getTabButton()} is called to obtain a unique id for the
  136. * Sections; the id should actually be an identifier for an icon in the
  137. * icon-font in use.
  138. * </p>
  139. * <p>
  140. * {@link Section#getControls()} and {@link Section#getContent()} is called
  141. * when the Section is activated (displayed). Additionally
  142. * {@link Section#show()} is called to allow the Section to initialize
  143. * itself as needed when shown. Conversely {@link Section#hide()} is called
  144. * when the Section is deactivated.
  145. * </p>
  146. * <p>
  147. * Sections should take care to prefix CSS classnames used with
  148. * {@link VDebugWindow}.{@link #STYLENAME} to avoid that application theme
  149. * interferes with the debug window content.
  150. * </p>
  151. * <p>
  152. * Some of the window state, such as position and size, is persisted to
  153. * localStorage. Sections can use
  154. * {@link #writeState(Storage, String, Object)} and
  155. * {@link #readState(Storage, String, String)} (and relatives) to write and
  156. * read own persisted settings, keys will automatically be prefixed with
  157. * {@value #STORAGE_PREFIX}.
  158. * </p>
  159. */
  160. public VDebugWindow() {
  161. super(false, false);
  162. getElement().getStyle().setOverflow(Overflow.HIDDEN);
  163. setStylePrimaryName(STYLENAME);
  164. setWidget(window);
  165. window.add(head);
  166. head.add(tabs);
  167. head.add(controls);
  168. head.add(sectionHead);
  169. window.add(content);
  170. addHandles();
  171. head.setStylePrimaryName(STYLENAME_HEAD);
  172. tabs.setStylePrimaryName(STYLENAME_TABS);
  173. controls.setStylePrimaryName(STYLENAME_CONTROLS);
  174. sectionHead.setStylePrimaryName(STYLENAME_SECTION_HEAD);
  175. content.setStylePrimaryName(STYLENAME_CONTENT);
  176. // add controls TODO move these
  177. controls.add(menu);
  178. menu.addClickHandler(new ClickHandler() {
  179. @Override
  180. public void onClick(ClickEvent event) {
  181. menuPopup.showRelativeTo(menu);
  182. }
  183. });
  184. controls.add(minimize);
  185. minimize.addClickHandler(new ClickHandler() {
  186. @Override
  187. public void onClick(ClickEvent event) {
  188. toggleMinimized();
  189. writeStoredState();
  190. }
  191. });
  192. controls.add(close);
  193. close.addClickHandler(new ClickHandler() {
  194. @Override
  195. public void onClick(ClickEvent event) {
  196. close();
  197. }
  198. });
  199. Style s = content.getElement().getStyle();
  200. s.setOverflow(Overflow.AUTO);
  201. // move/resize
  202. final MouseHandler mouseHandler = new MouseHandler();
  203. mouseDownHandler = this.addDomHandler(mouseHandler,
  204. MouseDownEvent.getType());
  205. mouseMoveHandler = this.addDomHandler(mouseHandler,
  206. MouseMoveEvent.getType());
  207. }
  208. /**
  209. * Adds dummy handle elements to the bottom corners that might have
  210. * scrollbars that interfere with resizing on some platforms.
  211. *
  212. * @since 7.1
  213. */
  214. private void addHandles() {
  215. Element el = DOM.createDiv();
  216. el.setClassName(VDebugWindow.STYLENAME + "-handle "
  217. + VDebugWindow.STYLENAME + "-handle-sw");
  218. content.getElement().appendChild(el);
  219. el = DOM.createDiv();
  220. el.setClassName(VDebugWindow.STYLENAME + "-handle "
  221. + VDebugWindow.STYLENAME + "-handle-se");
  222. content.getElement().appendChild(el);
  223. }
  224. /**
  225. * Gets the {@link #VDebugWindow()} singleton instance.
  226. *
  227. * @return
  228. */
  229. public static VDebugWindow get() {
  230. if (instance == null) {
  231. instance = GWT.create(VDebugWindow.class);
  232. }
  233. return instance;
  234. }
  235. /**
  236. * Closes the window and stops visual logging.
  237. */
  238. public void close() {
  239. // TODO disable even more
  240. if (mouseDownHandler != null) {
  241. mouseDownHandler.removeHandler();
  242. mouseMoveHandler.removeHandler();
  243. mouseDownHandler = null;
  244. mouseMoveHandler = null;
  245. }
  246. Highlight.hideAll();
  247. hide();
  248. }
  249. boolean isClosed() {
  250. return !isShowing();
  251. }
  252. /**
  253. * Reads the stored state from localStorage.
  254. */
  255. private void readStoredState() {
  256. Storage storage = Storage.getLocalStorageIfSupported();
  257. if (storage == null) {
  258. return;
  259. }
  260. fullX = readState(storage, STORAGE_FULL_X, -510);
  261. fullY = readState(storage, STORAGE_FULL_Y, -230);
  262. fullW = readState(storage, STORAGE_FULL_W, 500);
  263. fullH = readState(storage, STORAGE_FULL_H, 150);
  264. minX = readState(storage, STORAGE_MIN_X, -40);
  265. minY = readState(storage, STORAGE_MIN_Y, -70);
  266. setFontSize(readState(storage, STORAGE_FONT_SIZE, 1));
  267. activateSection(readState(storage, STORAGE_ACTIVE_SECTION, 0));
  268. setMinimized(readState(storage, STORAGE_IS_MINIMIZED, false));
  269. applyPositionAndSize();
  270. }
  271. /**
  272. * Writes the persistent state to localStorage.
  273. */
  274. private void writeStoredState() {
  275. if (isClosed()) {
  276. return;
  277. }
  278. Storage storage = Storage.getLocalStorageIfSupported();
  279. if (storage == null) {
  280. return;
  281. }
  282. writeState(storage, STORAGE_FULL_X, fullX);
  283. writeState(storage, STORAGE_FULL_Y, fullY);
  284. writeState(storage, STORAGE_FULL_W, fullW);
  285. writeState(storage, STORAGE_FULL_H, fullH);
  286. writeState(storage, STORAGE_MIN_X, minX);
  287. writeState(storage, STORAGE_MIN_Y, minY);
  288. writeState(storage, STORAGE_FONT_SIZE, fontSize);
  289. int activeIdx = getActiveSection();
  290. if (activeIdx >= 0) {
  291. writeState(storage, STORAGE_ACTIVE_SECTION, activeIdx);
  292. }
  293. writeState(storage, STORAGE_IS_MINIMIZED, minimized);
  294. }
  295. /**
  296. * Writes the given value to the given {@link Storage} using the given key
  297. * (automatically prefixed with {@value #STORAGE_PREFIX}).
  298. *
  299. * @param storage
  300. * @param key
  301. * @param value
  302. */
  303. static void writeState(Storage storage, String key, Object value) {
  304. storage.setItem(STORAGE_PREFIX + key, String.valueOf(value));
  305. }
  306. /**
  307. * Returns the item with the given key (automatically prefixed with
  308. * {@value #STORAGE_PREFIX}) as an int from the given {@link Storage},
  309. * returning the given default value instead if not successful (e.g missing
  310. * item).
  311. *
  312. * @param storage
  313. * @param key
  314. * @param def
  315. * @return stored or default value
  316. */
  317. static int readState(Storage storage, String key, int def) {
  318. try {
  319. return Integer.parseInt(storage.getItem(STORAGE_PREFIX + key));
  320. } catch (Exception e) {
  321. return def;
  322. }
  323. }
  324. /**
  325. * Returns the item with the given key (automatically prefixed with
  326. * {@value #STORAGE_PREFIX}) as a boolean from the given {@link Storage},
  327. * returning the given default value instead if not successful (e.g missing
  328. * item).
  329. *
  330. * @param storage
  331. * @param key
  332. * @param def
  333. * @return stored or default value
  334. */
  335. static boolean readState(Storage storage, String key, boolean def) {
  336. try {
  337. return Boolean.parseBoolean(storage.getItem(STORAGE_PREFIX + key));
  338. } catch (Exception e) {
  339. return def;
  340. }
  341. }
  342. /**
  343. * Returns the item with the given key (automatically prefixed with
  344. * {@value #STORAGE_PREFIX}) as a String from the given {@link Storage},
  345. * returning the given default value instead if not successful (e.g missing
  346. * item).
  347. *
  348. * @param storage
  349. * @param key
  350. * @param def
  351. * @return stored or default value
  352. */
  353. static String readState(Storage storage, String key, String def) {
  354. String val = storage.getItem(STORAGE_PREFIX + key);
  355. return val != null ? val : def;
  356. }
  357. /**
  358. * Resets (clears) the stored state from localStorage.
  359. */
  360. private void resetStoredState() {
  361. Storage storage = Storage.getLocalStorageIfSupported();
  362. if (storage == null) {
  363. return;
  364. }
  365. // note: length is live
  366. for (int i = 0; i < storage.getLength();) {
  367. String key = storage.key(i);
  368. if (key.startsWith(STORAGE_PREFIX)) {
  369. removeState(storage, key.substring(STORAGE_PREFIX.length()));
  370. } else {
  371. i++;
  372. }
  373. }
  374. }
  375. /**
  376. * Removes the item with the given key (automatically prefixed with
  377. * {@value #STORAGE_PREFIX}) from the given {@link Storage}.
  378. *
  379. * @param storage
  380. * @param key
  381. */
  382. private void removeState(Storage storage, String key) {
  383. storage.removeItem(STORAGE_PREFIX + key);
  384. }
  385. /**
  386. * Applies the appropriate instance variables for width, height, x, y
  387. * depending on if the window is minimized or not.
  388. *
  389. * If the value is negative, the window is positioned that amount of pixels
  390. * from the right/bottom instead of left/top.
  391. *
  392. * Finally, the position is bounds-checked so that the window is not moved
  393. * off-screen (the adjusted values are not saved).
  394. */
  395. private void applyPositionAndSize() {
  396. int x = 0;
  397. int y = 0;
  398. if (minimized) {
  399. x = minX;
  400. if (minX < 0) {
  401. x = Window.getClientWidth() + minX;
  402. }
  403. y = minY;
  404. if (minY < 0) {
  405. y = Window.getClientHeight() + minY;
  406. }
  407. } else {
  408. x = fullX;
  409. if (fullX < 0) {
  410. x = Window.getClientWidth() + fullX;
  411. }
  412. y = fullY;
  413. if (y < 0) {
  414. y = Window.getClientHeight() + fullY;
  415. }
  416. content.setWidth(fullW + "px");
  417. content.setHeight(fullH + "px");
  418. }
  419. applyBounds(x, y);
  420. }
  421. private void applyBounds() {
  422. int x = getPopupLeft();
  423. int y = getPopupTop();
  424. applyBounds(x, y);
  425. }
  426. private void applyBounds(int x, int y) {
  427. // bounds check
  428. if (x < 0) {
  429. x = 0;
  430. }
  431. if (x > Window.getClientWidth() - getOffsetWidth()) {
  432. // not allowed off-screen to the right
  433. x = Window.getClientWidth() - getOffsetWidth();
  434. }
  435. if (y > Window.getClientHeight() - getOffsetHeight()) {
  436. y = Window.getClientHeight() - getOffsetHeight();
  437. }
  438. if (y < 0) {
  439. y = 0;
  440. }
  441. setPopupPosition(x, y);
  442. }
  443. /**
  444. * Reads position and size from the DOM to local variables (which in turn
  445. * can be stored to localStorage)
  446. */
  447. private void readPositionAndSize() {
  448. int x = getPopupLeft();
  449. int fromRight = Window.getClientWidth() - x - getOffsetWidth();
  450. if (fromRight < x) {
  451. x -= Window.getClientWidth();
  452. }
  453. int y = getPopupTop();
  454. int fromBottom = Window.getClientHeight() - y - getOffsetHeight();
  455. if (fromBottom < y) {
  456. y -= Window.getClientHeight();
  457. }
  458. if (minimized) {
  459. minY = y;
  460. minX = x;
  461. } else {
  462. fullY = y;
  463. fullX = x;
  464. fullW = content.getOffsetWidth();
  465. fullH = content.getOffsetHeight();
  466. }
  467. }
  468. /**
  469. * Adds the given {@link Section} as a tab in the {@link VDebugWindow} UI.
  470. * {@link Section#getTabButton()} is called to obtain a button which is used
  471. * tab.
  472. *
  473. * @param section
  474. */
  475. public void addSection(final Section section) {
  476. Button b = section.getTabButton();
  477. b.addClickHandler(new ClickHandler() {
  478. @Override
  479. public void onClick(ClickEvent event) {
  480. activateSection(section);
  481. writeStoredState();
  482. }
  483. });
  484. b.setStylePrimaryName(STYLENAME_TAB);
  485. tabs.add(b);
  486. sections.add(section);
  487. if (activeSection == null) {
  488. activateSection(section);
  489. }
  490. }
  491. /**
  492. * Activates the given {@link Section}
  493. *
  494. * @param section
  495. */
  496. void activateSection(Section section) {
  497. if (section != null && section != activeSection) {
  498. Highlight.hideAll();
  499. // remove old stuff
  500. if (activeSection != null) {
  501. activeSection.hide();
  502. content.remove(activeSection.getContent());
  503. sectionHead.remove(activeSection.getControls());
  504. }
  505. // update tab styles
  506. for (int i = 0; i < tabs.getWidgetCount(); i++) {
  507. Widget tab = tabs.getWidget(i);
  508. tab.setStyleDependentName(STYLENAME_SELECTED,
  509. tab == section.getTabButton());
  510. }
  511. // add new stuff
  512. content.add(section.getContent());
  513. sectionHead.add(section.getControls());
  514. activeSection = section;
  515. activeSection.show();
  516. }
  517. }
  518. void activateSection(int n) {
  519. if (n < sections.size()) {
  520. activateSection(sections.get(n));
  521. }
  522. }
  523. int getActiveSection() {
  524. return sections.indexOf(activeSection);
  525. }
  526. /**
  527. * Toggles the window between minimized and full states.
  528. */
  529. private void toggleMinimized() {
  530. setMinimized(!minimized);
  531. writeStoredState();
  532. }
  533. /**
  534. * Sets whether or not the window is minimized.
  535. *
  536. * @param minimized
  537. */
  538. private void setMinimized(boolean minimized) {
  539. this.minimized = minimized;
  540. tabs.setVisible(!minimized);
  541. content.setVisible(!minimized);
  542. sectionHead.setVisible(!minimized);
  543. menu.setVisible(!minimized);
  544. applyPositionAndSize();
  545. }
  546. /**
  547. * Sets the font size in use.
  548. *
  549. * @param size
  550. */
  551. private void setFontSize(int size) {
  552. removeStyleDependentName("size" + fontSize);
  553. fontSize = size;
  554. addStyleDependentName("size" + size);
  555. }
  556. /**
  557. * Gets the font size currently in use.
  558. *
  559. * @return
  560. */
  561. private int getFontSize() {
  562. return fontSize;
  563. }
  564. /**
  565. * Gets the milliseconds since application start.
  566. *
  567. * @return
  568. */
  569. static int getMillisSinceStart() {
  570. return start.elapsedMillis();
  571. }
  572. /**
  573. * Gets the milliseconds since last {@link #resetTimer()} call.
  574. *
  575. * @return
  576. */
  577. static int getMillisSinceReset() {
  578. return lastReset.elapsedMillis();
  579. }
  580. /**
  581. * Resets the timer.
  582. *
  583. * @return Milliseconds elapsed since the timer was last reset.
  584. */
  585. static int resetTimer() {
  586. int sinceLast = lastReset.elapsedMillis();
  587. lastReset = new Duration();
  588. return sinceLast;
  589. }
  590. /**
  591. * Gets a nicely formatted string with timing information suitable for
  592. * display in tooltips.
  593. *
  594. * @param sinceStart
  595. * @param sinceReset
  596. * @return
  597. */
  598. static String getTimingTooltip(int sinceStart, int sinceReset) {
  599. String title = formatDuration(sinceStart) + " since start";
  600. title += ", &#10; " + formatDuration(sinceReset) + " since timer reset";
  601. title += " &#10; @ "
  602. + DateTimeFormat.getFormat("HH:mm:ss.SSS").format(new Date());
  603. return title;
  604. }
  605. /**
  606. * Formats the given milliseconds as hours, minutes, seconds and
  607. * milliseconds.
  608. *
  609. * @param ms
  610. * @return
  611. */
  612. static String formatDuration(int ms) {
  613. NumberFormat fmt = NumberFormat.getFormat("00");
  614. String seconds = fmt.format((ms / 1000) % 60);
  615. String minutes = fmt.format((ms / (1000 * 60)) % 60);
  616. String hours = fmt.format((ms / (1000 * 60 * 60)) % 24);
  617. String millis = NumberFormat.getFormat("000").format(ms % 1000);
  618. return hours + "h " + minutes + "m " + seconds + "s " + millis + "ms";
  619. }
  620. /**
  621. * Called when the window is initialized.
  622. */
  623. public void init() {
  624. show();
  625. /*
  626. * Finalize initialization when all entry points have had the chance to
  627. * e.g. register new sections.
  628. */
  629. Scheduler.get().scheduleFinally(new ScheduledCommand() {
  630. @Override
  631. public void execute() {
  632. readStoredState();
  633. Window.addResizeHandler(
  634. new com.google.gwt.event.logical.shared.ResizeHandler() {
  635. Timer t = new Timer() {
  636. @Override
  637. public void run() {
  638. applyPositionAndSize();
  639. }
  640. };
  641. @Override
  642. public void onResize(ResizeEvent event) {
  643. t.cancel();
  644. // TODO less
  645. t.schedule(1000);
  646. }
  647. });
  648. }
  649. });
  650. }
  651. /**
  652. * Called when the result from analyzeLayouts is received.
  653. *
  654. * @param ac
  655. * @param meta
  656. */
  657. public void meta(ApplicationConnection ac, ValueMap meta) {
  658. if (isClosed()) {
  659. return;
  660. }
  661. for (Section s : sections) {
  662. s.meta(ac, meta);
  663. }
  664. }
  665. /**
  666. * Called when a response is received
  667. *
  668. * @param ac
  669. * @param uidl
  670. */
  671. public void uidl(ApplicationConnection ac, ValueMap uidl) {
  672. if (isClosed()) {
  673. return;
  674. }
  675. for (Section s : sections) {
  676. s.uidl(ac, uidl);
  677. }
  678. }
  679. /**
  680. * Gets the container element for this window. The debug window is always
  681. * global to the document and not related to any
  682. * {@link ApplicationConnection} in particular.
  683. *
  684. * @return The global overlay container element.
  685. */
  686. @Override
  687. public com.google.gwt.user.client.Element getOverlayContainer() {
  688. return RootPanel.get().getElement();
  689. }
  690. /*
  691. * Inner classes
  692. */
  693. /**
  694. * Popup menu for {@link VDebugWindow}.
  695. *
  696. * @since 7.1
  697. * @author Vaadin Ltd
  698. */
  699. protected class Menu extends VOverlay {
  700. FlowPanel content = new FlowPanel();
  701. DebugButton[] sizes = new DebugButton[] {
  702. new DebugButton(null, "Small", "A"),
  703. new DebugButton(null, "Medium", "A"),
  704. new DebugButton(null, "Large", "A") };
  705. DebugButton[] modes = new DebugButton[] {
  706. new DebugButton(Icon.DEVMODE_OFF,
  707. "Debug only (causes page reload)"),
  708. new DebugButton(Icon.DEVMODE_ON,
  709. "DevMode (causes page reload)"),
  710. new DebugButton(Icon.DEVMODE_SUPER,
  711. "SuperDevMode (causes page reload)") };
  712. Menu() {
  713. super(true, true);
  714. setWidget(content);
  715. setStylePrimaryName(STYLENAME + "-menu");
  716. content.setStylePrimaryName(STYLENAME + "-menu-content");
  717. FlowPanel size = new FlowPanel();
  718. content.add(size);
  719. final ClickHandler sizeHandler = new ClickHandler() {
  720. @Override
  721. public void onClick(ClickEvent event) {
  722. for (int i = 0; i < sizes.length; i++) {
  723. Button b = sizes[i];
  724. if (b == event.getSource()) {
  725. setSize(i);
  726. }
  727. }
  728. hide();
  729. }
  730. };
  731. for (int i = 0; i < sizes.length; i++) {
  732. Button b = sizes[i];
  733. b.setStyleDependentName("size" + i, true);
  734. b.addClickHandler(sizeHandler);
  735. size.add(b);
  736. }
  737. FlowPanel mode = new FlowPanel();
  738. content.add(mode);
  739. final ClickHandler modeHandler = new ClickHandler() {
  740. @Override
  741. public void onClick(ClickEvent event) {
  742. for (int i = 0; i < modes.length; i++) {
  743. Button b = modes[i];
  744. if (b == event.getSource()) {
  745. setDevMode(i);
  746. }
  747. }
  748. hide();
  749. }
  750. };
  751. modes[getDevMode()].setActive(true);
  752. for (int i = 0; i < modes.length; i++) {
  753. Button b = modes[i];
  754. b.addClickHandler(modeHandler);
  755. mode.add(b);
  756. }
  757. Button reset = new DebugButton(Icon.RESET, "Restore defaults.",
  758. " Reset");
  759. reset.addClickHandler(new ClickHandler() {
  760. @Override
  761. public void onClick(ClickEvent event) {
  762. resetStoredState();
  763. readStoredState();
  764. hide();
  765. }
  766. });
  767. content.add(reset);
  768. }
  769. private void setSize(int size) {
  770. for (int i = 0; i < sizes.length; i++) {
  771. Button b = sizes[i];
  772. b.setStyleDependentName(STYLENAME_ACTIVE, i == size);
  773. }
  774. setFontSize(size);
  775. writeStoredState();
  776. }
  777. @Override
  778. public void show() {
  779. super.show();
  780. setSize(getFontSize());
  781. }
  782. private int getDevMode() {
  783. if (Location.getParameter("superdevmode") != null) {
  784. return 2;
  785. } else if (Location.getParameter("gwt.codesvr") != null) {
  786. return 1;
  787. } else {
  788. return 0;
  789. }
  790. }
  791. private void setDevMode(int mode) {
  792. UrlBuilder u = Location.createUrlBuilder();
  793. switch (mode) {
  794. case 2:
  795. u.setParameter("superdevmode", "");
  796. u.removeParameter("gwt.codesvr");
  797. break;
  798. case 1:
  799. u.setParameter("gwt.codesvr", "localhost:9997");
  800. u.removeParameter("superdevmode");
  801. break;
  802. default:
  803. u.removeParameter("gwt.codesvr");
  804. u.removeParameter("superdevmode");
  805. }
  806. Location.assign(u.buildString());
  807. }
  808. }
  809. /**
  810. * Handler for resizing and moving window, also updates cursor on mousemove.
  811. *
  812. * @since 7.1
  813. * @author Vaadin Ltd
  814. */
  815. protected class MouseHandler implements MouseMoveHandler, MouseDownHandler,
  816. NativePreviewHandler {
  817. boolean resizeLeft;
  818. boolean resizeRight;
  819. boolean resizeUp;
  820. boolean resizeDown;
  821. boolean move;
  822. boolean sizing;
  823. // dragging stopped, remove handler on next event
  824. boolean stop;
  825. HandlerRegistration dragHandler;
  826. int startX;
  827. int startY;
  828. int startW;
  829. int startH;
  830. int startTop;
  831. int startLeft;
  832. @Override
  833. public void onMouseMove(MouseMoveEvent event) {
  834. if (null == dragHandler) {
  835. updateResizeFlags(event);
  836. updateCursor();
  837. }
  838. }
  839. @Override
  840. public void onMouseDown(MouseDownEvent event) {
  841. if (event.getNativeButton() != NativeEvent.BUTTON_LEFT
  842. || dragHandler != null) {
  843. return;
  844. }
  845. updateResizeFlags(event);
  846. if (sizing || move) {
  847. // some os/browsers don't pass events trough scrollbars; hide
  848. // while dragging (esp. important for resize from right/bottom)
  849. content.getElement().getStyle().setOverflow(Overflow.HIDDEN);
  850. startX = event.getClientX();
  851. startY = event.getClientY();
  852. startW = content.getOffsetWidth();
  853. startH = content.getOffsetHeight();
  854. startTop = getPopupTop();
  855. startLeft = getPopupLeft();
  856. dragHandler = Event.addNativePreviewHandler(this);
  857. event.preventDefault();
  858. stop = false;
  859. }
  860. }
  861. @Override
  862. public void onPreviewNativeEvent(NativePreviewEvent event) {
  863. if (event.getTypeInt() == Event.ONMOUSEMOVE && !stop
  864. && hasMoved(event.getNativeEvent())) {
  865. int dx = event.getNativeEvent().getClientX() - startX;
  866. int dy = event.getNativeEvent().getClientY() - startY;
  867. if (sizing) {
  868. int minWidth = tabs.getOffsetWidth()
  869. + controls.getOffsetWidth();
  870. if (resizeLeft) {
  871. int w = startW - dx;
  872. if (w < minWidth) {
  873. w = minWidth;
  874. dx = startW - minWidth;
  875. }
  876. content.setWidth(w + "px");
  877. setPopupPosition(startLeft + dx, getPopupTop());
  878. } else if (resizeRight) {
  879. int w = startW + dx;
  880. if (w < minWidth) {
  881. w = minWidth;
  882. }
  883. content.setWidth(w + "px");
  884. }
  885. if (resizeUp) {
  886. int h = startH - dy;
  887. if (h < MIN_HEIGHT) {
  888. h = MIN_HEIGHT;
  889. dy = startH - MIN_HEIGHT;
  890. }
  891. content.setHeight(h + "px");
  892. setPopupPosition(getPopupLeft(), startTop + dy);
  893. } else if (resizeDown) {
  894. int h = startH + dy;
  895. if (h < MIN_HEIGHT) {
  896. h = MIN_HEIGHT;
  897. }
  898. content.setHeight(h + "px");
  899. }
  900. } else if (move) {
  901. setPopupPosition(startLeft + dx, startTop + dy);
  902. }
  903. event.cancel();
  904. } else if (event.getTypeInt() == Event.ONMOUSEUP) {
  905. stop = true;
  906. if (hasMoved(event.getNativeEvent())) {
  907. event.cancel();
  908. }
  909. } else if (event.getTypeInt() == Event.ONCLICK) {
  910. stop = true;
  911. if (hasMoved(event.getNativeEvent())) {
  912. event.cancel();
  913. }
  914. } else if (stop) {
  915. stop = false;
  916. dragHandler.removeHandler();
  917. dragHandler = null;
  918. sizing = false;
  919. move = false;
  920. // restore scrollbars
  921. content.getElement().getStyle().setOverflow(Overflow.AUTO);
  922. updateCursor();
  923. applyBounds();
  924. readPositionAndSize();
  925. writeStoredState();
  926. event.cancel();
  927. }
  928. }
  929. private boolean hasMoved(NativeEvent event) {
  930. return Math.abs(startX - event.getClientX()) > MOVE_TRESHOLD
  931. || Math.abs(startY - event.getClientY()) > MOVE_TRESHOLD;
  932. }
  933. private void updateCursor() {
  934. Element c = getElement();
  935. if (resizeLeft) {
  936. if (resizeUp) {
  937. c.getStyle().setCursor(Cursor.NW_RESIZE);
  938. } else if (resizeDown) {
  939. c.getStyle().setCursor(Cursor.SW_RESIZE);
  940. } else {
  941. c.getStyle().setCursor(Cursor.W_RESIZE);
  942. }
  943. } else if (resizeRight) {
  944. if (resizeUp) {
  945. c.getStyle().setCursor(Cursor.NE_RESIZE);
  946. } else if (resizeDown) {
  947. c.getStyle().setCursor(Cursor.SE_RESIZE);
  948. } else {
  949. c.getStyle().setCursor(Cursor.E_RESIZE);
  950. }
  951. } else if (resizeUp) {
  952. c.getStyle().setCursor(Cursor.N_RESIZE);
  953. } else if (resizeDown) {
  954. c.getStyle().setCursor(Cursor.S_RESIZE);
  955. } else if (move) {
  956. c.getStyle().setCursor(Cursor.MOVE);
  957. } else {
  958. c.getStyle().setCursor(Cursor.AUTO);
  959. }
  960. }
  961. protected void updateResizeFlags(MouseEvent event) {
  962. if (event.isShiftKeyDown()) {
  963. // resize from lower right
  964. resizeUp = false;
  965. resizeLeft = false;
  966. resizeRight = true;
  967. resizeDown = true;
  968. move = false;
  969. sizing = true;
  970. return;
  971. } else if (event.isAltKeyDown()) {
  972. // move it
  973. move = true;
  974. resizeUp = false;
  975. resizeLeft = false;
  976. resizeRight = false;
  977. resizeDown = false;
  978. sizing = false;
  979. return;
  980. }
  981. Element c = getElement();
  982. int w = c.getOffsetWidth();
  983. int h = c.getOffsetHeight();
  984. int x = event.getRelativeX(c);
  985. int y = event.getRelativeY(c);
  986. resizeLeft = x < HANDLE_SIZE && y > tabs.getOffsetHeight();
  987. resizeRight = (x > (w - HANDLE_SIZE) && y > tabs.getOffsetHeight())
  988. || (x > (w - 2 * HANDLE_SIZE) && y > (h - 2 * HANDLE_SIZE));
  989. resizeUp = y > tabs.getOffsetHeight()
  990. && y < tabs.getOffsetHeight() + HANDLE_SIZE;
  991. resizeDown = y > (h - HANDLE_SIZE)
  992. || (x > (w - 2 * HANDLE_SIZE) && y > (h - 2 * HANDLE_SIZE));
  993. move = !resizeDown && !resizeLeft && !resizeRight && !resizeUp
  994. && y < head.getOffsetHeight();
  995. sizing = resizeLeft || resizeRight || resizeUp || resizeDown;
  996. }
  997. }
  998. }