/******************************************************************************* * Copyright (c) 2009,2014 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 * Zend Technologies * Dawid PakuĊ‚a [339547] *******************************************************************************/ package org.eclipse.php.internal.debug.ui.preferences.phps; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.layout.PixelConverter; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.viewers.*; import org.eclipse.jface.window.Window; import org.eclipse.php.internal.core.util.VersionUtils; import org.eclipse.php.internal.debug.core.PHPExeUtil; import org.eclipse.php.internal.debug.core.PHPExeUtil.PHPModuleInfo; import org.eclipse.php.internal.debug.core.debugger.AbstractDebuggerConfiguration; import org.eclipse.php.internal.debug.core.preferences.PHPDebuggersRegistry; import org.eclipse.php.internal.debug.core.preferences.PHPexeItem; import org.eclipse.php.internal.debug.core.preferences.PHPexes; import org.eclipse.php.internal.debug.core.xdebug.dbgp.DBGpProxyHandlersManager; import org.eclipse.php.internal.debug.ui.PHPDebugUIImages; import org.eclipse.php.internal.debug.ui.PHPDebugUIMessages; import org.eclipse.php.internal.debug.ui.PHPDebugUIPlugin; import org.eclipse.php.internal.debug.ui.wizards.ClosableWizardDialog; import org.eclipse.php.internal.debug.ui.wizards.PHPExeEditDialog; import org.eclipse.php.internal.debug.ui.wizards.PHPExeWizard; import org.eclipse.php.internal.ui.util.SWTUtil; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.*; import org.eclipse.ui.PlatformUI; /** * A composite that displays installed PHP's in a table. PHPs can be added, * removed, edited, and searched for. * <p> * This block implements ISelectionProvider - it sends selection change events * when the checked PHP in the table changes, or when the "use default" button * check state changes. * </p> * * @author seva, shalom * */ public class InstalledPHPsBlock { /** * Content provider to show a list of PHPs */ class PHPsContentProvider implements IStructuredContentProvider { public void dispose() { } public Object[] getElements(final Object input) { return fPHPexes.toArray(); } public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) { } } /** * Label provider for installed PHPs table. */ class PHPExeLabelProvider extends LabelProvider implements ITableLabelProvider, IFontProvider { /** * @see ITableLabelProvider#getColumnImage(Object, int) */ public Image getColumnImage(final Object element, final int columnIndex) { switch (columnIndex) { case 0: return PHPDebugUIImages.get(PHPDebugUIImages.IMG_OBJ_PHP_EXE); } return null; } /** * @see ITableLabelProvider#getColumnText(Object, int) */ public String getColumnText(final Object element, final int columnIndex) { if (element instanceof PHPexeItem) { final PHPexeItem phpExe = (PHPexeItem) element; switch (columnIndex) { case 0: if (isDefault(element)) { return phpExe.getName() + PHPDebugUIMessages.PHPsPreferencePage_WorkspaceDefault; } return phpExe.getName(); case 1: String debuggerName = PHPDebuggersRegistry.getDebuggerName(phpExe.getDebuggerID()); if (debuggerName == null) { debuggerName = ""; //$NON-NLS-1$ } return debuggerName; case 2: String version = phpExe.getVersion(); if (version == null) { return ""; //$NON-NLS-1$ } return version; case 3: File executable = phpExe.getExecutable(); if (executable == null) { return ""; //$NON-NLS-1$ } return executable.getAbsolutePath(); } } return element.toString(); } public Font getFont(Object element) { if (isDefault(element)) { return JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT); } return null; } private boolean isDefault(Object element) { if (element instanceof PHPexeItem) { return ((PHPexeItem) element).isDefault(); } return false; } } private static final String[] PHP_CANDIDATE_BIN = { "php", "php-cli", //$NON-NLS-1$ //$NON-NLS-2$ "php-cgi", "php.exe", "php-cli.exe", "php-cgi.exe" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ // Action buttons private Button fAddButton; /** * This block's control */ private Composite fControl; private Button fEditButton; /** * The main list control */ private TableViewer fPHPExeList; /** * VMs being displayed */ private final List<PHPexeItem> fPHPexes = new ArrayList<PHPexeItem>(); private Button fRemoveButton; private Button fSearchButton; private Button fSetDefaultButton; public InstalledPHPsBlock() { } /** * Creates this block's control in the given control. * * @param ancestor * containing control the user that opens the installed PHPs pref * page for PHP management, or to provide 'add, remove, edit, and * search' buttons. */ public void createControl(final Composite ancestor) { final Composite parent = new Composite(ancestor, SWT.NULL); GridLayout layout = new GridLayout(); layout.numColumns = 2; layout.marginHeight = 0; layout.marginWidth = 0; parent.setLayout(layout); final Font font = ancestor.getFont(); parent.setFont(font); fControl = parent; GridData data; final Label tableLabel = new Label(parent, SWT.NONE); tableLabel.setText(PHPDebugUIMessages.InstalledPHPsBlock_15); data = new GridData(); data.horizontalSpan = 2; tableLabel.setLayoutData(data); tableLabel.setFont(font); Composite tableComposite = new Composite(parent, SWT.NONE); data = new GridData(GridData.FILL_BOTH); tableComposite.setLayoutData(data); Table table = new Table(tableComposite, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION); table.setHeaderVisible(true); table.setLinesVisible(true); fPHPExeList = new CheckboxTableViewer(table); fPHPExeList.setLabelProvider(new PHPExeLabelProvider()); fPHPExeList.setContentProvider(new PHPsContentProvider()); fPHPExeList.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(final SelectionChangedEvent evt) { enableButtons(); } }); fPHPExeList.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(final DoubleClickEvent e) { if (!fPHPExeList.getSelection().isEmpty()) editPHPexe(); } }); table.addKeyListener(new KeyAdapter() { public void keyPressed(final KeyEvent event) { if (fRemoveButton != null && !fRemoveButton.isDisposed() && !fRemoveButton.isEnabled()) { return; } if (event.character == SWT.DEL && event.stateMask == 0) removePHPexes(); } }); final TableColumn column1 = new TableColumn(table, SWT.NULL); column1.setText(PHPDebugUIMessages.InstalledPHPsBlock_0); column1.addSelectionListener(new SelectionAdapter() { public void widgetSelected(final SelectionEvent e) { sortByName(); } }); final TableColumn column2 = new TableColumn(table, SWT.NULL); column2.setText(PHPDebugUIMessages.InstalledPHPsBlock_17); column2.addSelectionListener(new SelectionAdapter() { public void widgetSelected(final SelectionEvent e) { sortByDebugger(); } }); final TableColumn column3 = new TableColumn(table, SWT.NULL); column3.setText(PHPDebugUIMessages.InstalledPHPsBlock_18); column3.addSelectionListener(new SelectionAdapter() { public void widgetSelected(final SelectionEvent e) { sortByVersion(); } }); final TableColumn column4 = new TableColumn(table, SWT.NULL); column4.setText(PHPDebugUIMessages.InstalledPHPsBlock_1); column4.addSelectionListener(new SelectionAdapter() { public void widgetSelected(final SelectionEvent e) { sortByLocation(); } }); // Create table column layout TableColumnLayout tableColumnLayout = new TableColumnLayout(); PixelConverter pixelConverter = new PixelConverter(font); tableColumnLayout.setColumnData(column1, new ColumnWeightData(20, pixelConverter.convertWidthInCharsToPixels(24))); tableColumnLayout.setColumnData(column2, new ColumnWeightData(15, pixelConverter.convertWidthInCharsToPixels(18))); tableColumnLayout.setColumnData(column3, new ColumnWeightData(10, pixelConverter.convertWidthInCharsToPixels(10))); tableColumnLayout.setColumnData(column4, new ColumnWeightData(35, pixelConverter.convertWidthInCharsToPixels(28))); tableComposite.setLayout(tableColumnLayout); final Composite buttons = new Composite(parent, SWT.NULL); buttons.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; buttons.setLayout(layout); buttons.setFont(font); fAddButton = createPushButton(buttons, PHPDebugUIMessages.InstalledPHPsBlock_3); fAddButton.addListener(SWT.Selection, new Listener() { public void handleEvent(final Event evt) { addPHPexe(); } }); fEditButton = createPushButton(buttons, PHPDebugUIMessages.InstalledPHPsBlock_4); fEditButton.addListener(SWT.Selection, new Listener() { public void handleEvent(final Event evt) { editPHPexe(); } }); fRemoveButton = createPushButton(buttons, PHPDebugUIMessages.InstalledPHPsBlock_5); fRemoveButton.addListener(SWT.Selection, new Listener() { public void handleEvent(final Event evt) { removePHPexes(); } }); fSetDefaultButton = createPushButton(buttons, PHPDebugUIMessages.InstalledPHPsBlock_setDefault); fSetDefaultButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { PHPexeItem defaultItem = (PHPexeItem) ((IStructuredSelection) fPHPExeList.getSelection()) .getFirstElement(); PHPexes.getInstance().setDefaultItem(defaultItem); commitChanges(); setPHPs(PHPexes.getInstance().getAllItems()); } }); // copied from ListDialogField.CreateSeparator() final Label separator = new Label(buttons, SWT.NONE); separator.setVisible(false); final GridData gd = new GridData(); gd.horizontalAlignment = GridData.FILL; gd.verticalAlignment = GridData.BEGINNING; gd.heightHint = 4; separator.setLayoutData(gd); fSearchButton = createPushButton(buttons, PHPDebugUIMessages.InstalledPHPsBlock_6); fSearchButton.addListener(SWT.Selection, new Listener() { public void handleEvent(final Event evt) { search(); } }); fillWithWorkspacePHPs(); // by default, sort by name sortByName(); enableButtons(); } /** * Returns this block's control * * @return control */ public Control getControl() { return fControl; } /** * Returns the PHPs currently being displayed in this block * * @return PHPs currently being displayed in this block */ public PHPexeItem[] getPHPs() { return fPHPexes.toArray(new PHPexeItem[fPHPexes.size()]); } /** * @see IAddPHPexeDialogRequestor#isDuplicateName(String) */ public boolean isDuplicateName(final String name) { for (int i = 0; i < fPHPexes.size(); i++) { final PHPexeItem phpExe = fPHPexes.get(i); if (phpExe.getName().equals(name)) return true; } return false; } public void commitChanges() { PHPexes.getInstance().save(); } public void removePHPs(final PHPexeItem[] phpExes) { for (PHPexeItem element : phpExes) { fPHPexes.remove(element); PHPexes.getInstance().removeItem(element); } fPHPExeList.refresh(); } protected Button createPushButton(final Composite parent, final String label) { return SWTUtil.createPushButton(parent, label, null); } /** * Populates the PHP table with existing PHPs defined in the workspace. */ protected void fillWithWorkspacePHPs() { // fill with PHPs final PHPexeItem[] items = PHPexes.getInstance().getAllItems(); setPHPs(items); } protected Shell getShell() { return getControl().getShell(); } /** * Search for installed VMs in the file system */ protected void search() { // choose a root directory for the search final DirectoryDialog dialog = new DirectoryDialog(getShell()); dialog.setMessage(PHPDebugUIMessages.InstalledPHPsBlock_9); dialog.setText(PHPDebugUIMessages.InstalledPHPsBlock_10); final String path = dialog.open(); if (path == null) return; final File rootDir = new File(path); final List<File> locations = new ArrayList<File>(); final List<PHPexeItem> found = new ArrayList<PHPexeItem>(); final IRunnableWithProgress r = new IRunnableWithProgress() { public void run(IProgressMonitor monitor) { monitor.beginTask(PHPDebugUIMessages.InstalledPHPsBlock_11, IProgressMonitor.UNKNOWN); search(rootDir, locations, monitor); if (!locations.isEmpty()) { monitor.setTaskName(PHPDebugUIMessages.InstalledPHPsBlock_Processing_search_results); Iterator<File> iter2 = locations.iterator(); while (iter2.hasNext()) { if (monitor.isCanceled()) break; File location = iter2.next(); PHPexeItem phpExe = new PHPexeItem(null, location, null, null, true); if (phpExe.getName() == null) continue; String nameCopy = new String(phpExe.getName()); monitor.subTask(MessageFormat .format(PHPDebugUIMessages.InstalledPHPsBlock_Fetching_php_exe_info, nameCopy)); List<PHPModuleInfo> modules = PHPExeUtil.getModules(phpExe); AbstractDebuggerConfiguration[] debuggers = PHPDebuggersRegistry.getDebuggersConfigurations(); for (AbstractDebuggerConfiguration debugger : debuggers) { for (PHPModuleInfo m : modules) if (m.getName().equalsIgnoreCase(debugger.getModuleId())) { phpExe.setDebuggerID(debugger.getDebuggerId()); break; } } if (phpExe.getDebuggerID() == null) phpExe.setDebuggerID(PHPDebuggersRegistry.NONE_DEBUGGER_ID); int i = 1; // Check for duplicated names duplicates: while (true) { if (isDuplicateName(nameCopy)) { nameCopy = phpExe.getName() + ' ' + '[' + i++ + ']'; continue duplicates; } else { for (PHPexeItem item : found) if (nameCopy.equalsIgnoreCase(item.getName())) { nameCopy = phpExe.getName() + ' ' + '[' + i++ + ']'; continue duplicates; } } break duplicates; } phpExe.setName(nameCopy); if (phpExe.getExecutable() != null) { found.add(phpExe); } } } monitor.done(); } }; // Perform searching try { final ProgressMonitorDialog progress = new ProgressMonitorDialog( PlatformUI.getWorkbench().getDisplay().getActiveShell()) { protected void configureShell(Shell shell) { super.configureShell(shell); shell.setText(PHPDebugUIMessages.InstalledPHPsBlock_PHP_executables_search); }; }; progress.run(true, true, r); } catch (final InvocationTargetException e) { PHPDebugUIPlugin.log(e); } catch (final InterruptedException e) { // cancelled return; } // Show results if (!found.isEmpty()) { Comparator<PHPexeItem> sorter = new Comparator<PHPexeItem>() { @Override public int compare(PHPexeItem a, PHPexeItem b) { return b.getVersion().compareTo(a.getVersion()); } }; Collections.sort(found, sorter); PHPsSearchResultDialog searchDialog = new PHPsSearchResultDialog(found, MessageFormat.format(PHPDebugUIMessages.InstalledPHPsBlock_Search_result_dialog_message, path)); searchDialog.open(); List<PHPexeItem> itemsToAdd = searchDialog.getPHPExecutables(); for (PHPexeItem item : itemsToAdd) { fPHPexes.add(item); PHPexes.getInstance().addItem(item); PHPexes.getInstance().save(); DBGpProxyHandlersManager.INSTANCE.registerHandler(item.getUniqueId()); PHPExeVerifier.verify(PHPexes.getInstance().getAllItems()); } fPHPExeList.refresh(); } else { MessageDialog.openInformation(PlatformUI.getWorkbench().getDisplay().getActiveShell(), PHPDebugUIMessages.InstalledPHPsBlock_12, MessageFormat.format(PHPDebugUIMessages.InstalledPHPsBlock_13, new Object[] { path })); } } /** * Searches the specified directory recursively for installed PHP * executables, adding each detected executable to the <code>found</code> * list. Any directories specified in the <code>ignore</code> are not * traversed. * * @param directory * @param found * @param types * @param ignore */ protected void search(final File directory, final List<File> found, final IProgressMonitor monitor) { if (monitor.isCanceled()) return; // Search the root directory List<File> foundExecs = findPHPExecutable(directory); if (!foundExecs.isEmpty()) { found.addAll(foundExecs); monitor.setTaskName( MessageFormat.format(PHPDebugUIMessages.InstalledPHPsBlock_Searching_with_found, found.size())); } final String[] names = directory.list(); if (names == null) return; final List<File> subDirs = new ArrayList<File>(); for (String element : names) { if (monitor.isCanceled()) return; final File file = new File(directory, element); if (file.isDirectory()) { try { monitor.subTask( MessageFormat.format(PHPDebugUIMessages.InstalledPHPsBlock_14, file.getCanonicalPath())); } catch (final IOException e) { } if (monitor.isCanceled()) return; subDirs.add(file); } } while (!subDirs.isEmpty()) { final File subDir = subDirs.remove(0); search(subDir, found, monitor); if (monitor.isCanceled()) return; } } /** * Sets the PHPs to be displayed in this block * * @param phpExes * PHPs to be displayed */ protected void setPHPs(final PHPexeItem[] phpExes) { fPHPexes.clear(); for (PHPexeItem element : phpExes) { fPHPexes.add(element); } fPHPExeList.setInput(fPHPexes); Display.getDefault().asyncExec(new Runnable() { public void run() { fPHPExeList.refresh(); } }); } /** * Bring up a dialog that lets the user create a new VM definition. */ private void addPHPexe() { Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); NullProgressMonitor monitor = new NullProgressMonitor(); PHPexeItem phpExeItem = null; PHPExeWizard wizard = new PHPExeWizard(PHPexes.getInstance().getAllItems()); ClosableWizardDialog dialog = new ClosableWizardDialog(shell, wizard); if (dialog.open() == Window.CANCEL) { monitor.setCanceled(true); return; } phpExeItem = (PHPexeItem) wizard.getRootFragment().getWizardModel().getObject(PHPExeWizard.MODEL); fPHPexes.add(phpExeItem); PHPexes.getInstance().addItem(phpExeItem); fPHPExeList.refresh(); commitChanges(); } private void editPHPexe() { final IStructuredSelection selection = (IStructuredSelection) fPHPExeList.getSelection(); final PHPexeItem phpExe = (PHPexeItem) selection.getFirstElement(); if (phpExe == null) { return; } PHPExeEditDialog dialog = new PHPExeEditDialog(getShell(), phpExe, PHPexes.getInstance().getAllItems()); if (dialog.open() != Window.OK) { return; } fPHPExeList.refresh(); commitChanges(); } private void enableButtons() { IStructuredSelection selection = (IStructuredSelection) fPHPExeList.getSelection(); Object[] elements = selection.toArray(); boolean canRemoveOrEdit = true; for (int i = 0; canRemoveOrEdit && i < elements.length; i++) { PHPexeItem item = (PHPexeItem) elements[i]; canRemoveOrEdit &= item.isEditable(); } final int selectionCount = selection.size(); fRemoveButton.setEnabled(canRemoveOrEdit && selectionCount > 0); PHPexeItem selectedItem = (PHPexeItem) ((IStructuredSelection) fPHPExeList.getSelection()).getFirstElement(); fSetDefaultButton.setEnabled(selectionCount == 1 && selectedItem != null && !selectedItem.isDefault()); } private void removePHPexes() { final IStructuredSelection selection = (IStructuredSelection) fPHPExeList.getSelection(); final PHPexeItem[] phpExes = new PHPexeItem[selection.size()]; final Iterator<?> iter = selection.iterator(); int i = 0; while (iter.hasNext()) { phpExes[i] = (PHPexeItem) iter.next(); i++; } removePHPs(phpExes); commitChanges(); } /** * Sorts by VM location. */ private void sortByLocation() { fPHPExeList.setComparator(new ViewerComparator() { @Override public int compare(Viewer viewer, Object e1, Object e2) { if (e1 instanceof PHPexeItem && e2 instanceof PHPexeItem) { final PHPexeItem left = (PHPexeItem) e1; final PHPexeItem right = (PHPexeItem) e2; return left.getExecutable().getAbsolutePath() .compareToIgnoreCase(right.getExecutable().getAbsolutePath()); } return super.compare(viewer, e1, e2); } }); } /** * Sorts by debugger type. */ private void sortByDebugger() { fPHPExeList.setComparator(new ViewerComparator() { @Override public int compare(Viewer viewer, Object e1, Object e2) { if (e1 instanceof PHPexeItem && e2 instanceof PHPexeItem) { final PHPexeItem left = (PHPexeItem) e1; final PHPexeItem right = (PHPexeItem) e2; String leftDebugger = PHPDebuggersRegistry.getDebuggerName(left.getDebuggerID()); String rightDebugger = PHPDebuggersRegistry.getDebuggerName(right.getDebuggerID()); return rightDebugger.compareToIgnoreCase(leftDebugger); } return super.compare(viewer, e1, e2); } }); } /** * Sorts by VM name. */ private void sortByName() { fPHPExeList.setComparator(new ViewerComparator() { @Override public int compare(Viewer viewer, Object e1, Object e2) { if (e1 instanceof PHPexeItem && e2 instanceof PHPexeItem) { final PHPexeItem left = (PHPexeItem) e1; final PHPexeItem right = (PHPexeItem) e2; return left.getName().compareToIgnoreCase(right.getName()); } return super.compare(viewer, e1, e2); } }); } /** * Sorts by VM type, and name within type. */ private void sortByVersion() { fPHPExeList.setComparator(new ViewerComparator() { @Override public int compare(Viewer viewer, Object e1, Object e2) { if (e1 instanceof PHPexeItem && e2 instanceof PHPexeItem) { final PHPexeItem left = (PHPexeItem) e1; final PHPexeItem right = (PHPexeItem) e2; final String leftVersion = left.getVersion(); final String rightVersion = right.getVersion(); if (VersionUtils.greater(leftVersion, rightVersion, 3)) { return 1; } else if (VersionUtils.equal(leftVersion, rightVersion, 3)) { return 0; } else { return -1; } } return super.compare(viewer, e1, e2); } }); } /** * Locate a PHP executable file in the PHP location given to this method. * The location should be a directory. The search is done for php and * php.exe only. * * @param phpLocation * A directory that might hold a PHP executable. * @return A PHP executable file. */ private static List<File> findPHPExecutable(File phpLocation) { List<File> found = new ArrayList<File>(0); for (String element : PHP_CANDIDATE_BIN) { File phpExecFile = new File(phpLocation, element); if (phpExecFile.exists() && !phpExecFile.isDirectory()) { found.add(phpExecFile); } } return found; } }