/******************************************************************************* * Copyright (c) 2006 RadRails.org and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package com.aptana.rdt.internal.rake.view; import java.text.MessageFormat; import java.util.Collection; import java.util.Map; import java.util.TreeSet; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.Preferences.IPropertyChangeListener; import org.eclipse.core.runtime.Preferences.PropertyChangeEvent; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StackLayout; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IActionBars; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.ViewPart; import org.rubypeople.rdt.core.RubyCore; import org.rubypeople.rdt.internal.ui.RubyExplorerTracker; import org.rubypeople.rdt.internal.ui.RubyPlugin; import org.rubypeople.rdt.internal.ui.RubyProjectSelectionAction; import org.rubypeople.rdt.internal.ui.RubyExplorerTracker.IRubyProjectListener; import org.rubypeople.rdt.internal.ui.text.RubyColorManager; import org.rubypeople.rdt.launching.IVMInstall; import org.rubypeople.rdt.launching.IVMInstallChangedListener; import org.rubypeople.rdt.launching.RubyRuntime; import com.aptana.rdt.rake.IRakeHelper; import com.aptana.rdt.rake.PreferenceConstants; import com.aptana.rdt.rake.RakePlugin; /** * RakeTasksView * * @author cwilliams * @author Kevin Sawicki (added labels) */ public class RakeTasksView extends ViewPart implements IVMInstallChangedListener, IPropertyChangeListener, IRubyProjectListener { private static final String PROJECT = "Current Ruby Project: "; private StackLayout fViewLayout; private Composite fRakeTasksView; private RubyProjectSelectionAction projectSelectionAction; private Label fSpecifyRakePath; private Label fSelectRailsProjectView; private Composite fParent; private Composite basicControls; private Label projectNameLabel; private Composite tasksComp; private Label tasksLabel; private Combo fTasksCombo; private Label paramLabel; private Text fParamText; private Button genButton; private Label descriptionLabel; private Label fDescripText; private RubyColorManager fColorManager; private Map<String, String> fTasks; private IProject project; private Job updateRakeTasksJob; private Cursor hand; private Button pretendButton; private Button quietButton; private Button backtraceButton; private Button systemButton; private Image fRunIcon; private Image fMaximizeIcon; private Image fMinimizeIcon; /** * RakeTasksView */ public RakeTasksView() { super(); } /** * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) */ public void createPartControl(Composite parent) { fColorManager = new RubyColorManager(true); fParent = parent; hand = new Cursor(parent.getDisplay(), SWT.CURSOR_HAND); fViewLayout = new StackLayout(); parent.setLayout(fViewLayout); fRakeTasksView = new Composite(parent, SWT.NONE); fRakeTasksView.setLayout(new GridLayout(1, true)); fRakeTasksView.setLayoutData(new GridData(GridData.FILL_BOTH)); createRakeControls(fRakeTasksView); createAdvancedSection(fRakeTasksView); fSpecifyRakePath = new Label(parent, SWT.NULL); fSpecifyRakePath.setText(RakeViewMessages.SpecifyRakePath_message); fSelectRailsProjectView = new Label(parent, SWT.NULL); fSelectRailsProjectView.setText(RakeViewMessages.SelectRubyProject_message); if (emptyRakePath()) { fViewLayout.topControl = fSpecifyRakePath; } else { if (getSelectedRubyProject() != null) { fViewLayout.topControl = fRakeTasksView; } else { fViewLayout.topControl = fSelectRailsProjectView; } } parent.layout(); getProjectTracker().addProjectListener(this); RubyRuntime.addVMInstallChangedListener(this); RakePlugin.getDefault().getPluginPreferences().addPropertyChangeListener(this); projectSelectionAction = new RubyProjectSelectionAction(); projectSelectionAction.setListener(this); IActionBars bars = getViewSite().getActionBars(); bars.getToolBarManager().add(projectSelectionAction); IProject project = getProjectTracker().getSelectedRubyProject(); IProject[] projects = RubyCore.getRubyProjects(); if (project != null) { this.projectSelected(project); } else if (projects != null && projects.length > 0) { this.projectSelected(projects[0]); } } /** * Create the rake controls * * @param parent * @return */ protected Composite createRakeControls(Composite parent) { basicControls = new Composite(parent, SWT.NULL); basicControls.setLayout(new GridLayout(3, false)); basicControls.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, false)); projectNameLabel = new Label(basicControls, SWT.LEFT); projectNameLabel.setText(PROJECT); projectNameLabel.setForeground(fColorManager.getColor(new RGB(128, 128, 128))); GridData pnlData = new GridData(SWT.FILL, SWT.FILL, true, false); pnlData.horizontalSpan = 3; projectNameLabel.setLayoutData(pnlData); // Create the combo box of tasks tasksComp = new Composite(basicControls, SWT.LEFT); tasksComp.setLayout(new GridLayout(2, false)); tasksLabel = new Label(tasksComp, SWT.LEFT); tasksLabel.setText("Tasks:"); fTasksCombo = new Combo(tasksComp, SWT.DROP_DOWN | SWT.READ_ONLY); fTasksCombo.setVisibleItemCount(20); fTasksCombo.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { // Do nothing } public void widgetSelected(SelectionEvent e) { setCurrentSelectedTaskDescription(); } }); // Create the parameters text field Composite paramsComp = new Composite(basicControls, SWT.LEFT); paramsComp.setLayout(new GridLayout(2, false)); paramsComp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); paramLabel = new Label(paramsComp, SWT.LEFT); paramLabel.setText("Parameters:"); fParamText = new Text(paramsComp, SWT.BORDER); GridData paramTextData = new GridData(GridData.FILL_HORIZONTAL); paramTextData.widthHint = 300; fParamText.setLayoutData(paramTextData); fParamText.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent e) { // Do nothing } public void keyReleased(KeyEvent e) { // Take action if Enter was pressed if (e.character == SWT.CR) { runRakeTask(); } } }); // Create the Go button genButton = new Button(basicControls, SWT.PUSH); genButton.setToolTipText("Run Rake Task"); genButton.setImage(getRunIcon()); genButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { runRakeTask(); } }); // Create the text area for the task descriptions Composite descripComp = new Composite(basicControls, SWT.LEFT); descripComp.setLayout(new GridLayout(2, false)); GridData gd = new GridData(); gd.horizontalSpan = 2; gd.verticalAlignment = SWT.TOP; gd.grabExcessHorizontalSpace = false; descripComp.setLayoutData(gd); descriptionLabel = new Label(descripComp, SWT.LEFT); descriptionLabel.setText("Description:"); fDescripText = new Label(descripComp, SWT.WRAP); return basicControls; } private Composite createAdvancedSection(final Composite parent) { final Composite advanced = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(2, false); layout.marginHeight = 0; layout.marginWidth = 0; advanced.setLayout(layout); GridData advancedData = new GridData(SWT.FILL, SWT.FILL, true, false); advancedData.horizontalSpan = 3; advanced.setLayoutData(advancedData); final Font boldFont = new Font(advanced.getDisplay(), boldFont(advanced.getFont())); advanced.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { if (hand != null && !hand.isDisposed()) { hand.dispose(); } if (boldFont != null && !boldFont.isDisposed()) { boldFont.dispose(); } } }); final Label advancedIcon = new Label(advanced, SWT.LEFT); advancedIcon.setImage(getMaximizeIcon()); advancedIcon.setCursor(hand); advancedIcon.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); Label advancedLabel = new Label(advanced, SWT.LEFT); advancedLabel.setText("Advanced Options"); advancedLabel.setCursor(hand); advancedLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); advancedLabel.setFont(boldFont); final Composite advancedOptions = new Composite(advanced, SWT.NONE); layout = new GridLayout(); layout.marginLeft = 15; advancedOptions.setLayout(layout); GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false); gridData.horizontalSpan = 2; gridData.exclude = true; advancedOptions.setLayoutData(gridData); advancedOptions.setVisible(false); MouseAdapter expander = new MouseAdapter() { public void mouseDown(MouseEvent e) { if (advancedOptions.isVisible()) { advancedOptions.setVisible(false); advancedIcon.setImage(getMaximizeIcon()); ((GridData) advancedOptions.getLayoutData()).exclude = true; } else { advancedOptions.setVisible(true); advancedIcon.setImage(getMinimizeIcon()); ((GridData) advancedOptions.getLayoutData()).exclude = false; } parent.pack(true); parent.layout(true, true); } }; advancedIcon.addMouseListener(expander); advancedLabel.addMouseListener(expander); // Create the text field for the options information Group optionsGroup = new Group(advancedOptions, SWT.NULL); optionsGroup.setLayout(new GridLayout(7, false)); optionsGroup.setText("Options"); pretendButton = new Button(optionsGroup, SWT.CHECK); pretendButton.setText("Dry run"); quietButton = new Button(optionsGroup, SWT.CHECK); quietButton.setText("Quiet"); backtraceButton = new Button(optionsGroup, SWT.CHECK); backtraceButton.setText("Trace"); systemButton = new Button(optionsGroup, SWT.CHECK); systemButton.setText("System"); return advanced; } private Image getRunIcon() { if (fRunIcon == null) { fRunIcon = RakePlugin.imageDescriptorFromPlugin(RakePlugin.PLUGIN_ID, "icons/nav_go.gif").createImage(); } return fRunIcon; } private Image getMaximizeIcon() { if (fMaximizeIcon == null) { fMaximizeIcon = RakePlugin.imageDescriptorFromPlugin(RakePlugin.PLUGIN_ID, "icons/maximize.png") .createImage(); } return fMaximizeIcon; } private Image getMinimizeIcon() { if (fMinimizeIcon == null) { fMinimizeIcon = RakePlugin.imageDescriptorFromPlugin(RakePlugin.PLUGIN_ID, "icons/minimize.png") .createImage(); } return fMinimizeIcon; } private static FontData[] boldFont(Font font) { FontData[] datas = font.getFontData(); if (datas.length > 0) { for (int i = 0; i < datas.length; i++) { FontData data = datas[i]; data.setStyle(data.getStyle() | SWT.BOLD); } } return datas; } /** * @see org.eclipse.ui.part.WorkbenchPart#dispose() */ public void dispose() { super.dispose(); disposeIcon(fMaximizeIcon); disposeIcon(fMinimizeIcon); disposeIcon(fRunIcon); fColorManager.dispose(); getProjectTracker().removeProjectListener(this); RubyRuntime.removeVMInstallChangedListener(this); RakePlugin.getDefault().getPluginPreferences().removePropertyChangeListener(this); } private void disposeIcon(Image icon) { if (icon == null) return; icon.dispose(); icon = null; } private RubyExplorerTracker getProjectTracker() { return RubyPlugin.getDefault().getProjectTracker(); } private void runRakeTask() { final IProject project = getSelectedRubyProject(); if (project == null) return; // TODO Show error final String task = fTasksCombo.getText(); final String args = getArgs(); Job job = new Job(MessageFormat.format("Running rake task {0} {1}", task, args)) { @Override protected IStatus run(IProgressMonitor monitor) { getRakeTasksHelper().runRakeTask(project, task, args, monitor); return Status.OK_STATUS; } }; job.setUser(true); job.schedule(); } private String getArgs() { String args = ""; if (pretendButton.getSelection()) args += "--dry-run "; if (quietButton.getSelection()) args += "--quiet "; if (backtraceButton.getSelection()) args += "--trace "; if (systemButton.getSelection()) args += "--system "; return args + fParamText.getText(); } private IProject getSelectedRubyProject() { return this.project; } private IRakeHelper getRakeTasksHelper() { return RakePlugin.getDefault().getRakeHelper(); } /** * Updates the rake tasks */ protected void updateRakeTasks(final boolean force) { fTasksCombo.removeAll(); if (project == null) return; // Part of ROR-1098 - We shouldn't allow multiple instances of this job to run simultaneously! if (updateRakeTasksJob != null) { updateRakeTasksJob.cancel(); } updateRakeTasksJob = new Job("Update rake tasks") { protected IStatus run(IProgressMonitor monitor) { if (monitor.isCanceled()) return Status.CANCEL_STATUS; monitor.beginTask("Loading rake tasks", 2); PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() { public void run() { if (fDescripText != null && !fDescripText.isDisposed()) { fDescripText.setText("Please wait, loading rake tasks..."); } } }); monitor.worked(1); if (monitor.isCanceled()) return Status.CANCEL_STATUS; fTasks = getRakeTasksHelper().getTasks(getSelectedRubyProject(), force, monitor); if (monitor.isCanceled()) return Status.CANCEL_STATUS; PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() { public void run() { if (fTasks.isEmpty()) { if (!fDescripText.isDisposed()) { fDescripText.redraw(); setTaskDescription("No Rake Tasks found. Likely cause is no Rakefile for project."); } setEnabled(false); return; } Collection<String> sortedItems = new TreeSet<String>(fTasks.keySet()); if (!fTasksCombo.isDisposed()) { fTasksCombo.setItems(sortedItems.toArray(new String[sortedItems.size()])); fTasksCombo.pack(true); if (fTasks != null && !fTasks.isEmpty()) fTasksCombo.select(0); setCurrentSelectedTaskDescription(); } if (!genButton.isDisposed()) genButton.setEnabled(true); } }); monitor.worked(1); monitor.done(); return Status.OK_STATUS; } }; updateRakeTasksJob.schedule(); } /** * @see org.eclipse.ui.part.WorkbenchPart#setFocus() */ public void setFocus() { fTasksCombo.setFocus(); setCurrentSelectedTaskDescription(); } /** * @see org.rubypeople.rdt.launching.IVMInstallChangedListener#defaultVMInstallChanged(org.rubypeople.rdt.launching.IVMInstall, * org.rubypeople.rdt.launching.IVMInstall) */ public void defaultVMInstallChanged(IVMInstall previous, IVMInstall current) { handlePossibleRakeChange(getRakePath()); } private String getRakePath() { return RakePlugin.getDefault().getRakePath(); } /** * @see org.rubypeople.rdt.launching.IVMInstallChangedListener#vmAdded(org.rubypeople.rdt.launching.IVMInstall) */ public void vmAdded(IVMInstall newVm) { // ignore } /** * @see org.rubypeople.rdt.launching.IVMInstallChangedListener#vmChanged(org.rubypeople.rdt.launching.PropertyChangeEvent) */ public void vmChanged(org.rubypeople.rdt.launching.PropertyChangeEvent event) { // ignore } /** * @see org.rubypeople.rdt.launching.IVMInstallChangedListener#vmRemoved(org.rubypeople.rdt.launching.IVMInstall) */ public void vmRemoved(IVMInstall removedVm) { // ignore } /** * propertyChange * * @param event */ public void propertyChange(PropertyChangeEvent event) { if (event.getProperty().equals(PreferenceConstants.PREF_RAKE_PATH)) { handlePossibleRakeChange(event.getNewValue()); } } private void handlePossibleRakeChange(final Object value) { if (!fParent.isDisposed()) { Display.getDefault().asyncExec(new Runnable() { public void run() { if (value == null || value.equals("")) { fViewLayout.topControl = fSpecifyRakePath; } else { fViewLayout.topControl = fRakeTasksView; } fParent.layout(); } }); } } /** * Sets the widget enablement * * @param enabled */ public void setEnabled(boolean enabled) { fTasksCombo.setEnabled(enabled); fParamText.setEnabled(enabled); genButton.setEnabled(enabled); } /** * @see org.rubypeople.rdt.internal.ui.RubyExplorerTracker.IRubyProjectListener#projectSelected(org.eclipse.core.resources.IProject) */ public void projectSelected(IProject project) { if (fParent.isDisposed()) { return; } if (project != null && RubyCore.isRubyProject(project) && project.exists() && project.isOpen()) { projectNameLabel.setText(PROJECT + project.getName()); this.project = project; setEnabled(true); updateRakeTasks(false); } else { fViewLayout.topControl = fSelectRailsProjectView; projectNameLabel.setText(PROJECT + "<Select an open Ruby project>"); setEnabled(false); this.project = null; clear(); fDescripText.setText("Selected project is not open or is not a Ruby project."); } if (emptyRakePath()) { fViewLayout.topControl = fSpecifyRakePath; } else { fViewLayout.topControl = fRakeTasksView; } fParent.layout(); } private void clear() { fTasksCombo.removeAll(); fParamText.setText(""); fDescripText.setText(""); } private boolean emptyRakePath() { return getRakePath() == null || getRakePath().equals(""); } private void setCurrentSelectedTaskDescription() { if (fTasks == null || fTasks.isEmpty() || fTasksCombo == null) return; String descrip = fTasks.get(fTasksCombo.getText()); if (descrip == null) return; setTaskDescription(descrip); } private void setTaskDescription(String descrip) { Point size = this.fParent.getSize(); fDescripText.setText(descrip); GridData gd = (GridData) fDescripText.getParent().getLayoutData(); gd.widthHint = size.x - 75; fTasksCombo.setToolTipText(descrip); fDescripText.pack(true); gd = new GridData(); gd.widthHint = size.x - 150; fDescripText.setLayoutData(gd); fDescripText.getParent().pack(true); fRakeTasksView.pack(true); fRakeTasksView.layout(true, true); } }