blob: d7eb196f499129641ebf0c0c696f2f7fa527d460 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.gct.studio.samples;
import com.android.tools.idea.ui.properties.BindingsManager;
import com.android.tools.idea.ui.properties.InvalidationListener;
import com.android.tools.idea.ui.properties.ListenerManager;
import com.android.tools.idea.ui.properties.ObservableValue;
import com.android.tools.idea.ui.properties.core.ObservableBool;
import com.android.tools.idea.ui.properties.core.OptionalProperty;
import com.android.tools.idea.ui.properties.core.StringProperty;
import com.android.tools.idea.ui.properties.core.StringValueProperty;
import com.android.tools.idea.ui.properties.expressions.value.TransformOptionalExpression;
import com.android.tools.idea.ui.properties.swing.TextProperty;
import com.android.tools.idea.ui.validation.Validator;
import com.android.tools.idea.ui.validation.ValidatorPanel;
import com.android.tools.idea.ui.wizard.StudioWizardStepPanel;
import com.android.tools.idea.wizard.model.ModelWizard;
import com.android.tools.idea.wizard.model.ModelWizardStep;
import com.android.utils.HtmlBuilder;
import com.appspot.gsamplesindex.samplesindex.model.Sample;
import com.appspot.gsamplesindex.samplesindex.model.SampleCollection;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.gct.studio.util.GctStudioBundle;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.BrowserHyperlinkListener;
import com.intellij.ui.DoubleClickListener;
import com.intellij.ui.SearchTextField;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.ui.SwingHelper;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import java.awt.event.MouseEvent;
import java.util.Collection;
/**
* SampleBrowserStep is the first page in the Sample Import wizard that allows the user to select a sample to import
*/
public class SampleBrowserStep extends ModelWizardStep<SampleModel> {
private final SampleCollection mySampleList;
private final SampleSetupStep mySampleSetupStep;
private final StudioWizardStepPanel myStudioPanel;
private final ValidatorPanel myValidatorPanel;
private final ListenerManager myListeners = new ListenerManager();
private final BindingsManager myBindings = new BindingsManager();
private Tree mySampleTree;
private SampleImportTreeManager mySampleTreeManager;
private JPanel myPanel;
private JEditorPane myDescriptionPane;
private SearchTextField mySearchBox;
private SamplePreviewPanel mySamplePreviewPanel;
private JBScrollPane mySamplePreviewScrollPanel;
private JPanel myDescriptionPanel;
public SampleBrowserStep(@NotNull SampleModel model, @NotNull SampleCollection sampleList) {
super(model, GctStudioBundle.message("sample.browser.title"));
mySampleList = sampleList;
mySampleTreeManager = new SampleImportTreeManager(mySampleTree, mySampleList);
myValidatorPanel = new ValidatorPanel(this, myPanel);
myStudioPanel = new StudioWizardStepPanel(myValidatorPanel, GctStudioBundle.message("sample.browser.description"));
mySampleSetupStep = new SampleSetupStep(model);
// Create description pane manually, via createHtmlViewer, instead of IntelliJ forms
myDescriptionPane = SwingHelper.createHtmlViewer(false, null, null, null);
myDescriptionPane.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE);
myDescriptionPanel.add(myDescriptionPane);
// for better mouse wheel scrolling
mySamplePreviewScrollPanel.getVerticalScrollBar().setUnitIncrement(16);
}
@NotNull
@Override
protected JComponent getComponent() {
return myStudioPanel;
}
@Override
public JComponent getPreferredFocusComponent() {
return mySearchBox;
}
@NotNull
@Override
protected Collection<? extends ModelWizardStep> createDependentSteps() {
return Lists.newArrayList(mySampleSetupStep);
}
@Override
protected void onWizardStarting(@NotNull ModelWizard.Facade wizard) {
final OptionalProperty<Sample> sample = getModel().sample();
sample.setNullableValue(mySampleTreeManager.getSelectedSample());
mySampleTree.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
sample.setNullableValue(mySampleTreeManager.getSelectedSample());
}
});
DoubleClickListener treeDoubleClicked = new DoubleClickListener() {
@Override
protected boolean onDoubleClick(MouseEvent event) {
return wizard.goForward();
}
};
treeDoubleClicked.installOn(mySampleTree);
myListeners.listenAndFire(sample, new InvalidationListener() {
@Override
public void onInvalidated(@NotNull ObservableValue<?> sender) {
Sample sampleValue = sample.getValueOrNull();
mySamplePreviewPanel.setSample(sampleValue);
mySampleSetupStep.setUrl(sampleValue != null ? sampleValue.getCloneUrl() : "");
}
});
final StringProperty htmlDesc = new StringValueProperty();
myBindings.bind(htmlDesc, new TransformOptionalExpression<Sample, String>("", sample) {
@NotNull
@Override
protected String transform(@NotNull Sample sample) {
HtmlBuilder description = new HtmlBuilder();
// Add sample summary and tags
if (sample.getDescription() != null) {
description.addHtml(sample.getDescription());
}
else {
description.add(GctStudioBundle.message("sample.browser.no.description"));
}
description.newlineIfNecessary().newline();
description.add("Tags: ");
description.add(StringUtil.join(sample.getCategories(), ","));
description.newlineIfNecessary().newline();
// Add "open source in browser" URL
StringBuilder urlBuilder = new StringBuilder();
String cloneUrl = sample.getCloneUrl();
String path = sample.getPath();
urlBuilder.append(cloneUrl);
if (!Strings.isNullOrEmpty(path)) {
urlBuilder.append(cloneUrl.endsWith("/") ? "" : "/");
urlBuilder.append("tree/master/");
urlBuilder.append(SampleModel.trimSlashes(path));
}
description.addLink(GctStudioBundle.message("sample.browse.source"), urlBuilder.toString());
return description.getHtml();
}
});
myListeners.listenAndFire(htmlDesc, new InvalidationListener() {
@Override
public void onInvalidated(@NotNull ObservableValue<?> sender) {
SwingHelper.setHtml(myDescriptionPane, htmlDesc.get(), UIUtil.getLabelForeground());
}
});
TextProperty searchValue = new TextProperty(mySearchBox.getTextEditor());
myListeners.receiveAndFire(searchValue, keyword -> mySampleTreeManager.filterTree(keyword));
myValidatorPanel.registerValidator(sample, new Validator<Optional<Sample>>() {
@NotNull
@Override
public Result validate(@NotNull Optional<Sample> sample) {
return (!sample.isPresent() ? new Result(Severity.ERROR, GctStudioBundle.message("sample.browser.please.select")) : Result.OK);
}
});
}
@NotNull
@Override
protected ObservableBool canGoForward() {
return myValidatorPanel.hasErrors().not();
}
@Override
public void dispose() {
myBindings.releaseAll();
myListeners.releaseAll();
}
}