/******************************************************************************* * Copyright (c) 2015 Zend Technologies 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: * Zend Technologies - initial API and implementation *******************************************************************************/ package org.eclipse.php.internal.debug.ui.wizards; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.eclipse.debug.internal.ui.SWTFactory; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.php.internal.core.IUniqueIdentityElement; 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.*; import org.eclipse.php.internal.debug.core.preferences.*; import org.eclipse.php.internal.debug.ui.PHPDebugUIImages; import org.eclipse.php.internal.server.core.Server; import org.eclipse.php.internal.ui.wizards.CompositeFragment; import org.eclipse.php.internal.ui.wizards.IControlHandler; import org.eclipse.php.internal.ui.wizards.IControlHandler.Kind; import org.eclipse.swt.SWT; import org.eclipse.swt.events.*; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.*; import org.eclipse.ui.PlatformUI; /** * Debugger settings composite fragment. * * @author Bartlomiej Laczkowski */ @SuppressWarnings("restriction") public class DebuggerCompositeFragment extends CompositeFragment { public static final String ID = "org.eclipse.php.debug.ui.fragments.debuggerCompositeFragment"; //$NON-NLS-1$ // A class used as a local original IServerWorkingCopy values cache. private class ValuesCache { String debuggerId; public ValuesCache() { } public ValuesCache(ValuesCache cache) { this.debuggerId = cache.debuggerId; } } private class EmptySettingsSection implements IDebuggerSettingsSection { private EmptySettingsSection(Composite settingsComposite) { Composite empty = new Composite(settingsComposite, SWT.NONE); empty.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); } @Override public boolean performOK() { return true; } @Override public boolean performCancel() { return true; } @Override public void validate() { // Reset state setMessage(getDescription(), IMessageProvider.NONE); } @Override public boolean canTest() { return false; } @Override public void performTest() { // ignore } } private class PHPExeListener implements IPHPexeItemListener { @Override public void phpExeChanged(PHPexeItemEvent event) { if (event.getProperty().equals(IPHPexeItemProperties.PROP_EXE_LOCATION) || event.getProperty().equals(IPHPexeItemProperties.PROP_INI_LOCATION) || event.getProperty().equals(IPHPexeItemProperties.PROP_USE_DEFAULT_INI)) { detectDebugger = true; } else if (!updatingDebuggerId && event.getProperty().equals(IPHPexeItemProperties.PROP_DEBUGGER_ID)) { modifiedValuesCache.debuggerId = (String) event.getNewValue(); Display.getDefault().syncExec(new SelectDebuggerRunnable()); } } } private class PHPServerListener implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent event) { if (!updatingDebuggerId && event.getPropertyName().equals(Server.DEBUGGER)) { modifiedValuesCache.debuggerId = (String) event.getNewValue(); Display.getDefault().syncExec(new SelectDebuggerRunnable()); } } } private class SelectDebuggerRunnable implements Runnable { @Override public void run() { selectDebugger(modifiedValuesCache.debuggerId); } } private List<String> debuggersIds; private Combo debuggerCombo; private Button debuggerTest; private Link debuggerGlobalSettings; private PHPExeListener phpExeListener; private PHPServerListener phpServerListener; private ValuesCache originalValuesCache = new ValuesCache(); private ValuesCache modifiedValuesCache; private IDebuggerSettingsSection debuggerSettingsSection; private IDebuggerSettingsWorkingCopy debuggerSettingsWC; private Map<String, IDebuggerSettingsWorkingCopy> settingsWCBuffer = new HashMap<String, IDebuggerSettingsWorkingCopy>(); private String detectedDebuggerId = null; private boolean detectDebugger; private Composite debuggerSettingsComposite; private Composite mainComposite; private boolean updatingDebuggerId = false; /** * Creates new debugger composite fragment. * * @param parent * @param handler * @param isForEditing */ public DebuggerCompositeFragment(Composite parent, IControlHandler handler, boolean isForEditing) { super(parent, handler, isForEditing); this.detectDebugger = handler.getKind() == Kind.WIZARD; addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { unregisterListeners(); removeDisposeListener(this); } }); } @Override public String getId() { return ID; } /* * (non-Javadoc) * * @see org.eclipse.php.internal.ui.wizards.CompositeFragment#performOk() */ @Override public boolean performOk() { if (detectDebugger) { detectDebugger(); detectDebugger = false; } if (debuggerSettingsSection != null) { boolean isOK = debuggerSettingsSection.performOK(); if (!isOK) return isOK; } if (debuggerSettingsWC != null) { if (debuggerSettingsWC.isDirty()) { DebuggerSettingsManager.INSTANCE.save(debuggerSettingsWC); } DebuggerSettingsManager.INSTANCE.dropWorkingCopy(debuggerSettingsWC); } return true; } /* * (non-Javadoc) * * @see * org.eclipse.php.internal.ui.wizards.CompositeFragment#performCancel() */ @Override public boolean performCancel() { if (debuggerSettingsSection != null) debuggerSettingsSection.performCancel(); if (debuggerSettingsWC != null) { DebuggerSettingsManager.INSTANCE.dropWorkingCopy(debuggerSettingsWC); } return super.performCancel(); } /* * (non-Javadoc) * * @see org.eclipse.php.internal.ui.wizards.CompositeFragment#validate() */ @Override public void validate() { setComplete(true); // Delegate validation to settings composite if (debuggerSettingsSection != null) debuggerSettingsSection.validate(); } /** * Override the super setData to handle only Server types. * * @throws IllegalArgumentException * if the given object is not a {@link Server} */ public void setData(Object debuggerOwner) throws IllegalArgumentException { if (debuggerOwner != null && !(debuggerOwner instanceof IUniqueIdentityElement)) { throw new IllegalArgumentException("The given object is not a PHP Server or Executable"); //$NON-NLS-1$ } unregisterListeners(); super.setData(debuggerOwner); registerListeners(); createDescription(debuggerOwner); init(); validate(); } public IUniqueIdentityElement getDebuggerOwner() { return (IUniqueIdentityElement) getData(); } void createSettings(String debuggerId) { IUniqueIdentityElement debuggerOwner = getDebuggerOwner(); if (debuggerOwner == null || debuggerId == null) return; IDebuggerSettings settings = DebuggerSettingsManager.INSTANCE.findSettings(debuggerOwner.getUniqueId(), debuggerId); boolean repaint = controlHandler.getKind() == Kind.WIZARD && !PHPDebuggersRegistry.NONE_DEBUGGER_ID.equals(debuggerId); if (debuggerSettingsSection != null) { debuggerSettingsComposite.dispose(); repaint = true; } // Rebuild settings composite debuggerSettingsComposite = new Composite(mainComposite, SWT.NONE); debuggerSettingsComposite.setLayout(new GridLayout()); GridData dscData = new GridData(SWT.FILL, SWT.FILL, true, true); debuggerSettingsComposite.setLayoutData(dscData); if (PHPDebuggersRegistry.NONE_DEBUGGER_ID.equals(debuggerId)) { debuggerSettingsSection = new EmptySettingsSection(debuggerSettingsComposite); debuggerGlobalSettings.setVisible(false); } else if (settings == null) { debuggerSettingsSection = new DebuggerUnsupportedSettingsSection(this, debuggerSettingsComposite); } else { debuggerGlobalSettings .setVisible(PHPDebuggersRegistry.getDebuggerConfiguration(debuggerId) != null ? true : false); debuggerSettingsWC = getSettingsWC(debuggerId, settings); IDebuggerSettingsProvider provider = DebuggerSettingsProviderRegistry.getProvider(debuggerId); IDebuggerSettingsSectionBuilder sectionBuilder = DebuggerSettingsSectionBuildersRegistry .getBuilder(provider.getId()); debuggerSettingsSection = sectionBuilder.build(this, debuggerSettingsComposite, debuggerSettingsWC); } if (!debuggerSettingsSection.canTest()) debuggerTest.setVisible(false); else debuggerTest.setVisible(true); this.getParent().layout(true, true); if (repaint) { repaint(); } } IDebuggerSettingsWorkingCopy getSettingsWC(String debuggerId, IDebuggerSettings settings) { IDebuggerSettingsWorkingCopy debuggerSettingsWC = settingsWCBuffer.get(debuggerId); if (debuggerSettingsWC == null) { debuggerSettingsWC = DebuggerSettingsManager.INSTANCE.fetchWorkingCopy(settings); settingsWCBuffer.put(debuggerId, debuggerSettingsWC); } return debuggerSettingsWC; } protected void createDescription(Object owner) { if (owner instanceof PHPexeItem) { setImageDescriptor(PHPDebugUIImages.getImageDescriptor(PHPDebugUIImages.IMG_WIZBAN_DEBUG_PHPEXE)); setDescription(Messages.DebuggerCompositeFragment_Configure_exe_debugger_settings); } else if (owner instanceof Server) { setImageDescriptor(PHPDebugUIImages.getImageDescriptor(PHPDebugUIImages.IMG_WIZBAN_DEBUG_SERVER)); setDescription(Messages.DebuggerCompositeFragment_Configure_server_debugger_settings); } setDisplayName(Messages.DebuggerCompositeFragment_Debugger); setTitle(Messages.DebuggerCompositeFragment_Debugger_settings); controlHandler.setTitle(getTitle()); controlHandler.setImageDescriptor(getImageDescriptor()); controlHandler.setDescription(getDescription()); } protected void createContents(Composite parent) { debuggersIds = new LinkedList<String>(PHPDebuggersRegistry.getDebuggersIds()); mainComposite = parent; Composite debuggerChoice = new Composite(parent, SWT.NONE); GridLayout dcLayout = new GridLayout(); dcLayout.numColumns = 4; debuggerChoice.setLayout(dcLayout); debuggerChoice.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); Label debuggerLabel = new Label(debuggerChoice, SWT.NONE); debuggerLabel.setText("Debugger:"); //$NON-NLS-1$ debuggerLabel.setLayoutData(new GridData()); debuggerCombo = new Combo(debuggerChoice, SWT.DROP_DOWN | SWT.READ_ONLY); GridData dcData = new GridData(SWT.LEFT, SWT.FILL, false, false); debuggerCombo.setLayoutData(dcData); debuggerCombo.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { if (getDebuggerOwner() != null) { modifiedValuesCache.debuggerId = debuggersIds.get(debuggerCombo.getSelectionIndex()); updateItem(); // create settings panel createSettings(modifiedValuesCache.debuggerId); // Validate debugger data validate(); } } }); debuggerTest = SWTFactory.createPushButton(debuggerChoice, Messages.DebuggerCompositeFragment_Test_button, null); debuggerTest.addSelectionListener(new SelectionListener() { @Override public void widgetSelected(SelectionEvent e) { widgetDefaultSelected(e); } @Override public void widgetDefaultSelected(SelectionEvent e) { if (debuggerSettingsSection != null) { debuggerSettingsSection.performTest(); } } }); debuggerGlobalSettings = new Link(debuggerChoice, SWT.NONE); GridData dgsData = new GridData(SWT.RIGHT, SWT.CENTER, true, false); debuggerGlobalSettings.setLayoutData(dgsData); debuggerGlobalSettings.setText(Messages.DebuggerCompositeFragment_Global_settings_link); debuggerGlobalSettings.addSelectionListener(new SelectionAdapter() { @Override public void widgetDefaultSelected(SelectionEvent e) { String selectedDebuggerId = debuggersIds.get(debuggerCombo.getSelectionIndex()); AbstractDebuggerConfiguration globalConfiguration = PHPDebuggersRegistry .getDebuggerConfiguration(selectedDebuggerId); globalConfiguration .openConfigurationDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell()); } @Override public void widgetSelected(SelectionEvent e) { widgetDefaultSelected(e); } }); for (int i = 0; i < debuggersIds.size(); ++i) { String id = debuggersIds.get(i); String debuggerName = PHPDebuggersRegistry.getDebuggerName(id); debuggerCombo.add(debuggerName, i); } Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL); separator.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); Dialog.applyDialogFont(this); // Initialize & validate init(); validate(); // Set focus on debuggers combo debuggerCombo.forceFocus(); } protected void init() { IUniqueIdentityElement debuggerOwner = getDebuggerOwner(); if (debuggerOwner == null) return; if (debuggerOwner instanceof Server) { originalValuesCache.debuggerId = ((Server) debuggerOwner).getDebuggerId(); } else if (debuggerOwner instanceof PHPexeItem) { originalValuesCache.debuggerId = ((PHPexeItem) debuggerOwner).getDebuggerID(); } // Clone the cache lazily if (modifiedValuesCache == null) modifiedValuesCache = new ValuesCache(originalValuesCache); setDebugger(); } private void setDebugger() { if (detectDebugger) { detectDebugger(); detectDebugger = false; } else { // Set combo to appropriate debugger ID selectDebugger(modifiedValuesCache.debuggerId); } } private void detectDebugger() { IUniqueIdentityElement data = (IUniqueIdentityElement) getData(); // Check if debugger module is installed on top of PHP executable if (data instanceof PHPexeItem) { PHPexeItem exeItem = (PHPexeItem) data; modifiedValuesCache.debuggerId = detectedDebuggerId = fetchDebugger(exeItem); } if (detectedDebuggerId == null && (modifiedValuesCache.debuggerId == null || modifiedValuesCache.debuggerId.equals(PHPDebuggersRegistry.NONE_DEBUGGER_ID))) detectedDebuggerId = PHPDebuggersRegistry.NONE_DEBUGGER_ID; else if (modifiedValuesCache.debuggerId != null) { detectedDebuggerId = modifiedValuesCache.debuggerId; } // Set combo to appropriate debugger ID selectDebugger(detectedDebuggerId); } private void selectDebugger(String debuggerId) { final String name = PHPDebuggersRegistry.getDebuggerName(debuggerId); String[] values = debuggerCombo.getItems(); for (int i = 0; i < values.length; i++) { if (values[i].equals(name)) { debuggerCombo.select(i); break; } } } private String fetchDebugger(PHPexeItem exeItem) { List<PHPModuleInfo> modules = PHPExeUtil.getModules(exeItem); AbstractDebuggerConfiguration[] debuggers = PHPDebuggersRegistry.getDebuggersConfigurations(); for (AbstractDebuggerConfiguration debugger : debuggers) { for (PHPModuleInfo module : modules) if (module.getName().equalsIgnoreCase(debugger.getModuleId())) { return debugger.getDebuggerId(); } } return null; } private void updateItem() { updatingDebuggerId = true; IUniqueIdentityElement debuggerOwner = getDebuggerOwner(); if (debuggerOwner instanceof Server) { ((Server) debuggerOwner).setDebuggerId(modifiedValuesCache.debuggerId); } else if (debuggerOwner instanceof PHPexeItem) { ((PHPexeItem) debuggerOwner).setDebuggerID(modifiedValuesCache.debuggerId); } updatingDebuggerId = false; } private void repaint() { Shell shell = this.getShell(); Point previousSize = new Point(shell.getSize().x, shell.getSize().y); Rectangle previousClientArea = shell.getClientArea(); shell.layout(true, true); final Point computedSize = shell.computeSize(previousClientArea.width, SWT.DEFAULT, false); boolean resize = computedSize.y > previousSize.y; if (resize) { shell.setSize(shell.computeSize(previousClientArea.width, computedSize.y - IDialogConstants.BUTTON_BAR_HEIGHT, true)); } else { // Workaround for incorrect redrawing in GTK 3 shell.setRedraw(false); shell.setSize(shell.computeSize(previousClientArea.width + 1, previousClientArea.height, true)); shell.setRedraw(true); shell.setSize(shell.computeSize(previousClientArea.width, previousClientArea.height, true)); } } private void registerListeners() { Object data = getData(); if (data instanceof PHPexeItem) { if (phpExeListener == null) { phpExeListener = new PHPExeListener(); } ((PHPexeItem) data).addPHPexeListener(phpExeListener); } if (data instanceof Server) { if (phpServerListener == null) { phpServerListener = new PHPServerListener(); } ((Server) data).addPropertyChangeListener(phpServerListener); } } private void unregisterListeners() { Object data = getData(); if (data == null) return; if (phpExeListener != null) { ((PHPexeItem) data).removePHPexeListener(phpExeListener); } if (phpServerListener != null) { ((Server) data).removePropertyChangeListener(phpServerListener); } } }