View Javadoc

1   /**********************************************
2    * Copyright (C) 2010 Lukas Laag
3    * This file is part of vectomatic2.
4    * 
5    * vectomatic2 is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    * 
10   * vectomatic2 is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   * 
15   * You should have received a copy of the GNU General Public License
16   * along with vectomatic2.  If not, see http://www.gnu.org/licenses/
17   **********************************************/
18  package org.vectomatic.svg.edit.client;
19  
20  import java.io.IOException;
21  import java.util.ArrayList;
22  import java.util.List;
23  
24  import org.vectomatic.dom.svg.OMSVGSVGElement;
25  import org.vectomatic.dom.svg.utils.OMSVGParser;
26  import org.vectomatic.dom.svg.utils.SVGConstants;
27  
28  import com.extjs.gxt.ui.client.event.Events;
29  import com.extjs.gxt.ui.client.event.Listener;
30  import com.extjs.gxt.ui.client.event.MenuEvent;
31  import com.extjs.gxt.ui.client.event.MessageBoxEvent;
32  import com.extjs.gxt.ui.client.event.SelectionListener;
33  import com.extjs.gxt.ui.client.event.WindowEvent;
34  import com.extjs.gxt.ui.client.util.Rectangle;
35  import com.extjs.gxt.ui.client.util.Size;
36  import com.extjs.gxt.ui.client.widget.Component;
37  import com.extjs.gxt.ui.client.widget.Info;
38  import com.extjs.gxt.ui.client.widget.MessageBox;
39  import com.extjs.gxt.ui.client.widget.Viewport;
40  import com.extjs.gxt.ui.client.widget.menu.Menu;
41  import com.extjs.gxt.ui.client.widget.menu.MenuBar;
42  import com.extjs.gxt.ui.client.widget.menu.MenuBarItem;
43  import com.extjs.gxt.ui.client.widget.menu.MenuItem;
44  import com.extjs.gxt.ui.client.widget.menu.SeparatorMenuItem;
45  import com.google.gwt.core.client.EntryPoint;
46  import com.google.gwt.core.client.GWT;
47  import com.google.gwt.http.client.Request;
48  import com.google.gwt.http.client.RequestBuilder;
49  import com.google.gwt.http.client.RequestCallback;
50  import com.google.gwt.http.client.RequestException;
51  import com.google.gwt.http.client.Response;
52  import com.google.gwt.user.client.DOM;
53  import com.google.gwt.user.client.ui.DialogBox;
54  import com.google.gwt.user.client.ui.RootPanel;
55  
56  /**
57   * Application main class.
58   * @author laaglu
59   */
60  public class VectomaticApp2 implements EntryPoint {
61  	static VectomaticApp2 APP;
62  	private List<SVGWindow> windows;
63  	private SVGWindow activeWindow;
64  	private int windowX, windowY;
65  	private Viewport viewPort;
66  	private MenuBar menuBar;
67  	private Menu recentDocsMenu;
68  	private MenuItem resetViewItem;
69  	private MenuItem tileWindowsItem;
70  	private MenuItem stackWindowsItem;
71  	private MenuItem closeWindowItem;
72  	private SelectionListener<MenuEvent> dispatcher;
73  	private MessageBox openUrlBox;
74  	private RSSReader rssReader;
75  	private AboutDialog aboutDialog;
76  	
77  	public void onModuleLoad() {
78  		APP = this;
79  		GWT.setUncaughtExceptionHandler(new GWT.UncaughtExceptionHandler() {
80  			public void onUncaughtException(Throwable throwable) {
81  				GWT.log("Uncaught exception", throwable);
82  				if (!GWT.isScript()) {
83  					String text = "Uncaught exception: ";
84  					while (throwable != null) {
85  						StackTraceElement[] stackTraceElements = throwable
86  								.getStackTrace();
87  						text += throwable.toString() + "\n";
88  						for (int i = 0; i < stackTraceElements.length; i++) {
89  							text += "    at " + stackTraceElements[i] + "\n";
90  						}
91  						throwable = throwable.getCause();
92  						if (throwable != null) {
93  							text += "Caused by: ";
94  						}
95  					}
96  					DialogBox dialogBox = new DialogBox(true);
97  					DOM.setStyleAttribute(dialogBox.getElement(),
98  							"backgroundColor", "#ABCDEF");
99  					System.err.print(text);
100 					text = text.replaceAll(" ", "&nbsp;");
101 					dialogBox.setHTML("<pre>" + text + "</pre>");
102 					dialogBox.center();
103 				}
104 			}
105 		});
106 		AppBundle.INSTANCE.css().ensureInjected();
107 		windows = new ArrayList<SVGWindow>();
108 		viewPort = new Viewport();
109 		
110 		Menu fileMenu = new Menu();
111 		final MenuItem openUrlItem = new MenuItem(AppConstants.INSTANCE.openUrlMenuItem());
112 		fileMenu.add(openUrlItem);
113 		final MenuItem openRssFeedItem = new MenuItem(AppConstants.INSTANCE.openRssFeedMenuItem());
114 		fileMenu.add(openRssFeedItem);
115 		MenuItem recentDocumentsItem = new MenuItem(AppConstants.INSTANCE.recentDocumentsMenuItem());
116 		recentDocsMenu = new Menu();
117 		recentDocumentsItem.setSubMenu(recentDocsMenu);
118 		fileMenu.add(recentDocumentsItem);
119 
120 		Menu windowMenu = new Menu();
121 		resetViewItem = new MenuItem(AppConstants.INSTANCE.resetViewMenuItem());
122 		windowMenu.add(resetViewItem);
123 		windowMenu.add(new SeparatorMenuItem());
124 		tileWindowsItem = new MenuItem(AppConstants.INSTANCE.tileWindowsMenuItem());
125 		windowMenu.add(tileWindowsItem);
126 		stackWindowsItem = new MenuItem(AppConstants.INSTANCE.stackWindowsMenuItem());
127 		windowMenu.add(stackWindowsItem);
128 		windowMenu.add(new SeparatorMenuItem());
129 		closeWindowItem = new MenuItem(AppConstants.INSTANCE.closeWindowMenuItem());
130 		windowMenu.add(closeWindowItem);
131 		
132 		Menu aboutMenu = new Menu();
133 		final MenuItem aboutItem = new MenuItem(AppConstants.INSTANCE.aboutMenuItem());
134 		aboutMenu.add(aboutItem);
135 
136 		menuBar = new MenuBar();
137 		menuBar.setBorders(true);  
138 		menuBar.setStyleAttribute("borderTop", "none");
139 		menuBar.add(new MenuBarItem(AppConstants.INSTANCE.fileMenu(), fileMenu));
140 		menuBar.add(new MenuBarItem(AppConstants.INSTANCE.windowMenu(), windowMenu));
141 		menuBar.add(new MenuBarItem(AppConstants.INSTANCE.aboutMenu(), aboutMenu));
142 		
143 		dispatcher = new SelectionListener<MenuEvent>() {
144 			@Override
145 			public void componentSelected(MenuEvent me) {
146 				MenuItem item = (MenuItem) me.getItem();
147 				if (item.getParentMenu() == recentDocsMenu) {
148 					openRecent(item.getText());
149 				} else if (item == openUrlItem) {
150 					openUrl();
151 				} else if (item == openRssFeedItem) {
152 					openRssFeed();
153 				} else if (item == resetViewItem) {
154 					resetView();
155 				} else if (item == tileWindowsItem) {
156 					tileWindows();
157 				} else if (item == stackWindowsItem) {
158 					stackWindows();
159 				} else if (item == closeWindowItem) {
160 					closeWindow(activeWindow);
161 				} else if (item == aboutItem) {
162 					about();
163 				}
164 			}
165 		};
166 		openUrlItem.addSelectionListener(dispatcher);
167 		openRssFeedItem.addSelectionListener(dispatcher);
168 		resetViewItem.addSelectionListener(dispatcher);
169 		tileWindowsItem.addSelectionListener(dispatcher);
170 		stackWindowsItem.addSelectionListener(dispatcher);
171 		closeWindowItem.addSelectionListener(dispatcher);
172 		aboutItem.addSelectionListener(dispatcher);
173 		
174 		viewPort.add(menuBar);
175 		addWindow(AppBundle.INSTANCE.fish().getSvg(), "fish.svg");
176 		addWindow(AppBundle.INSTANCE.fries().getSvg(), "fries.svg");
177 		viewPort.setStyleAttribute("background-color", SVGConstants.CSS_BEIGE_VALUE);
178 
179 		update();
180 		RootPanel.get().add(viewPort);
181 	}
182 	
183 	public SVGWindow addWindow(OMSVGSVGElement svg, String title) {
184 		SVGWindow window = GWT.create(SVGWindow.class);
185 		window.setSvg(svg);
186 		window.setHeading(title);
187 		windows.add(window);
188 		// To be notified when a window is activated in order to
189 		// keep track of the active window 
190 		window.addListener(Events.Activate, new Listener<WindowEvent>() {
191 
192 			@Override
193 			public void handleEvent(WindowEvent we) {
194 				activeWindow = (SVGWindow) we.getWindow();
195 			}
196 		});
197 		window.addListener(Events.BeforeHide, new Listener<WindowEvent>() {
198 
199 			@Override
200 			public void handleEvent(WindowEvent we) {
201 				closeWindow((SVGWindow) we.getWindow());
202 			}
203 		});
204 
205 		// Update the recent docs menu
206 		List<Component> recentDocs = recentDocsMenu.getItems();
207 		boolean alreadyInRecentDocs = false;
208 		for (Component item : recentDocs) {
209 			MenuItem menuItem = (MenuItem)item;
210 			if (title.equals(menuItem.getText())) {
211 				alreadyInRecentDocs = true;
212 				break;
213 			}
214 		}
215 		if (!alreadyInRecentDocs) {
216 			if (recentDocs.size() >= 8) {
217 				Component oldestItem = recentDocs.get(0);
218 				oldestItem.removeAllListeners();
219 				recentDocsMenu.remove(oldestItem);
220 			}
221 			MenuItem windowItem = new MenuItem(title);
222 			windowItem.addSelectionListener(dispatcher);
223 			recentDocsMenu.add(windowItem);
224 		}
225 
226 		
227 		int windowBarHeight = getWindowBarHeight();
228 		windowY += windowBarHeight;
229 		window.setPagePosition(windowX, windowY);
230 		windowX += windowBarHeight;
231 //		window.show();
232 		viewPort.add(window);
233 		window.setVisible(true);
234 		update();
235 		return window;
236 	}
237 
238 	public void openUrl() {
239 		GWT.log("openUrl()");
240 		openUrlBox = MessageBox.prompt(AppConstants.INSTANCE.openUrlMenuItem(), AppConstants.INSTANCE.openUrlText());
241 		openUrlBox.addCallback(new Listener<MessageBoxEvent>() {  
242 			public void handleEvent(MessageBoxEvent be) {
243 				load(be.getValue());
244 			}  
245 		});  
246 	}
247 	
248 	public void info(String command, String message) {
249 		Info.display(command, message);  
250 	}
251 	
252 	public void load(final String url) {
253 		OMSVGSVGElement svg = null;
254 		if ("fish.svg".equals(url)) {
255 			svg = AppBundle.INSTANCE.fish().getSvg();
256 			addWindow(svg, url);
257 		} else if ("fries.svg".equals(url)) {
258 			svg = AppBundle.INSTANCE.fries().getSvg();
259 			addWindow(svg, url);
260 		} else {
261 			String resourceUrl = GWT.getHostPageBaseURL() + "fetch?url=" + url + "&type=text/xml";
262 			RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, resourceUrl);
263 			requestBuilder.setCallback(new RequestCallback() {
264 				public void onError(Request request, Throwable e) {
265 					GWT.log("Cannot fetch " + url, e);
266 					info(AppConstants.INSTANCE.openUrlMenuItem(), AppMessages.INSTANCE.loadErrorMessage(url, e.getMessage()));
267 				}
268 
269 				private void onSuccess(Request request, Response response) {
270 					OMSVGSVGElement svg = OMSVGParser.parse(response.getText());
271 					addWindow(svg, url);
272 				}
273 				
274 				public void onResponseReceived(Request request, Response response) {
275 					if (response.getStatusCode() == Response.SC_OK) {
276 						onSuccess(request, response);
277 					} else {
278 						onError(request, new IOException(AppMessages.INSTANCE.httpErrorMessage(Integer.toString(response.getStatusCode()))));
279 					}
280 				}
281 			});
282 			try {
283 				requestBuilder.send();
284 			} catch (RequestException e) {
285 				GWT.log("Cannot fetch " + url, e);
286 				info(AppConstants.INSTANCE.openUrlMenuItem(), AppMessages.INSTANCE.loadErrorMessage(url, e.getMessage()));
287 			}
288 		}
289 	}
290 	
291 	public void openRecent(String url) {
292 		GWT.log("openRecent(" + url + ")");
293 		load(url);
294 	}
295 	
296 	public void openRssFeed() {
297 		GWT.log("openRssFeed()");
298 		if (rssReader == null) {
299 			rssReader = new RSSReader();
300 		}
301 		rssReader.show();
302 	}
303 	
304 	public void resetView() {
305 		GWT.log("resetView()");
306 		activeWindow.setRotationCompass(0);
307 		activeWindow.setScaleSlider(50);
308 	}
309 	public void tileWindows() {
310 		GWT.log("tileWindows()");
311 		Rectangle rect = getRectangle();
312 		int count = windows.size();
313 		int cols = (int)Math.ceil(Math.sqrt(count));
314 		int rows = (int)Math.ceil((double)count/cols);
315 		GWT.log("cols=" + cols + "; rows=" + rows);
316 		Size windowSize = new Size(rect.width / cols, rect.height / rows);
317 		int index = 0;
318 		for (SVGWindow window : windows) {
319 			window.setSize(windowSize.width, windowSize.height);
320 			int x = index % cols;
321 			int y = index / cols;
322 			window.setPagePosition(rect.x + x * windowSize.width, rect.y + y * windowSize.height);
323 			index++;
324 		}
325 	}
326 	public void stackWindows() {
327 		GWT.log("stackWindows()");
328 		Rectangle rect = getRectangle();
329 		Size size = viewPort.getSize();
330 		Size windowSize = new Size((int)(size.width * 0.75f), (int)(size.height * 0.75f));
331 		int windowBarHeight = getWindowBarHeight();
332 		int index = 0;
333 		for (SVGWindow window : windows) {
334 			window.setSize(windowSize.width, windowSize.height);
335 			int x = rect.x + index * windowBarHeight;
336 			int y = rect.y + index * windowBarHeight;
337 			window.setPagePosition(x, y);
338 			index++;
339 			if (y + windowSize.height > size.height) {
340 				x = rect.x;
341 				y = rect.y;
342 				index = 0;
343 			}
344 		}
345 	}
346 	public void closeWindow(SVGWindow window) {
347 		GWT.log("closeWindow()");
348 		if (window != null) {
349 			window.removeAllListeners();
350 			windows.remove(window);
351 			activeWindow = null;
352 			window.hide();
353 			update();
354 		}
355 	}
356 	public void about() {
357 		GWT.log("about()");
358 		if (aboutDialog == null) {
359 			aboutDialog = new AboutDialog();
360 		}
361 		aboutDialog.show();
362 	}
363 	public Rectangle getRectangle() {
364 		int windowBarHeight = getWindowBarHeight();
365 		Size viewPortSize = viewPort.getSize();
366 		Rectangle rect = new Rectangle(0, windowBarHeight, viewPortSize.width, viewPortSize.height - windowBarHeight);
367 		return rect;
368 	}
369 	public static final int getWindowBarHeight() {
370 		return 22;
371 	}
372 	
373 	private void update() {
374 		closeWindowItem.setEnabled(windows.size() > 0);
375 		tileWindowsItem.setEnabled(windows.size() > 0);
376 		stackWindowsItem.setEnabled(windows.size() > 0);
377 		resetViewItem.setEnabled(windows.size() > 0);
378 	}
379 }