diff --git a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/IJsonServerConfig.java b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/IJsonServerConfig.java index 213d66200..ec6d3e10a 100644 --- a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/IJsonServerConfig.java +++ b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/IJsonServerConfig.java @@ -16,6 +16,7 @@ public interface IJsonServerConfig String VISIBLE = "visible"; //$NON-NLS-1$ String RANGES = "ranges"; //$NON-NLS-1$ String VERSION = "version"; //$NON-NLS-1$ + String DEFAULTS = "defaults"; //$NON-NLS-1$ // data types String HEX_TYPE = "hex"; //$NON-NLS-1$ @@ -29,6 +30,7 @@ public interface IJsonServerConfig String SET = "set"; //$NON-NLS-1$ String SAVE = "save"; //$NON-NLS-1$ String LOAD = "load"; //$NON-NLS-1$ + String RESET = "reset"; //$NON-NLS-1$ String COMPONENT_CONFIG_TITLE = "Component config"; //$NON-NLS-1$ diff --git a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/KConfigMenuItem.java b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/KConfigMenuItem.java index ee469912b..a117af4a5 100644 --- a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/KConfigMenuItem.java +++ b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/KConfigMenuItem.java @@ -23,6 +23,7 @@ public class KConfigMenuItem private String type; private String id; private boolean isMenuConfig; + private boolean isDefault; public KConfigMenuItem(KConfigMenuItem parent) { @@ -173,5 +174,21 @@ private boolean isVisible(JSONObject visibleJsonMap, String configKey) return visibleJsonMap.get(configKey) != null ? (boolean) visibleJsonMap.get(configKey) : false; } + public boolean isDefault() + { + return isDefault; + } + + public void setDefault(boolean isDefault) + { + this.isDefault = isDefault; + } -} \ No newline at end of file + public void updateDefaultState(JSONObject defaultsJsonMap) + { + if (defaultsJsonMap != null && defaultsJsonMap.containsKey(getId())) + { + this.isDefault = (boolean) defaultsJsonMap.get(getId()); + } + } +} diff --git a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/CommandType.java b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/CommandType.java index 7feea31d6..f86b53269 100644 --- a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/CommandType.java +++ b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/CommandType.java @@ -29,6 +29,8 @@ public enum CommandType */ SET, + RESET, + /** * To represent server connection is closed */ diff --git a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/IJsonConfigOutput.java b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/IJsonConfigOutput.java index 350a777b6..f8e072307 100644 --- a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/IJsonConfigOutput.java +++ b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/IJsonConfigOutput.java @@ -27,4 +27,8 @@ public interface IJsonConfigOutput */ public JSONObject getRangesJsonMap(); + public JSONObject getDefaultsJsonMap(); + + public long getVersion(); + } diff --git a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigOutput.java b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigOutput.java index 23bcb9cc4..5a640d1e0 100644 --- a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigOutput.java +++ b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigOutput.java @@ -24,6 +24,14 @@ public class JsonConfigOutput implements IJsonConfigOutput private JSONObject valuesJsonMap; private JSONObject visibleJsonMap; private JSONObject rangesJsonMap; + private JSONObject defaultsJsonMap; + private long version = 1; + + @Override + public long getVersion() + { + return version; + } @Override public JSONObject getValuesJsonMap() @@ -56,12 +64,18 @@ public void parse(String response, boolean isUpdate) throws ParseException JSONObject jsonObj = (JSONObject) parser.parse(response); if (jsonObj != null) { + if (jsonObj.containsKey(IJsonServerConfig.VERSION)) + { + version = (long) jsonObj.get(IJsonServerConfig.VERSION); + } + if (isUpdate) { // newly updated values and visible items JSONObject visibleJson = (JSONObject) jsonObj.get(IJsonServerConfig.VISIBLE); JSONObject valuesJson = (JSONObject) jsonObj.get(IJsonServerConfig.VALUES); JSONObject rangesJson = (JSONObject) jsonObj.get(IJsonServerConfig.RANGES); + JSONObject defaultsJson = (JSONObject) jsonObj.get(IJsonServerConfig.DEFAULTS); // Updated visible items Set newVisibleKeyset = visibleJson.keySet(); @@ -89,9 +103,15 @@ public void parse(String response, boolean isUpdate) throws ParseException valuesJsonMap = (JSONObject) jsonObj.get(IJsonServerConfig.VALUES); visibleJsonMap = (JSONObject) jsonObj.get(IJsonServerConfig.VISIBLE); rangesJsonMap = (JSONObject) jsonObj.get(IJsonServerConfig.RANGES); + defaultsJsonMap = (JSONObject) jsonObj.get(IJsonServerConfig.DEFAULTS); } } } + public JSONObject getDefaultsJsonMap() + { + return defaultsJsonMap; + } + } diff --git a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigServer.java b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigServer.java index fa7f20468..fa5d050ff 100644 --- a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigServer.java +++ b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigServer.java @@ -53,6 +53,24 @@ public JsonConfigServer(IProject project, IFile file) this.file = file; } + public void resetElementById(String id) + { + String resetRequest = String.format("{\"version\": 3, \"reset\": [\"%s\"]}", id); + execute(resetRequest, CommandType.RESET); + } + + public void resetElementChildren(List children) + { + if (children == null || children.isEmpty()) + { + return; + } + + String joinedIds = String.join("\", \"", children); + String resetRequest = String.format("{\"version\": 3, \"reset\": [\"%s\"]}", joinedIds); + execute(resetRequest, CommandType.RESET); + } + @Override public void addListener(IMessageHandlerListener listener) { diff --git a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigServerRunnable.java b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigServerRunnable.java index ebb5eb566..057e85609 100644 --- a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigServerRunnable.java +++ b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigServerRunnable.java @@ -200,26 +200,20 @@ protected boolean isValidJson(String output) { String jsonOutput = new JsonConfigProcessor().getInitialOutput(output); if (StringUtil.isEmpty(jsonOutput)) - { return false; - } + try { JSONObject jsonObj = (JSONObject) new JSONParser().parse(jsonOutput); if (jsonObj != null) { - if (jsonObj.get(IJsonServerConfig.VISIBLE) != null && jsonObj.get(IJsonServerConfig.VALUES) != null - && jsonObj.get(IJsonServerConfig.RANGES) != null) - { - return true; - } + return jsonObj.containsKey(IJsonServerConfig.VERSION) && jsonObj.containsKey(IJsonServerConfig.VALUES); } } catch (ParseException e) { return false; } - return false; } diff --git a/bundles/com.espressif.idf.sdk.config.ui/icons/reset.png b/bundles/com.espressif.idf.sdk.config.ui/icons/reset.png new file mode 100644 index 000000000..69a4d72f2 Binary files /dev/null and b/bundles/com.espressif.idf.sdk.config.ui/icons/reset.png differ diff --git a/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/ConfigActionHandler.java b/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/ConfigActionHandler.java new file mode 100644 index 000000000..6146d81a9 --- /dev/null +++ b/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/ConfigActionHandler.java @@ -0,0 +1,16 @@ +package com.espressif.idf.sdk.config.ui; + +import org.json.simple.JSONObject; + +import com.espressif.idf.sdk.config.core.KConfigMenuItem; + +public interface ConfigActionHandler +{ + void onCommandExecuted(JSONObject jsonMap); + + void onTextModified(String key, Object value); + + void onResetRequested(String key); + + void onMenuResetRequested(KConfigMenuItem menu); +} diff --git a/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/ConfigUIRenderer.java b/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/ConfigUIRenderer.java new file mode 100644 index 000000000..2822aee59 --- /dev/null +++ b/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/ConfigUIRenderer.java @@ -0,0 +1,336 @@ +package com.espressif.idf.sdk.config.ui; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.PlatformUI; +import org.json.simple.JSONObject; + +import com.espressif.idf.core.logging.Logger; +import com.espressif.idf.sdk.config.core.IJsonServerConfig; +import com.espressif.idf.sdk.config.core.KConfigMenuItem; +import com.espressif.idf.ui.dialogs.HelpPopupDialog; + +public class ConfigUIRenderer +{ + + private static final String ICONS_INFO_OBJ_GIF = "icons/help.gif"; //$NON-NLS-1$ + private static final String ICONS_SDK_RESET_ACTION_PNG = "icons/reset.png"; //$NON-NLS-1$ + + private final Composite parent; + private final JSONObject valuesMap; + private final JSONObject visibleMap; + private final JSONObject modifiedMap; + private final JSONObject rangesMap; + private final ConfigActionHandler actionHandler; + private final boolean isResetSupported; + private HelpPopupDialog infoDialog; + + private record RenderContext(KConfigMenuItem item, String configKey, Object configValue, Object modifiedValue, + boolean isVisible, String helpInfo) + { + public RenderContext(KConfigMenuItem item, JSONObject visibleMap, JSONObject valuesMap, JSONObject modifiedMap) + { + this(item, item.getId(), valuesMap.get(item.getId()), modifiedMap.get(item.getId()), + Boolean.TRUE.equals(visibleMap.get(item.getId())), item.getHelp()); + } + } + + public ConfigUIRenderer(Composite parent, JSONObject valuesMap, JSONObject visibleMap, JSONObject modifiedMap, + JSONObject rangesMap, boolean isResetSupported, ConfigActionHandler actionHandler) + { + this.parent = parent; + this.valuesMap = valuesMap; + this.visibleMap = visibleMap; + this.modifiedMap = modifiedMap; + this.rangesMap = rangesMap; + this.isResetSupported = isResetSupported; + this.actionHandler = actionHandler; + } + + public void renderMenuItems(KConfigMenuItem selectedElement) + { + if (selectedElement == null || selectedElement.getChildren() == null) + return; + + for (KConfigMenuItem item : selectedElement.getChildren()) + { + var ctx = new RenderContext(item, visibleMap, valuesMap, modifiedMap); + String type = item.getType(); + + if (!ctx.isVisible() && !type.equals(IJsonServerConfig.MENU_TYPE)) + { + continue; + } + + switch (type) + { + case IJsonServerConfig.STRING_TYPE -> renderString(ctx); + case IJsonServerConfig.HEX_TYPE -> renderHex(ctx); + case IJsonServerConfig.INT_TYPE -> renderInt(ctx); + case IJsonServerConfig.BOOL_TYPE -> renderBool(ctx); + case IJsonServerConfig.CHOICE_TYPE -> renderChoice(ctx); + case IJsonServerConfig.MENU_TYPE -> renderMenuItems(item); + default -> Logger.log("Unhandled config type: " + type); + } + + if (!type.equals(IJsonServerConfig.MENU_TYPE) && !type.equals(IJsonServerConfig.CHOICE_TYPE)) + { + addTooltipImage(ctx); + addResetButton(ctx); + } + + if (item.hasChildren() && !type.equals(IJsonServerConfig.MENU_TYPE) + && !type.equals(IJsonServerConfig.CHOICE_TYPE) && !item.isMenuConfig()) + { + renderMenuItems(item); + } + } + } + + private void renderString(RenderContext ctx) + { + var labelName = new Label(parent, SWT.NONE); + labelName.setText(ctx.item().getTitle()); + + var textControl = new Text(parent, SWT.SINGLE | SWT.BORDER); + var gridData = new GridData(); + gridData.widthHint = 250; + textControl.setLayoutData(gridData); + textControl.setToolTipText(ctx.helpInfo()); + + if (ctx.configValue() != null) + { + textControl + .setText(ctx.modifiedValue() != null ? (String) ctx.modifiedValue() : (String) ctx.configValue()); + } + + textControl.addModifyListener(e -> { + String text = textControl.getText(); + actionHandler.onTextModified(ctx.configKey(), text.trim()); + }); + } + + private void renderHex(RenderContext ctx) + { + var labelName = new Label(parent, SWT.NONE); + labelName.setText(ctx.item().getTitle().concat(" (hex)")); + + var textControl = new Text(parent, SWT.SINGLE | SWT.BORDER); + var gridData = new GridData(); + gridData.widthHint = 250; + textControl.setLayoutData(gridData); + textControl.setToolTipText(ctx.helpInfo()); + + if (ctx.configValue() != null) + { + long valToFormat = ctx.modifiedValue() != null ? (long) ctx.modifiedValue() : (long) ctx.configValue(); + textControl.setText("0x" + Long.toHexString(valToFormat).toUpperCase()); + } + + textControl.addModifyListener(e -> { + String text = textControl.getText().toLowerCase(); + if (text.startsWith("0x") && text.length() > 2) + { + try + { + long hexVal = Long.parseLong(text.substring(2), 16); + actionHandler.onTextModified(ctx.configKey(), hexVal); + } + catch (NumberFormatException ignored) + { + } + } + }); + } + + private void renderInt(RenderContext ctx) + { + var labelName = new Label(parent, SWT.NONE); + labelName.setText(ctx.item().getTitle()); + + var textControl = new Text(parent, SWT.SINGLE | SWT.BORDER); + var gridData = new GridData(); + gridData.widthHint = 250; + textControl.setLayoutData(gridData); + textControl.setToolTipText(ctx.helpInfo()); + + if (ctx.configValue() != null) + { + textControl.setText(ctx.modifiedValue() != null ? String.valueOf(ctx.modifiedValue()) + : String.valueOf(ctx.configValue())); + } + + textControl.addModifyListener(e -> actionHandler.onTextModified(ctx.configKey(), textControl.getText().trim())); + } + + private void renderBool(RenderContext ctx) + { + var button = new Button(parent, SWT.CHECK); + button.setText(ctx.item().getTitle()); + button.setLayoutData(new GridData(SWT.NONE, SWT.CENTER, false, false, 2, 1)); + button.setToolTipText(ctx.helpInfo()); + + if (ctx.configValue() != null) + { + button.setSelection((boolean) ctx.configValue()); + } + + button.addSelectionListener(new SelectionAdapter() + { + @Override + public void widgetSelected(SelectionEvent e) + { + var jsonObj = new JSONObject(); + jsonObj.put(ctx.configKey(), button.getSelection()); + actionHandler.onCommandExecuted(jsonObj); + } + }); + } + + private void renderChoice(RenderContext ctx) + { + var labelName = new Label(parent, SWT.NONE); + labelName.setText(ctx.item().getTitle()); + + var choiceCombo = new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY); + var gridData = new GridData(); + gridData.widthHint = 250; + choiceCombo.setLayoutData(gridData); + + int index = 0; + for (KConfigMenuItem child : ctx.item().getChildren()) + { + String localConfigKey = child.getId(); + if (Boolean.TRUE.equals(visibleMap.get(localConfigKey))) + { + choiceCombo.add(child.getTitle()); + choiceCombo.setData(child.getTitle(), localConfigKey); + + if (Boolean.TRUE.equals(valuesMap.get(localConfigKey))) + { + choiceCombo.select(index); + } + index++; + } + } + + choiceCombo.addSelectionListener(new SelectionAdapter() + { + @Override + public void widgetSelected(SelectionEvent e) + { + String key = (String) choiceCombo.getData(choiceCombo.getText()); + if (key != null) + { + var jsonObj = new JSONObject(); + jsonObj.put(key, true); + actionHandler.onCommandExecuted(jsonObj); + } + } + }); + + addTooltipImage(ctx); + addResetButton(ctx); + } + + private void addTooltipImage(RenderContext ctx) + { + var labelName = new Label(parent, SWT.NONE); + labelName.setImage(SDKConfigUIPlugin.getImage(ICONS_INFO_OBJ_GIF)); + labelName.setToolTipText("Help"); + + labelName.addListener(SWT.MouseUp, event -> { + String message = """ + %s + + %s + """.formatted(ctx.item().getTitle(), ctx.helpInfo()); //$NON-NLS-1$ + + Object range = rangesMap.get(ctx.configKey()); + if (range != null) + { + message += """ + + Range Information: + %s + """.formatted(range.toString()); //$NON-NLS-1$ + } + + var activeShell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); + if (infoDialog != null) + infoDialog.close(); + + infoDialog = new HelpPopupDialog(activeShell, "Help > " + ctx.configKey(), message); + infoDialog.open(); + }); + } + + private void addResetButton(RenderContext ctx) + { + if (!isResetSupported) + { + new Label(parent, SWT.NONE).setLayoutData(new GridData(SWT.END, SWT.CENTER, false, false)); + return; + } + + var resetIconComposite = new Composite(parent, SWT.NONE); + resetIconComposite.setLayout(new FillLayout()); + resetIconComposite.setLayoutData(new GridData(SWT.END, SWT.CENTER, false, false)); + + var resetIcon = new Label(resetIconComposite, SWT.NONE); + resetIcon.setImage(SDKConfigUIPlugin.getImage(ICONS_SDK_RESET_ACTION_PNG)); + resetIcon.setToolTipText("Reset '" + ctx.item().getTitle() + "' to default value"); + + resetIcon.addListener(SWT.MouseUp, event -> { + if (ctx.configKey() != null) + { + actionHandler.onResetRequested(ctx.configKey()); + } + }); + } + + public void renderFullMenu(KConfigMenuItem selectedElement) + { + addResetMenuButton(selectedElement); + renderMenuItems(selectedElement); + } + + private void addResetMenuButton(KConfigMenuItem selectedElement) + { + if (!isResetSupported) + { + return; + } + + var resetGroupButton = new Button(parent, SWT.PUSH); + resetGroupButton.setText("Reset Menu Defaults"); + resetGroupButton.setLayoutData(new GridData(SWT.END, SWT.CENTER, false, false, 4, 1)); + resetGroupButton.setToolTipText("Reset all settings in this menu"); + + resetGroupButton.addSelectionListener(new SelectionAdapter() + + { + @Override + public void widgetSelected(SelectionEvent e) + { + boolean confirm = MessageDialog.openConfirm(parent.getShell(), "Reset Menu Configurations", + "This action will reset all configurations under '" + selectedElement.getTitle() + + "' to their default values. Continue?"); + + if (confirm) + { + actionHandler.onMenuResetRequested(selectedElement); + } + } + }); + } +} diff --git a/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/SDKConfigurationEditor.java b/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/SDKConfigurationEditor.java index ebff2c161..c093b3241 100644 --- a/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/SDKConfigurationEditor.java +++ b/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/SDKConfigurationEditor.java @@ -8,8 +8,11 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; @@ -25,30 +28,18 @@ import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.PageChangedEvent; import org.eclipse.jface.operation.IRunnableWithProgress; -import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.custom.StyledText; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Listener; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorSite; @@ -65,7 +56,7 @@ import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.part.MultiPageEditorPart; import org.eclipse.ui.progress.IProgressService; -import org.eclipse.ui.texteditor.AbstractTextEditor; +import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.ParseException; @@ -84,13 +75,11 @@ import com.espressif.idf.sdk.config.core.server.JsonConfigProcessor; import com.espressif.idf.sdk.config.core.server.JsonConfigServer; import com.espressif.idf.ui.IDFConsole; -import com.espressif.idf.ui.dialogs.HelpPopupDialog; /** * SDK Configuration editor which represents the UI for the all sdkconfig fields * * @author Kondal Kolipaka - * */ @SuppressWarnings("unchecked") public class SDKConfigurationEditor extends MultiPageEditorPart @@ -99,10 +88,6 @@ public class SDKConfigurationEditor extends MultiPageEditorPart public static String EDITOR_ID = "com.espressif.idf.sdk.config.ui.editor"; //$NON-NLS-1$ - private static final String ICONS_INFO_OBJ_GIF = "icons/help.gif"; //$NON-NLS-1$ - - private static final String ICONS_SDK_TOOL_CONFIG_PNG = "icons/sdk_tool_config.png"; //$NON-NLS-1$ - private TreeViewer treeViewer; private Group updateUIComposite; @@ -128,12 +113,12 @@ public class SDKConfigurationEditor extends MultiPageEditorPart */ private JSONObject modifiedJsonMap = new JSONObject(); - private HelpPopupDialog infoDialog; - private CommandType type; private ScrolledComposite sc; + private static final int MIN_VERSION_FOR_RESET = 3; + public SDKConfigurationEditor() { super(); @@ -224,7 +209,7 @@ private void createSourcePage() try { TextEditor editor = new TextEditor(); - Object control = ((AbstractTextEditor) editor).getAdapter(Control.class); + Object control = (editor).getAdapter(Control.class); if (control instanceof StyledText) { ((StyledText) control).setEditable(false); @@ -370,7 +355,7 @@ protected IProject getProject() * @throws IOException * @throws ParseException */ - protected void initConfigServer(IProject project) throws IOException, ParseException + protected void initConfigServer(IProject project) throws IOException { // Create console @@ -402,7 +387,7 @@ protected void initConfigServer(IProject project) throws IOException, ParseExcep } } - protected void update() throws ParseException + protected void update() { JsonConfigProcessor jsonProcessor = new JsonConfigProcessor(); String response = jsonProcessor.getInitialOutput(serverMessage); @@ -425,7 +410,7 @@ protected void update() throws ParseException * @return * @throws IOException */ - private boolean isReady(int maxAttempts, long sleepInterval, JsonConfigProcessor jsonProcessor) throws IOException + private boolean isReady(int maxAttempts, long sleepInterval, JsonConfigProcessor jsonProcessor) { int waitCount = 0; while (serverMessage == null || jsonProcessor.getInitialOutput(serverMessage) == null) @@ -470,9 +455,22 @@ public void dispose() public void doSave(IProgressMonitor monitor) { JSONObject jsonObject = new JSONObject(); - jsonObject.put(IJsonServerConfig.VERSION, 2); - jsonObject.put(IJsonServerConfig.SET, modifiedJsonMap); - jsonObject.put(IJsonServerConfig.SAVE, null); + var version = configServer.getOutput().getVersion(); + jsonObject.put(IJsonServerConfig.VERSION, version); + + if (!modifiedJsonMap.isEmpty()) + { + jsonObject.put(IJsonServerConfig.SET, modifiedJsonMap); + + valuesJsonMap.putAll(modifiedJsonMap); + } + else + { + jsonObject.put(IJsonServerConfig.SET, new JSONObject()); + } + + String filePath = getFile().getLocation().toOSString(); + jsonObject.put(IJsonServerConfig.SAVE, filePath); String command = jsonObject.toJSONString(); configServer.execute(command, CommandType.SAVE); @@ -540,20 +538,14 @@ public KConfigMenuItem getInitalInput() protected void hookListeners() { - treeViewer.addSelectionChangedListener(new ISelectionChangedListener() + treeViewer.addSelectionChangedListener(event -> { - @Override - public void selectionChanged(SelectionChangedEvent event) + if (event.getSelection() instanceof IStructuredSelection) { - if (event.getSelection() instanceof IStructuredSelection) - { - KConfigMenuItem selectedElement = (KConfigMenuItem) event.getStructuredSelection() - .getFirstElement(); - updateUI(selectedElement); + KConfigMenuItem selectedElement = (KConfigMenuItem) event.getStructuredSelection().getFirstElement(); + updateUI(selectedElement); - } } - }); } @@ -562,7 +554,7 @@ private FilteredTree createFilteredTree(Group group) { PatternFilter patternFilter = new SDKConfigurationFilter(); int style = SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER; - FilteredTree transfersTree = new FilteredTree(group, style, patternFilter, true) + return new FilteredTree(group, style, patternFilter, true) { @Override protected TreeViewer doCreateTreeViewer(Composite parent, int style) @@ -570,7 +562,6 @@ protected TreeViewer doCreateTreeViewer(Composite parent, int style) return new TreeViewer(parent, style); } }; - return transfersTree; } private void updateUI(KConfigMenuItem selectedElement) @@ -583,311 +574,152 @@ private void updateUI(KConfigMenuItem selectedElement) this.selectedElement = selectedElement; - // dispose old elements - Control[] updateUICompositeControls = updateUIComposite.getChildren(); - for (Control control : updateUICompositeControls) + for (Control control : updateUIComposite.getChildren()) { control.dispose(); } - updateUIComposite.setLayout((new GridLayout(3, false))); + updateUIComposite.setLayout((new GridLayout(4, false))); updateUIComposite.setText(selectedElement.getTitle()); + GridData updateCompsiteGD = new GridData(SWT.FILL, SWT.FILL, true, true); updateCompsiteGD.verticalIndent = 10; updateUIComposite.setLayoutData(updateCompsiteGD); - renderMenuItems(selectedElement); - - sc.setContent(updateUIComposite); - sc.setExpandHorizontal(true); - sc.setExpandVertical(true); - sc.setMinSize(updateUIComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); - sc.setShowFocusedControl(true); - - updateUIComposite.layout(true); - } - - protected void renderMenuItems(KConfigMenuItem selectedElement) - { - // add children here - List children = selectedElement.getChildren(); - for (KConfigMenuItem kConfigMenuItem : children) + ConfigActionHandler handler = new ConfigActionHandler() { - String type = kConfigMenuItem.getType(); - String configKey = kConfigMenuItem.getId(); - Object configValue = valuesJsonMap.get(configKey); - boolean isVisible = (visibleJsonMap.get(configKey) != null ? (boolean) visibleJsonMap.get(configKey) - : false); - Object newConfigValue = modifiedJsonMap.get(configKey); - String helpInfo = kConfigMenuItem.getHelp(); - if (isVisible && type.equals(IJsonServerConfig.STRING_TYPE)) + @Override + public void onCommandExecuted(JSONObject jsonMap) { - Label labelName = new Label(updateUIComposite, SWT.NONE); - labelName.setText(kConfigMenuItem.getTitle()); - - Text textControl = new Text(updateUIComposite, SWT.SINGLE | SWT.BORDER); - GridData gridData = new GridData(); - gridData.widthHint = 250; - textControl.setLayoutData(gridData); - textControl.setToolTipText(helpInfo); - if (configValue != null) - { - textControl.setText(newConfigValue != null ? (String) newConfigValue : (String) configValue); - } - textControl.addModifyListener(addModifyListener(configKey, textControl)); - addTooltipImage(kConfigMenuItem); - + executeCommand(jsonMap); } - else if (isVisible && type.equals(IJsonServerConfig.HEX_TYPE)) - { - Label labelName = new Label(updateUIComposite, SWT.NONE); - labelName.setText(kConfigMenuItem.getTitle().concat(" (hex)")); - - Text textControl = new Text(updateUIComposite, SWT.SINGLE | SWT.BORDER); - GridData gridData = new GridData(); - gridData.widthHint = 250; - textControl.setLayoutData(gridData); - textControl.setToolTipText(helpInfo); - if (configValue != null) - { - String hexText = newConfigValue != null ? Long.toHexString((long) newConfigValue) - : Long.toHexString((long) configValue); - textControl.setText("0x" + hexText.toUpperCase()); - } - textControl.addModifyListener(addModifyListener(configKey, textControl)); - addTooltipImage(kConfigMenuItem); - } - else if (kConfigMenuItem.isMenuConfig()) + @Override + public void onTextModified(String key, Object value) { - Button button = createCheckBox(kConfigMenuItem, configKey, configValue, helpInfo); - button.addSelectionListener(new SelectionAdapter() - { - @Override - public void widgetSelected(SelectionEvent e) - { - renderMenuItems(kConfigMenuItem); - } - - }); - if (kConfigMenuItem.hasChildren()) - { - button.setImage(SDKConfigUIPlugin.getImage(ICONS_SDK_TOOL_CONFIG_PNG)); - } - - if (button.getSelection()) - { - renderMenuItems(kConfigMenuItem); - } - + isDirty = true; + editorDirtyStateChanged(); + modifiedJsonMap.put(key, value); } - else if (isVisible && type.equals(IJsonServerConfig.BOOL_TYPE)) + + @Override + public void onResetRequested(String key) { - createCheckBox(kConfigMenuItem, configKey, configValue, helpInfo); + executeResetCommand(key); } - else if (isVisible && type.equals(IJsonServerConfig.INT_TYPE)) + @Override + public void onMenuResetRequested(KConfigMenuItem menu) { - Label labelName = new Label(updateUIComposite, SWT.NONE); - labelName.setText(kConfigMenuItem.getTitle()); + List childIds = new ArrayList<>(); + collectAllChildIds(menu, childIds); - Text text = new Text(updateUIComposite, SWT.SINGLE | SWT.BORDER); - GridData gridData = new GridData(); - gridData.widthHint = 250; - text.setLayoutData(gridData); - text.setToolTipText(helpInfo); - - if (configValue != null) + if (!childIds.isEmpty()) { - text.setText(newConfigValue != null ? String.valueOf(newConfigValue) : String.valueOf(configValue)); - + executeResetChildrenCommand(childIds); } - text.addModifyListener(addModifyListener(configKey, text)); - addTooltipImage(kConfigMenuItem); - - } - else if (isVisible && type.equals(IJsonServerConfig.MENU_TYPE)) - { - renderMenuItems(kConfigMenuItem); - } - else if (type.equals(IJsonServerConfig.CHOICE_TYPE)) - { - Logger.logTrace(SDKConfigUIPlugin.getDefault(), - "Config key >" + configKey + " visiblity status >" + isVisible); //$NON-NLS-1$ //$NON-NLS-2$ - - if (isExist(visibleJsonMap, configKey)) + else if (menu.getId() != null) { - List choiceItems = kConfigMenuItem.getChildren(); - Label labelName = new Label(updateUIComposite, SWT.NONE); - labelName.setText(kConfigMenuItem.getTitle()); - - Combo choiceCombo = new Combo(updateUIComposite, SWT.DROP_DOWN | SWT.READ_ONLY); - choiceCombo.setLayoutData(new GridData(SWT.NONE, SWT.NONE, false, false, 1, 1)); - - GridData gridData = new GridData(); - gridData.widthHint = 250; - choiceCombo.setLayoutData(gridData); + executeResetCommand(menu.getId()); + } + } + }; - int index = 0; - for (KConfigMenuItem item : choiceItems) - { - String localConfigKey = item.getId(); - if (isExist(visibleJsonMap, localConfigKey)) - { - choiceCombo.add(item.getTitle()); - choiceCombo.setData(item.getTitle(), localConfigKey); - - // Check if this selected? - if (isExist(valuesJsonMap, localConfigKey)) - { - choiceCombo.select(index); - } - index++; - } + ConfigUIRenderer renderer = new ConfigUIRenderer(updateUIComposite, valuesJsonMap, visibleJsonMap, + modifiedJsonMap, rangesJsonMap, isResetSupported(), handler); - } + renderer.renderFullMenu(selectedElement); - choiceCombo.addSelectionListener(new SelectionAdapter() - { - @Override - public void widgetSelected(SelectionEvent e) - { - Object key = choiceCombo.getData(choiceCombo.getText()); - if (key != null) - { - JSONObject jsonObj = new JSONObject(); - jsonObj.put(key, true); - executeCommand(jsonObj); - - } - } - }); - addTooltipImage(kConfigMenuItem); - } - } + sc.setContent(updateUIComposite); + sc.setExpandHorizontal(true); + sc.setExpandVertical(true); + sc.setMinSize(updateUIComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); + sc.setShowFocusedControl(true); - // kConfigMenuItem has children? - if (!type.equals(IJsonServerConfig.CHOICE_TYPE) && !type.equals(IJsonServerConfig.MENU_TYPE) - && kConfigMenuItem.hasChildren()) - { - renderMenuItems(kConfigMenuItem); - } - } + updateUIComposite.layout(true); + } + private boolean isResetSupported() + { + return configServer.getOutput().getVersion() >= MIN_VERSION_FOR_RESET; } - private Button createCheckBox(KConfigMenuItem kConfigMenuItem, String configKey, Object configValue, - String helpInfo) + /** + * Recursively collects all configuration IDs for a menu and its sub-menus + */ + private void collectAllChildIds(KConfigMenuItem item, List childIds) { - Button button = new Button(updateUIComposite, SWT.CHECK); - button.setText(kConfigMenuItem.getTitle()); - button.setLayoutData(new GridData(SWT.NONE, SWT.NONE, false, false, 2, 1)); - button.setToolTipText(helpInfo); - if (configValue != null) + if (item == null || item.getChildren() == null) { - button.setSelection((boolean) configValue); + return; } - button.addSelectionListener(new SelectionAdapter() + for (KConfigMenuItem child : item.getChildren()) { - @Override - public void widgetSelected(SelectionEvent e) + if (child.getId() != null && !child.getId().isEmpty()) { - JSONObject jsonObj = new JSONObject(); - jsonObj.put(configKey, button.getSelection()); - executeCommand(jsonObj); + childIds.add(child.getId()); } - - }); - addTooltipImage(kConfigMenuItem); - return button; + collectAllChildIds(child, childIds); + } } - protected boolean isExist(JSONObject jsonMap, String key) + protected void executeCommand(JSONObject jsonObj) { - return jsonMap.get(key) != null ? (boolean) jsonMap.get(key) : false; - } + isDirty = true; + editorDirtyStateChanged(); - protected void addTooltipImage(KConfigMenuItem kConfigMenuItem) - { - Label labelName = new Label(updateUIComposite, SWT.NONE); - labelName.setImage(SDKConfigUIPlugin.getImage(ICONS_INFO_OBJ_GIF)); - labelName.addListener(SWT.MouseUp, getMouseClickListener(kConfigMenuItem)); - labelName.setToolTipText(Messages.SDKConfigurationEditor_Help); + valuesJsonMap.putAll(jsonObj); + + JSONObject jsonObject = new JSONObject(); + var version = configServer.getOutput().getVersion(); + jsonObject.put(IJsonServerConfig.VERSION, version); + jsonObject.put(IJsonServerConfig.SET, jsonObj); + + String command = jsonObject.toJSONString(); + configServer.execute(command, CommandType.SET); } - private Listener getMouseClickListener(KConfigMenuItem kConfigMenuItem) + protected void executeResetCommand(String idToReset) { - return new Listener() + long version = configServer.getOutput().getVersion(); + if (version >= MIN_VERSION_FOR_RESET) { - @Override - public void handleEvent(Event event) - { - String help = kConfigMenuItem.getHelp(); - String title = kConfigMenuItem.getTitle(); - String configKey = kConfigMenuItem.getId(); - - // frame message - StringBuilder message = new StringBuilder(); - message.append(title); - message.append("\n\n"); //$NON-NLS-1$ - message.append(help); - - // get range info - Object range = rangesJsonMap.get(configKey); - if (range != null) - { - message.append("\n\n"); //$NON-NLS-1$ - message.append("Range Information:"); //$NON-NLS-1$ - message.append("\n"); //$NON-NLS-1$ - message.append(range.toString()); - } + isDirty = true; + editorDirtyStateChanged(); - if (StringUtil.isEmpty(help)) - { - String msg = MessageFormat.format(Messages.SDKConfigurationEditor_NoHelpAvailable, configKey); - Logger.log(SDKConfigUIPlugin.getDefault(), msg); - return; - } - Shell activeShell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); - if (infoDialog != null) - { - infoDialog.close(); - } - infoDialog = new HelpPopupDialog(activeShell, Messages.SDKConfigurationEditor_Help + " > " + configKey, //$NON-NLS-1$ - message.toString()); // $NON-NLS-2$ - infoDialog.open(); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(IJsonServerConfig.VERSION, version); + + JSONArray resetArray = new JSONArray(); + resetArray.add(idToReset); + jsonObject.put(IJsonServerConfig.RESET, resetArray); + + String command = jsonObject.toJSONString(); + configServer.execute(command, CommandType.RESET); + } - } - }; } - protected ModifyListener addModifyListener(String configKey, Text textControl) + protected void executeResetChildrenCommand(List idsToReset) { - return new ModifyListener() + long version = configServer.getOutput().getVersion(); + if (version >= MIN_VERSION_FOR_RESET) { - @Override - public void modifyText(ModifyEvent e) - { - String text = textControl.getText().toLowerCase(); - boolean isHex = text.startsWith("0x"); - isDirty = true; - editorDirtyStateChanged(); - modifiedJsonMap.put(configKey, - isHex ? Long.parseLong(text.substring(2), 16) : textControl.getText().trim()); - } - }; - } + if (idsToReset == null || idsToReset.isEmpty()) + return; - protected void executeCommand(JSONObject jsonObj) - { - isDirty = true; - editorDirtyStateChanged(); + isDirty = true; + editorDirtyStateChanged(); - JSONObject jsonObject = new JSONObject(); - jsonObject.put(IJsonServerConfig.VERSION, 2); - jsonObject.put(IJsonServerConfig.SET, jsonObj); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(IJsonServerConfig.VERSION, version); - String command = jsonObject.toJSONString(); - configServer.execute(command, CommandType.SET); + JSONArray resetArray = new JSONArray(); + resetArray.addAll(idsToReset); + jsonObject.put(IJsonServerConfig.RESET, resetArray); + + String command = jsonObject.toJSONString(); + configServer.execute(command, CommandType.RESET); + } } @Override @@ -905,9 +737,8 @@ public void editorDirtyStateChanged() } /* - * (non-Javadoc) - * - * @see com.espressif.idf.sdk.config.core.server.IMessageHandlerListener#notifyRequestServed(java.lang.String) + * (non-Javadoc) * @see + * com.espressif.idf.sdk.config.core.server.IMessageHandlerListener#notifyRequestServed(java.lang.String) */ @Override public void notifyRequestServed(String message, CommandType type) @@ -918,38 +749,50 @@ public void notifyRequestServed(String message, CommandType type) if (selectedElement != null) { - try + if (type == CommandType.LOAD) + { + modifiedJsonMap.clear(); + Display.getDefault().asyncExec(this::editorDirtyStateChanged); + } + else if (type == CommandType.RESET) { - // reset the modified map - if (type == CommandType.LOAD) + Pattern pattern = Pattern.compile("Reset\\s+([A-Za-z0-9_]+)\\s+to default value"); + Matcher matcher = pattern.matcher(serverMessage); + while (matcher.find()) { - modifiedJsonMap.clear(); - isDirty = false; - editorDirtyStateChanged(); + String resetKey = matcher.group(1); + modifiedJsonMap.remove(resetKey); } - // fetch the latest values - update(); + isDirty = true; + Display.getDefault().asyncExec(this::editorDirtyStateChanged); + } - // Update in UI thread - Display.getDefault().asyncExec(new Runnable() + else if (type == CommandType.SAVE) { - @Override - public void run() - { - if (!treeViewer.getControl().isDisposed()) + Display.getDefault().asyncExec(() -> { + try { - treeViewer.refresh(); - updateUI(selectedElement); + getFile().refreshLocal(org.eclipse.core.resources.IResource.DEPTH_ZERO, + new NullProgressMonitor()); } + catch (CoreException e) + { + Logger.log(SDKConfigUIPlugin.getDefault(), e); + } + }); + } + + update(); + + Display.getDefault().asyncExec(() -> + { + if (!treeViewer.getControl().isDisposed()) + { + treeViewer.refresh(); + updateUI(selectedElement); } }); - } - catch (ParseException e1) - { - Logger.log(SDKConfigUIPlugin.getDefault(), e1); - } - } } @@ -971,7 +814,6 @@ public void pageChanged(PageChangedEvent event) { doSave(new NullProgressMonitor()); } - return; } } @@ -991,7 +833,7 @@ private String getCurrentBuildFolder() String buildFolder = StringUtil.EMPTY; try { - buildFolder = IDFUtil.getBuildDir(project); + IDFUtil.getBuildDir(project); } catch (CoreException e) { @@ -1028,5 +870,4 @@ private Optional getSdkConfigParentFolderOpt() } return Optional.empty(); } - }