filter images by tags

This commit is contained in:
Anna Kozlova
2017-05-12 12:09:23 +03:00
parent 512b5011be
commit 44ab710d61
15 changed files with 488 additions and 16 deletions
+10 -1
View File
@@ -22,6 +22,8 @@
<fileLookupInfoProvider implementation="org.intellij.images.completion.ImageLookupInfoProvider"/>
<documentationProvider implementation="org.intellij.images.fileTypes.ImageDocumentationProvider"/>
<projectService serviceInterface="org.intellij.images.search.ImageTagManager"
serviceImplementation="org.intellij.images.search.ImageTagManager"/>
<statistics.usagesCollector implementation="org.intellij.images.statistics.BackgroundImagesUsageCollector"/>
</extensions>
@@ -132,7 +134,12 @@
id="Images.Thumbnails.FilterByTheme"
text="Filter by Theme"
description="Filter images by theme"/>
<action class="org.intellij.images.thumbnail.actions.FilterByTagComboBoxAction"
internal="true"
id="Images.Thumbnails.FilterByTag"
text="Filter by Tag"
description="Filter images by tag"/>
<action class="org.intellij.images.thumbnail.actions.HideThumbnailsAction"
id="Images.Thumbnails.Hide"
text="Hide"
@@ -146,6 +153,8 @@
id="Images.Thumbnails.EnterAction">
<keyboard-shortcut first-keystroke="ENTER" keymap="$default"/>
</action>
<action class="org.intellij.images.thumbnail.actions.AddTagGroup" id="Images.Thumbnails.AddTagGroup" text="Add Tag"/>
<action class="org.intellij.images.thumbnail.actions.RemoveTagGroup" id="Images.Thumbnails.RemoveTagGroup" text="Remove Tag"/>
<separator/>
<reference id="Images.ThumbnailsToolbar"/>
<separator/>
@@ -0,0 +1,111 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* 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 org.intellij.images.search;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.xmlb.annotations.AbstractCollection;
import com.intellij.util.xmlb.annotations.MapAnnotation;
import com.intellij.util.xmlb.annotations.Property;
import com.intellij.util.xmlb.annotations.Tag;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.stream.Collectors;
@State(name = "ImageTags", storages = @Storage("imageTags.xml"))
public class ImageTagManager implements PersistentStateComponent<ImageTagManager.State> {
private State myState = new State();
public static ImageTagManager getInstance(Project project) {
return ServiceManager.getService(project, ImageTagManager.class);
}
public boolean hasTag(String tag, VirtualFile file) {
State.Files files = myState.myTags.get(tag);
return files != null && files.contains(file.getPath());
}
public void addTag(String tag, VirtualFile file) {
State.Files files = myState.myTags.get(tag);
if (files == null) {
files = new State.Files();
myState.myTags.put(tag, files);
}
files.add(file.getPath());
}
public void removeTag(String tag, VirtualFile file) {
State.Files files = myState.myTags.get(tag);
if (files != null) {
files.remove(file.getPath());
}
}
public List<String> getTags(VirtualFile file) {
return myState.myTags.keySet()
.stream()
.filter(tag -> hasTag(tag, file))
.collect(Collectors.toList());
}
public List<String> getAllTags() {
return new ArrayList<>(myState.myTags.keySet());
}
@Nullable
@Override
public State getState() {
return myState;
}
@Override
public void loadState(State state) {
myState = state;
}
public static class State {
@Property(surroundWithTag = false)
@MapAnnotation(surroundKeyWithTag = false, surroundWithTag = false,
entryTagName = "tag", keyAttributeName = "name", valueAttributeName = "values")
public final Map<String, Files> myTags = new LinkedHashMap<>();
@Property(surroundWithTag = false)
@Tag("files")
public static class Files {
@Property(surroundWithTag = false)
@AbstractCollection(surroundWithTag = false, elementTag = "file", elementValueAttribute = "path")
public final Set<String> myFiles = new LinkedHashSet<>();
public void remove(String path) {
myFiles.remove(path);
}
public void add(String path) {
myFiles.add(path);
}
public boolean contains(String path) {
return myFiles.contains(path);
}
}
}
}
@@ -0,0 +1,51 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* 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 org.intellij.images.search;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.intellij.images.thumbnail.ThumbnailView;
import org.intellij.images.thumbnail.actions.Filter;
public class TagFilter implements Filter {
private String myTag;
private ImageTagManager myManager;
public TagFilter(String tag, ImageTagManager manager) {
myTag = tag;
myManager = manager;
}
@Override
public String getDisplayName() {
return myTag;
}
@Override
public boolean accepts(VirtualFile file) {
return myManager.hasTag(myTag, file);
}
@Override
public boolean isApplicableToProject(Project project) {
return true;
}
@Override
public void setFilter(ThumbnailView view) {
view.setTagFilter(this);
}
}
@@ -20,6 +20,7 @@ import com.intellij.openapi.actionSystem.DataKey;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.intellij.images.ImagesBundle;
import org.intellij.images.search.TagFilter;
import org.intellij.images.thumbnail.actions.ThemeFilter;
import org.intellij.images.ui.ImageComponentDecorator;
import org.jetbrains.annotations.NotNull;
@@ -82,4 +83,12 @@ public interface ThumbnailView extends Disposable, ImageComponentDecorator {
*/
@Nullable
ThemeFilter getFilter();
void setTagFilter(TagFilter filter);
/**
* null means all files accepted
*/
@Nullable
TagFilter getTagFilter();
}
@@ -0,0 +1,90 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* 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 org.intellij.images.thumbnail.actions;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import org.intellij.images.search.ImageTagManager;
import org.intellij.images.thumbnail.ThumbnailView;
import org.intellij.images.thumbnail.actionSystem.ThumbnailViewActionUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
public class AddTagGroup extends ActionGroup {
public AddTagGroup() {
setPopup(true);
}
@NotNull
@Override
public AnAction[] getChildren(@Nullable AnActionEvent e) {
if (e == null) return EMPTY_ARRAY;
Project project = e.getProject();
ImageTagManager tagManager = ImageTagManager.getInstance(project);
List<String> tags = tagManager.getAllTags();
int tagsNumber = tags.size();
AnAction[] actions = new AnAction[tagsNumber + 1];
for (int i = 0; i < tagsNumber; i++) {
String tag = tags.get(i);
actions[i] = new AnAction(tag) {
@Override
public void actionPerformed(AnActionEvent e) {
ThumbnailView view = ThumbnailViewActionUtil.getVisibleThumbnailView(e);
if (view != null) {
for (VirtualFile file : view.getSelection()) {
tagManager.addTag(tag, file);
}
}
}
@Override
public void update(AnActionEvent e) {
e.getPresentation().setEnabledAndVisible(false);
ThumbnailView view = ThumbnailViewActionUtil.getVisibleThumbnailView(e);
if (view != null) {
e.getPresentation().setEnabledAndVisible(Arrays.stream(view.getSelection()).noneMatch(file -> tagManager.hasTag(tag, file)));
}
}
};
}
actions[tagsNumber] = new AnAction("New Tag") {
@Override
public void actionPerformed(AnActionEvent e) {
ThumbnailView view = ThumbnailViewActionUtil.getVisibleThumbnailView(e);
if (view != null) {
VirtualFile[] selection = view.getSelection();
if (selection.length > 0) {
String tag = Messages.showInputDialog("", "New Tag Name", null);
if (tag != null) {
for (VirtualFile file : selection) {
tagManager.addTag(tag, file);
}
}
}
}
}
};
return actions;
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* 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 org.intellij.images.thumbnail.actions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.intellij.images.thumbnail.ThumbnailView;
public interface Filter {
String getDisplayName();
boolean accepts(VirtualFile file);
boolean isApplicableToProject(Project project);
void setFilter(ThumbnailView view);
}
@@ -0,0 +1,74 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* 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 org.intellij.images.thumbnail.actions;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.ex.ComboBoxAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.intellij.images.search.ImageTagManager;
import org.intellij.images.search.TagFilter;
import org.intellij.images.thumbnail.ThumbnailView;
import org.intellij.images.thumbnail.actionSystem.ThumbnailViewActionUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
public final class FilterByTagComboBoxAction extends ComboBoxAction {
public void update(final AnActionEvent e) {
Project project = e.getProject();
ThumbnailView view = ThumbnailViewActionUtil.getVisibleThumbnailView(e);
ImageTagManager tagManager = ImageTagManager.getInstance(project);
e.getPresentation().setVisible(view != null && !tagManager.getAllTags().isEmpty());
TagFilter filter = view != null ? view.getTagFilter() : null;
e.getPresentation().setText(filter == null ? "All" : filter.getDisplayName());
}
@NotNull
@Override
protected DefaultActionGroup createPopupActionGroup(JComponent button) {
DefaultActionGroup group = new DefaultActionGroup();
Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(button));
ImageTagManager tagManager = ImageTagManager.getInstance(project);
group.add(new FilterImagesAction(new TagFilter("All", tagManager) {
@Override
public String getDisplayName() {
return "All";
}
@Override
public boolean accepts(VirtualFile file) {
return true;
}
@Override
public boolean isApplicableToProject(Project project) {
return true;
}
}));
for (String tag : tagManager.getAllTags()) {
group.add(new FilterImagesAction(new TagFilter(tag, tagManager)));
}
return group;
}
}
@@ -45,7 +45,7 @@ public final class FilterByThemeComboBoxAction extends ComboBoxAction {
@Override
protected DefaultActionGroup createPopupActionGroup(JComponent button) {
DefaultActionGroup group = new DefaultActionGroup();
group.add(new FilterByThemeAction(new ThemeFilter() {
group.add(new FilterImagesAction(new ThemeFilter() {
@Override
public String getDisplayName() {
return "All";
@@ -60,9 +60,14 @@ public final class FilterByThemeComboBoxAction extends ComboBoxAction {
public boolean isApplicableToProject(Project project) {
return true;
}
@Override
public void setFilter(ThumbnailView view) {
view.setFilter(this);
}
}));
for (ThemeFilter filter : ThemeFilter.EP_NAME.getExtensions()) {
group.add(new FilterByThemeAction(filter));
group.add(new FilterImagesAction(filter));
}
return group;
@@ -20,10 +20,10 @@ import com.intellij.openapi.actionSystem.AnActionEvent;
import org.intellij.images.thumbnail.ThumbnailView;
import org.intellij.images.thumbnail.actionSystem.ThumbnailViewActionUtil;
public class FilterByThemeAction extends AnAction {
private final ThemeFilter myFilter;
public class FilterImagesAction extends AnAction {
private final Filter myFilter;
public FilterByThemeAction(ThemeFilter filter) {
public FilterImagesAction(Filter filter) {
myFilter = filter;
}
@@ -37,7 +37,7 @@ public class FilterByThemeAction extends AnAction {
ThumbnailView view = ThumbnailViewActionUtil.getVisibleThumbnailView(e);
if (view != null) {
view.setFilter(myFilter);
}
myFilter.setFilter(view);
}
}
}
@@ -0,0 +1,69 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* 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 org.intellij.images.thumbnail.actions;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.intellij.images.search.ImageTagManager;
import org.intellij.images.thumbnail.ThumbnailView;
import org.intellij.images.thumbnail.actionSystem.ThumbnailViewActionUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class RemoveTagGroup extends ActionGroup {
public RemoveTagGroup() {
setPopup(true);
}
@NotNull
@Override
public AnAction[] getChildren(@Nullable AnActionEvent e) {
if (e == null) return EMPTY_ARRAY;
Project project = e.getProject();
ThumbnailView view = ThumbnailViewActionUtil.getVisibleThumbnailView(e);
ImageTagManager tagManager = ImageTagManager.getInstance(project);
if (view != null) {
List<String> tags = null;
for (VirtualFile file : view.getSelection()) {
List<String> newTags = tagManager.getTags(file);
if (newTags.isEmpty()) return EMPTY_ARRAY;
if (tags == null) {
tags = new ArrayList<>(newTags);
}
else {
tags.retainAll(newTags);
}
}
if (tags == null) return EMPTY_ARRAY;
return tags.stream().map(tag -> new AnAction(tag) {
@Override
public void actionPerformed(AnActionEvent e) {
for (VirtualFile file : view.getSelection()) {
tagManager.removeTag(tag, file);
}
}
}).toArray(AnAction[]::new);
}
return EMPTY_ARRAY;
}
}
@@ -16,13 +16,7 @@
package org.intellij.images.thumbnail.actions;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
public interface ThemeFilter {
public interface ThemeFilter extends Filter {
ExtensionPointName<ThemeFilter> EP_NAME = ExtensionPointName.create("com.intellij.images.themeFilter");
String getDisplayName();
boolean accepts(VirtualFile file);
boolean isApplicableToProject(Project project);
}
@@ -27,10 +27,12 @@ import com.intellij.openapi.wm.ToolWindowManager;
import icons.ImagesIcons;
import org.intellij.images.editor.ImageZoomModel;
import org.intellij.images.editor.actionSystem.ImageEditorActions;
import org.intellij.images.search.TagFilter;
import org.intellij.images.thumbnail.ThumbnailView;
import org.intellij.images.thumbnail.actions.ThemeFilter;
import org.intellij.images.vfs.IfsUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
@@ -48,6 +50,7 @@ final class ThumbnailViewImpl implements ThumbnailView {
private VirtualFile root = null;
private final ThumbnailViewUI myThubmnailViewUi;
private ThemeFilter myFilter;
private TagFilter myTagFilter;
public ThumbnailViewImpl(Project project) {
this.project = project;
@@ -131,6 +134,18 @@ final class ThumbnailViewImpl implements ThumbnailView {
return myFilter;
}
@Override
public void setTagFilter(TagFilter filter) {
myTagFilter = filter;
updateUI();
}
@Nullable
@Override
public TagFilter getTagFilter() {
return myTagFilter;
}
public void setVisible(boolean visible) {
toolWindow.setAvailable(visible, null);
if (visible) {
@@ -40,6 +40,7 @@ import com.intellij.ui.SideBorder;
import com.intellij.ui.components.JBList;
import org.intellij.images.fileTypes.ImageFileTypeManager;
import org.intellij.images.options.*;
import org.intellij.images.search.TagFilter;
import org.intellij.images.thumbnail.ThumbnailView;
import org.intellij.images.thumbnail.actionSystem.ThumbnailViewActions;
import org.intellij.images.thumbnail.actions.ThemeFilter;
@@ -177,9 +178,12 @@ final class ThumbnailViewUI extends JPanel implements DataProvider, Disposable {
model.ensureCapacity(model.size() + virtualFiles.length + 1);
ThemeFilter filter = thumbnailView.getFilter();
TagFilter tagFilter = thumbnailView.getTagFilter();
for (VirtualFile virtualFile : virtualFiles) {
if (filter == null || filter.accepts(virtualFile)) {
model.addElement(virtualFile);
if (tagFilter == null || tagFilter.accepts(virtualFile)) {
model.addElement(virtualFile);
}
}
}
if (model.size() > 0) {
@@ -17,6 +17,7 @@ package org.jetbrains.idea.devkit.icons;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.intellij.images.thumbnail.ThumbnailView;
import org.intellij.images.thumbnail.actions.ThemeFilter;
import org.jetbrains.idea.devkit.util.PsiUtil;
@@ -41,4 +42,9 @@ public abstract class AbstractThemeFilter implements ThemeFilter {
public boolean isApplicableToProject(Project project) {
return PsiUtil.isIdeaProject(project) || PsiUtil.isPluginProject(project);
}
@Override
public void setFilter(ThumbnailView view) {
view.setFilter(this);
}
}
+5
View File
@@ -18,6 +18,7 @@ package org.jetbrains.idea.devkit.icons;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import org.intellij.images.thumbnail.ThumbnailView;
import org.intellij.images.thumbnail.actions.ThemeFilter;
public enum Theme implements ThemeFilter{
@@ -90,5 +91,9 @@ public enum Theme implements ThemeFilter{
return true;
}
@Override
public void setFilter(ThumbnailView view) {
view.setFilter(this);
}
}