/*
* Copyright (c) 2012, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.tools.ui.theme.preferences;
import com.google.dart.tools.deploy.Activator;
import com.google.dart.tools.internal.corext.refactoring.util.ReflectionUtils;
import com.google.dart.tools.ui.DartToolsPlugin;
import com.google.dart.tools.ui.internal.text.editor.CompilationUnitEditor;
import com.google.dart.tools.ui.internal.text.editor.DartSourceViewer;
import com.google.dart.tools.ui.theme.ColorTheme;
import com.google.dart.tools.ui.theme.ColorThemeManager;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.program.Program;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.internal.WorkbenchPage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Set;
// TODO(messick): Export strings.
/**
* The preference page for managing color themes.
*
* @see com.github.eclipsecolortheme.preferences.ColorThemePreferencePage
*/
@SuppressWarnings("restriction")
public class ThemePreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
private static final String SAMPLE_CODE_FILE_NAME = "DartSample.dart";
public static IPreferenceStore globalPreferences() {
return /*Activator*/DartToolsPlugin.getDefault().getPreferenceStore();
}
private static String loadPreviewContentFromFile(String filename) {
String line;
String separator = System.getProperty("line.separator"); //$NON-NLS-1$
StringBuffer buffer = new StringBuffer(512);
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(
ThemePreferencePage.class.getResourceAsStream(filename)));
while ((line = reader.readLine()) != null) {
buffer.append(line);
buffer.append(separator);
}
} catch (IOException io) {
Activator.logError(io);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
}
}
}
return buffer.toString();
}
private static String readFile(File file) throws IOException {
Reader in = new BufferedReader(new FileReader(file));
StringBuilder sb = new StringBuilder();
try {
char[] chars = new char[1 << 11];
int length;
while ((length = in.read(chars)) > 0) {
sb.append(chars, 0, length);
}
} finally {
in.close();
}
return sb.toString();
}
private static void setLinkTarget(Link link, final String target) {
link.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event event) {
Program.launch(target);
}
});
}
private static String viewerCode() {
String content = loadPreviewContentFromFile(SAMPLE_CODE_FILE_NAME);
return content;
}
private ColorThemeManager colorThemeManager = new ColorThemeManager();
private Composite container;
private List themeSelectionList;
private Composite themeSelection;
private Composite themeDetails;
private SourceViewer previewViewer;
private Label authorLabel;
private Link websiteLink;
private TemporaryProject project;
private IFile unit;
private CompilationUnitEditor editor;
private DartSourceViewer sourceViewer;
private WorkbenchPage page;
private boolean updating;
/**
* Creates a new color theme preference page.
*/
public ThemePreferencePage() {
setPreferenceStore(globalPreferences());
updating = false;
}
@Override
public void init(IWorkbench workbench) {
}
@Override
public boolean performCancel() {
while (updating) {
waitALittle();
}
colorThemeManager.undoPreview();
try {
if (editor != null) {
editor.close(false);
}
if (project != null) {
project.dispose();
}
} catch (CoreException ex) {
Activator.logError(ex);
}
return super.performCancel();
}
@Override
public boolean performOk() {
while (updating) {
waitALittle();
}
try {
if (editor != null) {
String selectedThemeName = themeSelectionList.getSelection()[0];
getPreferenceStore().setValue("colorTheme", selectedThemeName); // $NON-NLS-1$
colorThemeManager.applyTheme(selectedThemeName);
editor.close(false);
}
if (project != null) {
project.dispose();
}
} catch (PartInitException e) {
Activator.logError(e);
} catch (CoreException ex) {
Activator.logError(ex);
}
return super.performOk();
}
@Override
protected void contributeButtons(Composite parent) {
((GridLayout) parent.getLayout()).numColumns++;
Button button = new Button(parent, SWT.NONE);
button.setText("&Import a theme...");
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
FileDialog dialog = new FileDialog(getShell());
String file = dialog.open();
if (file == null) {
return;
}
ColorTheme theme;
try {
String content = readFile(new File(file));
theme = colorThemeManager.saveTheme(content);
} catch (IOException e) {
theme = null;
}
if (theme != null) {
reloadThemeSelectionList();
themeSelectionList.setSelection(new String[] {theme.getName()});
updateDetails(theme);
} else {
MessageBox box = new MessageBox(getShell(), SWT.OK);
box.setText("Theme not imported");
box.setMessage("This is not a valid theme file.");
box.open();
}
}
});
}
@Override
protected Control createContents(Composite parent) {
container = new Composite(parent, SWT.NONE);
GridData gridData = new GridData();
GridLayout containerLayout = new GridLayout(1, true);
containerLayout.marginWidth = 0;
container.setLayout(containerLayout);
gridData = new GridData(GridData.FILL_BOTH);
themeSelection = new Composite(container, SWT.NONE);
GridLayout themeSelectionLayout = new GridLayout(2, false);
themeSelectionLayout.marginWidth = 0;
themeSelectionLayout.marginHeight = 0;
themeSelection.setLayout(themeSelectionLayout);
themeSelection.setLayoutData(gridData);
gridData = new GridData(GridData.FILL_VERTICAL);
gridData.minimumWidth = 120;
themeSelectionList = new List(themeSelection, SWT.BORDER | SWT.V_SCROLL);
themeSelectionList.setLayoutData(gridData);
fillThemeSelectionList();
gridData = new GridData(GridData.FILL_BOTH);
gridData.verticalAlignment = SWT.TOP;
GridLayout themeDetailsLayout = new GridLayout(1, true);
themeDetailsLayout.marginWidth = 0;
themeDetailsLayout.marginHeight = 0;
themeDetails = new Composite(themeSelection, SWT.NONE);
createPreviewer(themeDetails);
themeDetails.setLayoutData(gridData);
themeDetails.setLayout(themeDetailsLayout);
authorLabel = new Label(themeDetails, SWT.NONE);
GridDataFactory.swtDefaults().grab(true, false).applyTo(authorLabel);
websiteLink = new Link(themeDetails, SWT.NONE);
GridDataFactory.swtDefaults().grab(true, false).applyTo(websiteLink);
themeSelectionList.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event event) {
updateDetails(colorThemeManager.getTheme(themeSelectionList.getSelection()[0]));
}
});
String activeThemeName = getPreferenceStore().getString("colorTheme"); // $NON-NLS-1$
if (colorThemeManager.getTheme(activeThemeName) == null) {
activeThemeName = ColorThemeManager.DEFAULT_THEME_NAME;
}
themeSelectionList.setSelection(new String[] {activeThemeName});
updateDetails(colorThemeManager.getTheme(activeThemeName));
// TODO(messick): Need to think about including this web site.
Link ectLink = new Link(container, SWT.NONE);
ectLink.setText("Download more themes or create your own on "
+ "<a>eclipsecolorthemes.org</a>.");
setLinkTarget(ectLink, "http://eclipsecolorthemes.org");
return container;
}
@Override
protected void performDefaults() {
getPreferenceStore().setToDefault("colorTheme"); // $NON-NLS-1$
colorThemeManager.clearImportedThemes();
reloadThemeSelectionList();
if (editor != null) {
String selectedThemeName = themeSelectionList.getSelection()[0];
getPreferenceStore().setValue("colorTheme", selectedThemeName); // $NON-NLS-1$
colorThemeManager.applyTheme(selectedThemeName);
}
super.performDefaults();
}
private void createPreviewer(Composite parent) {
setup();
Composite previewComp = new Composite(parent, SWT.NONE);
previewComp.setEnabled(false); // After re-parenting, it is too inconsistent to allow mouse clicks
GridLayout layout = new GridLayout();
layout.marginHeight = layout.marginWidth = 0;
previewComp.setLayout(layout);
previewComp.setLayoutData(new GridData(GridData.FILL_BOTH));
Label label = new Label(previewComp, SWT.NONE);
label.setText("Code Editor");
label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
previewViewer = getSourceViewer(previewComp); //new SourceViewer(previewComp, null, SWT.BORDER | SWT.V_SCROLL /*| SWT.H_SCROLL */);
previewViewer.getTextWidget().setSelection(173, 210); // TODO(messick): This is fragile.
previewViewer.setEditable(false);
Control control = previewViewer.getControl();
GridData controlData = new GridData(GridData.FILL_BOTH);
control.setLayoutData(controlData);
}
private void fillThemeSelectionList() {
Set<ColorTheme> themes = colorThemeManager.getThemes();
java.util.List<String> themeNames = new LinkedList<String>();
for (ColorTheme theme : themes) {
themeNames.add(theme.getName());
}
Collections.sort(themeNames, String.CASE_INSENSITIVE_ORDER);
themeNames.remove(ColorThemeManager.DEFAULT_THEME_NAME);
themeNames.add(0, ColorThemeManager.DEFAULT_THEME_NAME);
themeSelectionList.setItems(themeNames.toArray(new String[themeNames.size()]));
}
private SourceViewer getSourceViewer(Composite parent) {
(sourceViewer.getTextWidget()).setParent(parent);
return sourceViewer;
}
private void reloadThemeSelectionList() {
themeSelectionList.removeAll();
fillThemeSelectionList();
themeSelectionList.setSelection(new String[] {ColorThemeManager.DEFAULT_THEME_NAME});
updateDetails(null);
container.pack();
}
private void setup() {
Exception caughtException = null;
String sampleCode = viewerCode();
try {
page = (WorkbenchPage) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
project = new TemporaryProject();
String name = "Temp.dart";
unit = project.setUnitContent(name, sampleCode);
editor = (CompilationUnitEditor) IDE.openEditor(
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(),
unit);
project.getProject().setHidden(true);
name = editor.getPartName();
IEditorReference ref = null;
IEditorReference[] eds = page.getEditorManager().getEditors();
for (IEditorReference r : eds) {
if (r.getPartName() == name) { // intentional identity check
ref = r;
break;
}
}
if (ref != null) {
page.hideEditor(ref);
}
IPreferenceStore store = colorThemeManager.createCombinedPreferenceStore();
editor.setPreferences(store);
sourceViewer = ReflectionUtils.invokeMethod(editor, "getSourceViewer()");
sourceViewer.setPreferenceStore(store);
} catch (CoreException ex) {
caughtException = ex;
} catch (IOException ex) {
caughtException = ex;
}
if (caughtException != null) {
Activator.logError(caughtException);
}
}
private void updateDetails(ColorTheme theme) {
if (editor == null) {
return;
}
if (theme == null) {
// TODO(messick): Fix this awkward UX
themeDetails.setVisible(false);
} else {
try {
updating = true;
authorLabel.setText("Created by " + theme.getAuthor());
String website = theme.getWebsite();
if (website == null || website.length() == 0) {
websiteLink.setVisible(false);
} else {
websiteLink.setText("<a>" + website + "</a>"); // $NON-NLS-1$ // $NON-NLS-2$
for (Listener listener : websiteLink.getListeners(SWT.Selection)) {
websiteLink.removeListener(SWT.Selection, listener);
}
setLinkTarget(websiteLink, website);
websiteLink.setVisible(true);
}
themeDetails.setVisible(true);
colorThemeManager.previewTheme(theme.getName());
editor.reconciled(true, new NullProgressMonitor());
authorLabel.pack();
websiteLink.pack();
} finally {
updating = false;
}
}
}
private void waitALittle() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// ignore it
}
}
}