/******************************************************************************* * Copyright (c) 2003, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - Initial API and implementation * Martin Oberhuber (Wind River) - [293159] cyclic link when searching browser *******************************************************************************/ package org.eclipse.ui.internal.browser; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.preference.PreferencePage; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.TableLayout; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.window.Window; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.DirectoryDialog; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; import org.eclipse.ui.PlatformUI; /** * The preference page that holds Web browser preferences. */ public class WebBrowserPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { protected Button internal; protected Button external; protected Table table; protected CheckboxTableViewer tableViewer; protected Button edit; protected Button remove; protected Button search; protected Label location; protected Label parameters; protected IBrowserDescriptor checkedBrowser; class BrowserTableContentProvider implements IStructuredContentProvider { private BrowserManager input; @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { input = (BrowserManager) newInput; } @Override public Object[] getElements(Object inputElement) { return input.getWebBrowsers().toArray(); } } class BrowserTableLabelProvider implements ITableLabelProvider { @Override public Image getColumnImage(Object element, int columnIndex) { return null; } @Override public String getColumnText(Object element, int columnIndex) { IBrowserDescriptor browser = (IBrowserDescriptor) element; return notNull(browser.getName()); } protected String notNull(String s) { if (s == null) return ""; //$NON-NLS-1$ return s; } @Override public boolean isLabelProperty(Object element, String property) { return false; } @Override public void addListener(ILabelProviderListener listener) { // do nothing } @Override public void removeListener(ILabelProviderListener listener) { // do nothing } @Override public void dispose() { // do nothing } } /** * WebBrowserPreferencePage constructor comment. */ public WebBrowserPreferencePage() { super(); } /** * Create the preference options. * * @param parent * org.eclipse.swt.widgets.Composite * @return org.eclipse.swt.widgets.Control */ @Override protected Control createContents(Composite parent) { initializeDialogUnits(parent); PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, ContextIds.PREF_BROWSER); Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); layout.numColumns = 2; layout.horizontalSpacing = convertHorizontalDLUsToPixels(4); layout.verticalSpacing = convertVerticalDLUsToPixels(3); layout.marginWidth = 0; layout.marginHeight = 0; composite.setLayout(layout); GridData data = new GridData(SWT.FILL, SWT.FILL, true, false); composite.setLayoutData(data); Label label = new Label(composite, SWT.WRAP); label.setText(Messages.preferenceWebBrowserDescription); data = new GridData(SWT.FILL, SWT.NONE, false, false); data.horizontalSpan = 2; data.widthHint = 275; label.setLayoutData(data); internal = new Button(composite, SWT.RADIO); internal.setText(Messages.prefInternalBrowser); data = new GridData(SWT.FILL, SWT.NONE, true, false); data.horizontalSpan = 2; internal.setLayoutData(data); if (!WebBrowserUtil.canUseInternalWebBrowser()) internal.setEnabled(false); external = new Button(composite, SWT.RADIO); external.setText(Messages.prefExternalBrowser); data = new GridData(SWT.FILL, SWT.NONE, true, false); data.horizontalSpan = 2; external.setLayoutData(data); label = new Label(composite, SWT.NONE); label.setText(Messages.browserList); data = new GridData(SWT.FILL, SWT.CENTER, true, false); data.horizontalSpan = 2; label.setLayoutData(data); table = new Table(composite, SWT.CHECK | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION); data = new GridData(SWT.FILL, SWT.FILL, true, true); table.setLayoutData(data); table.setHeaderVisible(false); table.setLinesVisible(false); TableLayout tableLayout = new TableLayout(); new TableColumn(table, SWT.NONE); tableLayout.addColumnData(new ColumnWeightData(100)); table.setLayout(tableLayout); tableViewer = new CheckboxTableViewer(table); tableViewer.setContentProvider(new BrowserTableContentProvider()); tableViewer.setLabelProvider(new BrowserTableLabelProvider()); tableViewer.setInput(BrowserManager.getInstance()); // uncheck any other elements that might be checked and leave only the // element checked to remain checked since one can only chose one // brower at a time to be current. tableViewer.addCheckStateListener(e -> { checkNewDefaultBrowser(e.getElement()); checkedBrowser = (IBrowserDescriptor) e.getElement(); // if no other browsers are checked, don't allow the single one // currently checked to become unchecked, and lose a current // browser. That is, don't permit unchecking if no other item // is checked which is supposed to be the case. Object[] obj = tableViewer.getCheckedElements(); if (obj.length == 0) tableViewer.setChecked(e.getElement(), true); }); // set a default, checked browser based on the current browser. If there // is not a current browser, but the first item exists, use that instead. // This will work currently until workbench shutdown, because current // browser is not yet persisted. checkedBrowser = BrowserManager.getInstance().getCurrentWebBrowser(); if (checkedBrowser != null) tableViewer.setChecked(checkedBrowser, true); else { Object obj = tableViewer.getElementAt(0); if (obj != null) tableViewer.setChecked(obj, true); } tableViewer .addSelectionChangedListener(event -> { IStructuredSelection sele = tableViewer.getStructuredSelection(); boolean sel = sele.getFirstElement() != null && !(sele.getFirstElement() instanceof SystemBrowserDescriptor); remove.setEnabled(sel); edit.setEnabled(sel); }); tableViewer.addDoubleClickListener(event -> { IStructuredSelection sel = tableViewer.getStructuredSelection(); Object firstElem = sel.getFirstElement(); if (firstElem != null && !(firstElem instanceof SystemBrowserDescriptor)) { IBrowserDescriptor browser2 = (IBrowserDescriptor) sel.getFirstElement(); IBrowserDescriptorWorkingCopy wc = browser2.getWorkingCopy(); BrowserDescriptorDialog dialog = new BrowserDescriptorDialog(getShell(), wc); if (dialog.open() != Window.CANCEL) { try { tableViewer.refresh(wc.save()); } catch (Exception ex) { // ignore } } } }); table.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.character == SWT.DEL) { IStructuredSelection sel = tableViewer.getStructuredSelection(); if (sel.getFirstElement() != null) { IBrowserDescriptor browser2 = (IBrowserDescriptor) sel .getFirstElement(); try { browser2.delete(); tableViewer.remove(browser2); // need here to ensure that if the item deleted was // checked, ie. was // the current browser, that the new current browser // will be the first in the // list, typically, the internal browser, which // cannot be // deleted, and be current BrowserManager manager = BrowserManager.getInstance(); if (browser2 == checkedBrowser) { if (manager.browsers.size() > 0) { checkedBrowser = manager.browsers.get(0); tableViewer.setChecked(checkedBrowser, true); } } } catch (Exception ex) { // ignore } } } } }); Composite buttonComp = new Composite(composite, SWT.NONE); layout = new GridLayout(); layout.horizontalSpacing = 0; layout.verticalSpacing = convertVerticalDLUsToPixels(3); layout.marginWidth = 0; layout.marginHeight = 0; layout.numColumns = 1; buttonComp.setLayout(layout); data = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL); buttonComp.setLayoutData(data); final Button add = SWTUtil.createButton(buttonComp, Messages.add); add.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { BrowserDescriptorDialog dialog = new BrowserDescriptorDialog(getShell()); if (dialog.open() == Window.CANCEL) return; tableViewer.refresh(); if (checkedBrowser != null) tableViewer.setChecked(checkedBrowser, true); })); edit = SWTUtil.createButton(buttonComp, Messages.edit); edit.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { IStructuredSelection sel = tableViewer.getStructuredSelection(); IBrowserDescriptor browser2 = (IBrowserDescriptor) sel.getFirstElement(); IBrowserDescriptorWorkingCopy wc = browser2.getWorkingCopy(); BrowserDescriptorDialog dialog = new BrowserDescriptorDialog(getShell(), wc); if (dialog.open() != Window.CANCEL) { try { tableViewer.refresh(wc.save()); } catch (Exception ex) { // ignore } } })); remove = SWTUtil.createButton(buttonComp, Messages.remove); remove.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { IStructuredSelection sel = tableViewer.getStructuredSelection(); IBrowserDescriptor browser2 = (IBrowserDescriptor) sel.getFirstElement(); try { browser2.delete(); tableViewer.remove(browser2); // need here to ensure that if the item deleted was checked, // ie. was the current browser, that the new current browser // will be // the first in the list, typically, the internal browser, which // cannot be // deleted, and be current BrowserManager manager = BrowserManager.getInstance(); if (browser2 == checkedBrowser) { if (manager.browsers.size() > 0) { checkedBrowser = manager.browsers.get(0); tableViewer.setChecked(checkedBrowser, true); } } } catch (Exception ex) { // ignore } })); search = SWTUtil.createButton(buttonComp, Messages.search); data = (GridData) search.getLayoutData(); data.verticalIndent = 9; search.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { final List<IBrowserDescriptorWorkingCopy> foundBrowsers = new ArrayList<>(); final List<String> existingPaths = WebBrowserUtil.getExternalBrowserPaths(); // select a target directory for the search DirectoryDialog dialog = new DirectoryDialog(getShell()); dialog.setMessage(Messages.selectDirectory); dialog.setText(Messages.directoryDialogTitle); String path = dialog.open(); if (path == null) return; final File rootDir = new File(path); ProgressMonitorDialog pm = new ProgressMonitorDialog(getShell()); IRunnableWithProgress r = monitor -> { monitor.beginTask(Messages.searchingTaskName, IProgressMonitor.UNKNOWN); search(rootDir, existingPaths, foundBrowsers, new HashSet<String>(), monitor); monitor.done(); }; try { pm.run(true, true, r); } catch (InvocationTargetException ex) { Trace.trace(Trace.SEVERE, "Invocation Exception occured running monitor: " //$NON-NLS-1$ + ex); } catch (InterruptedException ex) { Trace.trace(Trace.SEVERE, "Interrupted exception occured running monitor: " //$NON-NLS-1$ + ex); return; } if (pm.getProgressMonitor().isCanceled()) return; List<IBrowserDescriptorWorkingCopy> browsersToCreate = foundBrowsers; if (browsersToCreate.isEmpty()) { // no browsers found WebBrowserUtil.openMessage(Messages.searchingNoneFound); return; } Iterator<IBrowserDescriptorWorkingCopy> iterator = browsersToCreate.iterator(); while (iterator.hasNext()) { IBrowserDescriptorWorkingCopy browser2 = iterator.next(); browser2.save(); } tableViewer.refresh(); if (checkedBrowser != null) tableViewer.setChecked(checkedBrowser, true); })); tableViewer.addCheckStateListener(e -> { checkNewDefaultBrowser(e.getElement()); checkedBrowser = (IBrowserDescriptor) e.getElement(); }); /*external.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { boolean sel = !tableViewer.getSelection().isEmpty(); edit.setEnabled(sel); remove.setEnabled(sel); } public void widgetDefaultSelected(SelectionEvent e) { // ignore } });*/ internal.setSelection(WebBrowserPreference.getBrowserChoice() == WebBrowserPreference.INTERNAL); external.setSelection(WebBrowserPreference.getBrowserChoice() == WebBrowserPreference.EXTERNAL); //boolean sel = !tableViewer.getSelection().isEmpty(); IStructuredSelection sele = tableViewer.getStructuredSelection(); boolean sel = sele.getFirstElement() != null && !(sele.getFirstElement() instanceof SystemBrowserDescriptor); edit.setEnabled(sel); remove.setEnabled(sel); Dialog.applyDialogFont(composite); return composite; } /** * Initializes this preference page using the passed workbench. * * @param workbench * the current workbench */ @Override public void init(IWorkbench workbench) { // do nothing } /** * */ @Override public void setVisible(boolean visible) { super.setVisible(visible); if (visible) setTitle(Messages.preferenceWebBrowserTitle); } protected Object getSelection(ISelection sel2) { IStructuredSelection sel = (IStructuredSelection) sel2; return sel.getFirstElement(); } // Uncheck all the items except the current one that was just checked protected void checkNewDefaultBrowser(Object browser) { for (TableItem item : tableViewer.getTable().getItems()) { if (!(item.getData().equals(browser))) item.setChecked(false); } } protected static void search(File directory, List<String> existingPaths, List<IBrowserDescriptorWorkingCopy> foundBrowsers, Set<String> directoriesVisited, IProgressMonitor monitor) { if (monitor.isCanceled()) return; try { //bug 293159: protect against recursion due to cyclic symbolic link String canonicalPath = directory.getCanonicalPath(); if (!directoriesVisited.add(canonicalPath)) { //already been here return; } } catch(IOException ioe) { /*ignore*/ } monitor.subTask(NLS.bind(Messages.searching, new String[] { Integer.toString(foundBrowsers.size()), directory.getAbsolutePath()})); String[] names = directory.list(); List<File> subDirs = new ArrayList<>(); for (String name : names) { if (monitor.isCanceled()) return; File file = new File(directory, name); if (existingPaths.contains(file.getAbsolutePath().toLowerCase())) continue; IBrowserDescriptorWorkingCopy wc = WebBrowserUtil .createExternalBrowser(file); if (wc != null) foundBrowsers.add(wc); if (file.isDirectory()) { if (monitor.isCanceled()) return; subDirs.add(file); } } while (!subDirs.isEmpty()) { File subDir = subDirs.remove(0); search(subDir, existingPaths, foundBrowsers, directoriesVisited, monitor); if (monitor.isCanceled()) { return; } } } /** * Performs special processing when this page's Defaults button has been * pressed. */ @Override protected void performDefaults() { internal.setSelection(WebBrowserPreference .isDefaultUseInternalBrowser()); external.setSelection(!WebBrowserPreference. isDefaultUseInternalBrowser()); BrowserManager.getInstance().currentBrowser = null; BrowserManager.getInstance().setupDefaultBrowsers(); tableViewer.refresh(); checkedBrowser = BrowserManager.getInstance().getCurrentWebBrowser(); if (checkedBrowser != null) tableViewer.setChecked(checkedBrowser, true); super.performDefaults(); } /** * Method declared on IPreferencePage. Subclasses should override */ @Override public boolean performOk() { int choice; if (internal.getSelection()) choice = WebBrowserPreference.INTERNAL; else choice = WebBrowserPreference.EXTERNAL; WebBrowserPreference.setBrowserChoice(choice); if (checkedBrowser != null) { BrowserManager.getInstance().setCurrentWebBrowser(checkedBrowser); } else { BrowserManager.getInstance().saveBrowsers(); } return true; } @Override public boolean performCancel() { BrowserManager.getInstance().loadBrowsers(); return super.performCancel(); } }