/*******************************************************************************
* Copyright (c) 2013, 2016-2017 Pivotal, 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
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.wizard;
import java.util.List;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.LocationKind;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Shell;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.core.ISpringBootProject;
import org.springframework.ide.eclipse.boot.core.SpringBootCore;
import org.springframework.ide.eclipse.boot.core.initializr.InitializrService;
import org.springframework.ide.eclipse.boot.core.initializr.InitializrServiceSpec.Dependency;
import org.springframework.ide.eclipse.boot.livexp.ui.DynamicSection;
import org.springframework.ide.eclipse.boot.wizard.CheckBoxesSection.CheckBoxModel;
import org.springsource.ide.eclipse.commons.livexp.ui.ChooseOneSectionCombo;
import org.springsource.ide.eclipse.commons.livexp.ui.CommentSection;
import org.springsource.ide.eclipse.commons.livexp.ui.DialogWithSections;
import org.springsource.ide.eclipse.commons.livexp.ui.GroupSection;
import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageSection;
import org.springsource.ide.eclipse.commons.livexp.util.Parser;
import com.google.common.collect.ImmutableList;
/**
* @author Kris De Volder
*/
public class EditStartersDialog extends DialogWithSections {
private static final int NUM_DEP_COLUMNS = 3;
private static final Point DEPENDENCY_SECTION_SIZE = new Point(SWT.DEFAULT, 300);
static final String NO_CONTENT_AVAILABLE = "No content available.";
private static final int SAVE_BUTTON = 256;
private static final String AUTO_SAVE_DIRTY_POM = "EditStartersDialog.pom.autosave";
public InitializrFactoryModel<EditStartersModel> model;
private boolean firstSearchBox = true; //becomes false after the searchBox is created first time.
private long openingTime;
public EditStartersDialog(InitializrFactoryModel<EditStartersModel> model, Shell shell) {
super("Edit Spring Boot Starters", model, shell);
this.setShellStyle(SWT.RESIZE | getShellStyle());
this.model = model;
}
@Override
protected List<WizardPageSection> createSections() throws CoreException {
openingTime = System.currentTimeMillis();
ChooseOneSectionCombo<String> comboSection = new ChooseOneSectionCombo<>(this, model.getServiceUrlField(), model.getUrls()).grabHorizontal(true);
comboSection.allowTextEdits(Parser.IDENTITY);
DynamicSection dynamicSection = new DynamicSection(this, model.getModel().apply((dynamicModel) -> {
if (dynamicModel != null) {
return createDynamicContents(dynamicModel);
}
return new CommentSection(this, NO_CONTENT_AVAILABLE);
} ));
return ImmutableList.of(comboSection, dynamicSection);
}
protected WizardPageSection createDynamicContents(EditStartersModel model) {
EditStartersDialog owner = EditStartersDialog.this;
GroupSection sections = new GroupSection(owner, null)
.grabVertical(true);
// sections.add(new CommentSection(this, "Project: "+model.getProjectName()));
List<CheckBoxModel<Dependency>> mostpopular = model.getFrequentlyUsedDependencies(4*NUM_DEP_COLUMNS);
if (!mostpopular.isEmpty()) {
sections.addSections(new GroupSection(this, "Frequently Used",
new CheckBoxesSection<>(this, mostpopular).columns(NUM_DEP_COLUMNS)
));
}
sections.addSections(
new GroupSection(owner, null,
//Column 1:
new GroupSection(owner, null,
new CommentSection(owner, "Available:"),
searchBox(model),
new GroupSection(owner, "",
new FilteredDependenciesSection(owner, model.dependencies, model.searchBox.getFilter())
.sizeHint(DEPENDENCY_SECTION_SIZE)
)
.noMargins(true)
.grabVertical(true)
).grabVertical(true),
//Column 2:
new GroupSection(owner, null,
new CommentSection(owner, "Selected:"),
new GroupSection(owner, "",
new SelectedDependenciesSection(owner, model.dependencies)
.sizeHint(DEPENDENCY_SECTION_SIZE)
)
.noMargins(true)
.grabVertical(true)
).grabVertical(true)
)
.grabVertical(true)
.columns(2)
);
return sections;
}
// private Color getSystemColor(int c) {
// Display display = Display.getDefault();
// LiveVariable<Color> color = new LiveVariable<>();
// display.syncExec(() -> {
// color.setValue(display.getSystemColor(c));
// });
// return color.getValue();
// }
private SearchBoxSection searchBox(EditStartersModel model) {
return new SearchBoxSection(this, model.searchBox.getText()) {
@Override
protected String getSearchHint() {
return "Type to search dependencies";
}
}
.grabFocus(searchBoxShouldGrabFocus());
}
private boolean searchBoxShouldGrabFocus() {
System.out.println("firstSeachBox = "+this.firstSearchBox);
try {
return this.firstSearchBox && isCloseToOpeningTime();
} finally {
this.firstSearchBox = false;
}
}
private boolean isCloseToOpeningTime() {
System.out.println("openingTime = "+openingTime);
long age = System.currentTimeMillis() - openingTime;
System.out.println("age = "+age);
return age < 3000;
}
public static int openFor(IProject selectedProject, Shell shell) throws CoreException {
IPreferenceStore preferenceStore = BootActivator.getDefault().getPreferenceStore();
ITextFileBuffer dirtyPom = getDirtyPomFile(selectedProject);
if (dirtyPom!=null) {
if (!savePom(selectedProject, shell, dirtyPom)) {
return MessageDialog.CANCEL;
}
}
InitializrFactoryModel<EditStartersModel> fmodel = new InitializrFactoryModel<>((url) -> {
if (url!=null) {
InitializrService initializr = InitializrService.create(BootActivator.getUrlConnectionFactory(), url);
SpringBootCore core = new SpringBootCore(initializr);
return new EditStartersModel(
selectedProject,
core,
preferenceStore
);
}
return null;
});
return new EditStartersDialog(fmodel, shell).open();
}
/**
* Ask the user if its okay to save the pom. If they say yes, save it.
* <p>
* @return true if the pom was saved.
*/
private static boolean savePom(IProject selectedProject, Shell shell, ITextFileBuffer dirtyPom) throws CoreException {
IPreferenceStore prefs = BootActivator.getDefault().getPreferenceStore();
boolean autoSave = prefs.getBoolean(AUTO_SAVE_DIRTY_POM);
boolean save = autoSave || askSavePom(selectedProject, shell, dirtyPom);
if (save) {
dirtyPom.commit(new NullProgressMonitor(), true);
}
return save;
}
private static boolean askSavePom(IProject selectedProject, Shell shell, ITextFileBuffer dirtyPom)
throws CoreException {
MessageDialogWithToggle dlg = new MessageDialogWithToggle(shell,
"Pom file needs saving!", null,
"The pom file for project '"+selectedProject.getName()+"' has unsaved edits. The 'Edit Starters' dialog makes changes to "
+ "the pom file on disk. Do you want to save it now?",
MessageDialog.WARNING,
new String[] { "Cancel", "Save Pom" },
1,
"Don't ask again and save automatically in the future",
false
);
int code = dlg.open();
if (code==SAVE_BUTTON) { //The Dialog has a weird logic but that's the number it gets for our 'save' button based on its position.
if (dlg.getToggleState()) {
BootActivator.getDefault().getPreferenceStore().setValue(AUTO_SAVE_DIRTY_POM, true);
}
return true;
}
return false;
}
private static ITextFileBuffer getDirtyPomFile(IProject project) {
if (project.isAccessible()) {
IFile pomFile = project.getFile("pom.xml");
ITextFileBuffer buffer = FileBuffers.getTextFileBufferManager().getTextFileBuffer(pomFile.getFullPath(), LocationKind.IFILE);
if (buffer!=null && buffer.isDirty()) {
return buffer;
}
}
return null;
}
}