diff --git a/images/settings.gif b/images/settings.gif index 9d05088..8c728cc 100644 Binary files a/images/settings.gif and b/images/settings.gif differ diff --git a/src/pa/iscde/minimap/internal/MinimapView.java b/src/pa/iscde/minimap/internal/MinimapView.java index f69f3b0..4574403 100644 --- a/src/pa/iscde/minimap/internal/MinimapView.java +++ b/src/pa/iscde/minimap/internal/MinimapView.java @@ -44,6 +44,7 @@ public class MinimapView implements PidescoView { private static final Logger LOGGER = Logger.getLogger(MinimapView.class); + public static final String VIEW_ID = "pt.iscte.pidesco.minimap.minimap"; public static final String EXT_POINT_INSPECTION = "pt.iscte.pidesco.minimap.inspection"; @@ -58,6 +59,8 @@ public class MinimapView implements PidescoView { private final Collection extensions; private final Collection activeRules; + private File activeFile = null; + /* SWT Components */ private Composite root; private ScrolledComposite scroll; @@ -93,7 +96,15 @@ private void loadInspections() { this.extensions.add(extension); this.activeRules.addAll(extension.rules); - extension.rules.forEach(rule -> SettingsManager.isEnabled(rule.id)); + // Load rule stat + for (ExtensionRule rule : extension.rules) { + if (rule.isErrored()) { + rule.setEnabled(false); + SettingsManager.setEnabled(rule); + } else { + rule.setEnabled(SettingsManager.isEnabled(rule)); + } + } } catch (Exception e) { LOGGER.error("Error loading extension '" + ext.getSimpleIdentifier() + "'", e); } @@ -163,43 +174,61 @@ public Composite getRoot() { return root; } - private class Listener implements JavaEditorListener { + public Collection getExtensions() { + return extensions; + } - @Override - public void fileOpened(File file) { - LOGGER.info("File opened: " + file); + public File getActiveFile() { + return activeFile; + } + + public void parseFile(File file) { + this.activeFile = file; - // TODO: do this in a worker thread - // TODO: implement way to enable/disable rules - Collection lines = new MinimapFile(file).parse(javaEditorServices, activeRules); + if (file == null) { + this.scroll.getContent().dispose(); + return; + } - createScrollComponent(lines); + // TODO: do this in a worker thread + // TODO: implement way to enable/disable rules + Collection lines = new MinimapFile(file).parse(javaEditorServices, activeRules); + createScrollComponent(lines); -// StyledText text = new StyledText(scroll, SWT.BORDER); -// text.setText("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"); +// StyledText text = new StyledText(scroll, SWT.BORDER); +// text.setText("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"); // -// FontData data = text.getFont().getFontData()[0]; -// Font font1 = new Font(scroll.getDisplay(), data.getName(), data.getHeight(), data.getStyle()); +// FontData data = text.getFont().getFontData()[0]; +// Font font1 = new Font(scroll.getDisplay(), data.getName(), data.getHeight(), data.getStyle()); // -// StyleRange style1 = new StyleRange(); -// style1.start = 0; -// style1.length = 10; -// style1.fontStyle = SWT.BOLD; -// style1.font = font1; -// style1.background = Colors.PURPLE; -// text.setStyleRange(style1); +// StyleRange style1 = new StyleRange(); +// style1.start = 0; +// style1.length = 10; +// style1.fontStyle = SWT.BOLD; +// style1.font = font1; +// style1.background = Colors.PURPLE; +// text.setStyleRange(style1); + } + + private class Listener implements JavaEditorListener { + + @Override + public void fileOpened(File file) { + LOGGER.info("File opened: " + file); + parseFile(file); } @Override public void fileClosed(File file) { LOGGER.debug("File closed: " + file); + parseFile(null); } @Override public void fileSaved(File file) { LOGGER.debug("File saved: " + file); - // TODO reload/reparse file + parseFile(file); } } diff --git a/src/pa/iscde/minimap/internal/SettingsDialog.java b/src/pa/iscde/minimap/internal/SettingsDialog.java index 19d142b..9574190 100644 --- a/src/pa/iscde/minimap/internal/SettingsDialog.java +++ b/src/pa/iscde/minimap/internal/SettingsDialog.java @@ -1,5 +1,6 @@ package pa.iscde.minimap.internal; +import org.apache.log4j.Logger; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.dialogs.TitleAreaDialog; import org.eclipse.swt.SWT; @@ -7,15 +8,18 @@ import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; +import pa.iscde.minimap.internal.extension.Extension; +import pa.iscde.minimap.internal.extension.ExtensionRule; public class SettingsDialog extends TitleAreaDialog { - private Text txtFirstName; + private static final Logger LOGGER = Logger.getLogger(SettingsDialog.class); + + private Tree tree; - private String firstName; /** * Instantiate a new title area dialog. @@ -29,8 +33,8 @@ public SettingsDialog(Shell parentShell) { @Override public void create() { super.create(); - setTitle("Custom dialog"); - setMessage("This is a TitleAreaDialog", IMessageProvider.INFORMATION); + setTitle("Settings"); + setMessage("Enable your desired inspection rules.", IMessageProvider.NONE); } @Override @@ -38,24 +42,103 @@ protected Control createDialogArea(Composite parent) { Composite area = (Composite) super.createDialogArea(parent); Composite container = new Composite(area, SWT.NONE); container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - GridLayout layout = new GridLayout(2, false); - container.setLayout(layout); + container.setLayout(new GridLayout(1, false)); - createFirstName(container); + tree = new Tree(container, SWT.CHECK | SWT.BORDER); + tree.setLayoutData(container.getLayoutData()); + fillTree(tree); return area; } - private void createFirstName(Composite container) { - Label lbtFirstName = new Label(container, SWT.NONE); - lbtFirstName.setText("First Name"); + /** + * Helper method to fill a tree with data + * + * @param tree the tree to fill + */ + private void fillTree(Tree tree) { + // Turn off drawing to avoid flicker + tree.setRedraw(false); + + tree.addListener(SWT.Selection, event -> { + TreeItem item = (TreeItem) event.item; + LOGGER.debug((event.detail == SWT.CHECK ? "CHECK" : "SELECT") + " EVENT: " + item); + + if (event.detail != SWT.CHECK) { + return; + } + + // Set checked state for himself and all it's children + setCheckedState(item, item.getChecked()); + + // Refresh checked state for it's parent + if (item.getParentItem() != null) { + item.getParentItem().setChecked(getCheckedState(item.getParentItem())); + } + }); + + for (Extension ext : MinimapView.getInstance().getExtensions()) { + TreeItem item = new TreeItem(tree, SWT.NONE); + item.setText(ext.name + " [" + ext.id + ']'); + item.setData(ext); + item.setExpanded(true); + + for (ExtensionRule rule : ext.rules) { + TreeItem child = new TreeItem(item, SWT.NONE); + child.setText(rule.name + " [" + rule.id + ']'); + child.setData(rule); + + if (rule.isErrored()) { + child.setGrayed(true); + child.setChecked(false); + } else { + child.setChecked(SettingsManager.isEnabled(rule)); + } + } + + item.setChecked(getCheckedState(item)); + } + + tree.setRedraw(true); + } + + private void setCheckedState(TreeItem item, boolean checked) { + item.setChecked(checked); + for (TreeItem child : item.getItems()) { + setCheckedState(child, checked); + } + } + + /** + * Find the checked state based on it's children's state. + * + * @param item The item to find the checked state + * @return {@code true} if at least one of it's children is checked, {@code false} otherwise + */ + private boolean getCheckedState(TreeItem item) { + if (item.getItems().length < 1) { + return item.getChecked(); + } - GridData dataFirstName = new GridData(); - dataFirstName.grabExcessHorizontalSpace = true; - dataFirstName.horizontalAlignment = GridData.FILL; + for (TreeItem child : item.getItems()) { + if (getCheckedState(child)) { + return true; + } + } - txtFirstName = new Text(container, SWT.BORDER); - txtFirstName.setLayoutData(dataFirstName); + return false; + } + + private void save(TreeItem item) { + if (item.getData() instanceof ExtensionRule) { + ExtensionRule rule = (ExtensionRule) item.getData(); + rule.setEnabled(item.getChecked()); + SettingsManager.setEnabled(rule); + } + + for (TreeItem child : item.getItems()) { + save(child); + } } @Override @@ -65,9 +148,18 @@ protected void cancelPressed() { @Override protected void okPressed() { - firstName = txtFirstName.getText(); + for (TreeItem item : tree.getItems()) { + save(item); + } + + boolean changed = SettingsManager.save(); + + if (changed) { + MinimapView view = MinimapView.getInstance(); + view.parseFile(view.getActiveFile()); + } + super.okPressed(); - SettingsManager.save(); } @Override diff --git a/src/pa/iscde/minimap/internal/SettingsManager.java b/src/pa/iscde/minimap/internal/SettingsManager.java index 68910e4..ecf0314 100644 --- a/src/pa/iscde/minimap/internal/SettingsManager.java +++ b/src/pa/iscde/minimap/internal/SettingsManager.java @@ -12,8 +12,10 @@ import java.util.stream.Collectors; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import org.apache.log4j.Logger; +import pa.iscde.minimap.internal.extension.ExtensionRule; public class SettingsManager { @@ -22,7 +24,7 @@ public class SettingsManager { /** path in which the settings file is stored */ private static final Path SETTINGS_PATH = Paths.get("minimap.ini"); - private static final Gson GSON = new Gson(); + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); private static final Type SETTINGS_TYPE = new TypeToken>(){}.getType(); /** the state that new extension rules will have */ @@ -49,10 +51,10 @@ public static void load() { } } - public static void save() { + public static boolean save() { if (!dirty) { LOGGER.debug("Settings unchanged"); - return; + return false; } try { @@ -67,17 +69,29 @@ public static void save() { dirty = false; LOGGER.info("Saved settings for " + settings.size() + " rules"); } + + return true; } - public static boolean isEnabled(String ruleId) { - return settings.computeIfAbsent(ruleId, key -> { + public static boolean isEnabled(ExtensionRule rule) { + if (rule == null) { + return false; + } + + return settings.computeIfAbsent(rule.id, key -> { dirty = true; return DEFAULT_STATE; }); } - public static void setEnabled(String ruleId, boolean enabled) { - settings.put(ruleId, enabled); - dirty = true; + public static void setEnabled(ExtensionRule rule) { + if (rule == null) { + return; + } + + Boolean prev = settings.put(rule.id, rule.isEnabled()); + + // Only mark dirty if the value changed + dirty |= !((Boolean) rule.isEnabled()).equals(prev); } } diff --git a/src/pa/iscde/minimap/internal/SettingsTool.java b/src/pa/iscde/minimap/internal/SettingsTool.java index 7900f9e..f729ae7 100644 --- a/src/pa/iscde/minimap/internal/SettingsTool.java +++ b/src/pa/iscde/minimap/internal/SettingsTool.java @@ -1,29 +1,25 @@ package pa.iscde.minimap.internal; +import org.apache.log4j.Logger; import org.eclipse.jface.dialogs.IDialogConstants; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.MessageBox; import pt.iscte.pidesco.extensibility.PidescoTool; public class SettingsTool implements PidescoTool { + private static final Logger LOGGER = Logger.getLogger(SettingsTool.class); + @Override public void run(boolean selected) { -// Activator.getContext(); -// ProjectBrowserActivator.getInstance().refreshWorkspace(); - MinimapView minimap = MinimapView.getInstance(); SettingsDialog dialog = new SettingsDialog(minimap.getRoot().getShell()); int returnCode = dialog.open(); if (returnCode == IDialogConstants.OK_ID) { - System.out.println("OK clicked"); + LOGGER.debug("OK clicked"); } else if (returnCode == IDialogConstants.CANCEL_ID) { - System.out.println("CANCEL clicked"); - } else { - System.out.println("UNK: " + returnCode); + LOGGER.debug("CANCEL clicked"); } } diff --git a/src/pa/iscde/minimap/internal/extension/ExtensionRule.java b/src/pa/iscde/minimap/internal/extension/ExtensionRule.java index ef324bd..44a345e 100644 --- a/src/pa/iscde/minimap/internal/extension/ExtensionRule.java +++ b/src/pa/iscde/minimap/internal/extension/ExtensionRule.java @@ -6,6 +6,7 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import pa.iscde.minimap.extensibility.MinimapInspection; +import pa.iscde.minimap.internal.SettingsManager; import static pa.iscde.minimap.extensibility.inspections.NoopInspection.NOOP; @@ -16,9 +17,10 @@ public class ExtensionRule { public final String id; public final String name; public final String description; - public final MinimapInspection rule; + private final MinimapInspection rule; - public final boolean errored; + private final boolean errored; + private boolean enabled; public ExtensionRule(Extension ext, String id, String name, String description, MinimapInspection rule) { this.id = ext.id + ":" + id; @@ -46,6 +48,22 @@ private static MinimapInspection loadRule(Extension ext, IConfigurationElement c } } + public MinimapInspection getRule() { + return isEnabled() ? rule : NOOP; + } + + public boolean isErrored() { + return errored; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/pa/iscde/minimap/internal/parser/AstVisitor.java b/src/pa/iscde/minimap/internal/parser/AstVisitor.java index 0875fb9..7239034 100644 --- a/src/pa/iscde/minimap/internal/parser/AstVisitor.java +++ b/src/pa/iscde/minimap/internal/parser/AstVisitor.java @@ -63,11 +63,10 @@ private void inspect(final ASTNode node) { for (ExtensionRule extRule : rules) { try { - extRule.rule.inspect(node, context); + extRule.getRule().inspect(node, context); } catch (Exception e) { LOGGER.error("Error running inspection rule: " + extRule.id, e); } - } }