2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.intellij.codeInsight.template.impl;
19 import com.google.common.collect.Sets;
20 import com.intellij.application.options.ExportSchemeAction;
21 import com.intellij.application.options.SchemesToImportPopup;
22 import com.intellij.codeInsight.CodeInsightBundle;
23 import com.intellij.codeInsight.template.TemplateContextType;
24 import com.intellij.ide.dnd.*;
25 import com.intellij.ide.dnd.aware.DnDAwareTree;
26 import com.intellij.openapi.Disposable;
27 import com.intellij.openapi.actionSystem.*;
28 import com.intellij.openapi.diagnostic.Logger;
29 import com.intellij.openapi.options.ConfigurationException;
30 import com.intellij.openapi.options.SchemesManager;
31 import com.intellij.openapi.project.DumbAwareAction;
32 import com.intellij.openapi.ui.InputValidator;
33 import com.intellij.openapi.ui.Messages;
34 import com.intellij.openapi.ui.Splitter;
35 import com.intellij.openapi.util.Comparing;
36 import com.intellij.openapi.util.text.StringUtil;
37 import com.intellij.ui.*;
38 import com.intellij.util.Alarm;
39 import com.intellij.util.NullableFunction;
40 import com.intellij.util.ObjectUtils;
41 import com.intellij.util.PlatformIcons;
42 import com.intellij.util.containers.Convertor;
43 import com.intellij.util.ui.tree.TreeUtil;
44 import com.intellij.util.ui.update.UiNotifyConnector;
45 import org.jetbrains.annotations.Nullable;
48 import javax.swing.border.EmptyBorder;
49 import javax.swing.event.TreeSelectionEvent;
50 import javax.swing.event.TreeSelectionListener;
51 import javax.swing.tree.DefaultMutableTreeNode;
52 import javax.swing.tree.DefaultTreeModel;
53 import javax.swing.tree.TreeNode;
54 import javax.swing.tree.TreePath;
56 import java.awt.event.*;
58 import java.util.List;
60 public class TemplateListPanel extends JPanel implements Disposable {
62 private static final String NO_SELECTION = "NoSelection";
63 private static final String TEMPLATE_SETTINGS = "TemplateSettings";
64 private static final TemplateImpl MOCK_TEMPLATE = new TemplateImpl("mockTemplate-xxx", "mockTemplateGroup-yyy");
65 public static final String ABBREVIATION = "<abbreviation>";
66 public static final Comparator<TemplateImpl> TEMPLATE_COMPARATOR = new Comparator<TemplateImpl>() {
67 public int compare(final TemplateImpl o1, final TemplateImpl o2) {
68 return o1.getKey().compareToIgnoreCase(o2.getKey());
73 MOCK_TEMPLATE.setString("");
76 private CheckboxTree myTree;
77 private final List<TemplateGroup> myTemplateGroups = new ArrayList<TemplateGroup>();
78 private JComboBox myExpandByCombo;
79 private static final String SPACE = CodeInsightBundle.message("template.shortcut.space");
80 private static final String TAB = CodeInsightBundle.message("template.shortcut.tab");
81 private static final String ENTER = CodeInsightBundle.message("template.shortcut.enter");
83 private CheckedTreeNode myTreeRoot = new CheckedTreeNode(null);
85 private final Alarm myAlarm = new Alarm();
86 private boolean myUpdateNeeded = false;
88 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.template.impl.TemplateListPanel");
90 private final Map<Integer, Map<TemplateOptionalProcessor, Boolean>> myTemplateOptions = new LinkedHashMap<Integer, Map<TemplateOptionalProcessor, Boolean>>();
91 private final Map<Integer, Map<TemplateContextType, Boolean>> myTemplateContext = new LinkedHashMap<Integer, Map<TemplateContextType, Boolean>>();
92 private JPanel myDetailsPanel = new JPanel(new CardLayout());
93 private LiveTemplateSettingsEditor myCurrentTemplateEditor;
95 public TemplateListPanel() {
96 super(new BorderLayout());
98 myDetailsPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
99 JLabel label = new JLabel("No live template is selected");
100 label.setHorizontalAlignment(SwingConstants.CENTER);
101 myDetailsPanel.add(label, NO_SELECTION);
102 createTemplateEditor(MOCK_TEMPLATE, "Tab", MOCK_TEMPLATE.createOptions(), MOCK_TEMPLATE.createContext());
104 add(createExpandByPanel(), BorderLayout.NORTH);
106 Splitter splitter = new Splitter(true, 0.9f);
107 splitter.setFirstComponent(createTable());
108 splitter.setSecondComponent(myDetailsPanel);
109 add(splitter, BorderLayout.CENTER);
112 public void dispose() {
113 myCurrentTemplateEditor.dispose();
114 myAlarm.cancelAllRequests();
117 public void reset() {
118 myTemplateOptions.clear();
119 myTemplateContext.clear();
121 TemplateSettings templateSettings = TemplateSettings.getInstance();
122 List<TemplateGroup> groups = new ArrayList<TemplateGroup>(templateSettings.getTemplateGroups());
124 Collections.sort(groups, new Comparator<TemplateGroup>() {
125 public int compare(final TemplateGroup o1, final TemplateGroup o2) {
126 return o1.getName().compareToIgnoreCase(o2.getName());
130 initTemplates(groups, templateSettings.getLastSelectedTemplateGroup(), templateSettings.getLastSelectedTemplateKey());
134 if (templateSettings.getDefaultShortcutChar() == TemplateSettings.TAB_CHAR) {
135 myExpandByCombo.setSelectedItem(TAB);
137 else if (templateSettings.getDefaultShortcutChar() == TemplateSettings.ENTER_CHAR) {
138 myExpandByCombo.setSelectedItem(ENTER);
141 myExpandByCombo.setSelectedItem(SPACE);
144 UiNotifyConnector.doWhenFirstShown(this, new Runnable() {
146 updateTemplateDetails(false);
150 myUpdateNeeded = true;
153 public void apply() throws ConfigurationException {
154 List<TemplateGroup> templateGroups = getTemplateGroups();
155 for (TemplateGroup templateGroup : templateGroups) {
156 Set<String> names = Sets.newHashSet();
158 for (TemplateImpl template : templateGroup.getElements()) {
159 if (StringUtil.isEmptyOrSpaces(template.getKey())) {
160 throw new ConfigurationException("A live template with an empty key has been found in " + templateGroup.getName() + " group, such live templates cannot be invoked");
163 if (StringUtil.isEmptyOrSpaces(template.getString())) {
164 throw new ConfigurationException("A live template with an empty text has been found in " + templateGroup.getName() + " group, such live templates cannot be invoked");
167 if (!names.add(template.getKey())) {
168 throw new ConfigurationException("Duplicate " + template.getKey() + " live templates in " + templateGroup.getName() + " group");
174 for (TemplateGroup templateGroup : templateGroups) {
175 for (TemplateImpl template : templateGroup.getElements()) {
176 template.applyOptions(getTemplateOptions(template));
177 template.applyContext(getTemplateContext(template));
180 TemplateSettings templateSettings = TemplateSettings.getInstance();
181 templateSettings.setTemplates(templateGroups);
182 templateSettings.setDefaultShortcutChar(getDefaultShortcutChar());
187 public boolean isModified() {
188 TemplateSettings templateSettings = TemplateSettings.getInstance();
189 if (templateSettings.getDefaultShortcutChar() != getDefaultShortcutChar()) {
193 List<TemplateGroup> originalGroups = templateSettings.getTemplateGroups();
194 List<TemplateGroup> newGroups = getTemplateGroups();
196 return !checkAreEqual(collectTemplates(originalGroups), collectTemplates(newGroups));
199 public void editTemplate(TemplateImpl template) {
200 selectTemplate(template.getGroupName(), template.getKey());
201 updateTemplateDetails(true);
205 public JComponent getPreferredFocusedComponent() {
206 if (getTemplate(getSingleSelectedIndex()) != null) {
207 return myCurrentTemplateEditor.getKeyField();
212 private static List<TemplateImpl> collectTemplates(final List<TemplateGroup> groups) {
213 ArrayList<TemplateImpl> result = new ArrayList<TemplateImpl>();
214 for (TemplateGroup group : groups) {
215 result.addAll(group.getElements());
217 Collections.sort(result, new Comparator<TemplateImpl>(){
218 public int compare(final TemplateImpl o1, final TemplateImpl o2) {
219 final int groupsEqual = o1.getGroupName().compareToIgnoreCase(o2.getGroupName());
220 if (groupsEqual != 0) {
223 return o1.getKey().compareToIgnoreCase(o2.getKey());
229 private boolean checkAreEqual(final List<TemplateImpl> originalGroup, final List<TemplateImpl> newGroup) {
230 if (originalGroup.size() != newGroup.size()) return false;
232 for (int i = 0; i < newGroup.size(); i++) {
233 TemplateImpl newTemplate = newGroup.get(i);
234 newTemplate.parseSegments();
235 TemplateImpl originalTemplate = originalGroup.get(i);
236 originalTemplate.parseSegments();
237 if (!originalTemplate.equals(newTemplate)) {
241 if (originalTemplate.isDeactivated() != newTemplate.isDeactivated()) {
245 if (!areOptionsEqual(newTemplate, originalTemplate)) {
249 if (!areContextsEqual(newTemplate, originalTemplate)) {
257 private boolean areContextsEqual(final TemplateImpl newTemplate, final TemplateImpl originalTemplate) {
258 Map<TemplateContextType, Boolean> templateContext = getTemplateContext(newTemplate);
259 for (TemplateContextType processor : templateContext.keySet()) {
260 if (originalTemplate.getTemplateContext().isEnabled(processor) != templateContext.get(processor).booleanValue())
266 private boolean areOptionsEqual(final TemplateImpl newTemplate, final TemplateImpl originalTemplate) {
267 Map<TemplateOptionalProcessor, Boolean> templateOptions = getTemplateOptions(newTemplate);
268 for (TemplateOptionalProcessor processor : templateOptions.keySet()) {
269 if (processor.isEnabled(originalTemplate) != templateOptions.get(processor).booleanValue()) return false;
274 private Map<TemplateContextType, Boolean> getTemplateContext(final TemplateImpl newTemplate) {
275 return myTemplateContext.get(getKey(newTemplate));
278 private Map<TemplateOptionalProcessor, Boolean> getTemplateOptions(final TemplateImpl newTemplate) {
279 return myTemplateOptions.get(getKey(newTemplate));
282 private char getDefaultShortcutChar() {
283 Object selectedItem = myExpandByCombo.getSelectedItem();
284 if (TAB.equals(selectedItem)) {
285 return TemplateSettings.TAB_CHAR;
287 else if (ENTER.equals(selectedItem)) {
288 return TemplateSettings.ENTER_CHAR;
291 return TemplateSettings.SPACE_CHAR;
295 private List<TemplateGroup> getTemplateGroups() {
296 return myTemplateGroups;
299 private void createTemplateEditor(final TemplateImpl template,
301 Map<TemplateOptionalProcessor, Boolean> options,
302 Map<TemplateContextType, Boolean> context) {
303 myCurrentTemplateEditor = new LiveTemplateSettingsEditor(template, shortcut, options, context, new Runnable() {
306 DefaultMutableTreeNode node = getNode(getSingleSelectedIndex());
308 ((DefaultTreeModel)myTree.getModel()).nodeChanged(node);
309 TemplateSettings.getInstance().setLastSelectedTemplate(template.getGroupName(), template.getKey());
312 }, TemplateSettings.getInstance().getTemplate(template.getKey(), template.getGroupName()) != null);
313 for (Component component : myDetailsPanel.getComponents()) {
314 if (component instanceof LiveTemplateSettingsEditor) {
315 myDetailsPanel.remove(component);
319 myDetailsPanel.add(myCurrentTemplateEditor, TEMPLATE_SETTINGS);
322 private Iterable<? extends TemplateImpl> collectAllTemplates() {
323 ArrayList<TemplateImpl> result = new ArrayList<TemplateImpl>();
324 for (TemplateGroup templateGroup : myTemplateGroups) {
325 result.addAll(templateGroup.getElements());
330 private void exportCurrentGroup() {
331 int selected = getSingleSelectedIndex();
332 if (selected < 0) return;
334 ExportSchemeAction.doExport(getGroup(selected), getSchemesManager());
338 private static SchemesManager<TemplateGroup, TemplateGroup> getSchemesManager() {
339 return (TemplateSettings.getInstance()).getSchemesManager();
342 private JPanel createExpandByPanel() {
343 JPanel panel = new JPanel(new GridBagLayout());
344 GridBagConstraints gbConstraints = new GridBagConstraints();
345 gbConstraints.weighty = 0;
346 gbConstraints.weightx = 0;
347 gbConstraints.gridy = 0;
348 panel.add(new JLabel(CodeInsightBundle.message("templates.dialog.shortcut.chooser.label")), gbConstraints);
350 gbConstraints.gridx = 1;
351 gbConstraints.insets = new Insets(0, 4, 0, 0);
352 myExpandByCombo = new JComboBox();
353 myExpandByCombo.addItem(SPACE);
354 myExpandByCombo.addItem(TAB);
355 myExpandByCombo.addItem(ENTER);
356 panel.add(myExpandByCombo, gbConstraints);
358 gbConstraints.gridx = 2;
359 gbConstraints.weightx = 1;
360 panel.add(new JPanel(), gbConstraints);
361 panel.setBorder(new EmptyBorder(0, 0, 10, 0));
366 private TemplateImpl getTemplate(int row) {
368 TreePath path = tree.getPathForRow(row);
370 DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
371 if (node.getUserObject() instanceof TemplateImpl) {
372 return (TemplateImpl)node.getUserObject();
380 private TemplateGroup getGroup(int row) {
382 TreePath path = tree.getPathForRow(row);
384 DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
385 if (node.getUserObject() instanceof TemplateGroup) {
386 return (TemplateGroup)node.getUserObject();
393 private void moveTemplates(Map<TemplateImpl, DefaultMutableTreeNode> map, String newGroupName) {
394 List<TreePath> toSelect = new ArrayList<TreePath>();
395 for (TemplateImpl template : map.keySet()) {
396 DefaultMutableTreeNode oldTemplateNode = map.get(template);
398 TemplateGroup oldGroup = getTemplateGroup(template.getGroupName());
399 if (oldGroup != null) {
400 oldGroup.removeElement(template);
403 template.setGroupName(newGroupName);
405 DefaultMutableTreeNode parent = (DefaultMutableTreeNode)oldTemplateNode.getParent();
406 removeNodeFromParent(oldTemplateNode);
407 if (parent.getChildCount() == 0) removeNodeFromParent(parent);
409 toSelect.add(new TreePath(registerTemplate(template).getPath()));
412 myTree.getSelectionModel().clearSelection();
413 for (TreePath path : toSelect) {
414 myTree.expandPath(path.getParentPath());
415 myTree.addSelectionPath(path);
416 myTree.scrollRowToVisible(myTree.getRowForPath(path));
421 private DefaultMutableTreeNode getNode(final int row) {
423 TreePath path = tree.getPathForRow(row);
425 return (DefaultMutableTreeNode)path.getLastPathComponent();
433 private TemplateGroup getTemplateGroup(final String groupName) {
434 for (TemplateGroup group : myTemplateGroups) {
435 if (group.getName().equals(groupName)) return group;
441 private void addRow() {
442 String defaultGroup = TemplateSettings.USER_GROUP_NAME;
443 final DefaultMutableTreeNode node = getNode(getSingleSelectedIndex());
445 if (node.getUserObject() instanceof TemplateImpl) {
446 defaultGroup = ((TemplateImpl) node.getUserObject()).getGroupName();
448 else if (node.getUserObject() instanceof TemplateGroup) {
449 defaultGroup = ((TemplateGroup) node.getUserObject()).getName();
453 addTemplate(new TemplateImpl(ABBREVIATION, "", defaultGroup));
456 public void addTemplate(TemplateImpl template) {
457 myTemplateOptions.put(getKey(template), template.createOptions());
458 myTemplateContext.put(getKey(template), template.createContext());
460 registerTemplate(template);
461 updateTemplateDetails(true);
464 private static int getKey(final TemplateImpl template) {
465 return System.identityHashCode(template);
468 private void copyRow() {
469 int selected = getSingleSelectedIndex();
470 if (selected < 0) return;
472 TemplateImpl orTemplate = getTemplate(selected);
473 LOG.assertTrue(orTemplate != null);
474 TemplateImpl template = orTemplate.copy();
475 template.setKey(ABBREVIATION);
476 myTemplateOptions.put(getKey(template), new HashMap<TemplateOptionalProcessor, Boolean>(getTemplateOptions(orTemplate)));
477 myTemplateContext.put(getKey(template), new HashMap<TemplateContextType, Boolean>(getTemplateContext(orTemplate)));
478 registerTemplate(template);
480 updateTemplateDetails(true);
483 private int getSingleSelectedIndex() {
484 int[] rows = myTree.getSelectionRows();
485 return rows != null && rows.length == 1 ? rows[0] : -1;
488 private void removeRows() {
489 TreeNode toSelect = null;
491 TreePath[] paths = myTree.getSelectionPaths();
492 if (paths == null) return;
494 for (TreePath path : paths) {
495 DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
496 Object o = node.getUserObject();
497 if (o instanceof TemplateGroup) {
498 //noinspection SuspiciousMethodCalls
499 myTemplateGroups.remove(o);
500 removeNodeFromParent(node);
501 } else if (o instanceof TemplateImpl) {
502 TemplateImpl template = (TemplateImpl)o;
503 TemplateGroup templateGroup = getTemplateGroup(template.getGroupName());
504 if (templateGroup != null) {
505 templateGroup.removeElement(template);
506 DefaultMutableTreeNode parent = (DefaultMutableTreeNode)node.getParent();
508 if (templateGroup.getElements().isEmpty()) {
509 myTemplateGroups.remove(templateGroup);
510 removeNodeFromParent(parent);
512 toSelect = parent.getChildAfter(node);
513 removeNodeFromParent(node);
519 if (toSelect instanceof DefaultMutableTreeNode) {
520 setSelectedNode((DefaultMutableTreeNode)toSelect);
524 private JPanel createTable() {
525 myTreeRoot = new CheckedTreeNode(null);
527 myTree = new CheckboxTree(new CheckboxTree.CheckboxTreeCellRenderer(){
528 public void customizeRenderer(final JTree tree,
530 final boolean selected,
531 final boolean expanded,
534 final boolean hasFocus) {
535 if (!(value instanceof DefaultMutableTreeNode)) return;
536 value = ((DefaultMutableTreeNode)value).getUserObject();
538 if (value instanceof TemplateImpl) {
539 getTextRenderer().append (((TemplateImpl)value).getKey(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
540 String description = ((TemplateImpl)value).getDescription();
541 if (description != null && description.length() > 0) {
542 getTextRenderer().append (" (" + description + ")", SimpleTextAttributes.GRAY_ATTRIBUTES);
545 else if (value instanceof TemplateGroup) {
546 getTextRenderer().append (((TemplateGroup)value).getName(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
552 protected void onNodeStateChanged(final CheckedTreeNode node) {
553 Object obj = node.getUserObject();
554 if (obj instanceof TemplateImpl) {
555 ((TemplateImpl)obj).setDeactivated(!node.isChecked());
560 protected void installSpeedSearch() {
561 new TreeSpeedSearch(this, new Convertor<TreePath, String>() {
563 public String convert(TreePath o) {
564 Object object = ((DefaultMutableTreeNode)o.getLastPathComponent()).getUserObject();
565 if (object instanceof TemplateGroup) {
566 return ((TemplateGroup)object).getName();
568 if (object instanceof TemplateImpl) {
569 return ((TemplateImpl)object).getKey();
577 myTree.setRootVisible(false);
578 myTree.setShowsRootHandles(true);
580 myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener(){
581 public void valueChanged(final TreeSelectionEvent e) {
582 TemplateSettings templateSettings = TemplateSettings.getInstance();
583 TemplateImpl template = getTemplate(getSingleSelectedIndex());
584 if (template != null) {
585 templateSettings.setLastSelectedTemplate(template.getGroupName(), template.getKey());
587 templateSettings.setLastSelectedTemplate(null, null);
588 ((CardLayout) myDetailsPanel.getLayout()).show(myDetailsPanel, NO_SELECTION);
590 if (myUpdateNeeded) {
591 myAlarm.cancelAllRequests();
592 myAlarm.addRequest(new Runnable() {
594 updateTemplateDetails(false);
601 myTree.registerKeyboardAction(new ActionListener() {
602 public void actionPerformed(ActionEvent event) {
603 myCurrentTemplateEditor.focusKey();
605 }, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_FOCUSED);
607 myTree.addMouseListener(new MouseAdapter() {
609 public void mouseClicked(MouseEvent e) {
610 if (e.getClickCount() == 2) {
619 DnDSupport.createBuilder(myTree)
620 .setBeanProvider(new NullableFunction<DnDActionInfo, DnDDragStartBean>() {
622 public DnDDragStartBean fun(DnDActionInfo dnDActionInfo) {
623 Point point = dnDActionInfo.getPoint();
624 if (myTree.getPathForLocation(point.x, point.y) == null) return null;
626 Map<TemplateImpl, DefaultMutableTreeNode> templates = getSelectedTemplates();
628 return !templates.isEmpty() ? new DnDDragStartBean(templates) : null;
631 setDisposableParent(this)
632 .setTargetChecker(new DnDTargetChecker() {
634 public boolean update(DnDEvent event) {
635 @SuppressWarnings("unchecked") Set<String> oldGroupNames = getAllGroups((Map<TemplateImpl, DefaultMutableTreeNode>)event.getAttachedObject());
636 TemplateGroup group = getDropGroup(event);
637 boolean differentGroup = group != null && !oldGroupNames.contains(group.getName());
638 boolean possible = differentGroup && !getSchemesManager().isShared(group);
639 event.setDropPossible(possible, differentGroup && !possible ? "Cannot modify a shared group" : "");
643 .setDropHandler(new DnDDropHandler() {
645 public void drop(DnDEvent event) {
646 //noinspection unchecked
647 moveTemplates((Map<TemplateImpl, DefaultMutableTreeNode>)event.getAttachedObject(),
648 ObjectUtils.assertNotNull(getDropGroup(event)).getName());
651 .setImageProvider(new NullableFunction<DnDActionInfo, DnDImage>() {
653 public DnDImage fun(DnDActionInfo dnDActionInfo) {
654 Point point = dnDActionInfo.getPoint();
655 TreePath path = myTree.getPathForLocation(point.x, point.y);
656 return path == null ? null : new DnDImage(DnDAwareTree.getDragImage(myTree, path, point).first);
661 if (myTemplateGroups.size() > 0) {
662 myTree.setSelectionInterval(0, 0);
665 return initToolbar().createPanel();
669 private ToolbarDecorator initToolbar() {
670 ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myTree)
671 .setAddAction(new AnActionButtonRunnable() {
673 public void run(AnActionButton button) {
677 .setRemoveAction(new AnActionButtonRunnable() {
679 public void run(AnActionButton anActionButton) {
685 .addExtraAction(new AnActionButton("Copy", PlatformIcons.DUPLICATE_ICON) {
687 public void actionPerformed(AnActionEvent e) {
692 public void updateButton(AnActionEvent e) {
693 e.getPresentation().setEnabled(getTemplate(getSingleSelectedIndex()) != null);
696 if (getSchemesManager().isExportAvailable()) {
697 decorator.addExtraAction(new AnActionButton("Share...", PlatformIcons.EXPORT_ICON) {
699 public void actionPerformed(AnActionEvent e) {
700 exportCurrentGroup();
704 public void updateButton(AnActionEvent e) {
705 TemplateGroup group = getGroup(getSingleSelectedIndex());
706 e.getPresentation().setEnabled(group != null && !getSchemesManager().isShared(group));
710 if (getSchemesManager().isImportAvailable()) {
711 decorator.addExtraAction(new AnActionButton("Import Shared...", PlatformIcons.IMPORT_ICON) {
713 public void actionPerformed(AnActionEvent e) {
714 new SchemesToImportPopup<TemplateGroup, TemplateGroup>(TemplateListPanel.this){
715 protected void onSchemeSelected(final TemplateGroup scheme) {
716 for (TemplateImpl newTemplate : scheme.getElements()) {
717 for (TemplateImpl existingTemplate : collectAllTemplates()) {
718 if (existingTemplate.getKey().equals(newTemplate.getKey())) {
719 Messages.showMessageDialog(
720 TemplateListPanel.this,
722 .message("dialog.edit.template.error.already.exists", existingTemplate.getKey(), existingTemplate.getGroupName()),
723 CodeInsightBundle.message("dialog.edit.template.error.title"),
724 Messages.getErrorIcon()
730 insertNewGroup(scheme);
731 for (TemplateImpl template : scheme.getElements()) {
732 registerTemplate(template);
735 }.show(getSchemesManager(), myTemplateGroups);
739 return decorator.setToolbarPosition(ActionToolbarPosition.RIGHT);
743 private TemplateGroup getDropGroup(DnDEvent event) {
744 Point point = event.getPointOn(myTree);
745 return getGroup(myTree.getRowForLocation(point.x, point.y));
748 private void installPopup() {
749 final DumbAwareAction rename = new DumbAwareAction("Rename") {
752 public void update(AnActionEvent e) {
753 final int selected = getSingleSelectedIndex();
754 final TemplateGroup templateGroup = getGroup(selected);
755 boolean enabled = templateGroup != null;
756 e.getPresentation().setEnabled(enabled);
757 e.getPresentation().setVisible(enabled);
762 public void actionPerformed(AnActionEvent e) {
766 rename.registerCustomShortcutSet(ActionManager.getInstance().getAction(IdeActions.ACTION_RENAME).getShortcutSet(), myTree);
768 final DefaultActionGroup move = new DefaultActionGroup("Move", true) {
770 public void update(AnActionEvent e) {
771 final Map<TemplateImpl, DefaultMutableTreeNode> templates = getSelectedTemplates();
772 boolean enabled = !templates.isEmpty();
773 e.getPresentation().setEnabled(enabled);
774 e.getPresentation().setVisible(enabled);
777 Set<String> oldGroups = getAllGroups(templates);
780 SchemesManager<TemplateGroup, TemplateGroup> schemesManager = TemplateSettings.getInstance().getSchemesManager();
782 for (TemplateGroup group : getTemplateGroups()) {
783 final String newGroupName = group.getName();
784 if (!oldGroups.contains(newGroupName) && !schemesManager.isShared(group)) {
785 add(new DumbAwareAction(newGroupName) {
787 public void actionPerformed(AnActionEvent e) {
788 moveTemplates(templates, newGroupName);
794 add(new DumbAwareAction("New group...") {
796 public void actionPerformed(AnActionEvent e) {
797 String newName = Messages.showInputDialog(myTree, "Enter the new group name:", "Move to a New Group", null, "", new TemplateGroupInputValidator(null));
798 if (newName != null) {
799 moveTemplates(templates, newName);
807 myTree.addMouseListener(new PopupHandler() {
809 public void invokePopup(Component comp, int x, int y) {
810 final DefaultActionGroup group = new DefaultActionGroup();
813 ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, group).getComponent().show(comp, x, y);
818 private static Set<String> getAllGroups(Map<TemplateImpl, DefaultMutableTreeNode> templates) {
819 Set<String> oldGroups = new HashSet<String>();
820 for (TemplateImpl template : templates.keySet()) {
821 oldGroups.add(template.getGroupName());
826 private Map<TemplateImpl, DefaultMutableTreeNode> getSelectedTemplates() {
827 TreePath[] paths = myTree.getSelectionPaths();
829 return Collections.emptyMap();
831 Map<TemplateImpl, DefaultMutableTreeNode> templates = new LinkedHashMap<TemplateImpl, DefaultMutableTreeNode>();
832 for (TreePath path : paths) {
833 DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
834 Object o = node.getUserObject();
835 if (!(o instanceof TemplateImpl)) {
836 return Collections.emptyMap();
838 templates.put((TemplateImpl)o, node);
843 private void renameGroup() {
844 final int selected = getSingleSelectedIndex();
845 final TemplateGroup templateGroup = getGroup(selected);
846 if (templateGroup == null) return;
848 final String oldName = templateGroup.getName();
849 String newName = Messages.showInputDialog(myTree, "Enter the new group name:", "Rename", null, oldName,
850 new TemplateGroupInputValidator(oldName));
852 if (newName != null && !newName.equals(oldName)) {
853 templateGroup.setName(newName);
854 ((DefaultTreeModel)myTree.getModel()).nodeChanged(getNode(selected));
858 private void updateTemplateDetails(boolean focusKey) {
859 int selected = getSingleSelectedIndex();
860 CardLayout layout = (CardLayout)myDetailsPanel.getLayout();
861 if (selected < 0 || getTemplate(selected) == null) {
862 layout.show(myDetailsPanel, NO_SELECTION);
865 TemplateImpl newTemplate = getTemplate(selected);
866 if (myCurrentTemplateEditor == null || myCurrentTemplateEditor.getTemplate() != newTemplate) {
867 if (myCurrentTemplateEditor != null) {
868 myCurrentTemplateEditor.dispose();
870 createTemplateEditor(newTemplate, (String)myExpandByCombo.getSelectedItem(), getTemplateOptions(newTemplate),
871 getTemplateContext(newTemplate));
873 myCurrentTemplateEditor.focusKey();
876 layout.show(myDetailsPanel, TEMPLATE_SETTINGS);
880 private CheckedTreeNode registerTemplate(TemplateImpl template) {
881 TemplateGroup newGroup = getTemplateGroup(template.getGroupName());
882 if (newGroup == null) {
883 newGroup = new TemplateGroup(template.getGroupName());
884 insertNewGroup(newGroup);
886 if (!newGroup.contains(template)) {
887 newGroup.addElement(template);
890 CheckedTreeNode node = new CheckedTreeNode(template);
891 node.setChecked(!template.isDeactivated());
892 for (DefaultMutableTreeNode child = (DefaultMutableTreeNode)myTreeRoot.getFirstChild();
894 child = (DefaultMutableTreeNode)myTreeRoot.getChildAfter(child)) {
895 if (((TemplateGroup)child.getUserObject()).getName().equals(template.getGroupName())) {
896 int index = getIndexToInsert (child, template.getKey());
897 child.insert(node, index);
898 ((DefaultTreeModel)myTree.getModel()).nodesWereInserted(child, new int[]{index});
899 setSelectedNode(node);
905 private void insertNewGroup(final TemplateGroup newGroup) {
906 myTemplateGroups.add(newGroup);
908 int index = getIndexToInsert(myTreeRoot, newGroup.getName());
909 DefaultMutableTreeNode groupNode = new CheckedTreeNode(newGroup);
910 myTreeRoot.insert(groupNode, index);
911 ((DefaultTreeModel)myTree.getModel()).nodesWereInserted(myTreeRoot, new int[]{index});
914 private static int getIndexToInsert(DefaultMutableTreeNode parent, String key) {
915 if (parent.getChildCount() == 0) return 0;
918 for (DefaultMutableTreeNode child = (DefaultMutableTreeNode)parent.getFirstChild();
920 child = (DefaultMutableTreeNode)parent.getChildAfter(child)) {
921 Object o = child.getUserObject();
922 String key1 = o instanceof TemplateImpl ? ((TemplateImpl)o).getKey() : ((TemplateGroup)o).getName();
923 if (key1.compareToIgnoreCase(key) > 0) return res;
929 private void setSelectedNode(DefaultMutableTreeNode node) {
930 TreePath path = new TreePath(node.getPath());
931 myTree.expandPath(path.getParentPath());
932 int row = myTree.getRowForPath(path);
933 myTree.setSelectionRow(row);
934 myTree.scrollRowToVisible(row);
937 private void removeNodeFromParent(DefaultMutableTreeNode node) {
938 TreeNode parent = node.getParent();
939 int idx = parent.getIndex(node);
940 node.removeFromParent();
942 ((DefaultTreeModel)myTree.getModel()).nodesWereRemoved(parent, new int[]{idx}, new TreeNode[]{node});
945 private void initTemplates(List<TemplateGroup> groups, String lastSelectedGroup, String lastSelectedKey) {
946 myTreeRoot.removeAllChildren();
947 myTemplateGroups.clear();
948 for (TemplateGroup group : groups) {
949 myTemplateGroups.add((TemplateGroup)group.copy());
952 for (TemplateGroup group : myTemplateGroups) {
953 CheckedTreeNode groupNode = new CheckedTreeNode(group);
954 addTemplateNodes(group, groupNode);
955 myTreeRoot.add(groupNode);
957 fireStructureChange();
959 selectTemplate(lastSelectedGroup, lastSelectedKey);
962 private void selectTemplate(final String lastSelectedGroup, final String lastSelectedKey) {
963 TreeUtil.traverseDepth(myTreeRoot, new TreeUtil.Traverse() {
965 public boolean accept(Object node) {
966 Object o = ((DefaultMutableTreeNode)node).getUserObject();
967 if (lastSelectedKey == null && o instanceof TemplateGroup && Comparing.equal(lastSelectedGroup, ((TemplateGroup)o).getName()) ||
968 o instanceof TemplateImpl && Comparing.equal(lastSelectedKey, ((TemplateImpl)o).getKey()) && Comparing.equal(lastSelectedGroup, ((TemplateImpl)o).getGroupName())) {
969 setSelectedNode((DefaultMutableTreeNode)node);
978 private void fireStructureChange() {
979 ((DefaultTreeModel)myTree.getModel()).nodeStructureChanged(myTreeRoot);
982 private void addTemplateNodes(TemplateGroup group, CheckedTreeNode groupNode) {
983 List<TemplateImpl> templates = new ArrayList<TemplateImpl>(group.getElements());
984 Collections.sort(templates, TEMPLATE_COMPARATOR);
985 for (final TemplateImpl template : templates) {
986 myTemplateOptions.put(getKey(template), template.createOptions());
987 myTemplateContext.put(getKey(template), template.createContext());
988 CheckedTreeNode node = new CheckedTreeNode(template);
989 node.setChecked(!template.isDeactivated());
994 private class TemplateGroupInputValidator implements InputValidator {
995 private final String myOldName;
997 public TemplateGroupInputValidator(String oldName) {
1002 public boolean checkInput(String inputString) {
1003 return StringUtil.isNotEmpty(inputString) &&
1004 (getTemplateGroup(inputString) == null || inputString.equals(myOldName));
1008 public boolean canClose(String inputString) {
1009 return checkInput(inputString);