/*
* Copyright 2012 Andrew C. Dvorak.
*
* 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 net.andydvorak.intellij.lessc.ui.configurable;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.NamedConfigurable;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.AnActionButton;
import com.intellij.ui.AnActionButtonRunnable;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.table.JBTable;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.util.containers.OrderedSet;
import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.ui.ListTableModel;
import net.andydvorak.intellij.lessc.LessManager;
import net.andydvorak.intellij.lessc.ui.messages.UIBundle;
import net.andydvorak.intellij.lessc.state.CssDirectory;
import net.andydvorak.intellij.lessc.state.LessProfile;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class LessProfileConfigurableForm extends NamedConfigurable<LessProfile> {
private static final int SIZEPOLICY_FILL_ALL = GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW;
private static final GridConstraints GRIDCONSTRAINTS_FILL_ALL = new GridConstraints(
/* row = */ 0,
/* col = */ 0,
/* rowSpan = */ 1,
/* colSpan = */ 1,
/* anchor = */ GridConstraints.ANCHOR_CENTER,
/* fill = */ GridConstraints.FILL_BOTH,
/* HSizePolicy = */ SIZEPOLICY_FILL_ALL,
/* VSizePolicy = */ SIZEPOLICY_FILL_ALL,
/* minimumSize = */ null,
/* preferredSize = */ null,
/* maximumSize = */ null
);
private final Project project;
private final LessManager lessManager;
// TODO: Clone profile HERE instead of in LessProfilesPanel (or wherever they're currently being cloned)
private final LessProfile lessProfile;
private final LessProfile currentLessProfileUiState;
private final int lessProfileId;
private String lessProfileName;
private final LessProfilesPanel lessProfilesPanel;
private boolean modified;
private JPanel rootPanel;
private JCheckBox compileAutomaticallyOnSaveCheckBox;
private JCheckBox compressCssCheckbox;
private JPanel cssDirPanel;
private JPanel lessDirPanelWrap;
private JPanel lessDirPanel;
private JButton resetPromptsButton;
private JTextField includePatternTextField;
private JTextField excludePatternTextField;
private JPanel inputPanel;
private JPanel buttonPanel;
private JPanel outputPanel;
private TextFieldWithBrowseButton lessDirTextField;
private final JBTable profileMappingTable;
private final ListTableModel<CssDirectory> profileMappingModel;
private List<CssDirectory> cssDirectories;
public LessProfileConfigurableForm(final Project project, final LessProfile lessProfile, final LessProfilesPanel lessProfilesPanel, final Runnable updater) {
super(true, updater);
this.project = project;
this.lessManager = LessManager.getInstance(project);
this.lessProfile = lessProfile;
this.currentLessProfileUiState = new LessProfile(lessProfile.getId(), lessProfile);
this.lessProfileId = lessProfile.getId();
this.lessProfileName = lessProfile.getName();
this.lessProfilesPanel = lessProfilesPanel;
cssDirectories = new ArrayList<CssDirectory>();
// Deep clone
for (final CssDirectory cssDirectory : lessProfile.getCssDirectories()) {
cssDirectories.add(new CssDirectory(cssDirectory));
}
final ColumnInfo[] columns = { new CssDirectoryColumn() };
profileMappingModel = new ListTableModel<CssDirectory>(columns, cssDirectories, 0);
profileMappingTable = new JBTable(profileMappingModel);
}
public LessProfile getCurrentState() {
// Forms that are not being displayed (i.e., all forms EXCEPT the currently visible one)
// have null input fields.
if (this.lessDirTextField == null) {
return lessProfile;
}
final LessProfile profile = new LessProfile(lessProfileId, lessProfile);
profile.setLessDirPath(this.lessDirTextField.getText());
profile.setIncludePattern(this.includePatternTextField.getText());
profile.setExcludePattern(this.excludePatternTextField.getText());
profile.setCssDirectories(new ArrayList<CssDirectory>(cssDirectories));
profile.setCompileAutomatically(this.compileAutomaticallyOnSaveCheckBox.isSelected());
profile.setCompressOutput(this.compressCssCheckbox.isSelected());
return profile;
}
public void setDisplayName(final String displayName) {
lessProfile.setName(displayName);
}
public LessProfile getEditableObject() {
return lessProfile;
}
public String getBannerSlogan() {
return lessProfile.getName();
}
public JComponent createOptionsPanel() {
lessDirTextField = new TextFieldWithBrowseButtonImpl(project, UIBundle.message("file.chooser.less.title"));
lessDirPanel.add(lessDirTextField, GRIDCONSTRAINTS_FILL_ALL);
profileMappingTable.addMouseListener(new MouseListener() {
@Override public void mouseClicked(final MouseEvent mouseEvent) {
if (mouseEvent.getClickCount() == 2 && !mouseEvent.isConsumed()) {
mouseEvent.consume();
editRow();
}
}
@Override public void mousePressed(final MouseEvent mouseEvent) {}
@Override public void mouseReleased(final MouseEvent mouseEvent) {}
@Override public void mouseEntered(final MouseEvent mouseEvent) {}
@Override public void mouseExited(final MouseEvent mouseEvent) {}
});
final ToolbarDecorator decorator = ToolbarDecorator.createDecorator(profileMappingTable);
decorator
.setAddAction(new AnActionButtonRunnable() {
@Override
public void run(final AnActionButton button) {
addRow();
}
})
.setEditAction(new AnActionButtonRunnable() {
@Override
public void run(final AnActionButton button) {
editRow();
}
})
.disableUpDownActions();
cssDirPanel.add(decorator.createPanel(), GRIDCONSTRAINTS_FILL_ALL);
resetPromptsButton.setMnemonic('r');
resetPromptsButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent actionEvent) {
lessManager.getState().resetPrompts();
lessProfilesPanel.setPromptButtonsEnabled(false);
}
});
resetPromptsButton.setEnabled(!lessManager.getState().hasDefaultPromptSettings());
return rootPanel;
}
public void setPromptButtonEnabled(final boolean enabled) {
resetPromptsButton.setEnabled(enabled);
}
private void addRow() {
final String path = promptForFilePath();
if (StringUtil.isNotEmpty(path)) {
profileMappingModel.addRow(new CssDirectory(path));
removeDuplicateRows();
}
}
private void editRow() {
if (profileMappingTable.getSelectedRowCount() != 1) {
return;
}
final CssDirectory cssDirectory = (CssDirectory) profileMappingModel.getItem(profileMappingTable.getSelectedRow());
if (cssDirectory == null)
return;
final String newPath = promptForFilePath(cssDirectory.getPath());
if (StringUtil.isNotEmpty(newPath)) {
cssDirectory.setPath(newPath);
removeDuplicateRows();
}
}
private void removeDuplicateRows() {
final Set<CssDirectory> withoutDups = new OrderedSet<CssDirectory>();
withoutDups.addAll(profileMappingModel.getItems());
cssDirectories = new ArrayList<CssDirectory>(withoutDups);
profileMappingModel.setItems(cssDirectories);
}
@Nullable
private String promptForFilePath() {
return promptForFilePath(null);
}
@Nullable
private String promptForFilePath(final @Nullable String initial) {
@NotNull
final String initialNN = StringUtils.defaultString(initial);
final FileChooserDescriptor d = getFileChooserDescriptor();
final Project defaultProject = ProjectManager.getInstance().getDefaultProject();
final VirtualFile initialFile = StringUtil.isNotEmpty(initialNN) ? LocalFileSystem.getInstance().findFileByPath(initialNN) : null;
final VirtualFile file = project != null ? FileChooser.chooseFile(d, project, initialFile) : FileChooser.chooseFile(d, defaultProject, initialFile);
String path = null;
if (file != null) {
path = file.getPresentableUrl();
if (SystemInfo.isWindows && path.length() == 2 && Character.isLetter(path.charAt(0)) && path.charAt(1) == ':') {
path += "\\"; // make path absolute
}
}
return path;
}
private FileChooserDescriptor getFileChooserDescriptor() {
final FileChooserDescriptor d = new FileChooserDescriptor(false, true, false, false, false, false);
d.setTitle(UIBundle.message("file.chooser.css.title"));
d.setShowFileSystemRoots(true);
return d;
}
@Nls
public String getDisplayName() {
return lessProfile.getName();
}
@Nullable
@NonNls
public String getHelpTopic() {
return null;
}
// TODO: Clone profile HERE instead of in LessProfilesPanel (or wherever they're currently being cloned)
public boolean isModified() {
currentLessProfileUiState.copyFrom(getCurrentState());
return modified ||
!Comparing.strEqual(lessProfileName, lessProfile.getName()) ||
!Comparing.strEqual(lessDirTextField.getText(), lessProfile.getLessDirPath()) ||
!Comparing.strEqual(includePatternTextField.getText(), lessProfile.getIncludePattern()) ||
!Comparing.strEqual(excludePatternTextField.getText(), lessProfile.getExcludePattern()) ||
!Comparing.equal(compileAutomaticallyOnSaveCheckBox.isSelected(), lessProfile.isCompileAutomatically()) ||
!Comparing.equal(compressCssCheckbox.isSelected(), lessProfile.isCompressOutput()) ||
!Comparing.equal(cssDirectories, lessProfile.getCssDirectories());
}
// TODO: Clone profile HERE instead of in LessProfilesPanel (or wherever they're currently being cloned)
public void apply() throws ConfigurationException {
lessProfile.setLessDirPath(lessDirTextField.getText());
lessProfile.setIncludePattern(includePatternTextField.getText());
lessProfile.setExcludePattern(excludePatternTextField.getText());
lessProfile.setCompileAutomatically(compileAutomaticallyOnSaveCheckBox.isSelected());
lessProfile.setCompressOutput(compressCssCheckbox.isSelected());
lessProfile.setCssDirectories(new ArrayList<CssDirectory>(cssDirectories));
LessManager.getInstance(project).putProfile(lessProfileId, lessProfile);
lessProfileName = lessProfile.getName();
modified = false;
currentLessProfileUiState.copyFrom(getCurrentState());
}
// TODO: Clone profile HERE instead of in LessProfilesPanel (or wherever they're currently being cloned)
public void reset() {
lessProfileName = lessProfile.getName();
lessDirTextField.setText(lessProfile.getLessDirPath());
includePatternTextField.setText(lessProfile.getIncludePattern());
excludePatternTextField.setText(lessProfile.getExcludePattern());
compileAutomaticallyOnSaveCheckBox.setSelected(lessProfile.isCompileAutomatically());
compressCssCheckbox.setSelected(lessProfile.isCompressOutput());
currentLessProfileUiState.copyFrom(getCurrentState());
}
public void disposeUIResources() {
}
public void setModified(final boolean modified) {
this.modified = modified;
}
}