Avoid NPE for "Mark Occurrences"
[phpeclipse.git] / net.sourceforge.phpeclipse.webbrowser / src / net / sourceforge / phpeclipse / webbrowser / internal / WebBrowser.java
1 /**
2  * Copyright (c) 2003 IBM Corporation and others. All rights reserved.   This program and the accompanying materials are made
3  * available under the terms of the Common Public License v1.0 which accompanies this distribution, and is available at
4  * http://www.eclipse.org/legal/cpl-v10.html  * Contributors: IBM - Initial API and implementation
5  */
6
7 //TODO 1. Handle the sizing of a popup running in shelled out secondary window.
8 //TODO 2. Support printing: waiting on eclipse bug 47937/44823.
9 package net.sourceforge.phpeclipse.webbrowser.internal;
10
11 import java.util.Iterator;
12
13 import net.sourceforge.phpeclipse.webbrowser.IURLMap;
14
15 import org.eclipse.swt.SWT;
16 import org.eclipse.swt.browser.Browser;
17 import org.eclipse.swt.browser.CloseWindowListener;
18 import org.eclipse.swt.browser.LocationEvent;
19 import org.eclipse.swt.browser.LocationListener;
20 import org.eclipse.swt.browser.OpenWindowListener;
21 import org.eclipse.swt.browser.ProgressEvent;
22 import org.eclipse.swt.browser.ProgressListener;
23 import org.eclipse.swt.browser.StatusTextEvent;
24 import org.eclipse.swt.browser.StatusTextListener;
25 import org.eclipse.swt.browser.TitleEvent;
26 import org.eclipse.swt.browser.TitleListener;
27 import org.eclipse.swt.browser.WindowEvent;
28 import org.eclipse.swt.dnd.Clipboard;
29 import org.eclipse.swt.events.SelectionAdapter;
30 import org.eclipse.swt.events.SelectionEvent;
31 import org.eclipse.swt.graphics.Point;
32 import org.eclipse.swt.graphics.Rectangle;
33 import org.eclipse.swt.layout.FillLayout;
34 import org.eclipse.swt.layout.GridData;
35 import org.eclipse.swt.layout.GridLayout;
36 import org.eclipse.swt.widgets.Combo;
37 import org.eclipse.swt.widgets.Composite;
38 import org.eclipse.swt.widgets.Control;
39 import org.eclipse.swt.widgets.Event;
40 import org.eclipse.swt.widgets.Label;
41 import org.eclipse.swt.widgets.Listener;
42 import org.eclipse.swt.widgets.Menu;
43 import org.eclipse.swt.widgets.MenuItem;
44 import org.eclipse.swt.widgets.ProgressBar;
45 import org.eclipse.swt.widgets.Shell;
46 import org.eclipse.swt.widgets.ToolBar;
47 import org.eclipse.swt.widgets.ToolItem;
48 import org.eclipse.ui.help.WorkbenchHelp;
49
50 public class WebBrowser extends Composite {
51   protected Composite toolbarComp;
52
53   protected Composite statusComp;
54
55   protected Combo combo;
56
57   protected Clipboard clipboard;
58
59   protected boolean showToolbar;
60
61   protected ToolItem back;
62
63   protected ToolItem forward;
64
65   protected ToolItem stop;
66
67   protected ToolItem favorites;
68
69   protected ToolItem refresh;
70
71   protected BusyIndicator busy;
72
73   protected boolean showStatusbar;
74
75   protected ProgressBar progress;
76
77   protected Label status;
78
79   private static int MAX_HISTORY = 50;
80
81   protected static java.util.List history;
82
83   protected Browser browser;
84
85   protected Shell shell;
86
87   protected WebBrowserEditor editor;
88
89   protected String title;
90
91   public WebBrowser(Composite parent, final boolean showToolbar, final boolean showStatusbar) {
92     super(parent, SWT.NONE);
93
94     this.showToolbar = showToolbar;
95     this.showStatusbar = showStatusbar;
96
97     GridLayout layout = new GridLayout();
98     layout.marginHeight = 3;
99     layout.marginWidth = 3;
100     layout.horizontalSpacing = 3;
101     layout.verticalSpacing = 3;
102     layout.numColumns = 1;
103     setLayout(layout);
104     setLayoutData(new GridData(GridData.FILL_BOTH));
105     clipboard = new Clipboard(parent.getDisplay());
106     WorkbenchHelp.setHelp(this, ContextIds.WEB_BROWSER);
107
108     if (showToolbar) {
109       toolbarComp = new Composite(this, SWT.NONE);
110       GridLayout outerLayout = new GridLayout();
111       outerLayout.numColumns = 2;
112       outerLayout.marginWidth = 0;
113       outerLayout.marginHeight = 0;
114       toolbarComp.setLayout(outerLayout);
115       toolbarComp.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING | GridData.FILL_HORIZONTAL));
116
117       // create the top line, with a combo box for history and a "go" button
118       Composite top = new Composite(toolbarComp, SWT.NONE);
119       GridLayout topLayout = new GridLayout();
120       topLayout.numColumns = 2;
121       topLayout.marginWidth = 0;
122       topLayout.marginHeight = 0;
123       top.setLayout(topLayout);
124       top.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.FILL_HORIZONTAL));
125
126       combo = new Combo(top, SWT.DROP_DOWN);
127
128       updateHistory();
129
130       combo.addSelectionListener(new SelectionAdapter() {
131         public void widgetSelected(SelectionEvent we) {
132           try {
133             if (combo.getSelectionIndex() != -1)
134               setURL(combo.getItem(combo.getSelectionIndex()));
135           } catch (Exception e) {
136           }
137         }
138       });
139       combo.addListener(SWT.DefaultSelection, new Listener() {
140         public void handleEvent(Event e) {
141           setURL(combo.getText());
142         }
143       });
144       combo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
145       WorkbenchHelp.setHelp(combo, ContextIds.WEB_BROWSER_URL);
146
147       ToolBar toolbar = new ToolBar(top, SWT.FLAT);
148       fillToolBar(toolbar);
149
150       new ToolItem(toolbar, SWT.SEPARATOR);
151
152       busy = new BusyIndicator(toolbarComp, SWT.NONE);
153       busy.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_END));
154     }
155
156     // create a new SWT Web browser widget, checking once again to make sure we can use it in this environment
157 //    if (WebBrowserUtil.canUseInternalWebBrowser() & WebBrowserUtil.isInternalBrowserOperational())
158     if (WebBrowserUtil.isInternalBrowserOperational())
159       this.browser = new Browser(this, SWT.NONE);
160     else {
161       WebBrowserUtil.openError(WebBrowserUIPlugin.getResource("%errorCouldNotLaunchInternalWebBrowser"));
162       return;
163     }
164
165     if (showToolbar) {
166       back.setEnabled(browser.isBackEnabled());
167       forward.setEnabled(browser.isForwardEnabled());
168     }
169
170     WorkbenchHelp.setHelp(browser, ContextIds.WEB_BROWSER_WEB);
171     GridData data = new GridData();
172     data.horizontalAlignment = GridData.FILL;
173     data.verticalAlignment = GridData.FILL;
174     data.horizontalSpan = 3;
175     data.grabExcessHorizontalSpace = true;
176     data.grabExcessVerticalSpace = true;
177     browser.setLayoutData(data);
178
179     if (showStatusbar)
180       createStatusArea(this);
181
182     addBrowserListeners();
183   }
184
185   /**
186    *
187    */
188   protected void addBrowserListeners() {
189     if (showStatusbar) {
190       // respond to Browser StatusTextEvents events by updating the status Text label
191       browser.addStatusTextListener(new StatusTextListener() {
192         public void changed(StatusTextEvent event) {
193           status.setText(event.text);
194         }
195       });
196     }
197
198     /**
199      * Add listener for new window creation so that we can instead of opening a separate new window in which the session is lost, we
200      * can instead open a new window in a new shell within the browser area thereby maintaining the session.
201      */
202     browser.addOpenWindowListener(new OpenWindowListener() {
203       public void open(WindowEvent event) {
204         Shell shell2 = new Shell(getDisplay());
205         shell2.setLayout(new FillLayout());
206         shell2.setText(WebBrowserUIPlugin.getResource("%viewWebBrowserTitle"));
207         shell2.setImage(getShell().getImage());
208         WebBrowser browser2 = new WebBrowser(shell2, showToolbar, showStatusbar);
209         browser2.shell = shell2;
210         event.browser = browser2.browser;
211         shell2.open();
212       }
213     });
214
215     browser.addCloseWindowListener(new CloseWindowListener() {
216       public void close(WindowEvent event) {
217         // if shell is not null, it must be a secondary popup window, else its an editor window
218         if (shell != null)
219           shell.dispose();
220         else
221           editor.closeEditor();
222       }
223     });
224
225     browser.addProgressListener(new ProgressListener() {
226       public void changed(ProgressEvent event) {
227         if (event.total == 0)
228           return;
229
230         boolean done = (event.current == event.total);
231
232         int percentProgress = event.current * 100 / event.total;
233         if (showStatusbar) {
234           if (done)
235             progress.setSelection(0);
236           else
237             progress.setSelection(percentProgress);
238         }
239
240         if (showToolbar) {
241           if (!busy.isBusy() && (percentProgress > 0 && percentProgress < 100)) {
242             busy.setBusy(true);
243           }
244           // Once the progress hits 100 percent, done, set busy to false
245           else if (busy.isBusy() && done) {
246             busy.setBusy(false);
247           }
248         }
249       }
250
251       public void completed(ProgressEvent event) {
252         if (showStatusbar)
253           progress.setSelection(0);
254         if (showToolbar) {
255           busy.setBusy(false);
256           back.setEnabled(browser.isBackEnabled());
257           forward.setEnabled(browser.isForwardEnabled());
258         }
259       }
260     });
261
262     if (showToolbar) {
263       browser.addLocationListener(new LocationListener() {
264         public void changed(LocationEvent event) {
265           if (!event.top)
266             return;
267           if (!isHome()) {
268             combo.setText(event.location);
269             addToHistory(event.location);
270             updateHistory();
271           } else
272             combo.setText("");
273         }
274
275         public void changing(LocationEvent event) {
276         }
277       });
278     }
279
280     browser.addTitleListener(new TitleListener() {
281       public void changed(TitleEvent event) {
282         title = event.title;
283       }
284     });
285   }
286
287   /**
288    * Return the underlying browser control.
289    *
290    * @return org.eclipse.swt.browser.Browser
291    */
292   public Browser getBrowser() {
293     return browser;
294   }
295
296   /**
297    *
298    */
299   protected void forward() {
300     browser.forward();
301   }
302
303   /**
304    *
305    */
306   protected void back() {
307     browser.back();
308   }
309
310   /**
311    *
312    */
313   protected void stop() {
314     browser.stop();
315   }
316
317   /**
318    *
319    */
320   protected void navigate(String url) {
321     Trace.trace(Trace.FINER, "Navigate: " + url);
322     if (url != null && url.equals(getURL())) {
323       refresh();
324       return;
325     }
326     browser.setUrl(url);
327   }
328
329   /**
330    * Refresh the currently viewed page.
331    */
332   public void refresh() {
333     browser.refresh();
334   }
335
336   protected void setURL(String url, boolean browse) {
337     Trace.trace(Trace.FINEST, "setURL: " + url + " " + browse);
338     if (url == null) {
339       home();
340       return;
341     }
342
343     if (url.endsWith(WebBrowserPreference.getHomePageURL().substring(9)))
344       return;
345
346     // check URL maps
347     Iterator iterator = WebBrowserUtil.getURLMaps().iterator();
348     String newURL = null;
349     while (iterator.hasNext() && newURL == null) {
350       try {
351         IURLMap map = (IURLMap) iterator.next();
352         newURL = map.getMappedURL(url);
353       } catch (Exception e) {
354       }
355     }
356     if (newURL != null)
357       url = newURL;
358
359     if (browse)
360       navigate(url);
361
362     addToHistory(url);
363     updateHistory();
364   }
365
366   protected void addToHistory(String url) {
367     if (history == null)
368       history = WebBrowserPreference.getInternalWebBrowserHistory();
369     int found = -1;
370     int size = history.size();
371     for (int i = 0; i < size; i++) {
372       String s = (String) history.get(i);
373       if (s.equals(url)) {
374         found = i;
375         break;
376       }
377     }
378
379     if (found == -1) {
380       if (size >= MAX_HISTORY)
381         history.remove(size - 1);
382       history.add(0, url);
383       WebBrowserPreference.setInternalWebBrowserHistory(history);
384     } else if (found != 0) {
385       history.remove(found);
386       history.add(0, url);
387       WebBrowserPreference.setInternalWebBrowserHistory(history);
388     }
389   }
390
391   public void setURL(String url) {
392     setURL(url, true);
393   }
394
395   /**
396    * Creates the Web browser status area.
397    */
398   private void createStatusArea(Composite parent) {
399     Composite composite = new Composite(parent, SWT.NONE);
400     GridLayout layout = new GridLayout();
401     layout.numColumns = 2;
402     layout.marginHeight = 0;
403     layout.marginWidth = 0;
404     layout.horizontalSpacing = 4;
405     layout.verticalSpacing = 0;
406     composite.setLayout(layout);
407     composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
408
409     // Add a label for displaying status messages as they are received from the control
410     status = new Label(composite, SWT.SINGLE | SWT.READ_ONLY);
411     GridData gridData = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL);
412     gridData.horizontalIndent = 2;
413     status.setLayoutData(gridData);
414
415     // Add a progress bar to display downloading progress information
416     progress = new ProgressBar(composite, SWT.BORDER);
417     gridData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.VERTICAL_ALIGN_FILL);
418     gridData.widthHint = 100;
419     gridData.heightHint = 10;
420     progress.setLayoutData(gridData);
421   }
422
423   /**
424    *
425    */
426   public void dispose() {
427     super.dispose();
428
429     showStatusbar = false;
430     showToolbar = false;
431
432     if (busy != null)
433       busy.dispose();
434     busy = null;
435
436     browser = null;
437   }
438
439   /**
440    * Populate the toolbar.
441    *
442    * @param toolbar
443    *          org.eclipse.swt.widgets.ToolBar
444    */
445   private void fillToolBar(final ToolBar toolbar) {
446     ToolItem go = new ToolItem(toolbar, SWT.NONE);
447     go.setImage(ImageResource.getImage(ImageResource.IMG_ELCL_NAV_GO));
448     go.setHotImage(ImageResource.getImage(ImageResource.IMG_CLCL_NAV_GO));
449     go.setDisabledImage(ImageResource.getImage(ImageResource.IMG_DLCL_NAV_GO));
450     go.setToolTipText(WebBrowserUIPlugin.getResource("%actionWebBrowserGo"));
451     go.addSelectionListener(new SelectionAdapter() {
452       public void widgetSelected(SelectionEvent event) {
453         setURL(combo.getText());
454       }
455     });
456
457     new ToolItem(toolbar, SWT.SEPARATOR);
458
459     favorites = new ToolItem(toolbar, SWT.DROP_DOWN);
460     favorites.setImage(ImageResource.getImage(ImageResource.IMG_ELCL_NAV_FAVORITES));
461     favorites.setHotImage(ImageResource.getImage(ImageResource.IMG_CLCL_NAV_FAVORITES));
462     favorites.setDisabledImage(ImageResource.getImage(ImageResource.IMG_DLCL_NAV_FAVORITES));
463     favorites.setToolTipText(WebBrowserUIPlugin.getResource("%actionWebBrowserFavorites"));
464
465     favorites.addSelectionListener(new SelectionAdapter() {
466       public void widgetSelected(SelectionEvent event) {
467         if (event.detail == SWT.ARROW) {
468           Rectangle r = favorites.getBounds();
469           showFavorites(toolbar, toolbar.toDisplay(r.x, r.y + r.height));
470         } else
471           addFavorite();
472       }
473     });
474
475     // create back and forward actions
476     back = new ToolItem(toolbar, SWT.NONE);
477     back.setImage(ImageResource.getImage(ImageResource.IMG_ELCL_NAV_BACKWARD));
478     back.setHotImage(ImageResource.getImage(ImageResource.IMG_CLCL_NAV_BACKWARD));
479     back.setDisabledImage(ImageResource.getImage(ImageResource.IMG_DLCL_NAV_BACKWARD));
480     back.setToolTipText(WebBrowserUIPlugin.getResource("%actionWebBrowserBack"));
481     back.addSelectionListener(new SelectionAdapter() {
482       public void widgetSelected(SelectionEvent event) {
483         back();
484       }
485     });
486
487     forward = new ToolItem(toolbar, SWT.NONE);
488     forward.setImage(ImageResource.getImage(ImageResource.IMG_ELCL_NAV_FORWARD));
489     forward.setHotImage(ImageResource.getImage(ImageResource.IMG_CLCL_NAV_FORWARD));
490     forward.setDisabledImage(ImageResource.getImage(ImageResource.IMG_DLCL_NAV_FORWARD));
491     forward.setToolTipText(WebBrowserUIPlugin.getResource("%actionWebBrowserForward"));
492     forward.addSelectionListener(new SelectionAdapter() {
493       public void widgetSelected(SelectionEvent event) {
494         forward();
495       }
496     });
497
498     // create refresh, stop, and print actions
499     stop = new ToolItem(toolbar, SWT.NONE);
500     stop.setImage(ImageResource.getImage(ImageResource.IMG_ELCL_NAV_STOP));
501     stop.setHotImage(ImageResource.getImage(ImageResource.IMG_CLCL_NAV_STOP));
502     stop.setDisabledImage(ImageResource.getImage(ImageResource.IMG_DLCL_NAV_STOP));
503     stop.setToolTipText(WebBrowserUIPlugin.getResource("%actionWebBrowserStop"));
504     stop.addSelectionListener(new SelectionAdapter() {
505       public void widgetSelected(SelectionEvent event) {
506         stop();
507       }
508     });
509
510     refresh = new ToolItem(toolbar, SWT.NONE);
511     refresh.setImage(ImageResource.getImage(ImageResource.IMG_ELCL_NAV_REFRESH));
512     refresh.setHotImage(ImageResource.getImage(ImageResource.IMG_CLCL_NAV_REFRESH));
513     refresh.setDisabledImage(ImageResource.getImage(ImageResource.IMG_DLCL_NAV_REFRESH));
514     refresh.setToolTipText(WebBrowserUIPlugin.getResource("%actionWebBrowserRefresh"));
515     refresh.addSelectionListener(new SelectionAdapter() {
516       public void widgetSelected(SelectionEvent event) {
517         refresh();
518       }
519     });
520   }
521
522   protected void addFavorite() {
523     java.util.List list = WebBrowserPreference.getInternalWebBrowserFavorites();
524     Favorite f = new Favorite(title, browser.getUrl());
525     if (!list.contains(f)) {
526       list.add(f);
527       WebBrowserPreference.setInternalWebBrowserFavorites(list);
528     }
529   }
530
531   protected void showFavorites(Control parent, Point p) {
532     Menu perspectiveBarMenu = null;
533     if (perspectiveBarMenu == null) {
534       Menu menu = new Menu(parent);
535
536       // locked favorites
537       Iterator iterator = WebBrowserUtil.getLockedFavorites().iterator();
538       if (iterator.hasNext()) {
539         while (iterator.hasNext()) {
540           final Favorite f = (Favorite) iterator.next();
541           MenuItem item = new MenuItem(menu, SWT.NONE);
542           item.setText(f.getName());
543           item.setImage(ImageResource.getImage(ImageResource.IMG_FAVORITE));
544           item.addSelectionListener(new SelectionAdapter() {
545             public void widgetSelected(SelectionEvent event) {
546               setURL(f.getURL());
547             }
548           });
549         }
550
551         new MenuItem(menu, SWT.SEPARATOR);
552       }
553
554       iterator = WebBrowserPreference.getInternalWebBrowserFavorites().iterator();
555       if (!iterator.hasNext()) {
556         MenuItem item = new MenuItem(menu, SWT.NONE);
557         item.setText(WebBrowserUIPlugin.getResource("%actionWebBrowserNoFavorites"));
558       }
559       while (iterator.hasNext()) {
560         final Favorite f = (Favorite) iterator.next();
561         MenuItem item = new MenuItem(menu, SWT.NONE);
562         item.setText(f.getName());
563         item.setImage(ImageResource.getImage(ImageResource.IMG_FAVORITE));
564         item.addSelectionListener(new SelectionAdapter() {
565           public void widgetSelected(SelectionEvent event) {
566             setURL(f.getURL());
567           }
568         });
569       }
570
571       new MenuItem(menu, SWT.SEPARATOR);
572
573       MenuItem item = new MenuItem(menu, SWT.NONE);
574       item.setText(WebBrowserUIPlugin.getResource("%actionWebBrowserOrganizeFavorites"));
575       item.addSelectionListener(new SelectionAdapter() {
576         public void widgetSelected(SelectionEvent event) {
577           OrganizeFavoritesDialog dialog = new OrganizeFavoritesDialog(shell);
578           dialog.open();
579         }
580       });
581
582       perspectiveBarMenu = menu;
583     }
584
585     if (perspectiveBarMenu != null) {
586       perspectiveBarMenu.setLocation(p.x, p.y);
587       perspectiveBarMenu.setVisible(true);
588     }
589   }
590
591   public void home() {
592     navigate(WebBrowserPreference.getHomePageURL());
593   }
594
595   /**
596    * Returns true if the homepage is currently being displayed.
597    *
598    * @return boolean
599    */
600   protected boolean isHome() {
601     return getURL() != null && getURL().endsWith(WebBrowserPreference.getHomePageURL().substring(9));
602   }
603
604   protected String getURL() {
605     return browser.getUrl();
606   }
607
608   /**
609    * Update the history list to the global copy.
610    */
611   protected void updateHistory() {
612     if (combo == null)
613       return;
614
615     String temp = combo.getText();
616     if (history == null)
617       history = WebBrowserPreference.getInternalWebBrowserHistory();
618
619     String[] historyList = new String[history.size()];
620     history.toArray(historyList);
621     combo.setItems(historyList);
622
623     combo.setText(temp);
624   }
625
626   public void addProgressListener(ProgressListener listener) {
627     browser.addProgressListener(listener);
628   }
629
630   public void addStatusTextListener(StatusTextListener listener) {
631     browser.addStatusTextListener(listener);
632   }
633
634   public void addTitleListener(TitleListener listener) {
635     browser.addTitleListener(listener);
636   }
637 }