source: trunk/src/org/expeditee/items/widgets/JfxBrowser.java@ 543

Last change on this file since 543 was 543, checked in by jts21, 11 years ago

Add correction for missing URL protocol, and detection of search URLs to JfxBrowser

  • Property svn:executable set to *
File size: 10.1 KB
Line 
1package org.expeditee.items.widgets;
2
3import java.awt.Color;
4import java.awt.Component;
5import java.awt.FlowLayout;
6import java.awt.event.ComponentEvent;
7import java.awt.event.ComponentListener;
8import java.io.File;
9import java.lang.reflect.Constructor;
10import java.lang.reflect.InvocationTargetException;
11import java.lang.reflect.Method;
12import java.net.URL;
13import java.net.URLClassLoader;
14import java.util.Arrays;
15import java.util.List;
16
17import javax.swing.JPanel;
18
19import org.expeditee.gui.FreeItems;
20import org.expeditee.gui.MessageBay;
21import org.expeditee.items.Text;
22
23/**
24 * Web browser using a JavaFX WebView.
25 *
26 * @author ngw8
27 * @author jts21
28 */
29public class JfxBrowser extends DataFrameWidget {
30 private WebBrowserPanel _browser;
31
32 static Class JFXPanel;
33
34 static Class Platform;
35 static Method PlatformRunLater;
36
37 static Class Group;
38 static Method GroupGetChildren;
39
40 static Class Scene;
41 static Constructor SceneConstructor;
42
43 static Class WebView;
44 static Method JFXPanelSetScene;
45 static Method WebViewSetPrefSize;
46 static Method WebViewGetEngine;
47
48 static Class WebEngine;
49 static Method WebEngineLoad;
50 static Method WebEngineGetLocation;
51 static Method WebEngineGetLoadWorker;
52
53 static Class Worker;
54 static Method WorkerStateProperty;
55
56 static Class ReadOnlyObjectProperty;
57 static Method ReadOnlyObjectPropertyAddListener;
58
59 static Class ChangeListener;
60
61 static Class ObservableValue;
62
63 static Class State;
64 static List<Object> StateConstants;
65
66 static {
67 String javaLibDir = System.getProperty("java.home") + File.separator + "lib" + File.separator;
68 try {
69 ClassLoader classLoader = ClassLoader.getSystemClassLoader();
70
71 File jar = new File(javaLibDir + "jfxrt.jar");
72
73 Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
74 addURL.setAccessible(true);
75 addURL.invoke(classLoader, jar.toURI().toURL());
76
77 JFXPanel = Class.forName("javafx.embed.swing.JFXPanel", false, classLoader);
78
79 Platform = classLoader.loadClass("javafx.application.Platform");
80 PlatformRunLater = Platform.getMethod("runLater", Runnable.class);
81
82 // Setting JFX to not exit when all panels are hidden, otherwise moving, resizing and changing frames will
83 // cause JFX to exit, preventing JFX elements from displaying at all
84 Platform.getMethod("setImplicitExit", boolean.class).invoke(null, false);
85
86 Group = classLoader.loadClass("javafx.scene.Group");
87 GroupGetChildren = Group.getMethod("getChildren");
88
89 Scene = classLoader.loadClass("javafx.scene.Scene");
90 SceneConstructor = Scene.getConstructor(classLoader.loadClass("javafx.scene.Parent"));
91
92 WebView = classLoader.loadClass("javafx.scene.web.WebView");
93 JFXPanelSetScene = JFXPanel.getMethod("setScene", Scene);
94 WebViewSetPrefSize = WebView.getMethod("setPrefSize", double.class, double.class);
95 WebViewGetEngine = WebView.getMethod("getEngine");
96
97 WebEngine = classLoader.loadClass("javafx.scene.web.WebEngine");
98 WebEngineLoad = WebEngine.getMethod("load", String.class);
99 WebEngineGetLocation = WebEngine.getMethod("getLocation");
100 WebEngineGetLoadWorker = WebEngine.getMethod("getLoadWorker");
101
102 Worker = classLoader.loadClass("javafx.concurrent.Worker");
103 WorkerStateProperty = Worker.getMethod("stateProperty");
104
105 ChangeListener = classLoader.loadClass("javafx.beans.value.ChangeListener");
106
107 ReadOnlyObjectProperty = classLoader.loadClass("javafx.beans.property.ReadOnlyObjectProperty");
108 ReadOnlyObjectPropertyAddListener = ReadOnlyObjectProperty.getMethod("addListener", ChangeListener);
109
110 ObservableValue = classLoader.loadClass("javafx.beans.value.ObservableValue");
111
112 State = classLoader.loadClass("javafx.concurrent.Worker$State");
113 StateConstants = Arrays.asList(State.getEnumConstants());
114
115 } catch (Exception e) {
116 e.printStackTrace();
117 }
118 }
119
120 private static class WebBrowserPanel extends JPanel implements ComponentListener {
121
122 private static final long serialVersionUID = 1L;
123
124 Object webview;
125 Object jfxPanel;
126
127 /**
128 * @return A JPanel that either contains a JavaFX Webview or an empty panel if the JFX panel can't be
129 * initialized
130 */
131 public WebBrowserPanel(final String url) {
132 this.addComponentListener(this);
133 try {
134 jfxPanel = JFXPanel.newInstance();
135
136 this.add((Component) jfxPanel);
137
138 // Removing the gap between the top of the panel and the JFXPanel
139 ((FlowLayout) this.getLayout()).setVgap(0);
140
141 // Setting bg color to be the same as bg color of widgets when they're being dragged/resized
142 this.setBackground(new Color(100, 100, 100));
143
144 PlatformRunLater.invoke(null, new Runnable() {
145 @Override
146 public void run() {
147 initFx(url);
148 }
149 });
150 } catch (Exception e) {
151 e.printStackTrace();
152 }
153 }
154
155 /**
156 * Sets up the browser frame. JFX requires this to be run on a new thread.
157 *
158 * @param url
159 * The URL to be loaded when the browser is created
160 */
161 private void initFx(String url) {
162 try {
163 this.webview = WebView.newInstance();
164 WebViewSetPrefSize.invoke(this.webview, this.getWidth(), this.getHeight());
165
166 Object scene = SceneConstructor.newInstance(this.webview);
167
168 JFXPanelSetScene.invoke(jfxPanel, scene);
169
170 ReadOnlyObjectPropertyAddListener.invoke(WorkerStateProperty.invoke(WebEngineGetLoadWorker
171 .invoke(WebViewGetEngine.invoke(this.webview))), java.lang.reflect.Proxy.newProxyInstance(
172 ChangeListener.getClassLoader(), new java.lang.Class[] { ChangeListener },
173 new java.lang.reflect.InvocationHandler() {
174 @Override
175 public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args)
176 throws java.lang.Throwable {
177 String method_name = method.getName();
178 // Class<?>[] classes = method.getParameterTypes();
179 // public void changed(ObservableValue ov, State oldState, State newState)
180 if (method_name.equals("changed")) {
181 // changed takes 3 args
182 if (args == null || args.length != 3) {
183 return null;
184 }
185 // args[0] is the ObservableValue
186 System.out.println(args[0]);
187 System.out.println(args[0].getClass());
188 // args[2] is the new State
189 if (args[2].getClass() == State) {
190 int id = StateConstants.indexOf(args[2]);
191 switch (id) {
192 case 0: // READY
193 MessageBay.displayMessage("WebEngine ready");
194 break;
195 case 1: // SCHEDULED
196 MessageBay.displayMessage("Scheduled page load");
197 break;
198 case 2: // RUNNING
199 MessageBay.displayMessage("WebEngine running");
200 break;
201 case 3: // SUCCEEDED
202 MessageBay.displayMessage("Finished loading page");
203 break;
204 case 4: // CANCELLED
205 MessageBay.displayMessage("Cancelled loading page");
206 break;
207 case 5: // FAILED
208 MessageBay.displayMessage("Failed to load page");
209 break;
210 }
211 }
212 System.out.println("\n");
213 }
214 return null;
215 }
216 }));
217
218 // webEngine.getLoadWorker().stateProperty().addListener(
219 // new ChangeListener<State>() {
220 // public void changed(ObservableValue ov, State oldState, State newState) {
221 // if (newState == State.SUCCEEDED) {
222 // stage.setTitle(webEngine.getLocation());
223 // }
224 // }
225 // });
226
227 WebEngineLoad.invoke(WebViewGetEngine.invoke(this.webview), url);
228 } catch (Exception e) {
229 e.printStackTrace();
230 }
231 }
232
233 @Override
234 public void componentHidden(ComponentEvent e) {
235 }
236
237 @Override
238 public void componentMoved(ComponentEvent e) {
239 }
240
241 @Override
242 public void componentResized(ComponentEvent e) {
243 // Webview is initialized in a 'run later' thread, so must check that it has actually been created before
244 // trying to resize
245 if (webview != null) {
246 try {
247 WebViewSetPrefSize.invoke(this.webview, this.getWidth(), this.getHeight());
248 } catch (Exception e1) {
249 e1.printStackTrace();
250 }
251 }
252 }
253
254 @Override
255 public void componentShown(ComponentEvent e) {
256 }
257 }
258
259 public JfxBrowser(Text source, String[] args) {
260 // Initial page is either the page stored in the arguments (if there is one stored) or Google
261
262 // super(source, new WebBrowserPanel((args != null && args.length > 0) ? args[0] : "http://google.com"), -1, 500, -1, -1, 300, -1);
263 super(source, new WebBrowserPanel(args[0]), -1, 500, -1, -1, 300, -1);
264 _browser = (WebBrowserPanel) _swingComponent;
265 }
266
267 public void navigate(final String url) {
268 System.out.println(url);
269 try {
270 PlatformRunLater.invoke(null, new Runnable() {
271 @Override
272 public void run() {
273 try {
274 WebEngineLoad.invoke(WebViewGetEngine.invoke(JfxBrowser.this._browser.webview), url);
275 } catch (Exception e) {
276 e.printStackTrace();
277 }
278 }
279 });
280 } catch (Exception e) {
281 e.printStackTrace();
282 }
283 }
284
285 @Override
286 public boolean ItemsLeftClickDropped() {
287 Text carried = null;
288 if ((carried = FreeItems.getTextAttachedToCursor()) != null) {
289 String text = carried.getText().trim();
290 String textLower = text.toLowerCase();
291 // check if protocol is missing
292 // TODO: can/should we support other protocols such as ftp ???
293 if(!(textLower.startsWith("http://") || text.startsWith("https://"))) {
294 // check if it's a search (will be a search if there is a ' ' before the first '.')
295 // TODO: maybe support changing the default search engine (using a settings frame like the proxy settings frame)
296 int firstSpace = text.indexOf(" ");
297 int firstDot = text.indexOf(".");
298 if(firstSpace != -1 && firstSpace < firstDot) {
299 // make it a search
300 text = "https://duckduckgo.com/?q=" + text;
301 } else {
302 // add the missing protocol
303 text = "http://" + text;
304 }
305 }
306 this.navigate(text);
307 FreeItems.getInstance().clear();
308 return true;
309 }
310 return false;
311 }
312
313 @Override
314 protected String[] getArgs() {
315 String[] r = null;
316 if (this._browser.webview != null) {
317 try {
318 r = new String[] { (String) WebEngineGetLocation.invoke(WebViewGetEngine.invoke(this._browser.webview)) };
319 } catch (Exception e) {
320 e.printStackTrace();
321 }
322 }
323 return r;
324 }
325}
Note: See TracBrowser for help on using the repository browser.