/*******************************************************************************
* Copyright 2011 Google Inc. All Rights Reserved.
*
* 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
*
* 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.gdt.eclipse.designer.model.property.css;
import com.google.common.collect.Maps;
import com.google.gdt.eclipse.designer.Activator;
import com.google.gdt.eclipse.designer.common.Constants;
import org.eclipse.wb.internal.core.DesignerPlugin;
import org.eclipse.wb.internal.core.utils.dialogfields.DialogField;
import org.eclipse.wb.internal.core.utils.dialogfields.StringItemDialogField;
import org.eclipse.wb.internal.core.utils.dialogfields.StringItemDialogField.IStringItemAdapter;
import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;
import org.eclipse.wb.internal.core.utils.ui.GridDataFactory;
import org.eclipse.wb.internal.core.utils.ui.GridLayoutFactory;
import org.eclipse.wb.internal.css.dialogs.style.StyleEditDialog;
import org.eclipse.wb.internal.css.dialogs.style.StyleEditOptions;
import org.eclipse.wb.internal.css.model.CssDocument;
import org.eclipse.wb.internal.css.model.CssRuleNode;
import org.eclipse.wb.internal.css.parser.CssEditContext;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.apache.commons.lang.StringUtils;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Dialog for editing CSS file.
*
* @author sablin_aa
* @author scheglov_ke
*/
public class StylesEditComposite
extends
org.eclipse.wb.internal.css.editors.multi.StylesEditComposite {
private final List<IFile> m_files;
////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
////////////////////////////////////////////////////////////////////////////
public StylesEditComposite(Composite parent,
List<IFile> files,
int style,
ICommandExceptionHandler exceptionHandler) {
super(parent, style, exceptionHandler);
m_files = files;
m_filesViewer.setInput(m_files);
}
////////////////////////////////////////////////////////////////////////////
//
// GUI
//
////////////////////////////////////////////////////////////////////////////
protected StringItemDialogField m_searchField;
protected ListViewer m_filesViewer;
@Override
protected void createRulesGroup(Composite parent) {
Composite explorerComposite = new Composite(parent, SWT.NONE);
GridLayoutFactory.create(explorerComposite).columns(1);
GridDataFactory.create(explorerComposite).grab().fill();
createFileGroup(explorerComposite);
super.createRulesGroup(explorerComposite);
}
/**
* Create CSS-file viewer.
*/
protected void createFileGroup(Composite parent) {
final int columns = 3;
Group filesGroup = new Group(parent, SWT.NONE);
filesGroup.setText("CSS files referenced from HTML");
GridLayoutFactory.create(filesGroup).columns(columns);
GridDataFactory.create(filesGroup).grabH().fill();
// search
{
m_searchField = new StringItemDialogField(new IStringItemAdapter() {
public void itemPressed(DialogField field) {
searchRule();
}
});
m_searchField.setLabelText("&Search CSS rule in files:");
m_searchField.setItemImage(DesignerPlugin.getImage("find.png"));
m_searchField.setItemToolTip("Search");
m_searchField.doFillIntoGrid(filesGroup, 3);
}
{
m_filesViewer = new ListViewer(filesGroup, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
GridDataFactory.create(m_filesViewer.getControl()).spanH(columns).grab().fill().hintC(80, 6);
// set providers and input
{
m_filesViewer.setContentProvider(new DefaultStructuredContextProvider() {
@SuppressWarnings("unchecked")
public Object[] getElements(Object inputElement) {
List<IFile> files = (List<IFile>) inputElement;
return files.toArray();
}
});
m_filesViewer.setLabelProvider(new LabelProvider() {
@Override
public String getText(Object element) {
IFile file = (IFile) element;
return file.getFullPath().toPortableString();
}
});
}
// add selection listener
m_filesViewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
IFile file = (IFile) ((IStructuredSelection) event.getSelection()).getFirstElement();
setContext(getContext(file));
{
Object firstElement = m_rulesViewer.getElementAt(0);
if (firstElement != null) {
m_rulesViewer.setSelection(new StructuredSelection(firstElement));
}
}
}
});
}
}
public void initializeState(String selectionValue) {
// always show rules of first CSS file
if (!m_files.isEmpty()) {
m_initialValue = selectionValue;
// locate initial CSS style
if (!StringUtils.isEmpty(selectionValue)) {
if (locateRuleInFiles(selectionValue)) {
return;
}
}
m_filesViewer.setSelection(new StructuredSelection(m_files.get(0)));
}
}
////////////////////////////////////////////////////////////////////////////
//
// GUI: Edit actions
//
////////////////////////////////////////////////////////////////////////////
@Override
protected StyleEditDialog getStyleEditDialog(CssRuleNode rule) {
StyleEditDialog dialog = super.getStyleEditDialog(rule);
StyleEditOptions options = dialog.getOptions();
IPreferenceStore store = Activator.getStore();
options.useNamedColors = store.getBoolean(Constants.P_CSS_USE_NAMED_COLORS);
return dialog;
}
protected String m_initialValue = null;
@Override
protected String getDefaultNewSelector() {
if (StringUtils.isEmpty(m_initialValue)) {
return super.getDefaultNewSelector();
} else {
if (isSelectorExists(m_initialValue)) {
return m_initialValue + "-new";
} else {
return m_initialValue;
}
}
}
protected final boolean isSelectorExists(String selector) {
CssDocument document = getContext().getCssDocument();
List<CssRuleNode> rules = document.getRules();
for (CssRuleNode rule : rules) {
String ruleSelector = rule.getSelector().getValue();
if (selector.equalsIgnoreCase(ruleSelector)) {
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////
//
// Access
//
////////////////////////////////////////////////////////////////////////////
/**
* Returns {@link IFile} for selected CSS file.
*/
protected IFile getSelectedFile() {
return (IFile) ((IStructuredSelection) m_filesViewer.getSelection()).getFirstElement();
}
/**
* Returns {@link CssEditContext} for selected CSS file.
*/
protected CssEditContext getSelectedContext() {
return getContext(getSelectedFile());
}
////////////////////////////////////////////////////////////////////////////
//
// Contexts
//
////////////////////////////////////////////////////////////////////////////
private final Map<IFile, CssEditContext> m_fileToContext = Maps.newHashMap();
/**
* Returns {@link CssEditContext} for given file.
*/
public CssEditContext getContext(IFile file) {
try {
CssEditContext context = m_fileToContext.get(file);
if (context == null) {
context = new CssEditContext(file);
m_fileToContext.put(file, context);
}
return context;
} catch (Throwable e) {
throw ReflectionUtils.propagate(e);
}
}
/**
* Saves and removes {@link CssEditContext}'s.
*/
public void saveContexts() throws CoreException {
for (Iterator<CssEditContext> I = m_fileToContext.values().iterator(); I.hasNext();) {
CssEditContext context = I.next();
context.commit();
context.disconnect();
I.remove();
}
}
////////////////////////////////////////////////////////////////////////////
//
// Utils
//
////////////////////////////////////////////////////////////////////////////
protected boolean locateRuleInFiles(String ruleValue) {
for (IFile file : m_files) {
if (locateRuleInFile(file, ruleValue)) {
return true;
}
}
return false;
}
protected boolean locateRuleInFile(IFile file, String searchValue) {
CssEditContext context = getContext(file);
List<CssRuleNode> rules = context.getCssDocument().getRules();
for (CssRuleNode rule : rules) {
String ruleValue = rule.getSelector().getValue();
if (ruleValue.equals(searchValue)
|| ruleValue.startsWith(".")
&& ruleValue.substring(1).equals(searchValue)) {
// select file
m_filesViewer.setSelection(new StructuredSelection(file));
// select rule
RuleWrapper wrapper = new RuleWrapper(rule);
m_rulesViewer.setSelection(new StructuredSelection(wrapper), true);
return true;
}
}
return false;
}
private void searchRule() {
String searchText = m_searchField.getText();
if (!StringUtils.isEmpty(searchText)) {
if (!locateRuleInFiles(searchText)) {
MessageDialog.openWarning(getShell(), "Search", "Rule selector \""
+ searchText
+ "\" not found.");
}
}
}
}