org.openide.util.lookup
diff --git a/CoreViewFramework/release/modules/ext/docs/CoreViewFramework/resources/viewPanel.png b/CoreViewFramework/release/modules/ext/docs/CoreViewFramework/resources/viewPanel.png
new file mode 100644
index 0000000000..2b6a82c54e
Binary files /dev/null and b/CoreViewFramework/release/modules/ext/docs/CoreViewFramework/resources/viewPanel.png differ
diff --git a/CoreViewFramework/release/modules/ext/docs/CoreViewFramework/view-preferences.md b/CoreViewFramework/release/modules/ext/docs/CoreViewFramework/view-preferences.md
new file mode 100644
index 0000000000..cd0234c639
--- /dev/null
+++ b/CoreViewFramework/release/modules/ext/docs/CoreViewFramework/view-preferences.md
@@ -0,0 +1,11 @@
+# View Default Floating Preferences
+
+View preferences can be accessed via Setup -> Options ->
+CONSTELLATION -> View.
+
+
+

+
+
+
+Checking a checkbox in the "Floating" column will determine whether the associated view opens as a docked pane, or as a floating window. If you alter a view's floating preference while it is open, the changes will not take effect until the view is closed and opened again.
\ No newline at end of file
diff --git a/CoreViewFramework/release/modules/ext/docs/CoreViewFramework/viewframework-toc.xml b/CoreViewFramework/release/modules/ext/docs/CoreViewFramework/viewframework-toc.xml
new file mode 100644
index 0000000000..81ad2b87bd
--- /dev/null
+++ b/CoreViewFramework/release/modules/ext/docs/CoreViewFramework/viewframework-toc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/CoreViewFramework/src/au/gov/asd/tac/constellation/views/AbstractTopComponent.java b/CoreViewFramework/src/au/gov/asd/tac/constellation/views/AbstractTopComponent.java
index d98b6e135f..b27eee5769 100644
--- a/CoreViewFramework/src/au/gov/asd/tac/constellation/views/AbstractTopComponent.java
+++ b/CoreViewFramework/src/au/gov/asd/tac/constellation/views/AbstractTopComponent.java
@@ -16,7 +16,17 @@
package au.gov.asd.tac.constellation.views;
import au.gov.asd.tac.constellation.plugins.logging.ConstellationLogger;
+import au.gov.asd.tac.constellation.utilities.datastructure.Tuple;
+import au.gov.asd.tac.constellation.views.preferences.ViewOptionsPanelController;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.Window;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Map;
+import java.util.prefs.Preferences;
import org.openide.util.HelpCtx;
+import org.openide.util.NbPreferences;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
@@ -30,12 +40,12 @@
public abstract class AbstractTopComponent extends TopComponent {
protected P content;
-
private boolean isVisible;
+ private PropertyChangeListener pcl;
+ private final Preferences prefs = NbPreferences.forModule(ViewOptionsPanelController.class);
/**
- * Checks if the view will need an update when a graph changes based on if
- * the view is visible currently.
+ * Checks if the view will need an update when a graph changes based on if the view is visible currently.
*
* @return true if the view is visible and needs updating
*/
@@ -48,15 +58,13 @@ protected void setComponentVisible(final boolean visibility) {
}
/**
- * Builds and initialises the content for this top component. You should
- * call this method in the constructor of your TopComponent implementation
- * after calling the initComponents() method.
+ * Builds and initialises the content for this top component. You should call this method in the constructor of your
+ * TopComponent implementation after calling the initComponents() method.
*/
protected abstract void initContent();
/**
- * This is where you pass in content which will be rendered within the
- * AbstractTopComponent.
+ * This is where you pass in content which will be rendered within the AbstractTopComponent.
*
* @return
*/
@@ -82,14 +90,103 @@ public boolean getVisibility() {
@Override
protected void componentOpened() {
+ final WindowManager windowManager = WindowManager.getDefault();
+ final ViewOptionsPanelController controller = new ViewOptionsPanelController();
+
+ pcl = (final PropertyChangeEvent evt) -> { // Fires when a view is floated or docked manually via the context menu.
+ prefs.putBoolean(this.getName(), windowManager.isTopComponentFloating(this));
+ controller.update();
+ };
+
+ this.addPropertyChangeListener(pcl);
super.componentOpened();
+ final Map defaultPrefs = controller.getPanel().getDefaultPrefs();
+
+ if (defaultPrefs.containsKey(this.getName())) {
+ final Boolean isFloating = prefs.getBoolean(this.getName(), defaultPrefs.get(this.getName()));
+ WindowManager.getDefault().setTopComponentFloating(this, isFloating);
+
+ if (isFloating) {
+ // This loops through all the current windows and compares this top component's top level ancestor
+ // with the window's parent. Sets the size and location for the floating component if a match is found.
+ for (final Window window : Window.getWindows()) {
+ if (this.getTopLevelAncestor() != null && this.getTopLevelAncestor().getName().equals(window.getName())) {
+ final Frame mainWindow = windowManager.getMainWindow();
+ final int mainWidth = mainWindow.getWidth();
+ final int mainHeight = mainWindow.getHeight();
+ final int mainX = mainWindow.getX();
+ final int mainY = mainWindow.getY();
+
+ final Dimension sideSize = new Dimension(
+ Math.round(mainWidth * 0.3F),
+ Math.round(mainHeight * (mainWidth > mainHeight ? 0.892F : 0.94F))
+ );
+
+ final Dimension bottomSize = new Dimension(
+ mainWidth,
+ Math.round(mainHeight * 0.3F)
+ );
+
+ final Dimension size;
+
+ switch (getModeName()) {
+ case "leftSlidingSide", "explorer", "navigator" -> {
+ size = sideSize;
+ window.setLocation(
+ mainX,
+ mainY + mainHeight - size.height
+ );
+ }
+ case "commonpalette", "properties", "rightSlidingSide" -> {
+ size = sideSize;
+ window.setLocation(
+ mainX + mainWidth - size.width,
+ mainY + mainHeight - size.height
+ );
+ }
+ case "output", "bottomSlidingSide", "isSliding" -> {
+ switch (this.getName()) {
+ case "Find and Replace" -> {
+ size = new Dimension(600, 350);
+ window.setLocation(
+ mainX,
+ mainY + mainHeight - sideSize.height
+ );
+ }
+ default -> {
+ size = bottomSize;
+ window.setLocation(
+ mainX,
+ mainY + mainHeight - size.height
+ );
+ }
+ }
+ }
+ default -> { // Any other mode, default to opening on the left side.
+ size = sideSize;
+ window.setLocation(
+ mainX,
+ mainY + mainHeight - size.height
+ );
+ }
+ }
+
+ window.setSize(size);
+ }
+ }
+
+ this.setRequestFocusEnabled(true);
+ }
+ }
+
isVisible = true;
ConstellationLogger.getDefault().viewStarted(this);
}
@Override
protected void componentClosed() {
+ this.removePropertyChangeListener(pcl);
super.componentClosed();
isVisible = false;
@@ -160,4 +257,8 @@ protected void componentDeactivated() {
public final HelpCtx getHelpCtx() {
return new HelpCtx(getClass().getName());
}
+
+ public abstract Tuple getDefaultFloatingInfo();
+
+ protected abstract String getModeName();
}
diff --git a/CoreViewFramework/src/au/gov/asd/tac/constellation/views/ViewFrameworkHelpProvider.java b/CoreViewFramework/src/au/gov/asd/tac/constellation/views/ViewFrameworkHelpProvider.java
new file mode 100644
index 0000000000..7aa6428ae5
--- /dev/null
+++ b/CoreViewFramework/src/au/gov/asd/tac/constellation/views/ViewFrameworkHelpProvider.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2010-2026 Australian Signals Directorate
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package au.gov.asd.tac.constellation.views;
+
+import au.gov.asd.tac.constellation.help.HelpPageProvider;
+import java.util.HashMap;
+import java.util.Map;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * Provider to get help pages for the View Framework module.
+ *
+ * @author sol695510
+ */
+@ServiceProvider(service = HelpPageProvider.class, position = 2900)
+@NbBundle.Messages("ViewFrameworkHelpProvider=View Framework Help Provider")
+public class ViewFrameworkHelpProvider extends HelpPageProvider {
+
+ private static final String MODULE_PATH = "ext" + SEP + "docs" + SEP + "CoreViewFramework" + SEP;
+
+ /**
+ * Provides a map of all the help files Maps the file name to the md file name
+ *
+ * @return Map of the file names vs md file names
+ */
+ @Override
+ public Map getHelpMap() {
+ final Map map = new HashMap<>();
+ map.put("au.gov.asd.tac.constellation.views.preferences", MODULE_PATH + "view-preferences.md");
+ return map;
+ }
+
+ /**
+ * Provides a location as a string of the TOC xml file in the module
+ *
+ * @return List of help resources
+ */
+ @Override
+ public String getHelpTOC() {
+ return MODULE_PATH + "viewframework-toc.xml";
+ }
+}
diff --git a/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPanel.form b/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPanel.form
new file mode 100644
index 0000000000..4ce2787fc8
--- /dev/null
+++ b/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPanel.form
@@ -0,0 +1,53 @@
+
+
+
diff --git a/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPanel.java b/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPanel.java
new file mode 100644
index 0000000000..b18dd8ab2d
--- /dev/null
+++ b/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPanel.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2010-2026 Australian Signals Directorate
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package au.gov.asd.tac.constellation.views.preferences;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.prefs.Preferences;
+import javax.swing.JPanel;
+import javax.swing.border.EmptyBorder;
+import javax.swing.table.DefaultTableModel;
+import org.openide.util.Lookup;
+import org.openide.util.NbPreferences;
+
+/**
+ * UI panel for view floating options.
+ *
+ * @author sol695510
+ */
+public class ViewOptionsPanel extends JPanel {
+
+ private final Preferences prefs = NbPreferences.forModule(ViewOptionsPanelController.class);
+ private final Map defaultPrefs = new TreeMap<>();
+ private DefaultTableModel tableModel;
+
+ protected ViewOptionsPanel() {
+ initComponents();
+ createTableModel();
+ jLabel.setText("Set whether a view opens docked or floating by default.");
+ jLabel.setBorder(new EmptyBorder(0, 0, 10, 0));
+ }
+
+ protected final void createTableModel() {
+ tableModel = new DefaultTableModel(new Object[]{"View", "Floating"}, 0) {
+ @Override
+ public Class getColumnClass(final int column) {
+ return column == 0 ? String.class : Boolean.class;
+ }
+
+ @Override
+ public boolean isCellEditable(final int row, final int column) {
+ return column != 0;
+ }
+ };
+
+ final Map options = getOptionsFromPrefs().isEmpty() ? getDefaultPrefs() : getOptionsFromPrefs();
+
+ for (final Map.Entry entry : options.entrySet()) {
+ tableModel.addRow(new Object[]{entry.getKey(), entry.getValue()});
+ }
+
+ jTable.setModel(tableModel);
+ jTable.getTableHeader().setReorderingAllowed(false);
+ }
+
+ protected final void fireTableDataChanged() {
+ tableModel.fireTableDataChanged();
+ }
+
+ /**
+ * Returns the view preference selections from the options menu.
+ *
+ * @return a map containing the view preference selections from the options menu.
+ */
+ protected final Map getOptionsFromUI() {
+ final Map optionsFromUI = new TreeMap<>();
+
+ for (int i = 0; i < tableModel.getRowCount(); i++) {
+ optionsFromUI.put((String) tableModel.getValueAt(i, 0), (Boolean) tableModel.getValueAt(i, 1));
+ }
+
+ return Collections.unmodifiableMap(optionsFromUI);
+ }
+
+ /**
+ * Returns the view preference selections from the NB Preferences.
+ *
+ * @return a map containing the view preference selections from the NB Preferences.
+ */
+ protected final Map getOptionsFromPrefs() {
+ final Map optionsFromPrefs = new TreeMap<>();
+
+ for (final Map.Entry entry : getDefaultPrefs().entrySet()) {
+ optionsFromPrefs.put(entry.getKey(), prefs.getBoolean(entry.getKey(), entry.getValue()));
+ }
+
+ return Collections.unmodifiableMap(optionsFromPrefs);
+ }
+
+ /**
+ * Returns the view preference selections from the defaults defined in each applicable view.
+ *
+ * @return a map containing the view preference selections from the defaults defined in each applicable view.
+ */
+ public final Map getDefaultPrefs() {
+
+ if (defaultPrefs.isEmpty()) {
+ Lookup.getDefault().lookupAll(ViewOptionsProvider.class).forEach(lookup -> defaultPrefs.putAll(lookup.getDefaultFloatingPreferences()));
+ }
+
+ return Collections.unmodifiableMap(defaultPrefs);
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The
+ * content of this method is always regenerated by the FormEditor.
+ */
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ jLabel = new javax.swing.JLabel();
+ jScrollPane = new javax.swing.JScrollPane();
+ jTable = new javax.swing.JTable();
+
+ setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
+ setLayout(new java.awt.BorderLayout());
+
+ jLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
+ jLabel.setText("View options description here.");
+ jLabel.setToolTipText("");
+ add(jLabel, java.awt.BorderLayout.PAGE_START);
+
+ jScrollPane.setViewportView(jTable);
+
+ add(jScrollPane, java.awt.BorderLayout.CENTER);
+ }// //GEN-END:initComponents
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JLabel jLabel;
+ private javax.swing.JScrollPane jScrollPane;
+ private javax.swing.JTable jTable;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPanelController.java b/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPanelController.java
new file mode 100644
index 0000000000..ce66f04dff
--- /dev/null
+++ b/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPanelController.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2010-2026 Australian Signals Directorate
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package au.gov.asd.tac.constellation.views.preferences;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.prefs.Preferences;
+import javax.swing.JComponent;
+import org.netbeans.spi.options.OptionsPanelController;
+import org.openide.util.HelpCtx;
+import org.openide.util.Lookup;
+import org.openide.util.NbPreferences;
+
+/**
+ * UI Controller for the view floating options panel.
+ *
+ * @author sol695510
+ */
+@OptionsPanelController.SubRegistration(
+ location = "constellation",
+ displayName = "#ViewOptions_DisplayName",
+ keywords = "#ViewOptions_Keywords",
+ keywordsCategory = "constellation/ViewPreferences",
+ position = 1000)
+@org.openide.util.NbBundle.Messages({
+ "ViewOptions_DisplayName=View",
+ "ViewOptions_Keywords=View"
+})
+public class ViewOptionsPanelController extends OptionsPanelController {
+
+ private final Preferences prefs = NbPreferences.forModule(ViewOptionsPanelController.class);
+ private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
+ private ViewOptionsPanel panel;
+
+ @Override
+ public void update() {
+ getPanel().fireTableDataChanged();
+ getPanel().createTableModel();
+ }
+
+ @Override
+ public void applyChanges() {
+ if (isValid()) {
+ pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
+
+ if (isChanged()) {
+ pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
+
+ for (final String view : panel.getDefaultPrefs().keySet()) {
+ prefs.putBoolean(view, getPanel().getOptionsFromUI().get(view));
+ }
+ }
+ }
+ }
+
+ @Override
+ public void cancel() {
+ // Required for OptionsPanelController, intentionally left blank.
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public boolean isChanged() {
+ return !getPanel().getOptionsFromUI().equals(getPanel().getOptionsFromPrefs());
+ }
+
+ @Override
+ public JComponent getComponent(final Lookup lookup) {
+ return getPanel();
+ }
+
+ @Override
+ public HelpCtx getHelpCtx() {
+ return new HelpCtx("au.gov.asd.tac.constellation.views.preferences");
+ }
+
+ @Override
+ public void addPropertyChangeListener(final PropertyChangeListener pcl) {
+ pcs.addPropertyChangeListener(pcl);
+ }
+
+ @Override
+ public void removePropertyChangeListener(final PropertyChangeListener pcl) {
+ pcs.removePropertyChangeListener(pcl);
+ }
+
+ public ViewOptionsPanel getPanel() {
+ if (panel == null) {
+ panel = new ViewOptionsPanel();
+ }
+
+ return panel;
+ }
+}
diff --git a/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPreferencesMap.java b/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPreferencesMap.java
new file mode 100644
index 0000000000..9a848c1fc1
--- /dev/null
+++ b/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPreferencesMap.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2010-2026 Australian Signals Directorate
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package au.gov.asd.tac.constellation.views.preferences;
+
+import au.gov.asd.tac.constellation.views.AbstractTopComponent;
+import java.awt.EventQueue;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * Creates a map of the default floating preferences for the currently applicable views.
+ *
+ * @author sol695510
+ */
+@ServiceProvider(service = ViewOptionsProvider.class)
+public class ViewOptionsPreferencesMap extends ViewOptionsProvider {
+
+ final Map dfpInfo = new TreeMap<>();
+
+ /**
+ * Gets a map of the default floating preferences.
+ *
+ * @return a map of the default floating preferences.
+ */
+ @Override
+ public Map getDefaultFloatingPreferences() {
+
+ if (dfpInfo.isEmpty()) {
+ EventQueue.invokeLater(() -> Lookup.getDefault().lookupAll(AbstractTopComponent.class).forEach(lookup -> dfpInfo.put(
+ (String) lookup.getDefaultFloatingInfo().getFirst(),
+ (Boolean) lookup.getDefaultFloatingInfo().getSecond()
+ )));
+ }
+
+ return Collections.unmodifiableMap(dfpInfo);
+ }
+}
diff --git a/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsProvider.java b/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsProvider.java
new file mode 100644
index 0000000000..15cee8b61f
--- /dev/null
+++ b/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsProvider.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010-2026 Australian Signals Directorate
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package au.gov.asd.tac.constellation.views.preferences;
+
+import java.util.Map;
+
+/**
+ * Class to provide the default floating preferences for each view.
+ *
+ * @author sol695510
+ */
+public abstract class ViewOptionsProvider {
+
+ public abstract Map getDefaultFloatingPreferences();
+}
diff --git a/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsUtility.java b/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsUtility.java
new file mode 100644
index 0000000000..d8d69e5453
--- /dev/null
+++ b/CoreViewFramework/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsUtility.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2010-2026 Australian Signals Directorate
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package au.gov.asd.tac.constellation.views.preferences;
+
+import au.gov.asd.tac.constellation.utilities.text.SeparatorConstants;
+import au.gov.asd.tac.constellation.views.AbstractTopComponent;
+import java.awt.EventQueue;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URI;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.TreeMap;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executors;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import org.openide.util.Lookup;
+import org.openide.windows.OnShowing;
+
+/**
+ * Handles operations on the file the default floating preferences are saved to.
+ *
+ * @author sol695510
+ */
+@OnShowing()
+public class ViewOptionsUtility implements Runnable {
+
+ private static final Logger LOGGER = Logger.getLogger(ViewOptionsUtility.class.getName());
+
+ private static String resourceDirectory = "";
+ private static File dfpFile;
+ private static final Map dfpFromFile = new TreeMap<>();
+
+ // This is the system property that is set to true in order to make the AWT thread run in headless mode for tests, etc.
+ private static final String AWT_HEADLESS_PROPERTY = "java.awt.headless";
+
+ /**
+ * Generate the default viewing preferences in developer versions of code.
+ */
+ @Override
+ public void run() {
+ if (Boolean.TRUE.toString().equalsIgnoreCase(System.getProperty(AWT_HEADLESS_PROPERTY))) {
+ return;
+ }
+
+ CompletableFuture.runAsync(this::updateDFPFile, Executors.newSingleThreadExecutor());
+ }
+
+ /**
+ * Update the file containing the default viewing preferences.
+ */
+ protected void updateDFPFile() {
+ // Change boolean to true to update default floating preferences file, revert back to false after updating.
+ final boolean updateDFP = false;
+
+ if (updateDFP) {
+ createDFPFile(getResourceDirectory());
+ }
+ }
+
+ /**
+ * Create a default viewing preferences file at the given file path.
+ *
+ * @param filePath
+ */
+ protected static void createDFPFile(final String filePath) {
+ if (filePath == null) {
+ throw new IllegalArgumentException("Null file path used for creation of DFP file");
+ }
+
+ try {
+ if (Files.deleteIfExists(FileSystems.getDefault().getPath(filePath))) {
+ LOGGER.log(Level.FINE, "Previous DFP file was replaced at: {0}", filePath);
+ }
+ } catch (final IOException ex) {
+ LOGGER.log(Level.SEVERE, "Path to DFP file was invalid: %s".formatted(filePath), ex);
+ }
+
+ dfpFile = new File(filePath);
+
+ try (final BufferedWriter writer = new BufferedWriter(new FileWriter(dfpFile))) {
+ final Map dfpFromLookUp = getDFPFromLookup();
+
+ for (final Map.Entry entry : dfpFromLookUp.entrySet()) {
+ writer.write(entry.getKey() + SeparatorConstants.COLON + entry.getValue() + "\n");
+ }
+
+ dfpFile.createNewFile();
+
+ LOGGER.log(Level.FINE, "DFP file was created at: {0}", filePath);
+ } catch (final IOException ex) {
+ LOGGER.log(Level.SEVERE, "Unable to create DFP file. FilePath: %s".formatted(filePath), ex);
+ }
+ }
+
+ /**
+ * Read the default floating preferences file from the given file path.
+ *
+ * @param filePath
+ */
+ protected static void readDFPFile(final String filePath) {
+ if (filePath == null) {
+ throw new IllegalArgumentException("Null file path used for reading of DFP file");
+ }
+
+ dfpFile = new File(filePath);
+
+ try (final BufferedReader reader = new BufferedReader(new FileReader(dfpFile))) {
+ final Scanner sc = new Scanner(reader);
+
+ while (sc.hasNextLine()) {
+ final String nextLine = sc.nextLine();
+ final String[] ss = nextLine.split(SeparatorConstants.COLON);
+ dfpFromFile.put(ss[0].trim(), Boolean.valueOf(ss[1].trim()));
+ }
+
+ LOGGER.log(Level.FINE, "DFP file was read at: {0}", filePath);
+ } catch (final IOException ex) {
+ LOGGER.log(Level.SEVERE, "Unable to read DFP file. FilePath: %s".formatted(filePath), ex);
+ }
+ }
+
+ /**
+ * Get a map of the default floating preferences from the lookup.
+ *
+ * @return a map of the default floating preferences.
+ */
+ protected static Map getDFPFromLookup() {
+ final Map dfpFromLookup = new TreeMap<>();
+
+ try {
+ EventQueue.invokeAndWait(() -> Lookup.getDefault().lookupAll(AbstractTopComponent.class).forEach(lookup -> dfpFromLookup.put(
+ (String) lookup.getDefaultFloatingInfo().getFirst(),
+ (Boolean) lookup.getDefaultFloatingInfo().getSecond()
+ )));
+ } catch (InterruptedException | InvocationTargetException ex) {
+ LOGGER.log(Level.SEVERE, "There was a problem retrieving the default floating info.", ex);
+ }
+
+ return Collections.unmodifiableMap(dfpFromLookup);
+ }
+
+ /**
+ * Get a map of the default floating preferences from the file they are saved to.
+ *
+ * @return a map of the default floating preferences.
+ */
+ public static Map getDFPFromFile() {
+ if (dfpFromFile.isEmpty()) {
+ readDFPFile(getResourceDirectory());
+ }
+
+ return Collections.unmodifiableMap(dfpFromFile);
+ }
+
+ /**
+ * Get the file path to the file where the default floating preferences are saved to.
+ *
+ * @return the file path to the default floating preferences file.
+ */
+ protected static String getResourceDirectory() {
+ if (resourceDirectory.isBlank()) {
+ try {
+ final String source = ViewOptionsUtility.class.getProtectionDomain().getCodeSource().getLocation().getPath();
+ final URI uri = URI.create(source);
+ final Path path = Paths.get(uri);
+
+ final String sep = File.separator;
+ String[] splitPath = path.toString().split(Pattern.quote(sep));
+
+ // Keep removing the last directory in the path array until the constellation directory is reached.
+ while (!"constellation".equals(splitPath[splitPath.length - 1])) {
+ splitPath = Arrays.copyOfRange(splitPath, 0, splitPath.length - 1);
+ }
+
+ resourceDirectory = String.join(sep, splitPath) + sep + "dfp.txt";
+
+ LOGGER.log(Level.FINE, "DFP file directory was retrieved at: {0}", resourceDirectory);
+ } catch (final IllegalArgumentException ex) {
+ LOGGER.log(Level.SEVERE, "There was a problem retrieving the directory of the DFP file.", ex);
+ }
+ }
+
+ return resourceDirectory;
+ }
+}
diff --git a/CoreViewFramework/test/unit/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPanelControllerNGTest.java b/CoreViewFramework/test/unit/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPanelControllerNGTest.java
new file mode 100644
index 0000000000..d7b6551dd8
--- /dev/null
+++ b/CoreViewFramework/test/unit/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPanelControllerNGTest.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2010-2025 Australian Signals Directorate
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package au.gov.asd.tac.constellation.views.preferences;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.List;
+import java.util.Map;
+import static java.util.Map.entry;
+import org.mockito.MockedConstruction;
+import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import org.netbeans.spi.options.OptionsPanelController;
+import org.openide.util.HelpCtx;
+import org.openide.util.Lookup;
+import static org.testng.Assert.assertEquals;
+import org.testng.annotations.Test;
+
+/**
+ * Test class of ViewOptionsPanelController.
+ *
+ * @author sol695510
+ */
+public class ViewOptionsPanelControllerNGTest {
+
+ final Map prefsAllFalse = Map.ofEntries(
+ entry("Analytic View", Boolean.FALSE),
+ entry("Attribute Editor", Boolean.FALSE),
+ entry("Conversation View", Boolean.FALSE),
+ entry("Data Access View", Boolean.FALSE),
+ entry("Error Report", Boolean.FALSE));
+
+ final Map prefsAllTrue = Map.ofEntries(
+ entry("Analytic View", Boolean.TRUE),
+ entry("Attribute Editor", Boolean.TRUE),
+ entry("Conversation View", Boolean.TRUE),
+ entry("Data Access View", Boolean.TRUE),
+ entry("Error Report", Boolean.TRUE));
+
+ /**
+ * Test of update method, of class ViewOptionsPanelController.
+ */
+ @Test
+ public void testUpdate() {
+ System.out.println("update");
+
+ try (MockedConstruction mockVOP = mockConstruction(ViewOptionsPanel.class)) {
+
+ final ViewOptionsPanelController instance = new ViewOptionsPanelController();
+ instance.update();
+
+ // Assert that a mock of the ViewOptionsPanel was constructed.
+ final List constructedVOP = mockVOP.constructed();
+ assertEquals(constructedVOP.size(), 1);
+
+ // Verify that these methods were run on the constructed mock.
+ verify(constructedVOP.get(0), times(1)).fireTableDataChanged();
+ verify(constructedVOP.get(0), times(1)).createTableModel();
+ }
+ }
+
+ /**
+ * Test of applyChanges method, of class ViewOptionsPanelController.
+ */
+ @Test
+ public void testApplyChanges() {
+ System.out.println("applyChanges");
+
+ // When isChanged() returns true.
+ try (MockedConstruction mockVOP = mockConstruction(ViewOptionsPanel.class, (mockInstance, context) -> {
+ when(mockInstance.getOptionsFromUI()).thenReturn(prefsAllFalse);
+ when(mockInstance.getOptionsFromPrefs()).thenReturn(prefsAllTrue);
+ }); MockedConstruction mockPCS = mockConstruction(PropertyChangeSupport.class)) {
+
+ final ViewOptionsPanelController instance = new ViewOptionsPanelController();
+ instance.applyChanges();
+
+ // Assert that a mock of the ViewOptionsPanel was constructed.
+ final List constructedVOP = mockVOP.constructed();
+ assertEquals(constructedVOP.size(), 1);
+
+ // Assert that a mock of the PropertyChangeSupport was constructed.
+ final List constructedPCS = mockPCS.constructed();
+ assertEquals(constructedPCS.size(), 1);
+
+ // Verify that this method was run during isValid().
+ verify(constructedPCS.get(0), times(1)).firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
+
+ // Verify that these methods were run during isChanged().
+ verify(constructedVOP.get(0), times(1)).getOptionsFromPrefs();
+ verify(constructedVOP.get(0), times(1)).getOptionsFromUI();
+
+ // Verify that this method was run due to isChanged() returning true.
+ verify(constructedPCS.get(0), times(1)).firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
+ }
+
+ // When isChanged() returns false.
+ try (MockedConstruction mockVOP = mockConstruction(ViewOptionsPanel.class, (mockInstance, context) -> {
+ when(mockInstance.getOptionsFromUI()).thenReturn(prefsAllFalse);
+ when(mockInstance.getOptionsFromPrefs()).thenReturn(prefsAllFalse);
+ }); MockedConstruction mockPCS = mockConstruction(PropertyChangeSupport.class)) {
+
+ final ViewOptionsPanelController instance = new ViewOptionsPanelController();
+ instance.applyChanges();
+
+ // Assert that a mock of the ViewOptionsPanel was not constructed.
+ final List constructedVOP = mockVOP.constructed();
+ assertEquals(constructedVOP.size(), 1);
+
+ // Assert that a mock of the PropertyChangeSupport was constructed.
+ final List constructedPCS = mockPCS.constructed();
+ assertEquals(constructedPCS.size(), 1);
+
+ // Verify that this method was run during isValid().
+ verify(constructedPCS.get(0), times(1)).firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
+
+ // Verify that these methods were run during isChanged().
+ verify(constructedVOP.get(0), times(1)).getOptionsFromPrefs();
+ verify(constructedVOP.get(0), times(1)).getOptionsFromUI();
+
+ // Verify that this method was not run due to isChanged() returning false.
+ verify(constructedPCS.get(0), times(0)).firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
+ }
+ }
+
+ /**
+ * Test of isChanged method, of class ViewOptionsPanelController.
+ */
+ @Test
+ public void testIsChanged() {
+ System.out.println("isChanged");
+
+ // When the options from the NbPreferences and UI differ.
+ try (MockedConstruction mockVOP = mockConstruction(ViewOptionsPanel.class, (mockInstance, context) -> {
+ when(mockInstance.getOptionsFromUI()).thenReturn(prefsAllFalse);
+ when(mockInstance.getOptionsFromPrefs()).thenReturn(prefsAllTrue);
+ })) {
+
+ final ViewOptionsPanelController instance = new ViewOptionsPanelController();
+ final boolean result = instance.isChanged();
+ final boolean expResult = true;
+ assertEquals(result, expResult);
+
+ // Assert that a mock of the ViewOptionsPanel was constructed.
+ final List constructed = mockVOP.constructed();
+ assertEquals(constructed.size(), 1);
+
+ // Verify that these methods were run during isChanged().
+ verify(constructed.get(0), times(1)).getOptionsFromPrefs();
+ verify(constructed.get(0), times(1)).getOptionsFromUI();
+ }
+
+ // When the options from the NbPreferences and UI match.
+ try (MockedConstruction mockVOP = mockConstruction(ViewOptionsPanel.class, (mockInstance, context) -> {
+ when(mockInstance.getOptionsFromUI()).thenReturn(prefsAllFalse);
+ when(mockInstance.getOptionsFromPrefs()).thenReturn(prefsAllFalse);
+ })) {
+
+ final ViewOptionsPanelController instance = new ViewOptionsPanelController();
+ final boolean result = instance.isChanged();
+ final boolean expResult = false;
+ assertEquals(result, expResult);
+
+ // Assert that a mock of the ViewOptionsPanel was constructed.
+ final List constructed = mockVOP.constructed();
+ assertEquals(constructed.size(), 1);
+
+ // Verify that these methods were run during isChanged().
+ verify(constructed.get(0), times(1)).getOptionsFromPrefs();
+ verify(constructed.get(0), times(1)).getOptionsFromUI();
+ }
+ }
+
+ /**
+ * Test of getComponent method, of class ViewOptionsPanelController.
+ */
+ @Test
+ public void testGetComponent() {
+ System.out.println("getComponent");
+ final ViewOptionsPanelController instance = new ViewOptionsPanelController();
+
+ final Object result = instance.getComponent(Lookup.EMPTY);
+ assertEquals(result.getClass(), ViewOptionsPanel.class);
+ }
+
+ /**
+ * Test of getHelpCtx method, of class ViewOptionsPanelController.
+ */
+ @Test
+ public void testGetHelpCtx() {
+ System.out.println("getHelpCtx");
+ final ViewOptionsPanelController instance = new ViewOptionsPanelController();
+
+ final Object result1 = instance.getHelpCtx();
+ assertEquals(result1.getClass(), HelpCtx.class);
+
+ final HelpCtx result2 = instance.getHelpCtx();
+ assertEquals(result2.getHelpID(), "au.gov.asd.tac.constellation.views.preferences");
+ }
+
+ /**
+ * Test of addPropertyChangeListener of class ViewOptionsPanelController.
+ */
+ @Test
+ public void testAddPropertyChangeListener() {
+ System.out.println("addPropertyChangeListener");
+ final PropertyChangeListener pcl = null;
+
+ try (MockedConstruction mockPCS = mockConstruction(PropertyChangeSupport.class)) {
+
+ final ViewOptionsPanelController instance = new ViewOptionsPanelController();
+ instance.addPropertyChangeListener(pcl);
+
+ // Assert that a mock of the PropertyChangeSupport was constructed.
+ final List constructed = mockPCS.constructed();
+ assertEquals(constructed.size(), 3); // Why 3 and not 1?
+
+ verify(constructed.get(2), times(1)).addPropertyChangeListener(pcl);
+ }
+ }
+
+ /**
+ * Test of removePropertyChangeListener methods of class ViewOptionsPanelController.
+ */
+ @Test
+ public void testRemovePropertyChangeListener() {
+ System.out.println("removePropertyChangeListener");
+ final PropertyChangeListener pcl = null;
+
+ try (MockedConstruction mockPCS = mockConstruction(PropertyChangeSupport.class)) {
+
+ final ViewOptionsPanelController instance = new ViewOptionsPanelController();
+ instance.removePropertyChangeListener(pcl);
+
+ // Assert that a mock of the PropertyChangeSupport was constructed.
+ final List constructed = mockPCS.constructed();
+ assertEquals(constructed.size(), 1);
+
+ verify(constructed.get(0), times(1)).removePropertyChangeListener(pcl);
+ }
+ }
+}
diff --git a/CoreViewFramework/test/unit/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPanelNGTest.java b/CoreViewFramework/test/unit/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPanelNGTest.java
new file mode 100644
index 0000000000..8d7383862c
--- /dev/null
+++ b/CoreViewFramework/test/unit/src/au/gov/asd/tac/constellation/views/preferences/ViewOptionsPanelNGTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2010-2026 Australian Signals Directorate
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package au.gov.asd.tac.constellation.views.preferences;
+
+import java.util.List;
+import javax.swing.table.DefaultTableModel;
+import org.mockito.MockedConstruction;
+import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import org.testng.annotations.Test;
+
+/**
+ * Test of class ViewOptionsPanel.
+ *
+ * @author sol695510
+ */
+public class ViewOptionsPanelNGTest {
+
+ /**
+ * Test of createTableModel method, of class ViewOptionsPanel.
+ */
+ @Test
+ public void testCreateTableModel() {
+ System.out.println("createTableModel");
+
+ try (MockedConstruction mockDTM = mockConstruction(DefaultTableModel.class)) {
+
+ final ViewOptionsPanel instance = new ViewOptionsPanel();
+ instance.createTableModel();
+
+ // Assert that a mock of the DefaultTableModel was constructed.
+ final List constructedDTM = mockDTM.constructed();
+ assertEquals(constructedDTM.size(), 3); // Why 3 and not 1?
+
+ // Verify that the DefaultTableModel was correctly constructed.
+ assertEquals(constructedDTM.get(2).getColumnClass(0), String.class);
+ assertEquals(constructedDTM.get(2).getColumnClass(1), Boolean.class);
+
+ for (int i = 1; i < 28; i++) {
+ assertFalse(constructedDTM.get(2).isCellEditable(i, 0));
+ assertTrue(constructedDTM.get(2).isCellEditable(i, 1));
+ }
+ }
+ }
+
+ /**
+ * Test of fireTableDataChanged method, of class ViewOptionsPanel.
+ */
+ @Test
+ public void testFireTableDataChanged() {
+ System.out.println("fireTableDataChanged");
+
+ try (MockedConstruction mockDTM = mockConstruction(DefaultTableModel.class)) {
+
+ final ViewOptionsPanel instance = new ViewOptionsPanel();
+ instance.fireTableDataChanged();
+
+ // Assert that a mock of the DefaultTableModel was constructed.
+ final List constructedDTM = mockDTM.constructed();
+ assertEquals(constructedDTM.size(), 2); // Why 2 and not 1?
+
+ // Verify that this method was run on the constructed mock.
+ verify(constructedDTM.get(1), times(1)).fireTableDataChanged();
+ }
+ }
+}
diff --git a/CoreWebView/nbproject/project.xml b/CoreWebView/nbproject/project.xml
index 2314dae94f..b2405fac8b 100644
--- a/CoreWebView/nbproject/project.xml
+++ b/CoreWebView/nbproject/project.xml
@@ -6,6 +6,14 @@
au.gov.asd.tac.constellation.views.webview
+
+ au.gov.asd.tac.constellation.views
+
+
+
+ 1.0
+
+
org.openide.util
diff --git a/CoreWelcomeView/src/au/gov/asd/tac/constellation/views/welcome/WelcomeTopComponent.java b/CoreWelcomeView/src/au/gov/asd/tac/constellation/views/welcome/WelcomeTopComponent.java
index 97222c9980..c19f552c76 100644
--- a/CoreWelcomeView/src/au/gov/asd/tac/constellation/views/welcome/WelcomeTopComponent.java
+++ b/CoreWelcomeView/src/au/gov/asd/tac/constellation/views/welcome/WelcomeTopComponent.java
@@ -15,6 +15,7 @@
*/
package au.gov.asd.tac.constellation.views.welcome;
+import au.gov.asd.tac.constellation.utilities.datastructure.Tuple;
import au.gov.asd.tac.constellation.utilities.javafx.JavafxStyleManager;
import au.gov.asd.tac.constellation.views.JavaFxTopComponent;
import java.awt.BorderLayout;
@@ -85,9 +86,8 @@ public void readProperties(final java.util.Properties p) {
}
/**
- * This method is called from within the constructor to initialize the form.
- * WARNING: Do NOT modify this code. The content of this method is always
- * regenerated by the Form Editor.
+ * This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The
+ * content of this method is always regenerated by the Form Editor.
*/
// //GEN-BEGIN:initComponents
private void initComponents() {
@@ -144,8 +144,8 @@ public void componentShown(java.awt.event.ComponentEvent evt) {
}// //GEN-END:initComponents
private void formComponentShown(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentShown
- Platform.runLater(()-> {
- if (pane.getBottomRecentSection().getChildren()!= null) {
+ Platform.runLater(() -> {
+ if (pane.getBottomRecentSection().getChildren() != null) {
pane.refreshRecentFiles();
}
});
@@ -166,4 +166,19 @@ protected String createStyle() {
protected WelcomeViewPane createContent() {
return pane;
}
+
+ @Override
+ protected void handleComponentOpened() {
+ super.handleComponentOpened();
+ }
+
+ @Override
+ public Tuple getDefaultFloatingInfo() {
+ return Tuple.create(Bundle.CTL_WelcomeTopComponentTopComponent(), Boolean.FALSE);
+ }
+
+ @Override
+ protected String getModeName() {
+ return "editor";
+ }
}
diff --git a/CoreWhatsNewView/src/au/gov/asd/tac/constellation/views/whatsnew/WhatsNewTopComponent.java b/CoreWhatsNewView/src/au/gov/asd/tac/constellation/views/whatsnew/WhatsNewTopComponent.java
index 94c7458650..af73721223 100644
--- a/CoreWhatsNewView/src/au/gov/asd/tac/constellation/views/whatsnew/WhatsNewTopComponent.java
+++ b/CoreWhatsNewView/src/au/gov/asd/tac/constellation/views/whatsnew/WhatsNewTopComponent.java
@@ -15,6 +15,7 @@
*/
package au.gov.asd.tac.constellation.views.whatsnew;
+import au.gov.asd.tac.constellation.utilities.datastructure.Tuple;
import au.gov.asd.tac.constellation.views.JavaFxTopComponent;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
@@ -90,9 +91,8 @@ public void readProperties(final java.util.Properties p) {
}
/**
- * This method is called from within the constructor to initialize the form.
- * WARNING: Do NOT modify this code. The content of this method is always
- * regenerated by the Form Editor.
+ * This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The
+ * content of this method is always regenerated by the Form Editor.
*/
// //GEN-BEGIN:initComponents
private void initComponents() {
@@ -162,4 +162,19 @@ protected WhatsNewViewPane createContent() {
public void actionPerformed(ActionEvent e) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
+
+ @Override
+ protected void handleComponentOpened() {
+ super.handleComponentOpened();
+ }
+
+ @Override
+ public Tuple getDefaultFloatingInfo() {
+ return Tuple.create(Bundle.CTL_TutorialTopComponentTopComponent(), Boolean.FALSE);
+ }
+
+ @Override
+ protected String getModeName() {
+ return "editor";
+ }
}
diff --git a/CoreWhatsNewView/src/au/gov/asd/tac/constellation/views/whatsnew/whatsnew.txt b/CoreWhatsNewView/src/au/gov/asd/tac/constellation/views/whatsnew/whatsnew.txt
index 8bf52a9190..151b6ff902 100644
--- a/CoreWhatsNewView/src/au/gov/asd/tac/constellation/views/whatsnew/whatsnew.txt
+++ b/CoreWhatsNewView/src/au/gov/asd/tac/constellation/views/whatsnew/whatsnew.txt
@@ -1,9 +1,13 @@
== 3030-12-31 Getting Started
If you're new to Constellation, read the getting started guide.
+== 2026-06-05 Add preference option to set whether views open docked or floating by default
+ Views can now be set to open docked or floating by default via Setup -> Options -> CONSTELLATION -> View.
+
== 2026-02-11 Add "Arrange in 2D" option to Layer By Time
This new option allows a user to arrange the result of Layer By Time into a more readable format.
+
== 2026-01-13 Renamed "Chinese Whispers" to "Label Propagation"
"Chinese Whispers" clustering has been renamed to "Label Propagation".
diff --git a/CoreWordCloudView/src/au/gov/asd/tac/constellation/views/wordcloud/ui/WordCloudTopComponent.java b/CoreWordCloudView/src/au/gov/asd/tac/constellation/views/wordcloud/ui/WordCloudTopComponent.java
index 4b1a80ae8b..5badb92078 100644
--- a/CoreWordCloudView/src/au/gov/asd/tac/constellation/views/wordcloud/ui/WordCloudTopComponent.java
+++ b/CoreWordCloudView/src/au/gov/asd/tac/constellation/views/wordcloud/ui/WordCloudTopComponent.java
@@ -17,7 +17,9 @@
import au.gov.asd.tac.constellation.graph.Graph;
import au.gov.asd.tac.constellation.graph.manager.GraphManager;
+import au.gov.asd.tac.constellation.utilities.datastructure.Tuple;
import au.gov.asd.tac.constellation.utilities.javafx.JavafxStyleManager;
+import au.gov.asd.tac.constellation.views.AbstractTopComponent;
import au.gov.asd.tac.constellation.views.JavaFxTopComponent;
import java.awt.BorderLayout;
import java.awt.Dimension;
@@ -29,6 +31,7 @@
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.util.NbBundle.Messages;
+import org.openide.util.lookup.ServiceProvider;
import org.openide.windows.TopComponent;
/**
@@ -63,6 +66,7 @@
"CTL_WordCloudTopComponent=Word Cloud View",
"HINT_WordCloudTopComponent=Word Cloud View"
})
+@ServiceProvider(service = AbstractTopComponent.class)
public final class WordCloudTopComponent extends JavaFxTopComponent {
private final JFXPanel panel = new JFXPanel();
@@ -92,8 +96,8 @@ public WordCloudTopComponent() {
panel.setScene(scene);
// Update word cloud pane's size when window size changes
- scene.heightProperty().addListener((obv, oldVal, newVal) ->
- wordCloudPane.setContentHeight(newVal.intValue()));
+ scene.heightProperty().addListener((obv, oldVal, newVal)
+ -> wordCloudPane.setContentHeight(newVal.intValue()));
}
@Override
@@ -116,7 +120,7 @@ public void handleNewGraph(final Graph graph) {
}
if (graph != null) {
- // Add listener to new graph
+ // Add listener to new graph
this.graph = graph;
this.graph.addGraphChangeListener(this);
@@ -152,7 +156,7 @@ protected void handleGraphClosed(final Graph graph) {
}
setPaneStatus();
}
-
+
@Override
protected void handleComponentOpened() {
super.handleComponentOpened();
@@ -166,15 +170,25 @@ protected void componentShowing() {
controller.updateGraph();
setPaneStatus();
}
-
+
/**
- * Sets the status of the pane dependent on if a graph is currently active.
- * The status is used to enable or disable the view when a graph exists.
+ * Sets the status of the pane dependent on if a graph is currently active. The status is used to enable or disable
+ * the view when a graph exists.
*/
- protected void setPaneStatus(){
+ protected void setPaneStatus() {
createContent().setEnabled(GraphManager.getDefault().getActiveGraph() != null);
}
+ @Override
+ public Tuple getDefaultFloatingInfo() {
+ return Tuple.create(Bundle.CTL_WordCloudTopComponent(), Boolean.FALSE);
+ }
+
+ @Override
+ protected String getModeName() {
+ return "explorer";
+ }
+
/**
* This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The
* content of this method is always regenerated by the Form Editor.
diff --git a/dfp.txt b/dfp.txt
new file mode 100644
index 0000000000..96fc43e50e
--- /dev/null
+++ b/dfp.txt
@@ -0,0 +1,27 @@
+Analytic View:false
+Attribute Editor:false
+Conversation View:false
+Data Access View:false
+Error Report:false
+Find and Replace:true
+From Database...:false
+From File...:false
+Hierarchical:false
+Histogram:false
+K-Truss:false
+Layers View:false
+Map View:false
+Memory Manager:false
+Named Selections:false
+New Histogram:false
+Notes View:false
+Perspective Bookmarks:false
+Plane Manager:false
+Plugin Reporter:false
+Quality Control View:false
+Scatter Plot:false
+Schema View:false
+Scripting View:false
+Table View:false
+Timeline:false
+Word Cloud View:false
diff --git a/search.json b/search.json
index c92fcbe174..faed40fcb7 100644
--- a/search.json
+++ b/search.json
@@ -774,198 +774,204 @@
}
,{
"id": 129,
+ "title": "View",
+ "category": "Preferences",
+ "link": "ext\\docs\\CorePreferences\\view-preferences.html"
+}
+,{
+ "id": 130,
"title": "Label Fonts",
"category": "Preferences",
"link": "ext\\docs\\CoreOpenGLDisplay\\label-fonts-preferences.html"
}
,{
- "id": 130,
+ "id": 131,
"title": "Online Help",
"category": "Preferences",
"link": "ext\\docs\\CoreHelp\\help-options.html"
}
,{
- "id": 131,
+ "id": 132,
"title": "Proxy",
"category": "Preferences",
"link": "ext\\docs\\CoreSecurity\\proxy-preferences.html"
}
,{
- "id": 132,
+ "id": 133,
"title": "Grid",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\grid-arrangement.html"
}
,{
- "id": 133,
+ "id": 134,
"title": "Line",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\line-arrangement.html"
}
,{
- "id": 134,
+ "id": 135,
"title": "Circle",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\circle-arrangement.html"
}
,{
- "id": 135,
+ "id": 136,
"title": "Tree",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\tree-arrangement.html"
}
,{
- "id": 136,
+ "id": 137,
"title": "Proximity",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\proximity-arrangement.html"
}
,{
- "id": 137,
+ "id": 138,
"title": "Hierarchy",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\hierarchy-arrangement.html"
}
,{
- "id": 138,
+ "id": 139,
"title": "Node Attribute",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\node-attribute-arrangement.html"
}
,{
- "id": 139,
+ "id": 140,
"title": "Scatter 3D",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\scatter3d-arrangement.html"
}
,{
- "id": 140,
+ "id": 141,
"title": "Sphere",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\sphere-arrangement.html"
}
,{
- "id": 141,
+ "id": 142,
"title": "Bubble Tree 3D",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\bubble-tree-3d-arrangement.html"
}
,{
- "id": 142,
+ "id": 143,
"title": "Spectral 3D",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\spectral-arrangement.html"
}
,{
- "id": 143,
+ "id": 144,
"title": "HDE 3D",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\hde-arrangement.html"
}
,{
- "id": 144,
+ "id": 145,
"title": "Flatten Z-Field",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\flatten-z-field.html"
}
,{
- "id": 145,
+ "id": 146,
"title": "Contract Graph",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\contract-graph.html"
}
,{
- "id": 146,
+ "id": 147,
"title": "Expand Graph",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\expand-graph.html"
}
,{
- "id": 147,
+ "id": 148,
"title": "Uncollide",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\uncollide-arrangement.html"
}
,{
- "id": 148,
+ "id": 149,
"title": "Layer by Time",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\layer-by-time.html"
}
,{
- "id": 149,
+ "id": 150,
"title": "Pin and Unpin Nodes",
"category": "Arrangements",
"link": "ext\\docs\\CoreArrangementPlugins\\pin-unpin-nodes.html"
}
,{
- "id": 150,
+ "id": 151,
"title": "Import From File",
"category": "Import/Export",
"link": "ext\\docs\\CoreImportExportPlugins\\import-from-file.html"
}
,{
- "id": 151,
+ "id": 152,
"title": "Import From Database",
"category": "Import/Export",
"link": "ext\\docs\\CoreImportExportPlugins\\import-from-database.html"
}
,{
- "id": 152,
+ "id": 153,
"title": "Add Hashmod (Experimental)",
"category": "Import/Export",
"link": "ext\\docs\\CoreImportExportPlugins\\add-hashmod.html"
}
,{
- "id": 153,
+ "id": 154,
"title": "Export From Constellation",
"category": "Export",
"link": "ext\\docs\\CoreImportExportPlugins\\export-from-constellation.html"
}
,{
- "id": 154,
+ "id": 155,
"title": "Export to Image",
"category": "Export",
"link": "ext\\docs\\CoreImportExportPlugins\\export-to-png.html"
}
,{
- "id": 155,
+ "id": 156,
"title": "Export to SVG",
"category": "Export",
"link": "ext\\docs\\CoreImportExportPlugins\\export-to-svg.html"
}
,{
- "id": 156,
+ "id": 157,
"title": "Export to Shapefile",
"category": "Export",
"link": "ext\\docs\\CoreImportExportPlugins\\export-to-shapefile.html"
}
,{
- "id": 157,
+ "id": 158,
"title": "Export to KML",
"category": "Export",
"link": "ext\\docs\\CoreImportExportPlugins\\export-to-kml.html"
}
,{
- "id": 158,
+ "id": 159,
"title": "Export to GeoJson",
"category": "Export",
"link": "ext\\docs\\CoreImportExportPlugins\\export-to-geojson.html"
}
,{
- "id": 159,
+ "id": 160,
"title": "Export to GeoPackage",
"category": "Export",
"link": "ext\\docs\\CoreImportExportPlugins\\export-to-geopackage.html"
}
,{
- "id": 160,
+ "id": 161,
"title": "Export to CSV",
"category": "Export",
"link": "ext\\docs\\CoreImportExportPlugins\\export-to-csv.html"
}
,{
- "id": 161,
+ "id": 162,
"title": "Export to XLSX",
"category": "Export",
"link": "ext\\docs\\CoreImportExportPlugins\\export-to-xlsx.html"
diff --git a/toc.md b/toc.md
index 55d73ae0fe..8c66b114f5 100644
--- a/toc.md
+++ b/toc.md
@@ -47,7 +47,16 @@
K-Truss
Hierarchical
Info Map
-
+
+