blob: 651fa15801cb03869f1637920d5390ae23ae1465 [file] [log] [blame]
/*
* Copyright (c) 2012 Google Inc.
*
* 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
*/
package com.google.eclipse.elt.view.ui;
import com.google.eclipse.elt.emulator.control.ITerminalListener;
import com.google.eclipse.elt.emulator.provisional.api.TerminalState;
import com.google.eclipse.elt.view.Activator;
import com.google.eclipse.elt.view.ImageKeys;
import com.google.eclipse.elt.view.connector.LifeCycleListener;
import com.google.eclipse.elt.view.preferences.AbstractPreferencesChangeListener;
import com.google.eclipse.elt.view.preferences.ColorsAndFontsPreferences;
import com.google.eclipse.elt.view.preferences.GeneralPreferences;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.ISaveablePart2;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.contexts.IContextActivation;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.progress.UIJob;
import java.io.File;
import java.util.UUID;
/**
* @author alruiz@google.com (Alex Ruiz)
*/
public class TerminalView extends ViewPart implements ISaveablePart2 {
private static final String SCROLL_LOCK_ENABLED = "scrollLock";
private static final String TITLE_STATE_TYPE = "title";
private static final String WORKING_DIRECTORY_STATE_TYPE = "workingDirectory";
private static final String VIEW_ID = "com.google.eclipse.terminal.local.localTerminalView";
private IPropertyChangeListener preferencesChangeListener;
private IPropertyChangeListener textFontChangeListener;
private IMemento savedState;
private TerminalWidget terminalWidget;
private IPath workingDirectory;
private Action newTerminalAction;
private Action scrollLockAction;
private boolean checkCanBeClosed;
private boolean forceClose;
private IContextActivation contextActivation;
/**
* Creates a new terminal view.
*
* @param workingDirectory the working directory to be opened on initialization
*/
public static void openTerminalView(IPath workingDirectory) {
openNewTerminalView(workingDirectory);
}
/**
* Opens a new {@link TerminalView}.
*
* @param workingDirectory the working directory to be opened on initialization
*/
private static void openNewTerminalView(IPath workingDirectory) {
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
IPath safeWorkingDirectory =
(workingDirectory != null) ? workingDirectory : getDefaultWorkingDirectory();
try {
String directoryName = safeWorkingDirectory.lastSegment();
// Use of a freshTerminalId to force freshness of the identifier.
String secondaryId = UUID.randomUUID().toString();
TerminalView view =
(TerminalView) page.showView(VIEW_ID, secondaryId, IWorkbenchPage.VIEW_ACTIVATE);
view.setPartName(directoryName);
view.open(safeWorkingDirectory);
} catch (PartInitException e) {
Activator.log("Unable to create Terminal View", e);
}
}
@Override public void init(IViewSite site, IMemento memento) throws PartInitException {
super.init(site, memento);
savedState = memento;
}
@Override public void saveState(IMemento memento) {
saveState(memento, SCROLL_LOCK_ENABLED, String.valueOf(terminalWidget.isScrollLockEnabled()));
saveState(memento, TITLE_STATE_TYPE, getPartName());
if (workingDirectory != null) {
saveState(memento, WORKING_DIRECTORY_STATE_TYPE, workingDirectory.toOSString());
}
}
private void saveState(IMemento memento, String type, String data) {
IMemento child = memento.createChild(type);
child.putTextData(data);
}
/**
* Returns a non-{@code null} directory to be used as the default working directory if one isn't
* explicitly provided.
*/
private static IPath getDefaultWorkingDirectory() {
File homeDirectory = new File(System.getProperty("user.home"));
if (homeDirectory.isDirectory()) {
return Path.fromOSString(homeDirectory.getAbsolutePath());
}
File currentDirectory = new File(System.getProperty("user.dir"));
if (currentDirectory.isDirectory()) {
return Path.fromOSString(currentDirectory.getAbsolutePath());
}
// If all else fails, pick the first root we can find.
for (File root : File.listRoots()) {
if (root.isDirectory()) {
return Path.fromOSString(root.getAbsolutePath());
}
}
throw new IllegalStateException("Could not find valid working directory.");
}
@Override public void createPartControl(Composite parent) {
terminalWidget = new TerminalWidget(parent, getViewSite());
terminalWidget.setLifeCycleListener(new LifeCycleListener() {
@Override public void executionFinished() {
closeViewOnExitIfPossible();
}
});
terminalWidget.setTerminalListener(new ITerminalListener() {
@Override public void setTerminalTitle(final String title) {
updatePartName(title);
}
@Override public void setState(TerminalState state) {}
});
IViewSite viewSite = getViewSite();
preferencesChangeListener = new AbstractPreferencesChangeListener() {
@Override protected void onBufferLineCountChanged() {
updateBufferLineCount();
}
@Override protected void onColorChanged() {
updateColors();
}
@Override protected void onFontChanged() {
updateFont();
}
@Override protected void onUseBlinkingCursorChanged() {
updateUsageOfBlinkingCursor();
}
};
Activator.preferenceStore().addPropertyChangeListener(preferencesChangeListener);
updateBufferLineCount();
updateColors();
updateUsageOfBlinkingCursor();
textFontChangeListener = new IPropertyChangeListener() {
@Override public void propertyChange(PropertyChangeEvent event) {
if (JFaceResources.TEXT_FONT.equals(event.getProperty())) {
if (!ColorsAndFontsPreferences.useCustomFont()) {
setFont(JFaceResources.getTextFont());
}
}
}
};
JFaceResources.getFontRegistry().addListener(textFontChangeListener);
updateFont();
setupToolBarActions();
IContextService contextService = contextService();
if (contextService != null) {
contextActivation =
contextService.activateContext("com.google.eclipse.terminal.local.context.localTerminal");
}
if (savedState != null) {
updateScrollLockUsingSavedState();
connectUsingSavedState();
return;
}
if (viewSite.getSecondaryId() == null) {
setPartName(Messages.defaultViewTitle);
open(getDefaultWorkingDirectory());
}
enableScrollLock(scrollLockAction.isChecked());
}
private void closeViewOnExitIfPossible() {
if (GeneralPreferences.closeViewOnExit() && terminalWidget != null
&& !terminalWidget.isDisposed()) {
// must run in UI thread.
forceClose = true;
terminalWidget.getDisplay().asyncExec(new Runnable() {
@Override public void run() {
IWorkbenchPartSite site = getSite();
site.getPage().hideView((IViewPart) site.getPart());
}
});
}
}
private void updateColors() {
terminalWidget.setColors(ColorsAndFontsPreferences.background(),
ColorsAndFontsPreferences.foreground());
}
private void updateFont() {
setFont(terminalFont());
}
private void updateUsageOfBlinkingCursor() {
terminalWidget.setBlinkingCursor(ColorsAndFontsPreferences.useBlinkingCursor());
}
private Font terminalFont() {
if (ColorsAndFontsPreferences.useCustomFont()) {
return new Font(Display.getDefault(), ColorsAndFontsPreferences.customFontData());
}
return JFaceResources.getTextFont();
}
private void setFont(Font font) {
terminalWidget.setFont(font);
}
private void updateBufferLineCount() {
terminalWidget.setBufferLineCount(GeneralPreferences.bufferLineCount());
}
private void setupToolBarActions() {
IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
toolBarManager.add(new ChangeViewNameAction());
toolBarManager.add(new Separator());
newTerminalAction = new NewTerminalAction();
toolBarManager.add(newTerminalAction);
scrollLockAction = new ScrollLockAction();
toolBarManager.add(scrollLockAction);
}
private void updateScrollLockUsingSavedState() {
boolean newValue = Boolean.valueOf(savedState(SCROLL_LOCK_ENABLED));
enableScrollLockAndUpdateAction(newValue);
}
private void enableScrollLockAndUpdateAction(boolean enabled) {
enableScrollLock(enabled);
scrollLockAction.setChecked(enabled);
}
private void enableScrollLock(boolean enabled) {
terminalWidget.enableScrollLock(enabled);
}
private void connectUsingSavedState() {
String title = savedState(TITLE_STATE_TYPE);
setPartName(title);
String savedWorkingDirectory = savedState(WORKING_DIRECTORY_STATE_TYPE);
if (savedWorkingDirectory == null) {
open(getDefaultWorkingDirectory());
} else {
open(Path.fromOSString(savedWorkingDirectory));
}
}
private String savedState(String type) {
IMemento child = savedState.getChild(type);
return (child != null) ? child.getTextData() : null;
}
private void open(IPath workingDirectory) {
// workingDirectory should be non-null.
if (terminalWidget.isConnected()) {
return;
}
this.workingDirectory = workingDirectory;
terminalWidget.setWorkingDirectory(workingDirectory);
terminalWidget.connect();
}
private void updatePartName(final String value) {
UIJob job = new UIJob("Update terminal view title") {
@Override public IStatus runInUIThread(IProgressMonitor monitor) {
setPartName(value);
return Status.OK_STATUS;
}
};
job.schedule();
}
@Override public void setFocus() {
terminalWidget.setFocus();
}
@Override public void dispose() {
if (contextActivation != null) {
IContextService contextService = contextService();
if (contextService != null) {
contextService.deactivateContext(contextActivation);
}
}
if (preferencesChangeListener != null) {
Activator.preferenceStore().removePropertyChangeListener(preferencesChangeListener);
}
if (textFontChangeListener != null) {
JFaceResources.getFontRegistry().removeListener(textFontChangeListener);
}
super.dispose();
}
private IContextService contextService() {
return (IContextService) getSite().getService(IContextService.class);
}
@Override public boolean isDirty() {
if (checkCanBeClosed) {
checkCanBeClosed = false;
return true;
}
return false;
}
@Override public boolean isSaveOnCloseNeeded() {
if (forceClose) {
return false;
}
checkCanBeClosed = true;
return true;
}
@Override public int promptToSaveOnClose() {
if (GeneralPreferences.warnOnClose()) {
boolean close = WarnOnCloseDialog.open(terminalWidget.getShell());
if (!close) {
return CANCEL;
}
}
return NO;
}
@Override public void doSave(IProgressMonitor monitor) {}
@Override public void doSaveAs() {}
@Override public boolean isSaveAsAllowed() {
return false;
}
private class NewTerminalAction extends Action {
NewTerminalAction() {
setImageDescriptor(Activator.imageDescriptor(ImageKeys.NEW_TERMINAL));
setText(Messages.newLocalTerminal);
}
@Override public void run() {
openNewTerminalView(workingDirectory);
}
}
private class ScrollLockAction extends Action {
ScrollLockAction() {
super(Messages.scrollLock, AS_RADIO_BUTTON);
setChecked(false);
setImageDescriptor(Activator.imageDescriptor(ImageKeys.SCROLL_LOCK));
}
@Override public void run() {
boolean newValue = !terminalWidget.isScrollLockEnabled();
enableScrollLockAndUpdateAction(newValue);
}
}
private class ChangeViewNameAction extends Action {
ChangeViewNameAction() {
setImageDescriptor(Activator.imageDescriptor(ImageKeys.CHANGE_TITLE));
setText(Messages.changeTerminalTitle);
}
@Override public void run() {
Shell shell = getViewSite().getShell();
final String currentTitle = getPartName();
InputDialog input = new InputDialog(shell, Messages.enterTerminalTitleDialogTitle,
Messages.enterTerminalTitlePrompt, currentTitle,
new IInputValidator() {
@Override public String isValid(String newText) {
if (newText == null || newText.isEmpty() || currentTitle.equals(newText)) {
return "";
}
return null;
}
});
input.setBlockOnOpen(true);
if (input.open() == Window.OK) {
setPartName(input.getValue());
}
}
}
}