You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by ju...@apache.org on 2019/05/25 00:14:58 UTC
[netbeans] branch master updated: [NETBEANS-1979] Create Mode from
client code (#1135)
This is an automated email from the ASF dual-hosted git repository.
junichi11 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 3b6c4f5 [NETBEANS-1979] Create Mode from client code (#1135)
3b6c4f5 is described below
commit 3b6c4f588a5602bebcf7d5ac1e7132c88a156ad3
Author: Mark Phipps <mw...@gmail.com>
AuthorDate: Sat May 25 01:14:53 2019 +0100
[NETBEANS-1979] Create Mode from client code (#1135)
Allow any kind of Mode to be created programatically from a ModeConfig XML String so that a TopComponent may then be programatically docked into that Mode.
---
.../src/org/netbeans/core/windows/ModeImpl.java | 18 +-
.../netbeans/core/windows/PersistenceHandler.java | 2 +-
.../netbeans/core/windows/WindowManagerImpl.java | 64 +++++++
.../core/windows/persistence/ModeConfig.java | 6 +
.../core/windows/persistence/ModeParser.java | 133 ++++++++++++---
.../windows/persistence/PersistenceManager.java | 13 +-
.../windows/persistence/WindowManagerParser.java | 109 +++++++++---
.../core/windows/WindowManagerModeTest.java | 188 +++++++++++++++++++++
.../core/windows/persistence/ModeParserTest.java | 2 +-
platform/openide.windows/apichanges.xml | 17 ++
platform/openide.windows/manifest.mf | 2 +-
.../src/org/openide/windows/Mode.java | 16 ++
.../src/org/openide/windows/ModeUtilities.java | 63 +++++++
.../src/org/openide/windows/WindowManager.java | 38 +++++
14 files changed, 617 insertions(+), 54 deletions(-)
diff --git a/platform/core.windows/src/org/netbeans/core/windows/ModeImpl.java b/platform/core.windows/src/org/netbeans/core/windows/ModeImpl.java
index 9a3ec40..2b2dc6a 100644
--- a/platform/core.windows/src/org/netbeans/core/windows/ModeImpl.java
+++ b/platform/core.windows/src/org/netbeans/core/windows/ModeImpl.java
@@ -32,7 +32,11 @@ import java.util.Set;
import javax.swing.SwingUtilities;
import java.awt.Image;
import java.awt.Rectangle;
+import java.io.IOException;
import java.util.Collection;
+import org.netbeans.core.windows.persistence.ModeConfig;
+import org.netbeans.core.windows.persistence.PersistenceManager;
+import org.openide.util.Exceptions;
/** This class is an implementation of Mode interface.
@@ -40,7 +44,7 @@ import java.util.Collection;
*
* @author Peter Zavadsky
*/
-public final class ModeImpl implements Mode {
+public final class ModeImpl implements Mode.Xml {
/** Name constant as a base for nonamed modes. */
private static final String MODE_ANONYMOUS_NAME = "anonymousMode"; // NOI18N
@@ -544,6 +548,16 @@ public final class ModeImpl implements Mode {
getCentral().setModeName(this, text);
}
-
+ @Override
+ public String toXml() {
+ ModeConfig config = PersistenceHandler.getDefault().getConfigFromMode(this);
+ try {
+ return PersistenceManager.getDefault().createXmlFromMode(config);
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ return "";
+ }
+ }
+
}
diff --git a/platform/core.windows/src/org/netbeans/core/windows/PersistenceHandler.java b/platform/core.windows/src/org/netbeans/core/windows/PersistenceHandler.java
index 30ed958..b2ff1c0 100644
--- a/platform/core.windows/src/org/netbeans/core/windows/PersistenceHandler.java
+++ b/platform/core.windows/src/org/netbeans/core/windows/PersistenceHandler.java
@@ -630,7 +630,7 @@ final public class PersistenceHandler implements PersistenceObserver {
return wmc;
}
- private ModeConfig getConfigFromMode(ModeImpl mode) {
+ public ModeConfig getConfigFromMode(ModeImpl mode) {
PersistenceManager pm = PersistenceManager.getDefault();
WindowManagerImpl wm = WindowManagerImpl.getInstance();
ModeConfig modeCfg = new ModeConfig();
diff --git a/platform/core.windows/src/org/netbeans/core/windows/WindowManagerImpl.java b/platform/core.windows/src/org/netbeans/core/windows/WindowManagerImpl.java
index 8f6faf1..82cecc4 100644
--- a/platform/core.windows/src/org/netbeans/core/windows/WindowManagerImpl.java
+++ b/platform/core.windows/src/org/netbeans/core/windows/WindowManagerImpl.java
@@ -23,6 +23,7 @@ import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.*;
+import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.logging.Level;
@@ -31,6 +32,7 @@ import javax.swing.*;
import org.netbeans.core.IDESettings;
import org.netbeans.core.windows.actions.ActionUtils;
import org.netbeans.core.windows.options.WinSysPrefs;
+import org.netbeans.core.windows.persistence.ModeConfig;
import org.netbeans.core.windows.persistence.PersistenceManager;
import org.netbeans.core.windows.view.dnd.TopComponentDraggable;
import org.netbeans.core.windows.view.ui.MainWindow;
@@ -457,7 +459,61 @@ public final class WindowManagerImpl extends WindowManager implements Workspace
System.setProperty("nb.native.filechooser", useNativeFileChooser ? "true" : "false"); //NOI18N
}
}
+
+ @Override
+ public Mode createModeFromXml(String xml) {
+ try {
+ ModeConfig modeConfig = PersistenceManager.getDefault().createModeFromXml(xml);
+ ModeImpl mode = createMode(modeConfig);
+ addMode(mode, modeConfig);
+ return mode;
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ return null;
+ }
+ }
+
+ public ModeImpl createMode(ModeConfig config) {
+ WindowManagerImpl wmi = WindowManagerImpl.getInstance();
+
+ ModeImpl res = wmi.createMode(config.name, config.kind, config.state, false, config.constraints);
+ Rectangle absBounds = config.bounds == null ? new Rectangle() : config.bounds;
+ Rectangle relBounds = config.relativeBounds == null ? new Rectangle() : config.relativeBounds;
+ Rectangle bounds = PersistenceHandler.computeBounds(false, false,
+ absBounds.x,
+ absBounds.y,
+ absBounds.width,
+ absBounds.height,
+ relBounds.x / 100.0F,
+ relBounds.y / 100.0F,
+ relBounds.width / 100.0F,
+ relBounds.height / 100.0F);
+ res.setBounds(bounds);
+ res.setFrameState(config.frameState);
+ res.setMinimized(config.minimized);
+ return res;
+ }
+
+ @Override
+ public boolean removeMode(Mode mode) {
+ String modeName = mode.getName();
+ removeMode((ModeImpl)mode);
+ return findMode(modeName) == null;
+ }
+ @Override
+ public boolean updateModeConstraintsFromXml(String xml) {
+ try {
+ ModeConfig modeConfig = PersistenceManager.getDefault().createModeFromXml(xml);
+ ModeImpl mode = findModeImpl(modeConfig.name);
+ mode.setConstraints(modeConfig.constraints);
+ return true;
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ return false;
+ }
+ }
+
private static class WrapMode implements Mode {
private Mode wrap;
@@ -875,6 +931,14 @@ public final class WindowManagerImpl extends WindowManager implements Workspace
}
}
+ private void addMode(ModeImpl mode, ModeConfig modeConfig) {
+ if (mode.getKind() == Constants.MODE_KIND_SLIDING) {
+ central.addSlidingMode(mode, null, modeConfig.side, modeConfig.slideInSizes);
+ } else {
+ central.addMode(mode, modeConfig.constraints);
+ }
+ }
+
/** Removes mode. */
public void removeMode(ModeImpl mode) {
if (mode.getKind() == Constants.MODE_KIND_SLIDING) {
diff --git a/platform/core.windows/src/org/netbeans/core/windows/persistence/ModeConfig.java b/platform/core.windows/src/org/netbeans/core/windows/persistence/ModeConfig.java
index 7cf9b5c..47238ac 100644
--- a/platform/core.windows/src/org/netbeans/core/windows/persistence/ModeConfig.java
+++ b/platform/core.windows/src/org/netbeans/core/windows/persistence/ModeConfig.java
@@ -22,6 +22,7 @@ package org.netbeans.core.windows.persistence;
import java.awt.Rectangle;
+import java.util.Arrays;
import java.util.Collection;
import org.netbeans.core.windows.SplitConstraint;
@@ -212,5 +213,10 @@ public class ModeConfig {
hash = 37 * hash + previousSelectedTopComponentID.hashCode();
return hash;
}
+
+ @Override
+ public String toString() {
+ return "ModeConfig{" + "name=" + name + ", otherNames=" + otherNames + ", state=" + state + ", kind=" + kind + ", side=" + side + ", constraints=" + Arrays.asList(constraints).toString() + ", bounds=" + bounds + ", relativeBounds=" + relativeBounds + ", frameState=" + frameState + ", selectedTopComponentID=" + selectedTopComponentID + ", permanent=" + permanent + ", minimized=" + minimized + ", tcRefConfigs=" + Arrays.asList(tcRefConfigs).toString() + ", slideInSizes=" + slideInS [...]
+ }
}
diff --git a/platform/core.windows/src/org/netbeans/core/windows/persistence/ModeParser.java b/platform/core.windows/src/org/netbeans/core/windows/persistence/ModeParser.java
index 5ffc7b8..8b4c597 100644
--- a/platform/core.windows/src/org/netbeans/core/windows/persistence/ModeParser.java
+++ b/platform/core.windows/src/org/netbeans/core/windows/persistence/ModeParser.java
@@ -37,6 +37,7 @@ import java.io.*;
import java.util.*;
import java.util.List;
import java.util.logging.Logger;
+import org.openide.util.io.ReaderInputStream;
/**
@@ -96,12 +97,23 @@ class ModeParser {
private final Object LOCK = new Object();
- public ModeParser (String name, Set maskSet) {
+ private final boolean fileObjectNameMustMatchModeName;
+
+ public static ModeParser parseFromFileObject(String name, Set maskSet) {
+ return new ModeParser(name, maskSet, true);
+ }
+
+ public static ModeParser parseFromString(String name, Set maskSet) {
+ return new ModeParser(name, maskSet, false);
+ }
+
+ private ModeParser (String name, Set maskSet, boolean fileObjectNameMustMatchModeName) {
this.modeName = name;
this.maskSet = maskSet;
+ this.fileObjectNameMustMatchModeName = fileObjectNameMustMatchModeName;
}
- /** Load mode configuration including all tcrefs. */
+ /** Load mode configuration including all tcrefs from an XML file. */
ModeConfig load () throws IOException {
synchronized( LOCK ) {
//if (DEBUG) Debug.log(ModeParser.class, "load ENTER" + " mo:" + name);
@@ -127,6 +139,30 @@ class ModeParser {
}
}
+ /** Load mode configuration from an XML String. */
+ ModeConfig load (String xml) throws IOException {
+ synchronized( LOCK ) {
+ //if (DEBUG) Debug.log(ModeParser.class, "load ENTER" + " mo:" + name);
+ ModeConfig mc = new ModeConfig();
+ readProperties(mc, xml);
+ if (mc.kind == Constants.MODE_KIND_SLIDING && mc.side != null && !mc.permanent) {
+ // now we have the 4.0 anonymous mode for the slide bar. replace with the
+ // predefined ones..
+ mc.permanent = true;
+ // well, the names are defined in core/ui.
+ // shall we at all care about the name? or is making it permanent just fine?
+ // if (mc.side.equals(Constants.BOTTOM)) {
+ // mc.name = "bottomSlidingSide"; //NOI18N
+ // } else if (mc.side.equals(Constants.LEFT)) {
+ // mc.name = "leftSlidingSide"; //NOI18N
+ // } else if (mc.side.equals(Constants.RIGHT)) {
+ // mc.name = "rightSlidingSide"; //NOI18N
+ // }
+ }
+ return mc;
+ }
+ }
+
/** Save mode configuration including all tcrefs. */
void save (ModeConfig mc) throws IOException {
synchronized( LOCK ) {
@@ -137,6 +173,15 @@ class ModeParser {
}
}
+ String modeConfigXml(ModeConfig mc) throws IOException {
+ synchronized( LOCK ) {
+ PropertyHandler propertyHandler = new PropertyHandler();
+ InternalConfig internalCfg = getInternalConfig();
+ StringBuffer buff = propertyHandler.generateData(mc, internalCfg);
+ return buff.toString();
+ }
+ }
+
private void readProperties (ModeConfig mc) throws IOException {
if (DEBUG) Debug.log(ModeParser.class, "readProperties ENTER" + " mo:" + getName());
PropertyHandler propertyHandler = new PropertyHandler();
@@ -152,6 +197,21 @@ class ModeParser {
if (DEBUG) Debug.log(ModeParser.class, "readProperties LEAVE" + " mo:" + getName());
}
+ private void readProperties (ModeConfig mc, String xml) throws IOException {
+ if (DEBUG) Debug.log(ModeParser.class, "readProperties ENTER" + " mo:" + getName());
+ PropertyHandler propertyHandler = new PropertyHandler();
+ InternalConfig internalCfg = getInternalConfig();
+ internalCfg.clear();
+ propertyHandler.readData(mc, internalCfg, xml);
+
+ /*if (DEBUG) Debug.log(ModeParser.class, " specVersion: " + internalCfg.specVersion);
+ if (DEBUG) Debug.log(ModeParser.class, " moduleCodeNameBase: " + internalCfg.moduleCodeNameBase);
+ if (DEBUG) Debug.log(ModeParser.class, " moduleCodeNameRelease: " + internalCfg.moduleCodeNameRelease);
+ if (DEBUG) Debug.log(ModeParser.class, "moduleSpecificationVersion: " + internalCfg.moduleSpecificationVersion);*/
+
+ if (DEBUG) Debug.log(ModeParser.class, "readProperties LEAVE" + " mo:" + getName());
+ }
+
private void readTCRefs (ModeConfig mc) throws IOException {
if (DEBUG) Debug.log(ModeParser.class, "readTCRefs ENTER" + " mo:" + getName());
@@ -894,22 +954,42 @@ class ModeParser {
return modeConfigFO;
}
}
- /**
- Reads mode configuration data from XML file.
- Data are returned in output params.
+
+ /**
+ * Reads mode configuration data from XML file. Data are returned in
+ * output params.
*/
- void readData (ModeConfig modeCfg, InternalConfig internalCfg)
- throws IOException {
- modeConfig = modeCfg;
- internalConfig = internalCfg;
- itemList.clear();
-
+ void readData(ModeConfig modeCfg, InternalConfig internalCfg)
+ throws IOException {
FileObject cfgFOInput = getConfigFOInput();
if (cfgFOInput == null) {
throw new FileNotFoundException("[WinSys] Missing Mode configuration file:" // NOI18N
- + ModeParser.this.getName());
+ + ModeParser.this.getName());
}
- InputStream is = null;
+ InputStream is = cfgFOInput.getInputStream();
+ readData(modeCfg, internalCfg, is, cfgFOInput);
+ }
+
+ /**
+ * Reads mode configuration data from XML String. Data are returned in
+ * output params.
+ */
+ void readData(ModeConfig modeCfg, InternalConfig internalCfg, String xml)
+ throws IOException {
+ InputStream is = new BufferedInputStream( new ReaderInputStream( new StringReader(xml)));
+ readData(modeCfg, internalCfg, is, xml);
+ }
+
+ /**
+ * Reads mode configuration data from an InputStream. Data are returned
+ * in output params.
+ */
+ private void readData(ModeConfig modeCfg, InternalConfig internalCfg, InputStream is, Object source)
+ throws IOException {
+ modeConfig = modeCfg;
+ internalConfig = internalCfg;
+ itemList.clear();
+
try {
synchronized (RW_LOCK) {
//DUMP BEGIN
@@ -922,13 +1002,12 @@ class ModeParser {
if (DEBUG) Debug.log(ModeParser.class, s);
}*/
//DUMP END
- is = cfgFOInput.getInputStream();
PersistenceManager.getDefault().getXMLParser(this).parse(new InputSource(is));
}
} catch (SAXException exc) {
// Turn into annotated IOException
String msg = NbBundle.getMessage(ModeParser.class,
- "EXC_ModeParse", cfgFOInput);
+ "EXC_ModeParse", source);
throw (IOException) new IOException(msg).initCause(exc);
} finally {
@@ -1049,11 +1128,11 @@ class ModeParser {
String name = attrs.getValue("unique"); // NOI18N
if (name != null) {
modeConfig.name = name;
- if (!name.equals(ModeParser.this.getName())) {
+ if (fileObjectNameMustMatchModeName && !name.equals(ModeParser.this.getName())) {
PersistenceManager.LOG.log(Level.INFO,
"[WinSys.ModeParser.handleName]" // NOI18N
+ " Error: Value of attribute \"unique\" of element \"name\"" // NOI18N
- + " and configuration file name must be the same."); // NOI18N
+ + " and configuration file name must be the same: " + name + " != " + ModeParser.this.getName() + "."); // NOI18N
throw new SAXException("Invalid attribute value"); // NOI18N
}
} else {
@@ -1096,13 +1175,13 @@ class ModeParser {
if( null != modeConfig.otherNames && !modeConfig.otherNames.isEmpty() ) {
PersistenceManager.LOG.log(Level.INFO,
"[WinSys.ModeParser.handleName]" // NOI18N
- + " Error: Sliding modes are not allowed to have additional names."); // NOI18N
+ + " Error: Sliding modes are not allowed to have additional names: " + modeConfig.otherNames + "."); // NOI18N
throw new SAXException("Invalid attribute value"); // NOI18N
}
} else {
PersistenceManager.LOG.log(Level.INFO,
"[WinSys.ModeParser.handleKind]" // NOI18N
- + " Warning: Invalid value of attribute \"type\"."); // NOI18N
+ + " Warning: Invalid value of attribute \"type\": " + type + "."); // NOI18N
modeConfig.kind = Constants.MODE_KIND_VIEW;
}
} else {
@@ -1154,7 +1233,7 @@ class ModeParser {
}
PersistenceManager.LOG.log(Level.INFO,
"[WinSys.ModeParser.handleSlideInSize]" // NOI18N
- + " Warning: Invalid attributes for preferred slide-in size."); // NOI18N
+ + " Warning: Invalid attributes for preferred slide-in size: tc-id=" + tcId + ", size=" + size + "."); // NOI18N
}
private void handleState(Attributes attrs) throws SAXException {
@@ -1167,7 +1246,7 @@ class ModeParser {
} else {
PersistenceManager.LOG.log(Level.INFO,
"[WinSys.ModeParser.handleState]" // NOI18N
- + " Warning: Invalid value of attribute \"type\"" // NOI18N
+ + " Warning: Invalid value " + type + " of attribute \"type\"" // NOI18N
+ " of element \"state\"."); // NOI18N
modeConfig.state = Constants.MODE_STATE_JOINED;
}
@@ -1193,7 +1272,7 @@ class ModeParser {
} else {
PersistenceManager.LOG.log(Level.INFO,
"[WinSys.ModeParser.handleState]" // NOI18N
- + " Warning: Invalid value of attribute \"minimized\"" // NOI18N
+ + " Warning: Invalid value " + minimized + " of attribute \"minimized\"" // NOI18N
+ " of element \"state\"."); // NOI18N
modeConfig.minimized = false;
}
@@ -1215,7 +1294,7 @@ class ModeParser {
} else {
PersistenceManager.LOG.log(Level.INFO,
"[WinSys.ModeParser.handlePath]" // NOI18N
- + " Warning: Invalid or missing value of attribute \"orientation\"."); // NOI18N
+ + " Warning: Invalid or missing value " + s + " of attribute \"orientation\"."); // NOI18N
orientation = Constants.VERTICAL;
}
@@ -1367,7 +1446,7 @@ class ModeParser {
} catch (NumberFormatException exc) {
PersistenceManager.LOG.log(Level.INFO,
"[WinSys.ModeParser.handleFrame]" // NOI18N
- + " Warning: Cannot read attribute \"state\"" // NOI18N
+ + " Warning: Cannot read value " + frameState + " for attribute \"state\"" // NOI18N
+ " of element \"frame\".", exc); // NOI18N
modeConfig.frameState = Frame.NORMAL;
}
@@ -1406,11 +1485,15 @@ class ModeParser {
} else {
PersistenceManager.LOG.log(Level.INFO,
"[WinSys.ModeParser.handleEmptyBehavior]" // NOI18N
- + " Warning: Invalid value of attribute \"permanent\"."); // NOI18N
+ + " Warning: Invalid value " + value + " of attribute \"permanent\"."); // NOI18N
modeConfig.permanent = false;
}
}
+ StringBuffer generateData(ModeConfig mc, InternalConfig ic) throws IOException {
+ return fillBuffer(mc, ic);
+ }
+
/** Writes data from asociated mode to the xml representation */
void writeData (ModeConfig mc, InternalConfig ic) throws IOException {
final StringBuffer buff = fillBuffer(mc, ic);
diff --git a/platform/core.windows/src/org/netbeans/core/windows/persistence/PersistenceManager.java b/platform/core.windows/src/org/netbeans/core/windows/persistence/PersistenceManager.java
index b15fb38..00fe28d 100644
--- a/platform/core.windows/src/org/netbeans/core/windows/persistence/PersistenceManager.java
+++ b/platform/core.windows/src/org/netbeans/core/windows/persistence/PersistenceManager.java
@@ -59,7 +59,6 @@ import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
-import org.openide.util.io.SafeException;
import org.openide.windows.TopComponent;
import org.openide.xml.XMLUtil;
import org.xml.sax.EntityResolver;
@@ -99,6 +98,8 @@ public final class PersistenceManager implements PropertyChangeListener {
/** default base name for noname top components */
private static final String DEFAULT_TC_NAME = "untitled_tc"; // NOI18N
+ private static final String UNNAMED_MODE_PARSER = "unnamed_mp"; // NOI18N
+
private static final boolean DEBUG = Debug.isLoggable(PersistenceManager.class);
/** Root folder for win sys module */
@@ -1364,6 +1365,16 @@ public final class PersistenceManager implements PropertyChangeListener {
}
return tcName;
}
+
+ public ModeConfig createModeFromXml(String xml) throws IOException {
+ ModeParser modeParser = ModeParser.parseFromString(UNNAMED_MODE_PARSER, new HashSet());
+ return modeParser.load(xml);
+ }
+
+ public String createXmlFromMode(ModeConfig modeConfig) throws IOException {
+ ModeParser modeParser = ModeParser.parseFromString(UNNAMED_MODE_PARSER, new HashSet());
+ return modeParser.modeConfigXml(modeConfig);
+ }
/**
* This class is used to clean internal maps containing <String,WeakReference<TopComponent>>
diff --git a/platform/core.windows/src/org/netbeans/core/windows/persistence/WindowManagerParser.java b/platform/core.windows/src/org/netbeans/core/windows/persistence/WindowManagerParser.java
index 685e261..7ea9aff 100644
--- a/platform/core.windows/src/org/netbeans/core/windows/persistence/WindowManagerParser.java
+++ b/platform/core.windows/src/org/netbeans/core/windows/persistence/WindowManagerParser.java
@@ -98,6 +98,37 @@ public class WindowManagerParser {
}
}
+ /** Extract all ModeConfigs in XML.
+ *
+ * @param wmc where all knowledge of the ModeConfigs is kept
+ * @return All the known modes in the XML configuration that they would
+ * be saved to disk as.
+ * @throws IOException
+ */
+ List<String> modeConfigXmls(WindowManagerConfig wmc) throws IOException {
+ synchronized (SAVING_LOCK) {
+ Map<String, ModeConfig> modeConfigMap = gatherModeConfigs(wmc);
+ return xmlModeConfigs(modeConfigMap);
+ }
+ }
+
+ /** Get a ModeConfig as XML.
+ *
+ * @param modeName the name of the Mode for which we want the XML
+ * @param wmc where all knowledge of the ModeConfigs is kept
+ * @return XML String of the Mode with modeName
+ * @throws IOException
+ */
+ public String modeConfigXml(String modeName, WindowManagerConfig wmc) throws IOException {
+ synchronized (SAVING_LOCK) {
+ Map<String, ModeConfig> modeConfigMap = gatherModeConfigs(wmc);
+ ModeConfig modeConfig = modeConfigMap.get(modeName);
+ ModeParser modeParser = modeParserMap.get(modeName);
+ String xml = modeParser.modeConfigXml(modeConfig);
+ return xml;
+ }
+ }
+
/** Called from ModuleChangeHandler when wsmode file is deleted from module folder.
* Do not remove ModeParser. Only set that it is not present in module folder.
* @param modeName unique name of mode
@@ -123,7 +154,7 @@ public class WindowManagerParser {
ModeParser modeParser = (ModeParser) modeParserMap.get(modeName);
if (modeParser == null) {
//Create new ModeParser if it does not exist.
- modeParser = new ModeParser(modeName,tcRefNameLocalSet);
+ modeParser = ModeParser.parseFromFileObject(modeName,tcRefNameLocalSet);
modeParserMap.put(modeName, modeParser);
}
FileObject modesModuleFolder = null;
@@ -360,7 +391,7 @@ public class WindowManagerParser {
//wsmode file
ModeParser modeParser = (ModeParser) modeParserMap.get(files[i].getName());
if (modeParser == null) {
- modeParser = new ModeParser(files[i].getName(),tcRefNameLocalSet);
+ modeParser = ModeParser.parseFromFileObject(files[i].getName(),tcRefNameLocalSet);
modeParserMap.put(files[i].getName(), modeParser);
}
modeParser.setInModuleFolder(true);
@@ -382,7 +413,7 @@ public class WindowManagerParser {
if (modeParserMap.containsKey(files[i].getName())) {
modeParser = (ModeParser) modeParserMap.get(files[i].getName());
} else {
- modeParser = new ModeParser(files[i].getName(),tcRefNameLocalSet);
+ modeParser = ModeParser.parseFromFileObject(files[i].getName(),tcRefNameLocalSet);
modeParserMap.put(files[i].getName(), modeParser);
}
modeParser.setInLocalFolder(true);
@@ -644,8 +675,53 @@ public class WindowManagerParser {
if (DEBUG) Debug.log(WindowManagerParser.class, "writeProperties LEAVE");
}
- private void writeModes (WindowManagerConfig wmc) throws IOException {
- if (DEBUG) Debug.log(WindowManagerParser.class, "writeModes ENTER");
+ private void writeModes(WindowManagerConfig wmc) throws IOException {
+ if (DEBUG) {
+ Debug.log(WindowManagerParser.class, "writeModes ENTER");
+ }
+
+ Map<String, ModeConfig> modeConfigMap = gatherModeConfigs(wmc);
+
+ saveModeConfigs(modeConfigMap);
+
+ if (DEBUG) {
+ Debug.log(WindowManagerParser.class, "writeModes LEAVE");
+ }
+ }
+
+ private void saveModeConfigs(Map<String, ModeConfig> modeConfigMap) throws IOException {
+ FileObject modesLocalFolder = pm.getRootLocalFolder().getFileObject(PersistenceManager.MODES_FOLDER);
+ if ((modesLocalFolder == null) && (modeParserMap.size() > 0)) {
+ modesLocalFolder = pm.getModesLocalFolder();
+ }
+ //Step 3: Save all modes
+ for (Iterator it = modeParserMap.keySet().iterator(); it.hasNext();) {
+ ModeParser modeParser = (ModeParser) modeParserMap.get(it.next());
+ modeParser.setLocalParentFolder(modesLocalFolder);
+ modeParser.setInLocalFolder(true);
+ modeParser.save((ModeConfig) modeConfigMap.get(modeParser.getName()));
+ }
+ }
+
+ private List<String> xmlModeConfigs(Map<String, ModeConfig> modeConfigMap) throws IOException {
+ // Convert ModeConfigs into xml.
+ List<String> modeConfigXmls = new ArrayList<>();
+ for (Iterator it = modeParserMap.keySet().iterator(); it.hasNext();) {
+ ModeParser modeParser = (ModeParser) modeParserMap.get(it.next());
+ String xml = modeParser.modeConfigXml((ModeConfig) modeConfigMap.get(modeParser.getName()));
+ modeConfigXmls.add(xml);
+ }
+ return modeConfigXmls;
+ }
+
+ /**
+ * Cleans obsolete ModeParsers and creates missing ModeParsers, populating
+ * modeParserMap
+ *
+ * @param wmc where all knowledge of the ModeConfigs is kept
+ * @return named index of Mode configurations
+ */
+ private Map<String, ModeConfig> gatherModeConfigs(WindowManagerConfig wmc) {
//Step 1: Clean obsolete mode parsers
Map<String, ModeConfig> modeConfigMap = new HashMap<String, ModeConfig>();
for (int i = 0; i < wmc.modes.length; i++) {
@@ -668,28 +744,15 @@ public class WindowManagerParser {
//Step 2: Create missing mode parsers
for (int i = 0; i < wmc.modes.length; i++) {
if (!modeParserMap.containsKey(wmc.modes[i].name)) {
- ModeParser modeParser = new ModeParser(wmc.modes[i].name,tcRefNameLocalSet);
+ ModeParser modeParser = ModeParser.parseFromFileObject(wmc.modes[i].name,tcRefNameLocalSet);
modeParserMap.put(wmc.modes[i].name, modeParser);
//if (DEBUG) Debug.log(WindowManagerParser.class, "-- WMParser.writeModes ** CREATE modeParser:" + modeParser.getName());
}
}
-
- FileObject modesLocalFolder = pm.getRootLocalFolder().getFileObject(PersistenceManager.MODES_FOLDER);
- if ((modesLocalFolder == null) && (modeParserMap.size() > 0)) {
- modesLocalFolder = pm.getModesLocalFolder();
- }
- //Step 3: Save all modes
- for (Iterator it = modeParserMap.keySet().iterator(); it.hasNext(); ) {
- ModeParser modeParser = (ModeParser) modeParserMap.get(it.next());
- modeParser.setLocalParentFolder(modesLocalFolder);
- modeParser.setInLocalFolder(true);
- modeParser.save((ModeConfig) modeConfigMap.get(modeParser.getName()));
- }
-
- if (DEBUG) Debug.log(WindowManagerParser.class, "writeModes LEAVE");
+ return modeConfigMap;
}
-
- private void writeGroups (WindowManagerConfig wmc) throws IOException {
+
+ private void writeGroups(WindowManagerConfig wmc) throws IOException {
if (DEBUG) Debug.log(WindowManagerParser.class, "writeGroups ENTER");
//Step 1: Clean obsolete group parsers
Map<String, GroupConfig> groupConfigMap = new HashMap<String, GroupConfig>();
@@ -1948,7 +2011,7 @@ public class WindowManagerParser {
*/
public static ModeConfig loadModeConfigFrom( FileObject fo ) throws IOException {
String modeName = fo.getName();
- ModeParser parser = new ModeParser(modeName, new HashSet(1));
+ ModeParser parser = ModeParser.parseFromFileObject(modeName, new HashSet(1));
parser.setInLocalFolder(true);
parser.setLocalParentFolder(fo.getParent());
return parser.load();
diff --git a/platform/core.windows/test/unit/src/org/netbeans/core/windows/WindowManagerModeTest.java b/platform/core.windows/test/unit/src/org/netbeans/core/windows/WindowManagerModeTest.java
new file mode 100644
index 0000000..680ce1b
--- /dev/null
+++ b/platform/core.windows/test/unit/src/org/netbeans/core/windows/WindowManagerModeTest.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.netbeans.core.windows;
+
+import java.awt.GraphicsEnvironment;
+import static junit.framework.TestCase.assertNotNull;
+import junit.framework.TestSuite;
+import org.netbeans.junit.NbTestCase;
+import org.openide.modules.ModuleInfo;
+import org.openide.util.Lookup;
+import org.openide.windows.Mode;
+import org.openide.windows.ModeUtilities;
+import org.openide.windows.TopComponent;
+import org.openide.windows.WindowManager;
+
+/**
+ * This test exercises the Mode management methods of WindowManager
+ * and Mode XML extraction via ModeUtilities.
+ * <br>
+ * <br>
+ * It would be better placed in Windows System API but it needs
+ * PersistenceHandler to load the window system.
+ *
+ * @author Mark Phipps
+ */
+public class WindowManagerModeTest extends NbTestCase {
+
+ public static junit.framework.Test suite() {
+ return GraphicsEnvironment.isHeadless() ? new TestSuite() : new TestSuite(WindowManagerModeTest.class);
+ }
+
+ private static boolean loaded = false;
+
+ private final String anonymousModeXml
+ = "<mode version=\"2.4\">"
+ + "<name unique=\"anonymousMode_1\"/>"
+ + "<kind type=\"editor\"/>"
+ + "<state type=\"joined\"/>"
+ + "<constraints>"
+ + "<path weight=\"0.5\" number=\"1\" orientation=\"horizontal\"/>"
+ + "</constraints>"
+ + "<bounds height=\"0\" width=\"0\" y=\"0\" x=\"0\"/>"
+ + "<frame state=\"0\"/>"
+ + "<empty-behavior permanent=\"false\"/>"
+ + "</mode>";
+
+ private final String editorModeXml
+ = "<mode version=\"2.4\">"
+ + "<name unique=\"editor\"/>"
+ + "<kind type=\"editor\"/>"
+ + "<state type=\"joined\"/>"
+ + "<constraints>"
+ + "<path weight=\"0.5\" number=\"0\" orientation=\"horizontal\" />"
+ + "</constraints>"
+ + "<bounds height=\"0\" width=\"0\" y=\"0\" x=\"0\" />"
+ + "<frame state=\"0\" />"
+ + "<empty-behavior permanent=\"true\" />"
+ + "</mode>";
+
+ public WindowManagerModeTest(String testName) {
+ super(testName);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ if (!loaded) {
+ // Load just once for all tests in this class
+ Lookup.getDefault().lookup(ModuleInfo.class);
+ PersistenceHandler.getDefault().load();
+ loaded = true;
+ }
+ }
+
+ @Override
+ protected boolean runInEQ() {
+ return true;
+ }
+
+ public void testGetEditorModeXml() {
+
+ WindowManager wm = WindowManager.getDefault();
+ assertNotNull(wm);
+
+ Mode mode = wm.findMode("editor");
+
+ String xml = ModeUtilities.toXml(mode);
+ assertNotNull("editor Mode XML should not be null", xml);
+
+ }
+
+ public void testCreateAndRemoveAnonymousMode() {
+
+ WindowManager wm = WindowManager.getDefault();
+ assertNotNull(wm);
+
+ Mode mode = wm.createModeFromXml(anonymousModeXml);
+ assertNotNull("Anonymous Mode should have been created", mode);
+
+ assertEquals("Anonymous Mode should be find-able", mode, wm.findMode(mode.getName()));
+
+ assertTrue("Anonymous Mode should have been removed", wm.removeMode(mode));
+ }
+
+ public void testUpdateModeConstraints() {
+
+ WindowManager wm = WindowManager.getDefault();
+ assertNotNull(wm);
+
+ boolean updated = wm.updateModeConstraintsFromXml(editorModeXml);
+
+ assertTrue("Should have found and updated the editor Mode", updated);
+ }
+
+ public void testExampleWorkFlowSaveAndLoadWorkSpace() {
+ WindowManager wm = WindowManager.getDefault();
+ assertNotNull(wm);
+
+ // User creates TopComponents in the application, drags them around.
+ TopComponent testTc = new TopComponent();
+ wm.findMode("editor").dockInto(testTc);
+ testTc.open();
+
+ // Let's pretend thet the user dragged testTc into a new anonymous Mode.
+ // Now wm.findMode(testTc).getName() would be "anonymousMode_1";
+
+ // User decides to save the layout.
+ for (Mode mode : wm.getModes()) {
+ String xml = ModeUtilities.toXml(mode);
+ // Save the Mode xml somehow...
+ }
+
+ for (TopComponent tc : wm.getRegistry().getOpened()) {
+ // Save the state of the TopComponent somehow...
+ // Also save the name of the Mode the TopComponent was docked into:
+ String modeName = wm.findMode(tc).getName();
+ }
+
+ // Later you restore the layout...
+ // Close open TopComponents.
+ for (TopComponent tc : wm.getRegistry().getOpened()) {
+ tc.close();
+ }
+
+ // Remove unwanted Modes.
+ for (Mode mode: wm.getModes()) {
+ if (mode.getName().startsWith("anonymous")) {
+ wm.removeMode(mode);
+ }
+ }
+
+ // Restore the XML of the Modes somehow...
+ String[] modeXmls = new String[] {editorModeXml, anonymousModeXml};
+ for (String modeXml: modeXmls) {
+ // Use some XML magic of your choice to determine if this is
+ // an anonymous Mode or a defined Mode.
+ if (modeXml.contains("anonymous")) {
+ // Create the new Mode
+ wm.createModeFromXml(modeXml);
+ } else {
+ // Adjust the constraints of defined Modes.
+ wm.updateModeConstraintsFromXml(modeXml);
+ }
+ }
+
+ // Restore the TopComponents and the names of their Modes somehow...
+ testTc = new TopComponent();
+ // Earlier in the test we pretended that testTc was dragged into anonymousMode_1.
+ wm.findMode("anonymousMode_1").dockInto(testTc);
+ assertEquals("anonymousMode_1", wm.findMode(testTc).getName());
+
+ }
+}
diff --git a/platform/core.windows/test/unit/src/org/netbeans/core/windows/persistence/ModeParserTest.java b/platform/core.windows/test/unit/src/org/netbeans/core/windows/persistence/ModeParserTest.java
index 8cb2203..2d43f83 100644
--- a/platform/core.windows/test/unit/src/org/netbeans/core/windows/persistence/ModeParserTest.java
+++ b/platform/core.windows/test/unit/src/org/netbeans/core/windows/persistence/ModeParserTest.java
@@ -613,7 +613,7 @@ public class ModeParserTest extends NbTestCase {
assertNotNull("Test parent folder not found. ParentFolder is null.",url);
Set setLocal = new HashSet();
- ModeParser modeParser = new ModeParser(name,setLocal);
+ ModeParser modeParser = ModeParser.parseFromFileObject(name,setLocal);
modeParser.setInLocalFolder(true);
modeParser.setLocalParentFolder(parentFolder);
diff --git a/platform/openide.windows/apichanges.xml b/platform/openide.windows/apichanges.xml
index e8a167e..268f916 100644
--- a/platform/openide.windows/apichanges.xml
+++ b/platform/openide.windows/apichanges.xml
@@ -26,6 +26,23 @@
<apidef name="winsys">Window System API</apidef>
</apidefs>
<changes>
+ <change id="ModeCreation">
+ <api name="winsys"/>
+ <summary>Allow Modes to be created directly from ModeConfig XML</summary>
+ <version major="6" minor="6.81"/>
+ <date day="18" month="3" year="2019"/>
+ <author login="phipma"/>
+ <compatibility addition="yes" modification="yes"/>
+ <description>
+ <p>
+ Plugin implementors can save and restore individual Modes providing
+ scope for custom TopComponent layouts.
+ </p>
+ </description>
+ <class package="org.openide.windows" name="Mode"/>
+ <class package="org.openide.windows" name="ModeUtilities"/>
+ <class package="org.openide.windows" name="WindowManager"/>
+ </change>
<change id="ModeSelector">
<api name="winsys"/>
<summary>Allow to select Mode for opening a TopComponent instance</summary>
diff --git a/platform/openide.windows/manifest.mf b/platform/openide.windows/manifest.mf
index 71df22f..db311fe 100644
--- a/platform/openide.windows/manifest.mf
+++ b/platform/openide.windows/manifest.mf
@@ -1,6 +1,6 @@
Manifest-Version: 1.0
OpenIDE-Module: org.openide.windows
-OpenIDE-Module-Specification-Version: 6.81
+OpenIDE-Module-Specification-Version: 6.82
OpenIDE-Module-Localizing-Bundle: org/openide/windows/Bundle.properties
AutoUpdate-Essential-Module: true
diff --git a/platform/openide.windows/src/org/openide/windows/Mode.java b/platform/openide.windows/src/org/openide/windows/Mode.java
index 36d2f19..57adfd4 100644
--- a/platform/openide.windows/src/org/openide/windows/Mode.java
+++ b/platform/openide.windows/src/org/openide/windows/Mode.java
@@ -124,4 +124,20 @@ public interface Mode extends Serializable {
/** Gets selected <code>TopComponent</code> in this mode.
* @since 4.13 */
public TopComponent getSelectedTopComponent();
+
+ /**
+ * Extension to provide exposure of XML configuration.
+ *
+ * @see http://wiki.apidesign.org/wiki/ExtendingInterfaces
+ * @since 6.82
+ */
+ public interface Xml extends Mode {
+
+ /**
+ * Generates the Mode configuration as XML.
+ * @return an XML representation of the Mode's configuration.
+ * @since 6.82
+ */
+ public String toXml();
+ }
}
diff --git a/platform/openide.windows/src/org/openide/windows/ModeUtilities.java b/platform/openide.windows/src/org/openide/windows/ModeUtilities.java
new file mode 100644
index 0000000..796e5fc
--- /dev/null
+++ b/platform/openide.windows/src/org/openide/windows/ModeUtilities.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.openide.windows;
+
+/**
+ * Utility class to help clients manage {@link Mode}s in order to layout TopComponents
+ * in predefined "work-spaces".
+ * <br>
+ * <br>
+ * In a NetBeans Platform application, a user may create various TopComponents and
+ * drag them around, into and out of the existing defined Modes (e.g. editor, explorer) or
+ * new "anonymous" Modes which get created by the system as needed.
+ * <br>
+ * <br>
+ * The configuration of each Mode can be expressed in XML. This class gives access
+ * to that XML. {@link WindowManager} provides a
+ * {@link WindowManager#createModeFromXml mechanism to reproduce a Mode from XML}
+ * so you can construct ways to save Mode and TopComponent combinations, which can
+ * subsequently be chosen and reloaded at a later time.
+ * <br>
+ * <br>
+ * Note that this is a different approach to how the Platform tends to save the layout
+ * of TopComponents at shut-down time in the Windows2Local file system. Note also that
+ * this is not connected with the deprecated notion of {@link Workspace}.
+ *
+ * @see http://wiki.apidesign.org/wiki/ExtendingInterfaces
+ *
+ * @author Mark Phipps
+ * @since 6.82
+ */
+public final class ModeUtilities {
+
+ private ModeUtilities() {
+ }
+
+ /**
+ * Expose the Mode's configuration as XML.
+ *
+ * @param mode the {@link Mode} whose XML configuration is required.
+ * @return the XML of the Mode's configuration or {@code null} if not supported.
+ */
+ public final static String toXml(Mode mode) {
+ return mode instanceof Mode.Xml
+ ? ((Mode.Xml) mode).toXml()
+ : null;
+ }
+}
diff --git a/platform/openide.windows/src/org/openide/windows/WindowManager.java b/platform/openide.windows/src/org/openide/windows/WindowManager.java
index 1d7d97e..7717b6c 100644
--- a/platform/openide.windows/src/org/openide/windows/WindowManager.java
+++ b/platform/openide.windows/src/org/openide/windows/WindowManager.java
@@ -741,6 +741,44 @@ public abstract class WindowManager extends Object implements Serializable {
return null;
}
+ /**
+ * Given some XML, attempts to create a Mode that can
+ * subsequently be used to dock a TopComponent into.
+ * Usually this will be an anonymous Mode.
+ *
+ * @param xml ModeConfig XML that was originally produced by {@link ModeUtilities#toXml}
+ * @return an instance of Mode or null if the attempt to create the Mode failed
+ * @see ModeUtilities
+ */
+ public Mode createModeFromXml(String xml) {
+ return null;
+ }
+
+ /**
+ * Before restoring a whole bunch of Modes (for example with XML that has been
+ * previously saved somewhere and now loaded), it is useful to remove the
+ * anonymous modes from the system.
+ *
+ * @param mode the {@link Mode} to be removed
+ * @return success or failure of the attempt to remove the {@link Mode}
+ */
+ public boolean removeMode(Mode mode) {
+ return false;
+ }
+
+ /**
+ * Before restoring anonymous Modes, it is useful to update whatever defined Modes
+ * may exist like editor, explorer etc., so that all the Modes will eventually
+ * re-appear in the desired locations.
+ *
+ * @param xml ModeConfig XML that was originally produced by {@link ModeUtilities#toXml}
+ * @return success or failure of the attempt to find the Mode and update it
+ * @see ModeUtilities
+ */
+ public boolean updateModeConstraintsFromXml(String xml) {
+ return false;
+ }
+
/** A manager that handles operations on top components.
* It is always attached to a {@link TopComponent}.
* @deprecated Do not use anymore. This interface is replaced by bunch of protected methods
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists