.
- */
-package net.rptools.maptool.client.swing;
-
-import java.awt.Component;
-import javax.swing.JLabel;
-import javax.swing.JTable;
-import javax.swing.SwingConstants;
-import javax.swing.table.TableCellRenderer;
-
-/**
- * A customizable, theme-respecting table cell renderer.
- *
- * This is primarily intended for customizing header cells. Since Java does not expose the
- * default header cell renderer implementation, we cannot simply create new instances and customize
- * them as we can with regular table cells. This makes it difficult to customize header cells while
- * respecting the UI theme.
- *
- *
This cell renderer allows customizing header cells by decorating the pre-existing header cell
- * renderer, then modifying the component returned in {@link #getTableCellRendererComponent(JTable,
- * Object, boolean, boolean, int, int)}.
- *
- *
For now, only the text alignment can be customized, but support for other properties may be
- * added in the future.
- */
-public class TableCellRendererDecorator implements TableCellRenderer {
- private final TableCellRenderer decorated;
- private int horizontalAlignment = SwingConstants.LEADING;
- private int verticalAlignment = SwingConstants.CENTER;
-
- public TableCellRendererDecorator(TableCellRenderer decorated) {
- this.decorated = decorated;
- }
-
- public int getHorizontalAlignment() {
- return horizontalAlignment;
- }
-
- public void setHorizontalAlignment(int horizontalAlignment) {
- this.horizontalAlignment = horizontalAlignment;
- }
-
- public int getVerticalAlignment() {
- return verticalAlignment;
- }
-
- public void setVerticalAlignment(int verticalAlignment) {
- this.verticalAlignment = verticalAlignment;
- }
-
- @Override
- public Component getTableCellRendererComponent(
- JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
- var component =
- decorated.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
-
- /* Typical renderers use a label, which just so happens to be the component that allows us to
- * support alignment as we want. */
- if (component instanceof JLabel label) {
- label.setHorizontalAlignment(horizontalAlignment);
- label.setVerticalAlignment(verticalAlignment);
- }
-
- return component;
- }
-}
diff --git a/src/main/java/net/rptools/maptool/client/swing/table/MTMultilineStringCellEditor.java b/src/main/java/net/rptools/maptool/client/swing/table/MTMultilineStringCellEditor.java
new file mode 100644
index 0000000000..de81c1f489
--- /dev/null
+++ b/src/main/java/net/rptools/maptool/client/swing/table/MTMultilineStringCellEditor.java
@@ -0,0 +1,134 @@
+/*
+ * This software Copyright by the RPTools.net development team, and
+ * licensed under the Affero GPL Version 3 or, at your option, any later
+ * version.
+ *
+ * MapTool Source Code is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License * along with this source Code. If not, please visit
+ * and specifically the Affero license
+ * text at .
+ */
+package net.rptools.maptool.client.swing.table;
+
+import com.jidesoft.combobox.MultilineStringExComboBox;
+import com.jidesoft.combobox.PopupPanel;
+import com.jidesoft.grid.MultilineStringCellEditor;
+import com.jidesoft.plaf.basic.BasicExComboBoxUI;
+import java.awt.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import javax.swing.*;
+import net.rptools.maptool.client.AppConstants;
+import net.rptools.maptool.client.AppPreferences;
+import net.rptools.maptool.language.I18N;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
+import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
+import org.fife.ui.rsyntaxtextarea.Theme;
+import org.fife.ui.rtextarea.RTextScrollPane;
+
+/* Pop-up cell editor using RSyntaxTextArea. Used in Token Property editor. */
+public class MTMultilineStringCellEditor extends MultilineStringCellEditor {
+ private static final Logger log = LogManager.getLogger(MTMultilineStringCellEditor.class);
+
+ public MTMultilineStringExComboBox createMultilineStringComboBox() {
+ MTMultilineStringExComboBox localMultilineStringExComboBox = new MTMultilineStringExComboBox();
+ localMultilineStringExComboBox.setEditable(true);
+ localMultilineStringExComboBox.setUI(new BasicExComboBoxUI());
+ return localMultilineStringExComboBox;
+ }
+
+ /* needed to change the popup for properties */
+ public static class MTMultilineStringExComboBox extends MultilineStringExComboBox {
+ final ResourceBundle a = ResourceBundle.getBundle("com.jidesoft.combobox.combobox");
+
+ public ResourceBundle getResourceBundle(Locale paramLocale) {
+ return ResourceBundle.getBundle("com.jidesoft.combobox.combobox", paramLocale);
+ }
+
+ public PopupPanel createPopupComponent() {
+ return new MTMultilineStringPopupPanel(
+ getResourceBundle(Locale.getDefault()).getString("ComboBox.multilineStringTitle"));
+ }
+ }
+
+ /* the property popup table */
+ private static class MTMultilineStringPopupPanel extends PopupPanel {
+ private final RSyntaxTextArea j = createTextArea();
+
+ public MTMultilineStringPopupPanel() {
+ this("");
+ }
+
+ public MTMultilineStringPopupPanel(String paramString) {
+ this.setResizable(true);
+ /* Set the color style via Theme */
+ try {
+ File themeFile =
+ new File(
+ AppConstants.THEMES_DIR, AppPreferences.defaultMacroEditorTheme.get() + ".xml");
+ Theme theme = Theme.load(new FileInputStream(themeFile));
+ theme.apply(j);
+
+ j.revalidate();
+ } catch (IOException e) {
+ log.error("Error while loading multiline property editor theme", e);
+ }
+ JScrollPane localJScrollPane = new RTextScrollPane(j);
+ localJScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
+ localJScrollPane.setAutoscrolls(true);
+ localJScrollPane.setPreferredSize(new Dimension(300, 200));
+ setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 5));
+ setLayout(new BorderLayout());
+ setTitle(paramString);
+ add(localJScrollPane, "Center");
+ setDefaultFocusComponent(j);
+ j.setLineWrap(false);
+ JCheckBox wrapToggle = new JCheckBox(I18N.getString("EditTokenDialog.msg.wrap"));
+ wrapToggle.addActionListener(e -> j.setLineWrap(!j.getLineWrap()));
+
+ DefaultComboBoxModel syntaxListModel = new DefaultComboBoxModel<>();
+ syntaxListModel.addElement(SyntaxConstants.SYNTAX_STYLE_NONE);
+ syntaxListModel.addElement(SyntaxConstants.SYNTAX_STYLE_JSON);
+ syntaxListModel.addElement(SyntaxConstants.SYNTAX_STYLE_PROPERTIES_FILE);
+ syntaxListModel.addElement(SyntaxConstants.SYNTAX_STYLE_HTML);
+ syntaxListModel.addElement(SyntaxConstants.SYNTAX_STYLE_XML);
+ JComboBox syntaxComboBox = new JComboBox<>(syntaxListModel);
+ syntaxComboBox.addActionListener(
+ e -> j.setSyntaxEditingStyle((String) syntaxComboBox.getSelectedItem()));
+
+ add(syntaxComboBox, BorderLayout.BEFORE_FIRST_LINE);
+ add(wrapToggle, BorderLayout.AFTER_LAST_LINE);
+ }
+
+ public Object getSelectedObject() {
+ return j.getText();
+ }
+
+ public void setSelectedObject(Object paramObject) {
+ if (paramObject != null) {
+ j.setText(paramObject.toString());
+ } else {
+ j.setText("");
+ }
+ }
+
+ protected RSyntaxTextArea createTextArea() {
+ RSyntaxTextArea textArea = new RSyntaxTextArea();
+ textArea.setUseFocusableTips(false);
+ textArea.setAnimateBracketMatching(true);
+ textArea.setBracketMatchingEnabled(true);
+ textArea.setLineWrap(false);
+ textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_NONE);
+ return textArea;
+ }
+ }
+}
diff --git a/src/main/java/net/rptools/maptool/client/swing/table/MultiLineTableHeaderRenderer.java b/src/main/java/net/rptools/maptool/client/swing/table/MultiLineTableHeaderRenderer.java
new file mode 100644
index 0000000000..826718e335
--- /dev/null
+++ b/src/main/java/net/rptools/maptool/client/swing/table/MultiLineTableHeaderRenderer.java
@@ -0,0 +1,75 @@
+/*
+ * This software Copyright by the RPTools.net development team, and
+ * licensed under the Affero GPL Version 3 or, at your option, any later
+ * version.
+ *
+ * MapTool Source Code is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License * along with this source Code. If not, please visit
+ * and specifically the Affero license
+ * text at .
+ */
+package net.rptools.maptool.client.swing.table;
+
+import com.formdev.flatlaf.FlatIconColors;
+import com.formdev.flatlaf.util.ColorFunctions;
+import java.awt.*;
+import javax.swing.*;
+import javax.swing.table.TableCellRenderer;
+
+public class MultiLineTableHeaderRenderer implements TableCellRenderer {
+ private static final Color HEADER_BACKGROUND =
+ UIManager.getDefaults().getColor("TableHeader.background");
+ private static final Color HEADER_FOREGROUND =
+ UIManager.getDefaults().getColor("TableHeader.foreground");
+ private static final Color HEADER_ALTERNATE_BACKGROUND;
+ private static final Color HEADER_ALTERNATE_FOREGROUND;
+
+ static {
+ // this is so tests can be passed as they do not install the LaF
+ Color c1, c2;
+ try {
+ Color mixWith = UIManager.getColor(FlatIconColors.OBJECTS_BLACK_TEXT.key);
+ c1 = ColorFunctions.mix(HEADER_BACKGROUND, mixWith, 0.94f);
+ c2 = ColorFunctions.mix(HEADER_FOREGROUND, mixWith, 0.31f);
+ } catch (Exception e) {
+ c1 = Color.WHITE;
+ c2 = Color.BLACK;
+ }
+ HEADER_ALTERNATE_BACKGROUND = c1;
+ HEADER_ALTERNATE_FOREGROUND = c2;
+ }
+
+ public MultiLineTableHeaderRenderer() {}
+
+ @Override
+ public Component getTableCellRendererComponent(
+ JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+ JPanel panel = new JPanel();
+ Color foreground = column % 2 == 0 ? HEADER_ALTERNATE_FOREGROUND : HEADER_FOREGROUND;
+ Color background = column % 2 == 0 ? HEADER_ALTERNATE_BACKGROUND : HEADER_BACKGROUND;
+ panel.setBackground(background);
+ panel.setForeground(foreground);
+
+ LookAndFeel.installBorder(panel, "TableHeader.cellBorder");
+
+ BoxLayout box = new BoxLayout(panel, BoxLayout.PAGE_AXIS);
+ panel.setLayout(box);
+
+ String[] heading = ((String) value).split(" ");
+ for (String word : heading) {
+ JLabel label = new JLabel(word, null, SwingConstants.CENTER);
+ label.setBackground(background);
+ label.setForeground(foreground);
+ label.setOpaque(true);
+ label.setAlignmentX(0.5f);
+ panel.add(label);
+ }
+ panel.invalidate();
+
+ return panel;
+ }
+}
diff --git a/src/main/java/net/rptools/maptool/client/swing/TextFieldEditorButtonTableCellEditor.java b/src/main/java/net/rptools/maptool/client/swing/table/TextFieldEditorButtonTableCellEditor.java
similarity index 98%
rename from src/main/java/net/rptools/maptool/client/swing/TextFieldEditorButtonTableCellEditor.java
rename to src/main/java/net/rptools/maptool/client/swing/table/TextFieldEditorButtonTableCellEditor.java
index 67a85cc9fc..554ca6e536 100644
--- a/src/main/java/net/rptools/maptool/client/swing/TextFieldEditorButtonTableCellEditor.java
+++ b/src/main/java/net/rptools/maptool/client/swing/table/TextFieldEditorButtonTableCellEditor.java
@@ -12,7 +12,7 @@
* and specifically the Affero license
* text at .
*/
-package net.rptools.maptool.client.swing;
+package net.rptools.maptool.client.swing.table;
import java.awt.*;
import javax.swing.*;
@@ -40,6 +40,7 @@ public TextFieldEditorButtonTableCellEditor() {
textField.addActionListener(l -> fireEditingStopped());
panel.add(textField);
JButton button = new JButton("...");
+
button.addActionListener(
l ->
MacroEditorDialog.createModalDialog(
diff --git a/src/main/java/net/rptools/maptool/client/swing/table/WordWrapCellRenderer.java b/src/main/java/net/rptools/maptool/client/swing/table/WordWrapCellRenderer.java
new file mode 100644
index 0000000000..41c6b73c2e
--- /dev/null
+++ b/src/main/java/net/rptools/maptool/client/swing/table/WordWrapCellRenderer.java
@@ -0,0 +1,64 @@
+/*
+ * This software Copyright by the RPTools.net development team, and
+ * licensed under the Affero GPL Version 3 or, at your option, any later
+ * version.
+ *
+ * MapTool Source Code is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License * along with this source Code. If not, please visit
+ * and specifically the Affero license
+ * text at .
+ */
+package net.rptools.maptool.client.swing.table;
+
+import com.formdev.flatlaf.ui.FlatUIUtils;
+import java.awt.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import javax.swing.*;
+import javax.swing.table.TableCellRenderer;
+import net.rptools.maptool.client.AppConstants;
+import net.rptools.maptool.client.AppPreferences;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
+import org.fife.ui.rsyntaxtextarea.Theme;
+
+/* cell renderer for properties table */
+public class WordWrapCellRenderer extends RSyntaxTextArea implements TableCellRenderer {
+ private static final Logger log = LogManager.getLogger();
+
+ public WordWrapCellRenderer() {
+ setLineWrap(false);
+ setWrapStyleWord(true);
+
+ /* Set the color style via Theme */
+ try {
+ File themeFile =
+ new File(AppConstants.THEMES_DIR, AppPreferences.defaultMacroEditorTheme.get() + ".xml");
+ Theme theme = Theme.load(new FileInputStream(themeFile));
+ theme.apply(this);
+ setFont(FlatUIUtils.nonUIResource(UIManager.getFont("monospaced.font")));
+ revalidate();
+ } catch (IOException e) {
+ log.error("Error while loading theme", e);
+ }
+ }
+
+ public Component getTableCellRendererComponent(
+ JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+ if (value == null) {
+ value = "";
+ }
+ setText(value.toString());
+ setSize(table.getColumnModel().getColumn(column).getWidth(), getPreferredSize().height);
+ if (table.getRowHeight(row) != getPreferredSize().height) {
+ table.setRowHeight(row, getPreferredSize().height);
+ }
+ return this;
+ }
+}
diff --git a/src/main/java/net/rptools/maptool/client/tool/PointerTool.java b/src/main/java/net/rptools/maptool/client/tool/PointerTool.java
index 2bfb126081..862c7a6711 100644
--- a/src/main/java/net/rptools/maptool/client/tool/PointerTool.java
+++ b/src/main/java/net/rptools/maptool/client/tool/PointerTool.java
@@ -1430,6 +1430,11 @@ && new StatSheetManager().isLegacyStatSheet(tokenUnderMouse.getStatSheet())) {
if (property.isOwnerOnly() && !AppUtil.playerOwns(tokenUnderMouse)) {
continue;
}
+ if (!property
+ .getStatSheetViewPermission()
+ .hasPermission(MapTool.getPlayer(), tokenUnderMouse)) {
+ continue;
+ }
timer.start(property.getName());
MapToolVariableResolver resolver = new MapToolVariableResolver(tokenUnderMouse);
resolver.initialize();
@@ -1437,7 +1442,8 @@ && new StatSheetManager().isLegacyStatSheet(tokenUnderMouse.getStatSheet())) {
Object propertyValue =
tokenUnderMouse.getEvaluatedProperty(resolver, property.getName());
resolver.flush();
- if (propertyValue != null && propertyValue.toString().length() > 0) {
+ //noinspection ConstantValue
+ if (propertyValue != null && !propertyValue.toString().isEmpty()) {
String propName = property.getShortName();
if (StringUtils.isEmpty(propName)) {
propName = property.getName();
diff --git a/src/main/java/net/rptools/maptool/client/ui/campaignproperties/TokenPropertiesManagementPanel.java b/src/main/java/net/rptools/maptool/client/ui/campaignproperties/TokenPropertiesManagementPanel.java
index ff41626f4f..94e7edafb8 100644
--- a/src/main/java/net/rptools/maptool/client/ui/campaignproperties/TokenPropertiesManagementPanel.java
+++ b/src/main/java/net/rptools/maptool/client/ui/campaignproperties/TokenPropertiesManagementPanel.java
@@ -14,6 +14,7 @@
*/
package net.rptools.maptool.client.ui.campaignproperties;
+import com.jidesoft.grid.TableUtils;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.io.BufferedReader;
@@ -24,19 +25,19 @@
import java.util.stream.Stream;
import javax.swing.*;
import javax.swing.table.JTableHeader;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
import net.rptools.CaseInsensitiveHashMap;
import net.rptools.maptool.client.MapTool;
import net.rptools.maptool.client.swing.AbeillePanel;
-import net.rptools.maptool.client.swing.TableCellRendererDecorator;
-import net.rptools.maptool.client.swing.TextFieldEditorButtonTableCellEditor;
+import net.rptools.maptool.client.swing.table.MultiLineTableHeaderRenderer;
+import net.rptools.maptool.client.swing.table.TextFieldEditorButtonTableCellEditor;
import net.rptools.maptool.client.ui.campaignproperties.TokenPropertiesTableModel.LargeEditableText;
import net.rptools.maptool.client.ui.sheet.stats.StatSheetComboBoxRenderer;
import net.rptools.maptool.client.ui.theme.Icons;
import net.rptools.maptool.client.ui.theme.RessourceManager;
import net.rptools.maptool.language.I18N;
-import net.rptools.maptool.model.Campaign;
-import net.rptools.maptool.model.CampaignProperties;
-import net.rptools.maptool.model.TokenProperty;
+import net.rptools.maptool.model.*;
import net.rptools.maptool.model.sheet.stats.StatSheet;
import net.rptools.maptool.model.sheet.stats.StatSheetLocation;
import net.rptools.maptool.model.sheet.stats.StatSheetManager;
@@ -58,7 +59,6 @@ public class TokenPropertiesManagementPanel extends AbeillePanel
tokenTypeMap.put(k, new ArrayList<>(v.stream().map(TokenProperty::new).toList())));
- var ssManager = new StatSheetManager();
tokenTypeMap
.keySet()
.forEach(
@@ -82,7 +81,6 @@ public void copyCampaignToUI(CampaignProperties cp) {
}
public void copyUIToCampaign(Campaign campaign) {
-
campaign.getTokenTypeMap().clear();
campaign.getTokenTypeMap().putAll(tokenTypeMap);
campaign
@@ -146,7 +144,7 @@ public JComboBox getStatSheetLocationComboBox() {
return (JComboBox) getComponent("statSheetLocationComboBox");
}
- public JComboBox getStatSheetComboBox() {
+ public JComboBox getStatSheetComboBox() {
return (JComboBox) getComponent("statSheetComboBox");
}
@@ -372,9 +370,16 @@ public void initHelpButton() {
public void initPropertyTable() {
var propertyTable = getTokenPropertiesTable();
+ propertyTable.setRowHeight(propertyTable.getRowHeight() + 8);
propertyTable.setModel(new TokenPropertiesTableModel());
+
propertyTable.setDefaultEditor(
LargeEditableText.class, new TextFieldEditorButtonTableCellEditor());
+
+ propertyTable.setDefaultEditor(
+ Permissions.class, new DefaultCellEditor(new JComboBox<>(Permissions.values())));
+
+ propertyTable.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
propertyTable
.getSelectionModel()
.addListSelectionListener(
@@ -432,7 +437,6 @@ private void updateExistingTokenTypes(String oldName, String newName) {
}
public void initTypeList() {
-
getTokenTypeList()
.addListSelectionListener(
e -> {
@@ -461,11 +465,7 @@ public void initTypeList() {
getTypeDuplicateButton().setEnabled(true);
getTokenTypeName().setEditable(true);
// Can't delete the default property
- if (propertyType.equals(defaultPropertyType)) {
- getTypeDeleteButton().setEnabled(false);
- } else {
- getTypeDeleteButton().setEnabled(true);
- }
+ getTypeDeleteButton().setEnabled(!propertyType.equals(defaultPropertyType));
getStatSheetComboBox().setEnabled(true);
populateStatSheetComboBoxes(propertyType);
if (!propertyType.equals(defaultPropertyType)) {
@@ -546,7 +546,6 @@ private void bind(String type) {
}
private void reset() {
-
bind((String) null);
}
@@ -606,7 +605,7 @@ private List parseTokenProperties(String propertyText)
String original, line;
while ((original = reader.readLine()) != null) {
line = original = original.trim();
- if (line.length() == 0) {
+ if (line.isEmpty()) {
continue;
}
@@ -615,17 +614,17 @@ private List parseTokenProperties(String propertyText)
// Prefix
while (true) {
if (line.startsWith("*")) {
- property.setShowOnStatSheet(true);
+ property.setStatSheetViewPermission(Permissions.ALL);
line = line.substring(1);
continue;
}
if (line.startsWith("@")) {
- property.setOwnerOnly(true);
+ property.setStatSheetViewPermission(Permissions.OWNER);
line = line.substring(1);
continue;
}
if (line.startsWith("#")) {
- property.setGMOnly(true);
+ property.setStatSheetViewPermission(Permissions.GM);
line = line.substring(1);
continue;
}
@@ -641,7 +640,7 @@ private List parseTokenProperties(String propertyText)
int indexDefault = line.indexOf(':');
if (indexDefault > 0) {
String defaultVal = line.substring(indexDefault + 1).trim();
- if (defaultVal.length() > 0) {
+ if (!defaultVal.isEmpty()) {
property.setDefaultValue(defaultVal);
}
@@ -659,7 +658,7 @@ private List parseTokenProperties(String propertyText)
throw new IllegalArgumentException("Missing parenthesis");
}
String shortName = line.substring(index + 1, indexClose).trim();
- if (shortName.length() > 0) {
+ if (!shortName.isEmpty()) {
property.setShortName(shortName);
}
line = line.substring(0, index);
@@ -720,7 +719,7 @@ public void prettify() {
propertyTable.getTableHeader().setResizingAllowed(true);
// Custom header that uses the column model to decide tooltips.
- var header =
+ JTableHeader header =
new JTableHeader(propertyTable.getColumnModel()) {
@Override
public String getToolTipText(MouseEvent event) {
@@ -728,22 +727,22 @@ public String getToolTipText(MouseEvent event) {
return model.getColumnTooltipText(columnIndex);
}
};
+
propertyTable.setTableHeader(header);
// The custom renderer delegates to the default one.
- var customHeaderRenderer = new TableCellRendererDecorator(header.getDefaultRenderer());
- customHeaderRenderer.setHorizontalAlignment(SwingConstants.CENTER);
- customHeaderRenderer.setVerticalAlignment(SwingConstants.CENTER);
+ TableCellRenderer customHeaderRenderer = new MultiLineTableHeaderRenderer();
for (int i = 0; i < propertyTable.getColumnCount(); i++) {
- var column = propertyTable.getColumnModel().getColumn(i);
-
+ TableColumn column = propertyTable.getColumnModel().getColumn(i);
column.setHeaderRenderer(customHeaderRenderer);
}
+ TableUtils.autoResizeColumn(propertyTable, 3);
+ propertyTable.doLayout();
}
- private class TypeListModel extends AbstractListModel {
- public Object getElementAt(int index) {
+ private class TypeListModel extends AbstractListModel {
+ public String getElementAt(int index) {
List names = new ArrayList(tokenTypeMap.keySet());
Collections.sort(names);
return names.get(index);
diff --git a/src/main/java/net/rptools/maptool/client/ui/campaignproperties/TokenPropertiesManagementPanelView.form b/src/main/java/net/rptools/maptool/client/ui/campaignproperties/TokenPropertiesManagementPanelView.form
index e5b1bff2fd..fb316077ca 100644
--- a/src/main/java/net/rptools/maptool/client/ui/campaignproperties/TokenPropertiesManagementPanelView.form
+++ b/src/main/java/net/rptools/maptool/client/ui/campaignproperties/TokenPropertiesManagementPanelView.form
@@ -23,7 +23,7 @@
-
+
@@ -92,12 +92,12 @@
-
+
-
+
@@ -115,6 +115,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -127,88 +151,233 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
@@ -350,7 +519,7 @@
-
+
diff --git a/src/main/java/net/rptools/maptool/client/ui/campaignproperties/TokenPropertiesTableModel.java b/src/main/java/net/rptools/maptool/client/ui/campaignproperties/TokenPropertiesTableModel.java
index 3b7d6f1ff8..831b32b46a 100644
--- a/src/main/java/net/rptools/maptool/client/ui/campaignproperties/TokenPropertiesTableModel.java
+++ b/src/main/java/net/rptools/maptool/client/ui/campaignproperties/TokenPropertiesTableModel.java
@@ -21,6 +21,7 @@
import java.util.Map;
import javax.swing.table.AbstractTableModel;
import net.rptools.maptool.language.I18N;
+import net.rptools.maptool.model.Permissions;
import net.rptools.maptool.model.TokenProperty;
/** Table model for the token properties type table. */
@@ -41,7 +42,7 @@ public record LargeEditableText(String text) {}
*/
private Map> tokenTypeMap = new HashMap<>();
- /** The token type that is currently displayed in the table. */
+ /** The token property type on display in the table. */
private String tokenType = "";
/**
@@ -56,7 +57,7 @@ public void setPropertyType(String propertyType) {
@Override
public int getRowCount() {
- var properties = tokenTypeMap.get(tokenType);
+ List properties = tokenTypeMap.get(tokenType);
return properties == null ? 0 : properties.size();
}
@@ -67,19 +68,16 @@ public int getColumnCount() {
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
- var properties = tokenTypeMap.get(tokenType);
- var property = properties.get(rowIndex);
+ List properties = tokenTypeMap.get(tokenType);
+ TokenProperty property = properties.get(rowIndex);
return switch (columnIndex) {
case 0 -> property.getName();
case 1 -> property.getShortName();
- case 2 -> {
- var displayName = property.getDisplayName();
- yield displayName == null || displayName.isBlank() ? null : displayName;
- }
+ case 2 -> property.getDisplayName();
case 3 -> property.getDefaultValue();
- case 4 -> property.isShowOnStatSheet();
- case 5 -> property.isGMOnly() & property.isShowOnStatSheet();
- case 6 -> property.isOwnerOnly() & property.isShowOnStatSheet();
+ case 4 -> property.getEditorViewPermission().equals(Permissions.OWNER);
+ case 5 -> property.getEditorEditPermission().equals(Permissions.OWNER);
+ case 6 -> property.getStatSheetViewPermission();
default -> null;
};
}
@@ -90,9 +88,9 @@ public String getColumnTooltipText(int column) {
case 1 -> I18N.getText("campaignPropertiesTable.column.shortName.description");
case 2 -> I18N.getText("campaignPropertiesTable.column.displayName.description");
case 3 -> I18N.getText("campaignPropertiesTable.column.default.description");
- case 4 -> I18N.getText("campaignPropertiesTable.column.statSheet.description");
- case 5 -> I18N.getText("campaignPropertiesTable.column.gm.description");
- case 6 -> I18N.getText("campaignPropertiesTable.column.owner.description");
+ case 4 -> I18N.getText("campaignPropertiesTable.column.playerViewable.description");
+ case 5 -> I18N.getText("campaignPropertiesTable.column.playerEditable.description");
+ case 6 -> I18N.getText("campaignPropertiesTable.column.statSheet.description");
default -> "";
};
}
@@ -104,9 +102,9 @@ public String getColumnName(int column) {
case 1 -> I18N.getText("campaignPropertiesTable.column.shortName");
case 2 -> I18N.getText("campaignPropertiesTable.column.displayName");
case 3 -> I18N.getText("campaignPropertiesTable.column.defaultValue");
- case 4 -> I18N.getText("campaignPropertiesTable.column.onStatSheet");
- case 5 -> I18N.getText("campaignPropertiesTable.column.gmStatSheet");
- case 6 -> I18N.getText("campaignPropertiesTable.column.ownerStatSheet");
+ case 4 -> I18N.getText("campaignPropertiesTable.column.playerViewable");
+ case 5 -> I18N.getText("campaignPropertiesTable.column.playerEditable");
+ case 6 -> I18N.getText("campaignPropertiesTable.column.statSheetVisibility");
default -> "";
};
}
@@ -116,43 +114,39 @@ public Class> getColumnClass(int columnIndex) {
return switch (columnIndex) {
case 0, 1, 2 -> String.class;
case 3 -> LargeEditableText.class;
- case 4, 5, 6 -> Boolean.class;
+ case 4, 5 -> Boolean.class;
+ case 6 -> Permissions.class;
default -> null;
};
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
- var properties = tokenTypeMap.get(tokenType);
- var tokenProperty = properties.get(rowIndex);
- return switch (columnIndex) {
- case 5, 6 ->
- tokenProperty.isShowOnStatSheet(); // GM, Owner only editable if show on stat sheet is set
- default -> true;
- };
+ if (columnIndex == 5) {
+ return (boolean) getValueAt(rowIndex, 4);
+ }
+ return true;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
- var properties = tokenTypeMap.get(tokenType);
- var tokenProperty = properties.get(rowIndex);
+ List properties = tokenTypeMap.get(tokenType);
+ TokenProperty tokenProperty = properties.get(rowIndex);
+
switch (columnIndex) {
case 0 -> tokenProperty.setName((String) aValue);
case 1 -> tokenProperty.setShortName((String) aValue);
case 2 -> tokenProperty.setDisplayName((String) aValue);
case 3 -> tokenProperty.setDefaultValue((String) aValue);
- case 4 -> {
- tokenProperty.setShowOnStatSheet((Boolean) aValue);
- fireTableRowsUpdated(rowIndex, rowIndex);
- }
- case 5 -> tokenProperty.setGMOnly((Boolean) aValue);
- case 6 -> tokenProperty.setOwnerOnly((Boolean) aValue);
+ case 4 -> tokenProperty.setEditorViewPermission((boolean) aValue);
+ case 5 -> tokenProperty.setEditorEditPermission((boolean) aValue);
+ case 6 -> tokenProperty.setStatSheetViewPermission((Permissions) aValue);
}
}
/** Adds a new token property, with a generated name. */
public void addProperty(int selectedRow) {
- var properties = tokenTypeMap.get(tokenType);
+ List properties = tokenTypeMap.get(tokenType);
// First find a unique name, there are so few entries we don't have to worry
// about being fancy
@@ -189,13 +183,13 @@ public void addProperty(int selectedRow) {
* @param selectedRow the selected row to delete.
*/
public void deleteProperty(int selectedRow) {
- var properties = tokenTypeMap.get(tokenType);
+ List properties = tokenTypeMap.get(tokenType);
properties.remove(selectedRow);
fireTableRowsDeleted(selectedRow, selectedRow);
}
public void movePropertyUp(int selectedRow) {
- var properties = tokenTypeMap.get(tokenType);
+ List properties = tokenTypeMap.get(tokenType);
if (selectedRow <= 0 || selectedRow >= properties.size()) {
// Either already at the top or a nonsense index.
throw new ArrayIndexOutOfBoundsException(selectedRow);
@@ -206,7 +200,7 @@ public void movePropertyUp(int selectedRow) {
}
public void movePropertyDown(int selectedRow) {
- var properties = tokenTypeMap.get(tokenType);
+ List properties = tokenTypeMap.get(tokenType);
if (selectedRow < 0 || selectedRow >= properties.size() - 1) {
// Either already at the bottom or a nonsense index.
throw new ArrayIndexOutOfBoundsException(selectedRow);
diff --git a/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheetListener.java b/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheetListener.java
index c39201e107..b42b7c1fcb 100644
--- a/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheetListener.java
+++ b/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheetListener.java
@@ -16,11 +16,9 @@
import com.google.common.eventbus.Subscribe;
import net.rptools.maptool.client.AppPreferences;
-import net.rptools.maptool.client.AppUtil;
import net.rptools.maptool.client.MapTool;
import net.rptools.maptool.client.events.TokenHoverEnter;
import net.rptools.maptool.client.events.TokenHoverExit;
-import net.rptools.maptool.model.Token.Type;
import net.rptools.maptool.model.sheet.stats.StatSheetManager;
/**
@@ -55,16 +53,9 @@ public void onHoverEnter(TokenHoverEnter event) {
statSheet = new StatSheet();
var ssId = ssProperties.id();
var ssRecord = ssManager.getStatSheet(ssId);
- var token = event.token();
- if (MapTool.getPlayer().isGM()
- || AppUtil.playerOwns(token)
- || token.getType() == Type.NPC) {
- statSheet.setContent(
- event,
- ssManager.getStatSheetContent(ssId),
- ssRecord.entry(),
- ssProperties.location());
- }
+
+ statSheet.setContent(
+ event, ssManager.getStatSheetContent(ssId), ssRecord.entry(), ssProperties.location());
}
}
}
diff --git a/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/EditTokenDialog.java b/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/EditTokenDialog.java
index 4478170f9e..c25f4d7b0b 100644
--- a/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/EditTokenDialog.java
+++ b/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/EditTokenDialog.java
@@ -15,19 +15,10 @@
package net.rptools.maptool.client.ui.token.dialog.edit;
import com.google.common.collect.Iterables;
-import com.jidesoft.combobox.MultilineStringExComboBox;
-import com.jidesoft.combobox.PopupPanel;
-import com.jidesoft.grid.MultilineStringCellEditor;
-import com.jidesoft.grid.NavigableModel;
-import com.jidesoft.grid.Property;
-import com.jidesoft.grid.PropertyPane;
import com.jidesoft.grid.PropertyTable;
-import com.jidesoft.grid.PropertyTableModel;
-import com.jidesoft.plaf.basic.BasicExComboBoxUI;
import com.jidesoft.swing.CheckBoxListWithSelectable;
import com.jidesoft.swing.DefaultSelectable;
import com.jidesoft.swing.Selectable;
-import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
@@ -44,14 +35,12 @@
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
-import javax.annotation.Nullable;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.AbstractTableModel;
-import javax.swing.table.TableCellRenderer;
import javax.swing.text.Position.Bias;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
@@ -113,7 +102,6 @@ public class EditTokenDialog extends AbeillePanel {
private final TokenPropertiesDialog view;
private final RSyntaxTextArea xmlStatblockRSyntaxTextArea = new RSyntaxTextArea(2, 2);
private final RSyntaxTextArea textStatblockRSyntaxTextArea = new RSyntaxTextArea(2, 2);
- private final WordWrapCellRenderer propertyCellRenderer = new WordWrapCellRenderer();
private boolean tokenSaved;
private final GenericDialogFactory dialogFactory =
@@ -125,9 +113,12 @@ public class EditTokenDialog extends AbeillePanel {
private AutoGenerateTopologySwingWorker autoGenerateTopologySwingWorker =
new AutoGenerateTopologySwingWorker(false, Color.BLACK);
+ private TokenPropertiesEditorPanel tokenPropertiesEditorPanel;
+
private EditTokenDialog(TokenPropertiesDialog view) {
super(view.getRootComponent());
this.view = view;
+ tokenPropertiesEditorPanel = new TokenPropertiesEditorPanel(this);
panelInit();
}
@@ -136,11 +127,6 @@ public EditTokenDialog() {
this(new TokenPropertiesDialog());
}
- @SuppressWarnings("unused")
- public void initPropertyTable() {
- getPropertyTable().setModel(new TokenPropertyTableModel());
- }
-
@SuppressWarnings("unused")
public void initGMNotesEditorPane() {
setGmNotesEnabled(MapTool.getPlayer().isGM());
@@ -374,7 +360,7 @@ public void bind(final Token token) {
var propertyType = token.getPropertyType();
getPropertyTypeCombo().setSelectedItem(propertyType);
/* Make sure the right properties are displayed. */
- updatePropertiesTable(token, propertyType);
+ tokenPropertiesEditorPanel.updatePropertiesTable(token, propertyType);
getSightTypeCombo()
.setSelectedItem(
@@ -647,7 +633,7 @@ public void initPropertyTypeCombo() {
.addItemListener(
e -> {
if (e.getStateChange() == ItemEvent.SELECTED) {
- updatePropertiesTable(
+ tokenPropertiesEditorPanel.updatePropertiesTable(
getModel(), (String) getPropertyTypeCombo().getSelectedItem());
}
});
@@ -680,22 +666,6 @@ private void updateImageTableCombo() {
getImageTableCombo().setModel(model);
}
- /**
- * Updates the property table.
- *
- * @param propertyType the property type of the token (unused).
- */
- private void updatePropertiesTable(@Nullable Token token, final String propertyType) {
- EventQueue.invokeLater(
- () -> {
- PropertyTable pp = getPropertyTable();
- var propertyList = MapTool.getCampaign().getTokenPropertyList(propertyType);
- pp.setModel(
- new TokenPropertyTableModel(token, propertyType, propertyList, propertyCellRenderer));
- pp.expandAll();
- });
- }
-
public JComboBox getSizeCombo() {
return (JComboBox) getComponent("size");
}
@@ -917,12 +887,13 @@ public boolean commit() {
/* Properties */
var tableModel = getPropertyTable().getModel();
- if (getPropertyTable().getModel() instanceof TokenPropertyTableModel tokenPropertyTableModel) {
+ if (getPropertyTable().getModel()
+ instanceof TokenPropertiesEditorPanel.TokenPropertyTableModel tokenPropertyTableModel) {
tokenPropertyTableModel.applyTo(token);
} else {
log.warn(
"Property table model is not of the expected type; expected {} but got {}",
- TokenPropertyTableModel.class,
+ TokenPropertiesEditorPanel.TokenPropertyTableModel.class,
tableModel.getClass());
}
@@ -1251,31 +1222,8 @@ public void initOwnershipPanel() {
}
public void initPropertiesPanel() {
- PropertyTable propertyTable =
- new PropertyTable() {
- @Override
- public String getToolTipText(MouseEvent event) {
- String text = super.getToolTipText(event);
- return text != null && text.length() > 100 ? text.substring(0, 100) + " ..." : text;
- }
- };
- propertyTable.setFillsViewportHeight(true);
- propertyTable.setName("propertiesTable");
-
- /* wrap button and functionality */
- JPanel buttonsAndPropertyTable = new JPanel();
- buttonsAndPropertyTable.setLayout(new BorderLayout());
- JCheckBox wrapToggle = new JCheckBox(I18N.getString("EditTokenDialog.msg.wrap"));
- wrapToggle.addActionListener(
- e -> {
- propertyCellRenderer.setLineWrap(wrapToggle.isSelected());
- propertyTable.repaint();
- });
- buttonsAndPropertyTable.add(wrapToggle, BorderLayout.PAGE_END);
-
- PropertyPane pane = new PropertyPane(propertyTable);
- buttonsAndPropertyTable.add(pane, BorderLayout.CENTER);
- replaceComponent("propertiesPanel", "propertiesTable", buttonsAndPropertyTable);
+ tokenPropertiesEditorPanel.reset(getModel());
+ replaceComponent("propertiesPanel", "propertiesTable", tokenPropertiesEditorPanel);
}
public void initTokenLayoutPanel() {
@@ -1918,143 +1866,6 @@ public Map getMap() {
}
}
- /* needed to change the popup for properties */
- private static class MTMultilineStringExComboBox extends MultilineStringExComboBox {
-
- final ResourceBundle a = ResourceBundle.getBundle("com.jidesoft.combobox.combobox");
-
- public ResourceBundle getResourceBundle(Locale paramLocale) {
- return ResourceBundle.getBundle("com.jidesoft.combobox.combobox", paramLocale);
- }
-
- public PopupPanel createPopupComponent() {
- MTMultilineStringPopupPanel pp =
- new MTMultilineStringPopupPanel(
- getResourceBundle(Locale.getDefault()).getString("ComboBox.multilineStringTitle"));
- return pp;
- }
- }
-
- /* the cell editor for property popups */
- private static class MTMultilineStringCellEditor extends MultilineStringCellEditor {
-
- protected MTMultilineStringExComboBox createMultilineStringComboBox() {
- MTMultilineStringExComboBox localMultilineStringExComboBox =
- new MTMultilineStringExComboBox();
- localMultilineStringExComboBox.setEditable(true);
- localMultilineStringExComboBox.setUI(new BasicExComboBoxUI());
- return localMultilineStringExComboBox;
- }
- }
-
- /* the property popup table */
- private static class MTMultilineStringPopupPanel extends PopupPanel {
-
- private RSyntaxTextArea j = createTextArea();
-
- public MTMultilineStringPopupPanel() {
- this("");
- }
-
- public MTMultilineStringPopupPanel(String paramString) {
- this.setResizable(true);
- /* Set the color style via Theme */
- try {
- File themeFile =
- new File(
- AppConstants.THEMES_DIR, AppPreferences.defaultMacroEditorTheme.get() + ".xml");
- Theme theme = Theme.load(new FileInputStream(themeFile));
- theme.apply(j);
-
- j.revalidate();
- } catch (IOException e) {
- log.error("Error while loading multiline property editor theme", e);
- }
- JScrollPane localJScrollPane = new RTextScrollPane(j);
- localJScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
- localJScrollPane.setAutoscrolls(true);
- localJScrollPane.setPreferredSize(new Dimension(300, 200));
- setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 5));
- setLayout(new BorderLayout());
- setTitle(paramString);
- add(localJScrollPane, "Center");
- setDefaultFocusComponent(j);
- j.setLineWrap(false);
- JCheckBox wrapToggle = new JCheckBox(I18N.getString("EditTokenDialog.msg.wrap"));
- wrapToggle.addActionListener(e -> j.setLineWrap(!j.getLineWrap()));
-
- DefaultComboBoxModel syntaxListModel = new DefaultComboBoxModel();
- syntaxListModel.addElement(SyntaxConstants.SYNTAX_STYLE_NONE);
- syntaxListModel.addElement(SyntaxConstants.SYNTAX_STYLE_JSON);
- syntaxListModel.addElement(SyntaxConstants.SYNTAX_STYLE_PROPERTIES_FILE);
- syntaxListModel.addElement(SyntaxConstants.SYNTAX_STYLE_HTML);
- syntaxListModel.addElement(SyntaxConstants.SYNTAX_STYLE_XML);
- JComboBox syntaxComboBox = new JComboBox(syntaxListModel);
- syntaxComboBox.addActionListener(
- e -> j.setSyntaxEditingStyle(syntaxComboBox.getSelectedItem().toString()));
-
- add(syntaxComboBox, BorderLayout.BEFORE_FIRST_LINE);
- add(wrapToggle, BorderLayout.AFTER_LAST_LINE);
- }
-
- public Object getSelectedObject() {
- return j.getText();
- }
-
- public void setSelectedObject(Object paramObject) {
- if (paramObject != null) {
- j.setText(paramObject.toString());
- } else {
- j.setText("");
- }
- }
-
- protected RSyntaxTextArea createTextArea() {
- RSyntaxTextArea textArea = new RSyntaxTextArea();
- textArea.setUseFocusableTips(false);
- textArea.setAnimateBracketMatching(true);
- textArea.setBracketMatchingEnabled(true);
- textArea.setLineWrap(false);
- textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_NONE);
- return textArea;
- }
- }
-
- /* cell renderer for properties table */
- private static class WordWrapCellRenderer extends RSyntaxTextArea implements TableCellRenderer {
-
- WordWrapCellRenderer() {
- setLineWrap(false);
- setWrapStyleWord(true);
-
- /* Set the color style via Theme */
- try {
- File themeFile =
- new File(
- AppConstants.THEMES_DIR, AppPreferences.defaultMacroEditorTheme.get() + ".xml");
- Theme theme = Theme.load(new FileInputStream(themeFile));
- theme.apply(this);
-
- revalidate();
- } catch (IOException e) {
- log.error("Error while loading theme", e);
- }
- }
-
- public Component getTableCellRendererComponent(
- JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
- if (value == null) {
- value = "";
- }
- setText(value.toString());
- setSize(table.getColumnModel().getColumn(column).getWidth(), getPreferredSize().height);
- if (table.getRowHeight(row) != getPreferredSize().height) {
- table.setRowHeight(row, getPreferredSize().height);
- }
- return this;
- }
- }
-
private class AutoGenerateTopologySwingWorker extends SwingWorker {
private final boolean regenerate;
private final Color ignoredColor;
@@ -2147,87 +1958,6 @@ public Component getListCellRendererComponent(
}
/* MODELS */
- private static class TokenPropertyTableModel
- extends PropertyTableModel
- implements NavigableModel {
- private final List propertyList;
- private final Map propertyMap;
-
- public TokenPropertyTableModel() {
- propertyList = List.of();
- propertyMap = Map.of();
- }
-
- public TokenPropertyTableModel(
- @Nullable Token model,
- String propertyType,
- List propertyList,
- TableCellRenderer propertyCellRenderer) {
- this.propertyList = propertyList;
-
- this.propertyMap = new HashMap<>();
- for (TokenProperty property : propertyList) {
- String value = model == null ? null : (String) model.getProperty(property.getName());
- if (value == null) {
- value = property.getDefaultValue();
- }
- this.propertyMap.put(property.getName(), value);
- }
-
- var gridProperties = new ArrayList();
- for (var tokenProperty : propertyList) {
- var gridProperty = new EditTokenProperty(tokenProperty.getName(), propertyType);
- gridProperty.setTableCellRenderer(propertyCellRenderer);
- gridProperty.setCellEditor(new MTMultilineStringCellEditor());
- gridProperties.add(gridProperty);
- }
- setOriginalProperties(gridProperties);
- }
-
- public void applyTo(Token token) {
- for (TokenProperty property : propertyList) {
- String value = propertyMap.get(property.getName());
- if (property.getDefaultValue() != null && property.getDefaultValue().equals(value)) {
- token.setProperty(property.getName(), null); // Clear original value
- continue;
- }
- token.setProperty(property.getName(), value);
- }
- }
-
- @Override
- public boolean isNavigableAt(int rowIndex, int columnIndex) {
- /* make the property name column non-navigable so that tab takes you directly to the next property value cell. */
- return (columnIndex != 0);
- }
-
- @Override
- public boolean isNavigationOn() {
- return true;
- }
-
- class EditTokenProperty extends Property {
- public EditTokenProperty(String key, String propertyType) {
- super(key, key, String.class, propertyType);
- }
-
- @Override
- public Object getValue() {
- return propertyMap.get(getName());
- }
-
- @Override
- public void setValue(Object value) {
- propertyMap.put(getName(), (String) value);
- }
-
- @Override
- public boolean hasValue() {
- return propertyMap.get(getName()) != null;
- }
- }
- }
-
private static final class OwnerListModel extends AbstractListModel {
private static final long serialVersionUID = 2375600545516097234L;
diff --git a/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/TokenPropertiesEditorPanel.java b/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/TokenPropertiesEditorPanel.java
new file mode 100644
index 0000000000..c57099610c
--- /dev/null
+++ b/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/TokenPropertiesEditorPanel.java
@@ -0,0 +1,283 @@
+/*
+ * This software Copyright by the RPTools.net development team, and
+ * licensed under the Affero GPL Version 3 or, at your option, any later
+ * version.
+ *
+ * MapTool Source Code is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License * along with this source Code. If not, please visit
+ * and specifically the Affero license
+ * text at .
+ */
+package net.rptools.maptool.client.ui.token.dialog.edit;
+
+import static com.jidesoft.converter.ConverterContext.DEFAULT_CONTEXT;
+import static com.jidesoft.swing.SearchableBar.*;
+
+import com.jidesoft.converter.ConverterContext;
+import com.jidesoft.grid.*;
+import com.jidesoft.swing.*;
+import java.awt.*;
+import java.awt.event.MouseEvent;
+import java.util.*;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import javax.swing.*;
+import net.rptools.maptool.client.MapTool;
+import net.rptools.maptool.client.swing.table.MTMultilineStringCellEditor;
+import net.rptools.maptool.client.swing.table.WordWrapCellRenderer;
+import net.rptools.maptool.language.I18N;
+import net.rptools.maptool.model.Permissions;
+import net.rptools.maptool.model.Token;
+import net.rptools.maptool.model.TokenProperty;
+import net.rptools.maptool.model.player.Player;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+public class TokenPropertiesEditorPanel extends PropertyPane {
+ private static final Logger log = LogManager.getLogger();
+ private final EditTokenDialog editTokenDialog;
+ private final WordWrapCellRenderer wordWrapCellRenderer = new WordWrapCellRenderer();
+ private static final String GM = I18N.getText("permission.displayName.gm");
+ private static final String GM_SUFFIX = String.format(" (%s)", GM);
+ private Token token;
+ private TableSearchable searchable;
+
+ public TokenPropertiesEditorPanel(EditTokenDialog editTokenDialog) {
+ super(
+ new PropertyTable() {
+ @Override
+ public String getToolTipText(MouseEvent event) {
+ String text = super.getToolTipText(event);
+ return text != null && text.length() > 100 ? text.substring(0, 100) + " ..." : text;
+ }
+ },
+ 1);
+
+ this.editTokenDialog = editTokenDialog;
+
+ setShowDescription(false);
+ searchable = SearchableUtils.installSearchable(getPropertyTable());
+ searchable.setSearchColumnIndices(new int[] {0, 1});
+
+ SearchableBar searchableBar = new SearchableBar(searchable, true);
+ searchableBar.setHighlightAll(true);
+ searchableBar.setVisibleButtons(
+ SHOW_NAVIGATION
+ | SHOW_HIGHLIGHTS
+ | SHOW_MATCHCASE
+ | SHOW_REPEATS
+ | SHOW_STATUS
+ | SHOW_WHOLE_WORDS);
+
+ UIManager.getDefaults()
+ .entrySet()
+ .forEach(
+ entry -> {
+ if (entry.getKey() instanceof String key) {
+ if (key.toLowerCase().contains("table")
+ || key.contains("jide")
+ || key.startsWith("com.")) {
+ System.out.println(key + ":" + entry.getValue());
+ }
+ }
+ });
+ UIManager.getDefaults().put("Table.rowHeight", 24);
+ // move sort buttons to searchable toolbar
+ JideBoxLayout toolbarLayout = (JideBoxLayout) searchableBar.getLayout();
+ toolbarLayout.addLayoutComponent(Box.createHorizontalStrut(2), JideBoxLayout.VARY);
+ searchableBar.add(new JSeparator(), 0);
+ for (Component c : getToolBar().getComponents()) {
+ searchableBar.add(c, 0);
+ }
+ add(searchableBar, BorderLayout.BEFORE_FIRST_LINE);
+
+ getPropertyTable().setModel(new TokenPropertyTableModel());
+ getPropertyTable().setFillsViewportHeight(true);
+ getPropertyTable().setDisableUneditableCells(false);
+ getPropertyTable().setName("propertiesTable");
+
+ /* wrap button and functionality */
+ JPanel buttonsAndPropertyTable = new JPanel();
+ buttonsAndPropertyTable.setLayout(new BorderLayout());
+ JCheckBox wrapToggle = new JCheckBox(I18N.getString("EditTokenDialog.msg.wrap"));
+ wrapToggle.addActionListener(
+ e -> {
+ wordWrapCellRenderer.setLineWrap(wrapToggle.isSelected());
+ getPropertyTable().repaint();
+ });
+ buttonsAndPropertyTable.add(wrapToggle, BorderLayout.PAGE_END);
+
+ buttonsAndPropertyTable.add(this, BorderLayout.CENTER);
+ setMinimumSize(new Dimension(300, 200));
+ setPreferredSize(new Dimension(-1, -1));
+ setVisible(true);
+ }
+
+ public void reset(Token token) {}
+
+ /**
+ * Updates the property table.
+ *
+ * @param propertyType the property type of the token (unused).
+ */
+ protected void updatePropertiesTable(@Nullable Token token, final String propertyType) {
+ EventQueue.invokeLater(
+ () -> {
+ PropertyTable pp = getPropertyTable();
+ List propertyTypeProperties =
+ MapTool.getCampaign().getTokenPropertyList(propertyType);
+ pp.setModel(
+ new TokenPropertyTableModel(
+ token, propertyType, propertyTypeProperties, wordWrapCellRenderer));
+ pp.expandAll();
+ });
+ }
+
+ protected static class TokenPropertyTableModel
+ extends PropertyTableModel
+ implements NavigableModel {
+
+ private final Map propertyMap;
+ private final ArrayList gridProperties = new ArrayList<>();
+ private Set otherPropertyNames;
+
+ public TokenPropertyTableModel() {
+ otherPropertyNames = Set.of();
+ propertyMap = Map.of();
+ }
+
+ public TokenPropertyTableModel(
+ @Nullable Token model,
+ String propertyTypeName,
+ List propertyList,
+ WordWrapCellRenderer wordWrapCellRenderer) {
+ gridProperties.clear();
+
+ Player player = MapTool.getPlayer();
+
+ Set typePropertyNames =
+ propertyList.stream().map(TokenProperty::getName).collect(Collectors.toSet());
+ Set otherPropertyNames = Set.of();
+ if (model != null) {
+ otherPropertyNames = model.getPropertyNamesRaw();
+ otherPropertyNames =
+ otherPropertyNames.stream()
+ .filter(name -> !typePropertyNames.stream().toList().contains(name))
+ .collect(Collectors.toSet());
+ }
+
+ this.propertyMap = new HashMap<>();
+
+ for (TokenProperty tp : propertyList) {
+ String value = null;
+ if (model != null) {
+ value = (String) model.getProperty(tp.getName());
+ }
+ this.propertyMap.put(
+ tp.getName(), value == null && tp.hasDefaultValue() ? tp.getDefaultValue() : value);
+
+ TableTokenProperty gridProperty = new TableTokenProperty(tp, propertyTypeName);
+ gridProperty.setTableCellRenderer(wordWrapCellRenderer);
+ gridProperty.setEditable(tp.getEditorEditPermission().hasPermission(player, model));
+ gridProperty.setCellEditor(new MTMultilineStringCellEditor());
+ gridProperties.add(gridProperty);
+ }
+ for (String propName : otherPropertyNames) {
+ this.propertyMap.put(propName, (String) model.getProperty(propName));
+
+ TableTokenProperty gridProperty = new TableTokenProperty(propName, "", GM);
+ gridProperty.setTableCellRenderer(wordWrapCellRenderer);
+ gridProperty.setCellEditor(new MTMultilineStringCellEditor());
+ gridProperties.add(gridProperty);
+ }
+
+ setOriginalProperties(gridProperties);
+ }
+
+ public void applyTo(Token token) {
+ for (TableTokenProperty tableProp : gridProperties) {
+ String value = propertyMap.get(tableProp.getName());
+ if (tableProp.hasTokenProperty()) {
+ TokenProperty tp = tableProp.getTokenProperty();
+ if (tp.getDefaultValue() != null && tp.getDefaultValue().equals(value)) {
+ token.setProperty(tableProp.getName(), null); // Clear original value
+ continue;
+ }
+ }
+ token.setProperty(tableProp.getName(), value);
+ }
+ }
+
+ @Override
+ public boolean isNavigableAt(int rowIndex, int columnIndex) {
+ /* make the property name column non-navigable so that tab takes you directly to the next property value cell. */
+ return (columnIndex != 0);
+ }
+
+ @Override
+ public boolean isNavigationOn() {
+ return true;
+ }
+
+ class TableTokenProperty extends Property {
+ TokenProperty tokenProperty;
+
+ public TableTokenProperty(TokenProperty tokenProperty, String propertyType) {
+ this(
+ tokenProperty.getName(),
+ tokenProperty.getDisplayName(),
+ String.class,
+ propertyType,
+ DEFAULT_CONTEXT,
+ null);
+ this.tokenProperty = tokenProperty;
+ if (tokenProperty.getEditorViewPermission().equals(Permissions.GM)) {
+ this.setCategory(this.getCategory() + GM_SUFFIX);
+ }
+ }
+
+ public TableTokenProperty(String propertyName, String propertyType, String category) {
+ this(propertyName, propertyName, String.class, propertyType, DEFAULT_CONTEXT, null);
+ }
+
+ public TableTokenProperty(
+ String name,
+ String displayName,
+ Class> klass,
+ String category,
+ ConverterContext converterContext,
+ List children) {
+ super(name, "", klass, category, converterContext, children);
+ setDisplayName(displayName);
+ }
+
+ public boolean hasTokenProperty() {
+ return tokenProperty != null;
+ }
+
+ public TokenProperty getTokenProperty() {
+ return tokenProperty;
+ }
+
+ @Override
+ public Object getValue() {
+ return propertyMap.get(getName());
+ }
+
+ @Override
+ public void setValue(Object value) {
+ propertyMap.put(getName(), (String) value);
+ }
+
+ @Override
+ public boolean hasValue() {
+ return propertyMap.get(getName()) != null;
+ }
+ }
+ }
+}
diff --git a/src/main/java/net/rptools/maptool/model/CampaignProperties.java b/src/main/java/net/rptools/maptool/model/CampaignProperties.java
index f59f806f3a..964f3d6541 100644
--- a/src/main/java/net/rptools/maptool/model/CampaignProperties.java
+++ b/src/main/java/net/rptools/maptool/model/CampaignProperties.java
@@ -51,6 +51,7 @@
import net.rptools.maptool.server.proto.HaloListDto;
import net.rptools.maptool.server.proto.LightSourceListDto;
import net.rptools.maptool.server.proto.TokenPropertyListDto;
+import org.apache.commons.text.CaseUtils;
public class CampaignProperties implements Serializable {
@@ -138,9 +139,10 @@ public CampaignProperties() {}
public CampaignProperties(CampaignProperties properties) {
for (Entry> entry : properties.tokenTypeMap.entrySet()) {
- List typeList = new ArrayList<>(properties.tokenTypeMap.get(entry.getKey()));
+ List typeProperties =
+ new ArrayList<>(properties.tokenTypeMap.get(entry.getKey()));
- tokenTypeMap.put(entry.getKey(), typeList);
+ tokenTypeMap.put(entry.getKey(), typeProperties);
}
tokenTypeStatSheetMap.putAll(properties.tokenTypeStatSheetMap);
@@ -199,7 +201,7 @@ public Map> getTokenTypeMap() {
}
/**
- * Returns the default stat sheet details for a token property type.
+ * Returns the default stat sheet details for a token's type properties.
*
* @param propertyType the token property type to get the details for.
* @return the stat sheet details.
@@ -587,46 +589,39 @@ private void initTokenTypeMap() {
}
List list = new ArrayList<>();
- list.add(
- new TokenProperty(
- I18N.getText(PROP_PREFIX + "strength"), I18N.getText(SHORT_PROP_PREFIX + "strength")));
- list.add(
- new TokenProperty(
- I18N.getText(PROP_PREFIX + "dexterity"),
- I18N.getText(SHORT_PROP_PREFIX + "dexterity")));
- list.add(
- new TokenProperty(
- I18N.getText(PROP_PREFIX + "constitution"),
- I18N.getText(SHORT_PROP_PREFIX + "constitution")));
- list.add(
- new TokenProperty(
- I18N.getText(PROP_PREFIX + "intelligence"),
- I18N.getText(SHORT_PROP_PREFIX + "intelligence")));
- list.add(
- new TokenProperty(
- I18N.getText(PROP_PREFIX + "wisdom"), I18N.getText(SHORT_PROP_PREFIX + "wisdom")));
- list.add(
- new TokenProperty(
- I18N.getText(PROP_PREFIX + "charisma"), I18N.getText(SHORT_PROP_PREFIX + "charisma")));
- list.add(new TokenProperty(I18N.getText(PROP_PREFIX + "hp"), true, true, false));
- list.add(new TokenProperty(I18N.getText(PROP_PREFIX + "ac"), true, true, false));
- list.add(
- new TokenProperty(
- I18N.getText(PROP_PREFIX + "defense"), I18N.getText(SHORT_PROP_PREFIX + "defense")));
- list.add(
- new TokenProperty(
- I18N.getText(PROP_PREFIX + "movement"), I18N.getText(SHORT_PROP_PREFIX + "movement")));
- list.add(
- new TokenProperty(
- I18N.getText(PROP_PREFIX + "elevation"),
- I18N.getText(SHORT_PROP_PREFIX + "elevation"),
- true,
- false,
- false));
- list.add(
- new TokenProperty(
- I18N.getText(PROP_PREFIX + "description"),
- I18N.getText(SHORT_PROP_PREFIX + "description")));
+ final String[] basicPropNames =
+ new String[] {
+ "strength",
+ "dexterity",
+ "constitution",
+ "intelligence",
+ "wisdom",
+ "charisma",
+ "hp",
+ "ac",
+ "defense",
+ "movement",
+ "elevation",
+ "description"
+ };
+ for (String propName : basicPropNames) {
+ Permissions visibility =
+ switch (propName) {
+ case "hp", "ac" -> Permissions.OWNER;
+ case "elevation", "description" -> Permissions.ALL;
+ default -> Permissions.NONE;
+ };
+
+ String displayName = I18N.getText(PROP_PREFIX + propName);
+ TokenProperty tp =
+ new TokenProperty(
+ CaseUtils.toCamelCase(displayName, false),
+ I18N.getText(SHORT_PROP_PREFIX + propName),
+ displayName,
+ visibility);
+
+ list.add(tp);
+ }
tokenTypeMap.put(getDefaultTokenPropertyType(), list);
}
diff --git a/src/main/java/net/rptools/maptool/model/DisplayNames.java b/src/main/java/net/rptools/maptool/model/DisplayNames.java
new file mode 100644
index 0000000000..3fd4e3dc9e
--- /dev/null
+++ b/src/main/java/net/rptools/maptool/model/DisplayNames.java
@@ -0,0 +1,33 @@
+/*
+ * This software Copyright by the RPTools.net development team, and
+ * licensed under the Affero GPL Version 3 or, at your option, any later
+ * version.
+ *
+ * MapTool Source Code is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License * along with this source Code. If not, please visit
+ * and specifically the Affero license
+ * text at .
+ */
+package net.rptools.maptool.model;
+
+public interface DisplayNames {
+ String getName();
+
+ String getDisplayName();
+
+ String getShortName();
+
+ void setName(String value);
+
+ void setDisplayName(String value);
+
+ void setShortName(String value);
+
+ boolean hasShortName();
+
+ boolean hasDisplayName();
+}
diff --git a/src/main/java/net/rptools/maptool/model/Permissions.java b/src/main/java/net/rptools/maptool/model/Permissions.java
new file mode 100644
index 0000000000..7b0b1adcb9
--- /dev/null
+++ b/src/main/java/net/rptools/maptool/model/Permissions.java
@@ -0,0 +1,107 @@
+/*
+ * This software Copyright by the RPTools.net development team, and
+ * licensed under the Affero GPL Version 3 or, at your option, any later
+ * version.
+ *
+ * MapTool Source Code is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License * along with this source Code. If not, please visit
+ * and specifically the Affero license
+ * text at .
+ */
+package net.rptools.maptool.model;
+
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nullable;
+import net.rptools.maptool.client.MapTool;
+import net.rptools.maptool.language.I18N;
+import net.rptools.maptool.model.player.Player;
+
+/**
+ *
+ *
+ * Defined Player permissions
+ *
+ * Accommodates ordinary RPG use for GM vs Players
+ * Adds a level {@link #ALLIED} where friendlies have permission as a step before {@link #ALL}
+ * This opens the door for Teams of Players
+ * Introduces exclusive permissions beyond GM Only
+ *
+ * Used in {@link TokenProperty} for determining who can see a property on a pop-up attribute
+ * sheet, and whether a property appears in the token property editor.
+ */
+public enum Permissions {
+ // Order follows increasing visibility for accumulated permissions
+
+ /** Nobody has permission */
+ NONE("permission.displayName.none"),
+ /** Only the GMs have permission */
+ GM("permission.displayName.gm"),
+ /** The GMs and the Owners have permission */
+ OWNER("permission.displayName.owner"),
+ /** The GMs, Owners, and the Owners' Allies have permission */
+ ALLIED("permission.displayName.allies"),
+ /** Everyone has permission */
+ ALL("permission.displayName.all"),
+
+ // Exclusive permissions, i.e. Can exclude the GM, etc.
+ /** Only the Owners have permission */
+ OWNER_ONLY("permission.displayName.owner.discrete"),
+ /** Only the Owners' Allies have permission */
+ ALLIED_ONLY("permission.displayName.allies.discrete"),
+ /** Only the Owners and the Owners' Allies have permission */
+ OWNER_ALLIED_ONLY("permission.displayName.allies.discrete"),
+ /** Only the Owners' Opponents have permission */
+ OPPONENT_ONLY("permission.displayName.opponent");
+
+ final String displayName;
+
+ Permissions(String i18nKey) {
+ this.displayName = I18N.getText(i18nKey);
+ }
+
+ public boolean hasPermission(@Nullable Player player, @Nullable Token token) {
+ if (player == null || token == null) {
+ return this.equals(ALL);
+ }
+ return switch (this) {
+ case ALL -> true;
+ case NONE -> false;
+ case GM -> player.isGM();
+ case OWNER -> player.isGM() || token.isOwner(player.getName());
+ case ALLIED -> player.isGM() || token.isOwner(player.getName()) || isAllied(token, player);
+ case OWNER_ONLY -> token.isOwner(player.getName());
+ case ALLIED_ONLY -> isAllied(token, player);
+ case OWNER_ALLIED_ONLY -> token.isOwner(player.getName()) || isAllied(token, player);
+ case OPPONENT_ONLY -> !token.isOwner(player.getName()) && !isAllied(token, player);
+ };
+ }
+
+ /**
+ * Simplistic check for alliance based on GM vs. players where all players on the same side. All
+ * PCs and player-owned NPCs are on the same side.
+ *
+ * @param token to check is ally
+ * @param player whose side we are checking for
+ * @return if token belongs to the same side
+ */
+ private boolean isAllied(Token token, Player player) {
+ if (!player.isGM() && token.getType().equals(Token.Type.PC)) {
+ return true;
+ } else {
+ List teamMates =
+ MapTool.getPlayerList().stream().dropWhile(p -> p.isGM() != player.isGM()).toList();
+ final Set owners = token.getOwners();
+ return !teamMates.stream().filter(p -> owners.contains(p.getName())).toList().isEmpty();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return displayName;
+ }
+}
diff --git a/src/main/java/net/rptools/maptool/model/TokenProperty.java b/src/main/java/net/rptools/maptool/model/TokenProperty.java
index 40ddd86506..fa51529238 100644
--- a/src/main/java/net/rptools/maptool/model/TokenProperty.java
+++ b/src/main/java/net/rptools/maptool/model/TokenProperty.java
@@ -16,41 +16,115 @@
import com.google.protobuf.StringValue;
import java.io.Serializable;
+import java.util.Objects;
import net.rptools.maptool.server.proto.TokenPropertyDto;
-public class TokenProperty implements Serializable {
+public class TokenProperty implements DisplayNames, Serializable {
private String name;
private String shortName;
- private boolean highPriority; // showOnStatSheet; so that 1.3b28 files load in 1.3b29
- private boolean ownerOnly;
- private boolean gmOnly;
- private String defaultValue;
-
private String displayName;
+ private String defaultValue = "";
+
+ // old stat-sheet permissions
+ @Deprecated private boolean highPriority; // showOnStatSheet; so that 1.3b28 files load in 1.3b29
+ @Deprecated private boolean ownerOnly;
+ @Deprecated private boolean gmOnly;
+
+ // new permissions
+ private Permissions editorViewPermission = Permissions.OWNER;
+ private Permissions editorEditPermission = Permissions.OWNER;
+ private Permissions statSheetViewPermission = Permissions.NONE;
public TokenProperty() {
// For serialization
}
public TokenProperty(String name) {
- this(name, null, false, false, false);
+ this(name, null, (String) null);
}
public TokenProperty(String name, String shortName) {
- this(name, shortName, false, false, false);
+ this(name, shortName, (String) null);
+ }
+
+ public TokenProperty(String name, String shortName, String displayName) {
+ this.name = name;
+ this.shortName = shortName;
+ this.displayName = displayName;
+ }
+
+ public TokenProperty(String name, boolean playerEditable) {
+ this(name, null, null, playerEditable, Permissions.NONE, null);
+ }
+
+ public TokenProperty(String name, Permissions statSheetViewPermission) {
+ this(name, null, null, true, statSheetViewPermission, null);
+ }
+
+ public TokenProperty(String name, String shortName, boolean playerEditable) {
+ this(name, shortName, null, playerEditable, Permissions.NONE, null);
}
- public TokenProperty(String name, boolean highPriority, boolean isOwnerOnly, boolean isGMOnly) {
- this(name, null, highPriority, isOwnerOnly, isGMOnly);
+ public TokenProperty(String name, String shortName, String displayName, boolean playerEditable) {
+ this(name, shortName, displayName, playerEditable, Permissions.NONE, null);
+ }
+
+ public TokenProperty(String name, boolean playerEditable, Permissions statSheetViewPermission) {
+ this(name, null, null, playerEditable, statSheetViewPermission, null);
+ }
+
+ public TokenProperty(String name, String shortName, Permissions statSheetViewPermission) {
+ this(name, shortName, null, true, statSheetViewPermission, null);
}
public TokenProperty(
- String name, String shortName, boolean highPriority, boolean isOwnerOnly, boolean isGMOnly) {
- this.name = name;
- this.shortName = shortName;
- this.highPriority = highPriority;
- this.ownerOnly = isOwnerOnly;
- this.gmOnly = isGMOnly;
+ String name, String shortName, boolean playerEditable, Permissions statSheetViewPermission) {
+ this(name, shortName, null, playerEditable, statSheetViewPermission, null);
+ }
+
+ public TokenProperty(
+ String name, String shortName, Permissions statSheetViewPermission, String defaultValue) {
+ this(name, shortName, null, true, statSheetViewPermission, defaultValue);
+ }
+
+ public TokenProperty(
+ String name,
+ String shortName,
+ boolean playerEditable,
+ Permissions statSheetViewPermission,
+ String defaultValue) {
+ this(name, shortName, null, playerEditable, statSheetViewPermission, defaultValue);
+ }
+
+ public TokenProperty(
+ String name, String shortName, String displayName, Permissions statSheetViewPermission) {
+ this(name, shortName, displayName, true, statSheetViewPermission, null);
+ }
+
+ public TokenProperty(
+ String name,
+ String shortName,
+ String displayName,
+ boolean playerEditable,
+ Permissions statSheetViewPermission) {
+ this(name, shortName, displayName, playerEditable, statSheetViewPermission, null);
+ }
+
+ public TokenProperty(
+ String name,
+ String shortName,
+ String displayName,
+ boolean playerEditable,
+ Permissions statSheetViewPermission,
+ String defaultValue) {
+ this(
+ name,
+ shortName,
+ displayName,
+ playerEditable,
+ playerEditable,
+ statSheetViewPermission,
+ defaultValue);
}
public TokenProperty(
@@ -68,51 +142,95 @@ public TokenProperty(
this.defaultValue = defaultValue;
}
+ public TokenProperty(
+ String name,
+ String shortName,
+ String displayName,
+ boolean playerViewable,
+ boolean playerEditable,
+ Permissions statSheetViewPermission,
+ String defaultValue) {
+ this.name = name;
+ this.shortName = shortName;
+ this.displayName = displayName;
+ this.editorEditPermission = playerEditable ? Permissions.OWNER : Permissions.GM;
+ this.editorViewPermission = playerViewable ? Permissions.OWNER : Permissions.GM;
+ if (statSheetViewPermission != null) {
+ this.statSheetViewPermission = statSheetViewPermission;
+ }
+ this.defaultValue = defaultValue;
+ }
+
/**
* Creates a new TokenProperty that's a copy of another.
*
* @param prop the property to copy the values from.
*/
+ @SuppressWarnings("CopyConstructorMissesField")
public TokenProperty(TokenProperty prop) {
- this.name = prop.name;
- this.shortName = prop.shortName;
- this.highPriority = prop.highPriority;
- this.ownerOnly = prop.ownerOnly;
- this.gmOnly = prop.gmOnly;
- this.defaultValue = prop.defaultValue;
- this.displayName = prop.displayName;
+ this.name = prop.getName();
+ this.shortName = prop.getShortName();
+ this.displayName = prop.getDisplayName();
+ this.defaultValue = prop.getDefaultValue();
+
+ if (prop.highPriority) {
+ if (prop.ownerOnly) {
+ setStatSheetViewPermission(Permissions.OWNER);
+ } else if (prop.gmOnly) {
+ setStatSheetViewPermission(Permissions.GM);
+ } else {
+ setStatSheetViewPermission(Permissions.ALL);
+ }
+ } else if (prop.hasStatSheetViewPermission()) {
+ setStatSheetViewPermission(prop.getStatSheetViewPermission());
+ }
+ // because these getters substitute nulls for defaults
+ setEditorEditPermission(prop.getEditorEditPermission());
+ setEditorViewPermission(prop.getEditorViewPermission());
}
- public boolean isOwnerOnly() {
- return ownerOnly;
+ public boolean isShowOnStatSheet() {
+ return statSheetViewPermission != null && !statSheetViewPermission.equals(Permissions.NONE);
}
- public void setOwnerOnly(boolean ownerOnly) {
- this.ownerOnly = ownerOnly;
+ public String getName() {
+ return name;
}
- public boolean isShowOnStatSheet() {
- return highPriority;
+ public void setName(String name) {
+ this.name = name;
}
- public void setShowOnStatSheet(boolean showOnStatSheet) {
- this.highPriority = showOnStatSheet;
+ public boolean hasDisplayName() {
+ return displayName != null && !displayName.isBlank();
}
- public String getName() {
- return name;
+ public boolean hasShortName() {
+ return shortName != null && !shortName.isBlank();
}
- public boolean hasDisplayName() {
- return displayName != null;
+ public boolean hasDefaultValue() {
+ return defaultValue != null && !defaultValue.isBlank();
+ }
+
+ public boolean hasEditorViewPermission() {
+ return editorViewPermission != null;
+ }
+
+ public boolean hasEditorEditPermission() {
+ return editorEditPermission != null;
+ }
+
+ public boolean hasStatSheetViewPermission() {
+ return statSheetViewPermission != null;
}
public String getDisplayName() {
return displayName;
}
- public void setName(String name) {
- this.name = name;
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
}
public String getShortName() {
@@ -123,12 +241,23 @@ public void setShortName(String shortName) {
this.shortName = shortName;
}
+ public Permissions getStatSheetViewPermission() {
+ if (!hasStatSheetViewPermission()) {
+ statSheetViewPermission = Permissions.NONE;
+ }
+ return statSheetViewPermission;
+ }
+
+ public void setStatSheetViewPermission(Permissions statSheetViewPermission) {
+ this.statSheetViewPermission = statSheetViewPermission;
+ }
+
public boolean isGMOnly() {
- return gmOnly;
+ return statSheetViewPermission.equals(Permissions.GM);
}
- public void setGMOnly(boolean gmOnly) {
- this.gmOnly = gmOnly;
+ public boolean isOwnerOnly() {
+ return statSheetViewPermission.equals(Permissions.OWNER);
}
public String getDefaultValue() {
@@ -139,17 +268,56 @@ public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
- public void setDisplayName(String displayName) {
- this.displayName = displayName;
+ public Permissions getEditorViewPermission() {
+ if (!hasEditorViewPermission()) {
+ this.editorViewPermission = Permissions.OWNER;
+ }
+ return editorViewPermission;
+ }
+
+ public void setEditorViewPermission(Permissions editorViewPermission) {
+ this.editorViewPermission = editorViewPermission;
+ }
+
+ public void setEditorViewPermission(boolean value) {
+ if (value) {
+ setEditorViewPermission(Permissions.OWNER);
+ } else {
+ setEditorViewPermission(Permissions.GM);
+ setEditorEditPermission(Permissions.GM);
+ }
+ }
+
+ public Permissions getEditorEditPermission() {
+ if (!hasEditorEditPermission()) {
+ this.editorEditPermission = Permissions.OWNER;
+ }
+ return editorEditPermission;
+ }
+
+ public void setEditorEditPermission(boolean value) {
+ setEditorEditPermission(value ? Permissions.OWNER : Permissions.GM);
+ }
+
+ public void setEditorEditPermission(Permissions editorEditPermission) {
+ this.editorEditPermission = editorEditPermission;
}
public static TokenProperty fromDto(TokenPropertyDto dto) {
var prop = new TokenProperty();
prop.name = dto.getName();
prop.shortName = dto.hasShortName() ? dto.getShortName().getValue() : null;
- prop.highPriority = dto.getHighPriority();
- prop.ownerOnly = dto.getOwnerOnly();
- prop.gmOnly = dto.getGmOnly();
+
+ prop.editorEditPermission =
+ dto.hasEditorEditPermission()
+ ? Permissions.valueOf(dto.getEditorEditPermission())
+ : Permissions.OWNER;
+ prop.editorViewPermission =
+ dto.hasEditorViewPermission()
+ ? Permissions.valueOf(dto.getEditorViewPermission())
+ : Permissions.OWNER;
+ prop.statSheetViewPermission = Permissions.valueOf(dto.getStatSheetViewPermission());
+
prop.defaultValue = dto.hasDefaultValue() ? dto.getDefaultValue().getValue() : null;
prop.displayName = dto.hasDisplayName() ? dto.getDisplayName().getValue() : null;
return prop;
@@ -158,18 +326,32 @@ public static TokenProperty fromDto(TokenPropertyDto dto) {
public TokenPropertyDto toDto() {
var dto = TokenPropertyDto.newBuilder();
dto.setName(name);
- if (shortName != null) {
- dto.setShortName(StringValue.of(shortName));
+ dto.setEditorEditPermission(
+ Objects.requireNonNullElse(editorEditPermission, Permissions.OWNER).name());
+ dto.setEditorViewPermission(
+ Objects.requireNonNullElse(editorViewPermission, Permissions.OWNER).name());
+ // for campaigns pre 1.19
+ if (highPriority || ownerOnly || gmOnly) {
+ if (ownerOnly) {
+ dto.setStatSheetViewPermission(Permissions.OWNER.name());
+ } else if (gmOnly) {
+ dto.setStatSheetViewPermission(Permissions.GM.name());
+ } else {
+ dto.setStatSheetViewPermission(Permissions.ALL.name());
+ }
+ } else {
+ dto.setStatSheetViewPermission(
+ Objects.requireNonNullElse(statSheetViewPermission, Permissions.NONE).name());
}
- dto.setHighPriority(highPriority);
- dto.setOwnerOnly(ownerOnly);
- dto.setGmOnly(gmOnly);
- if (defaultValue != null) {
- dto.setDefaultValue(StringValue.of(defaultValue));
+ if (hasShortName()) {
+ dto.setShortName(StringValue.of(shortName));
}
if (hasDisplayName()) {
dto.setDisplayName(StringValue.of(displayName));
}
+ if (hasDefaultValue()) {
+ dto.setDefaultValue(StringValue.of(defaultValue));
+ }
return dto.build();
}
}
diff --git a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java
index 87d475b9c9..30f5249f67 100644
--- a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java
+++ b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java
@@ -164,19 +164,19 @@ public String getShortName() {
/** The notes of the token. */
private final String notes;
- /** The notes type of the token. */
+ /** The token notes type. */
private final String notesType;
/** The GM notes of the token. */
private final String gmNotes;
- /** The GM notes type of the token. */
+ /** The token's GM notes type. */
private final String gmNotesType;
/** The speech name of the token. */
private final String speechName;
- /** The type of the token. */
+ /** The token type. */
private final String tokenType;
/** True if the player is a GM. */
@@ -247,21 +247,10 @@ public StatSheetContext(TokenHoverEnter hoverEvent, Player player, StatSheetLoca
.getTokenPropertyList(token.getPropertyType())
.forEach(
tp -> {
- if (tp.isShowOnStatSheet()) {
- if (tp.isGMOnly() && !playerIsGm) {
- return;
- }
-
- if (tp.isOwnerOnly() && !playerOwns) {
- return;
- }
-
+ if (tp.getStatSheetViewPermission().hasPermission(player, token)) {
Object value = token.getEvaluatedProperty(resolver, tp.getName());
- if (value == null) {
- return;
- }
-
- if (value instanceof String sValue && sValue.isBlank()) {
+ //noinspection ConstantValue
+ if (value == null || value instanceof String sValue && sValue.isBlank()) {
return;
}
properties.add(
@@ -434,6 +423,15 @@ public String getPortrait() {
return portraitAsset != null ? "asset://" + portraitAsset : null;
}
+ /**
+ * Returns the handout asset of the token.
+ *
+ * @return The portrait asset of the token.
+ */
+ public String getHandout() {
+ return handoutAsset != null ? "asset://" + handoutAsset : null;
+ }
+
/**
* Returns the label of the token.
*
@@ -515,9 +513,9 @@ public String getSpeechName() {
}
/**
- * Returns the type of the token.
+ * Returns the token type.
*
- * @return The type of the token.
+ * @return The token type.
*/
public String getTokenType() {
return tokenType;
diff --git a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetManager.java b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetManager.java
index 1491216184..b430a27967 100644
--- a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetManager.java
+++ b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetManager.java
@@ -185,8 +185,7 @@ public Set getStatSheets(String propertyType) {
* @return the id of the stat sheet.
*/
public SortedSet getOrderedStatSheets(String propertyType) {
- TreeSet sheets =
- new TreeSet((s1, s2) -> StatSheetManager.compareStatSheets(s1, s2));
+ TreeSet sheets = new TreeSet<>(StatSheetManager::compareStatSheets);
sheets.addAll(getStatSheets(propertyType));
return sheets;
}
diff --git a/src/main/resources/net/rptools/maptool/client/ui/themes/AahLAF.properties b/src/main/resources/net/rptools/maptool/client/ui/themes/AahLAF.properties
index 274ed441c4..cf0fb302bf 100644
--- a/src/main/resources/net/rptools/maptool/client/ui/themes/AahLAF.properties
+++ b/src/main/resources/net/rptools/maptool/client/ui/themes/AahLAF.properties
@@ -12,6 +12,7 @@
@col90 = changeLightness(@col50, 10%)
@col100= shade(@baseColour,80%)
@blue = #2675bf
+@green = #26bf75
@red = #c42b1c
@yellow= #fcf2d7
@gradientEnd = mix(@col100,@col0,25%)
@@ -105,7 +106,7 @@ ToggleButton.toolbar.disabledSelectedForeground = $Button.disabledSelectedForegr
ToggleButton.toolbar.disabledSelectedBackground = $Button.disabledSelectedBackground
Button.toolbar.margin = 3,3,3,3
-Button.toolbar.spacingInsets = 1,2,1,0
+Button.toolbar.spacingInsets = 1,2,1,0
CheckBox.border = 3,3,5,3, #a3964d, 1, 6
@@ -115,7 +116,7 @@ Component.borderWidth = 1
Component.focusWidth = 1
Component.focusedBorderColor = mix(@gradientBorderStart, @gradientBorderEnd, 80%)
Component.innerFocusWidth = 0
-Component.titleBarCaption = true
+Component.titleBarCaption = true
HelpButton.innerFocusWidth = 1
HelpButton.questionMarkColor = @blue
@@ -169,6 +170,7 @@ ScrollBar.buttonArrowColor = @col60
ScrollBar.buttonDisabledArrowColor = fadein(@col90,40%,derived)
ScrollBar.hoverButtonBackground = @col30
ScrollBar.minimumButtonSize = 10, 10
+ScrollBar.thumbBorderColor = @col60
Slider.focusWidth = 1
Slider.focusedColor = @col40
@@ -199,8 +201,53 @@ TabbedPane.tabSeparatorsFullHeight= true
TabbedPane.underlineColor = @col50
TabbedPane.hoverColor = @col0
+Table.font = +1
+Table.cellMargins = 3,3,3,3
+# Table.selectionInsets
+Table.background = @col5
+Table.foreground = @col80
+Table.selectionBackground = @col20
+Table.selectionForeground= @col90
+Table.disabledForeground = @col100
+Table.selectionInactiveBackground = @col30
+Table.selectionInactiveForeground = @col100
+Table.alternateRowColor = @col10
+Table.gridColor = @col50
+Table.cellFocusColor = @blue
+Table.focusCellForeground = @red
+Table.focusCellBackground = @blue
+Table.dropLineColor = @red
+# Table.dropLineShortColor
+# Table.dropCellBackground
+# Table.dropCellForeground
+Table.showHorizontalLines = true
+Table.showVerticalLines = true
+Table.showTrailingVerticalLine = false
+Table.showCellFocusIndicator = true
+Table.intercellSpacing = 2,1
+Table.rowHeight = 18
+
+# Table.cellBorder = 0,0,0,0, 1
+# Table.focusCellHighlightBorder = 3,3,3,3, @blue, 2, 6
+Table.focusSelectedCellHighlightBorder = 3,3,3,3, @blue, 2, 6
+# Table.paintOutsideAlternateRows = true
+# Table.editorSelectAllOnStartEditing
+# Table.consistentHomeEndKeyBehavior
+Table.sortIconColor = @blue
+
+TableHeader.background = @col20
+TableHeader.foreground = @col90
+# TableHeader.hoverBackground
+# TableHeader.hoverForeground
+# TableHeader.pressedBackground
+# TableHeader.pressedForeground
+TableHeader.separatorColor = @col40
+TableHeader.bottomSeparatorColor = @col60
+
TextComponent.arc = 8
+TitlePane.menuBarEmbedded = true
+
ToolBar.background = @col5
ToolBar.foreground = @col90
ToolBar.floatable = true
@@ -211,4 +258,34 @@ ToolBar.separatorColor = @col90
ToolTip.background = @yellow
ToolTip.foreground = @col90
-ToolTip.border = 3,3,5,3, @gradientBorderEnd, 1, 6
\ No newline at end of file
+ToolTip.border = 3,3,5,3, @gradientBorderEnd, 1, 6
+
+#Tree.rowHeight = 24
+Tree.showsRootHandles = true
+Tree.repaintWholeRow = true
+# Tree.selectionBorderColor Color focus indicator border color
+Tree.drawsFocusBorderAroundIcon = true
+# Tree.drawDashedFocusIndicator boolean
+Tree.rendererFillBackground = true
+#Tree.rendererMargins Insets
+#Tree.dropCellBackground Color
+#Tree.dropCellForeground Color
+#Tree.editorBorder Border
+#Tree.editorBorderSelectionColor Color
+#Tree.border Border
+#Tree.selectionBackground Color
+#Tree.selectionForeground Color
+#Tree.selectionInactiveBackground Color
+#Tree.selectionInactiveForeground Color
+#Tree.alternateRowColor Color
+#Tree.selectionInsets Insets
+#Tree.selectionArc int
+Tree.wideSelection = true
+Tree.wideCellRenderer = true
+Tree.showCellFocusIndicator = true
+Tree.showDefaultIcons = true
+Tree.icon.expandedColor = @red
+Tree.icon.collapsedColor = @green
+#Tree.icon.leafColor Color
+#Tree.icon.closedColor Color
+#Tree.icon.openColor = @green
\ No newline at end of file
diff --git a/src/main/resources/net/rptools/maptool/language/i18n.properties b/src/main/resources/net/rptools/maptool/language/i18n.properties
index 7ca803a223..b8207aa266 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n.properties
@@ -223,8 +223,8 @@ Default.campaign.tokenProperty.name.constitution = Constitution
Default.campaign.tokenProperty.name.intelligence = Intelligence
Default.campaign.tokenProperty.name.wisdom = Wisdom
Default.campaign.tokenProperty.name.charisma = Charisma
-Default.campaign.tokenProperty.name.hp = HP
-Default.campaign.tokenProperty.name.ac = AC
+Default.campaign.tokenProperty.name.hp = Hit Points
+Default.campaign.tokenProperty.name.ac = Armour Class
Default.campaign.tokenProperty.name.defense = Defense
Default.campaign.tokenProperty.name.movement = Movement
Default.campaign.tokenProperty.name.elevation = Elevation
@@ -235,6 +235,8 @@ Default.campaign.tokenProperty.name.short.constitution = Con
Default.campaign.tokenProperty.name.short.intelligence = Int
Default.campaign.tokenProperty.name.short.wisdom = Wis
Default.campaign.tokenProperty.name.short.charisma = Cha
+Default.campaign.tokenProperty.name.short.hp = HP
+Default.campaign.tokenProperty.name.short.ac = AC
Default.campaign.tokenProperty.name.short.defense = Def
Default.campaign.tokenProperty.name.short.movement = Mov
Default.campaign.tokenProperty.name.short.elevation = Elv
@@ -1068,6 +1070,23 @@ Button.networkingHelp = Networking Help
Button.networkingHelp.mnemonic = {f1}
ServerDialog.generatePassword = Generate Password
+permission.displayName.none = None
+permission.displayName.gm = GM
+permission.displayName.owner = Owner
+permission.displayName.allies = Allies
+permission.displayName.all = All
+permission.displayName.owner.discrete = Owner Only
+permission.displayName.allies.discrete = Allies Only
+permission.displayName.opponent = Opponents
+
+permission.description.none = Nobody has permission.
+permission.description.gm = Only the GMs have permission.
+permission.description.owner = The GMs and Owners have permission.
+permission.description.allies = The GMs, Owners and the Owners' Allies have permission.
+permission.description.all = Everyone has permission.
+permission.description.owner.discrete = Only the Owner has permission.
+permission.description.allies.discrete = Only the Owner's Allies have permission.
+permission.description.opponent = Only the Owner's Opponents have permission.
CampaignPropertiesDialog.tab.token = Token Properties
CampaignPropertiesDialog.tab.repo = Repositories
@@ -1096,17 +1115,17 @@ campaignProperties.macroEditDialog.default.title = Default value for Property
campaignPropertiesTable.column.name = Name
campaignPropertiesTable.column.shortName = Short Name
campaignPropertiesTable.column.displayName = Display Name
-campaignPropertiesTable.column.onStatSheet = Stat Sheet
-campaignPropertiesTable.column.gmStatSheet = GM
-campaignPropertiesTable.column.ownerStatSheet = Owner
+campaignPropertiesTable.column.playerViewable = Player Viewable
+campaignPropertiesTable.column.playerEditable = Player Editable
+campaignPropertiesTable.column.statSheetVisibility = Stat-Sheet
campaignPropertiesTable.column.defaultValue = Default
campaignPropertiesTable.column.name.description = The actual name of the property, used by the application.
campaignPropertiesTable.column.shortName.description = Short version of the property name. Used in stat-sheets and character sheets.
campaignPropertiesTable.column.displayName.description = Display version of the property name. Used in stat-sheets and character sheets.
-campaignPropertiesTable.column.statSheet.description = Show the property on the stat-sheet.
-campaignPropertiesTable.column.gm.description = Restrict visibility to the GM view of the stat-sheet.
-campaignPropertiesTable.column.owner.description = Restrict visibility to the owner's view of the stat-sheet.
+campaignPropertiesTable.column.playerViewable.description = Show the property to players in the Edit Token dialog.
+campaignPropertiesTable.column.playerEditable.description = Allow players to edit in the Edit Token dialog.
+campaignPropertiesTable.column.statSheet.description = Define who has permission to see the property on the stat-sheet.
campaignPropertiesTable.column.default.description = The default value assigned to the property.
# Bar propertyType
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_cs_CZ.properties b/src/main/resources/net/rptools/maptool/language/i18n_cs_CZ.properties
index 5830329fe6..dd6956304c 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_cs_CZ.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_cs_CZ.properties
@@ -916,7 +916,7 @@ campaignProperties.macroEditDialog.default.title = Default value for Property
campaignPropertiesTable.column.name = Name
campaignPropertiesTable.column.shortName = Short Name
campaignPropertiesTable.column.displayName = Display Name
-campaignPropertiesTable.column.onStatSheet = Stat Sheet
+campaignPropertiesTable.column.statSheetVisibility = Stat Sheet
campaignPropertiesTable.column.gmStatSheet = GM
campaignPropertiesTable.column.ownerStatSheet = Owner
campaignPropertiesTable.column.defaultValue = Default
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_da_DK.properties b/src/main/resources/net/rptools/maptool/language/i18n_da_DK.properties
index e583490f91..11442e3fec 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_da_DK.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_da_DK.properties
@@ -916,7 +916,7 @@ campaignProperties.macroEditDialog.default.title = Default value for Property
campaignPropertiesTable.column.name = Name
campaignPropertiesTable.column.shortName = Short Name
campaignPropertiesTable.column.displayName = Display Name
-campaignPropertiesTable.column.onStatSheet = Stat Sheet
+campaignPropertiesTable.column.statSheetVisibility = Stat Sheet
campaignPropertiesTable.column.gmStatSheet = GM
campaignPropertiesTable.column.ownerStatSheet = Owner
campaignPropertiesTable.column.defaultValue = Default
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_de_DE.properties b/src/main/resources/net/rptools/maptool/language/i18n_de_DE.properties
index c155aea777..623d406101 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_de_DE.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_de_DE.properties
@@ -916,7 +916,7 @@ campaignProperties.macroEditDialog.default.title = Vorgabewert für diese Eigens
campaignPropertiesTable.column.name = Name
campaignPropertiesTable.column.shortName = Kurzname
campaignPropertiesTable.column.displayName = Anzeigename
-campaignPropertiesTable.column.onStatSheet = Werteblatt
+campaignPropertiesTable.column.statSheetVisibility = Werteblatt
campaignPropertiesTable.column.gmStatSheet = SL
campaignPropertiesTable.column.ownerStatSheet = Besitzer
campaignPropertiesTable.column.defaultValue = Vorgabe
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_en_AU.properties b/src/main/resources/net/rptools/maptool/language/i18n_en_AU.properties
index 2b55dc952f..cafa103a9c 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_en_AU.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_en_AU.properties
@@ -916,18 +916,20 @@ campaignProperties.macroEditDialog.default.title = Default value for Property
campaignPropertiesTable.column.name = Name
campaignPropertiesTable.column.shortName = Short Name
campaignPropertiesTable.column.displayName = Display Name
-campaignPropertiesTable.column.onStatSheet = Stat-Sheet
-campaignPropertiesTable.column.gmStatSheet = GM
-campaignPropertiesTable.column.ownerStatSheet = Owner
+campaignPropertiesTable.column.statSheetVisibility = Stat-Sheet View
campaignPropertiesTable.column.defaultValue = Default
+campaignPropertiesTable.column.playerEditable = Player Editable
+campaignPropertiesTable.column.playerViewable = Player Viewable
campaignPropertiesTable.column.name.description = The actual name of the property, used by the application.
campaignPropertiesTable.column.shortName.description = Short version of the property name. Used in stat-sheets and character sheets.
campaignPropertiesTable.column.displayName.description = Display version of the property name. Used in stat-sheets and character sheets.
-campaignPropertiesTable.column.statSheet.description = Show the property on the stat-sheet.
-campaignPropertiesTable.column.gm.description = Restrict visibility to the GM view of the stat-sheet.
-campaignPropertiesTable.column.owner.description = Restrict visibility to the owner's view of the stat-sheet.
+campaignPropertiesTable.column.statSheet.description = Define who has permission to see the property on the stat-sheet.
campaignPropertiesTable.column.default.description = The default value assigned to the property.
+campaignPropertiesTable.column.playerViewable.description = Show the property to players in the Edit Token dialogue.
+campaignPropertiesTable.column.playerEditable.description = Allow players to edit in the Edit Token dialogue.
+
+
# Bar propertyType
CampaignPropertiesDialog.combo.bars.type.twoImages = Two Images
@@ -2934,3 +2936,21 @@ advanced.roll.noTokenInContext = No token in context.
advanced.roll.inputNotNumber = Input {0} is not a number.
Preferences.label.tokens.stack.hide=Hide token stack indicator
Preferences.label.tokens.stack.hide.tooltip=Token Layer stack indicator will be hidden
+
+permission.displayName.none = None
+permission.displayName.gm = GM
+permission.displayName.owner = Owner
+permission.displayName.allies = Allies
+permission.displayName.all = All
+permission.displayName.owner.discrete = Owner Only
+permission.displayName.allies.discrete = Allies Only
+permission.displayName.opponent = Opponents
+
+permission.description.none = Nobody has permission.
+permission.description.gm = Only the GMs have permission.
+permission.description.owner = The GMs and Owners have permission.
+permission.description.allies = The GMs, Owners and the Owners' Allies have permission.
+permission.description.all = Everyone has permission.
+permission.description.owner.discrete = Only the Owner has permission.
+permission.description.allies.discrete = Only the Owner's Allies have permission.
+permission.description.opponent = Only the Owner's Opponents have permission.
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_en_GB.properties b/src/main/resources/net/rptools/maptool/language/i18n_en_GB.properties
index 03444cebcc..5dc69d5867 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_en_GB.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_en_GB.properties
@@ -916,7 +916,7 @@ campaignProperties.macroEditDialog.default.title = Default value for Property
campaignPropertiesTable.column.name = Name
campaignPropertiesTable.column.shortName = Short Name
campaignPropertiesTable.column.displayName = Display Name
-campaignPropertiesTable.column.onStatSheet = Stat-Sheet
+campaignPropertiesTable.column.statSheetVisibility = Stat-Sheet
campaignPropertiesTable.column.gmStatSheet = GM
campaignPropertiesTable.column.ownerStatSheet = Owner
campaignPropertiesTable.column.defaultValue = Default
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_es_ES.properties b/src/main/resources/net/rptools/maptool/language/i18n_es_ES.properties
index 317a541cc0..860b0a9c34 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_es_ES.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_es_ES.properties
@@ -916,7 +916,7 @@ campaignProperties.macroEditDialog.default.title = Default value for Property
campaignPropertiesTable.column.name = Name
campaignPropertiesTable.column.shortName = Short Name
campaignPropertiesTable.column.displayName = Display Name
-campaignPropertiesTable.column.onStatSheet = Stat Sheet
+campaignPropertiesTable.column.statSheetVisibility = Stat Sheet
campaignPropertiesTable.column.gmStatSheet = GM
campaignPropertiesTable.column.ownerStatSheet = Owner
campaignPropertiesTable.column.defaultValue = Default
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_fr_FR.properties b/src/main/resources/net/rptools/maptool/language/i18n_fr_FR.properties
index e86c59731b..def0b29993 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_fr_FR.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_fr_FR.properties
@@ -916,7 +916,7 @@ campaignProperties.macroEditDialog.default.title = Default value for Property
campaignPropertiesTable.column.name = Name
campaignPropertiesTable.column.shortName = Short Name
campaignPropertiesTable.column.displayName = Display Name
-campaignPropertiesTable.column.onStatSheet = Stat Sheet
+campaignPropertiesTable.column.statSheetVisibility = Stat Sheet
campaignPropertiesTable.column.gmStatSheet = GM
campaignPropertiesTable.column.ownerStatSheet = Owner
campaignPropertiesTable.column.defaultValue = Default
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_it_IT.properties b/src/main/resources/net/rptools/maptool/language/i18n_it_IT.properties
index 7efef2dc07..08717837a9 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_it_IT.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_it_IT.properties
@@ -916,7 +916,7 @@ campaignProperties.macroEditDialog.default.title = Default value for Property
campaignPropertiesTable.column.name = Name
campaignPropertiesTable.column.shortName = Short Name
campaignPropertiesTable.column.displayName = Display Name
-campaignPropertiesTable.column.onStatSheet = Stat Sheet
+campaignPropertiesTable.column.statSheetVisibility = Stat Sheet
campaignPropertiesTable.column.gmStatSheet = GM
campaignPropertiesTable.column.ownerStatSheet = Owner
campaignPropertiesTable.column.defaultValue = Default
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_ja_JP.properties b/src/main/resources/net/rptools/maptool/language/i18n_ja_JP.properties
index 07ea475e79..859f921dcc 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_ja_JP.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_ja_JP.properties
@@ -916,7 +916,7 @@ campaignProperties.macroEditDialog.default.title = 特性値の初期値
campaignPropertiesTable.column.name = 名称
campaignPropertiesTable.column.shortName = 短縮名
campaignPropertiesTable.column.displayName = 表示名称
-campaignPropertiesTable.column.onStatSheet = シート
+campaignPropertiesTable.column.statSheetVisibility = シート
campaignPropertiesTable.column.gmStatSheet = GM
campaignPropertiesTable.column.ownerStatSheet = 所有
campaignPropertiesTable.column.defaultValue = 初期値
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_nl_NL.properties b/src/main/resources/net/rptools/maptool/language/i18n_nl_NL.properties
index d108b271dc..ff2ba4c6d0 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_nl_NL.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_nl_NL.properties
@@ -916,7 +916,7 @@ campaignProperties.macroEditDialog.default.title = Standaard waarde voor eigensc
campaignPropertiesTable.column.name = Name
campaignPropertiesTable.column.shortName = Short Name
campaignPropertiesTable.column.displayName = Display Name
-campaignPropertiesTable.column.onStatSheet = Stat Sheet
+campaignPropertiesTable.column.statSheetVisibility = Stat Sheet
campaignPropertiesTable.column.gmStatSheet = GM
campaignPropertiesTable.column.ownerStatSheet = Eigenaar
campaignPropertiesTable.column.defaultValue = Default
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_pl_PL.properties b/src/main/resources/net/rptools/maptool/language/i18n_pl_PL.properties
index 71a3d2eb4a..298463b1b4 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_pl_PL.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_pl_PL.properties
@@ -916,7 +916,7 @@ campaignProperties.macroEditDialog.default.title = Domyślna wartość dla wła
campaignPropertiesTable.column.name = Nazwa
campaignPropertiesTable.column.shortName = Skrócona Nazwa
campaignPropertiesTable.column.displayName = Wyświetlana nazwa
-campaignPropertiesTable.column.onStatSheet = Arkusz statystyk
+campaignPropertiesTable.column.statSheetVisibility = Arkusz statystyk
campaignPropertiesTable.column.gmStatSheet = MG
campaignPropertiesTable.column.ownerStatSheet = Właściciel
campaignPropertiesTable.column.defaultValue = Domyślny
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_pt_BR.properties b/src/main/resources/net/rptools/maptool/language/i18n_pt_BR.properties
index 6494d2e403..4406a53e74 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_pt_BR.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_pt_BR.properties
@@ -916,7 +916,7 @@ campaignProperties.macroEditDialog.default.title = Default value for Property
campaignPropertiesTable.column.name = Name
campaignPropertiesTable.column.shortName = Short Name
campaignPropertiesTable.column.displayName = Display Name
-campaignPropertiesTable.column.onStatSheet = Stat Sheet
+campaignPropertiesTable.column.statSheetVisibility = Stat Sheet
campaignPropertiesTable.column.gmStatSheet = GM
campaignPropertiesTable.column.ownerStatSheet = Owner
campaignPropertiesTable.column.defaultValue = Default
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_ru_RU.properties b/src/main/resources/net/rptools/maptool/language/i18n_ru_RU.properties
index 6009213980..a8266c2e8a 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_ru_RU.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_ru_RU.properties
@@ -916,7 +916,7 @@ campaignProperties.macroEditDialog.default.title = Значение параме
campaignPropertiesTable.column.name = Название
campaignPropertiesTable.column.shortName = Сокращение
campaignPropertiesTable.column.displayName = Отображаемое название
-campaignPropertiesTable.column.onStatSheet = Блок пар.
+campaignPropertiesTable.column.statSheetVisibility = Блок пар.
campaignPropertiesTable.column.gmStatSheet = ГМ
campaignPropertiesTable.column.ownerStatSheet = Владелец
campaignPropertiesTable.column.defaultValue = По умолчанию
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_si_LK.properties b/src/main/resources/net/rptools/maptool/language/i18n_si_LK.properties
index d8fac82570..cf5e5125b2 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_si_LK.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_si_LK.properties
@@ -916,7 +916,7 @@ campaignProperties.macroEditDialog.default.title = Default value for property
campaignPropertiesTable.column.name = Name
campaignPropertiesTable.column.shortName = Short Name
campaignPropertiesTable.column.displayName = Display Name
-campaignPropertiesTable.column.onStatSheet = Stat-Sheet
+campaignPropertiesTable.column.statSheetVisibility = Stat-Sheet
campaignPropertiesTable.column.gmStatSheet = GM
campaignPropertiesTable.column.ownerStatSheet = Owner
campaignPropertiesTable.column.defaultValue = Default
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_sv_SE.properties b/src/main/resources/net/rptools/maptool/language/i18n_sv_SE.properties
index e4d2da76a1..7e20c2eebd 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_sv_SE.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_sv_SE.properties
@@ -916,7 +916,7 @@ campaignProperties.macroEditDialog.default.title = Default value for Property
campaignPropertiesTable.column.name = Name
campaignPropertiesTable.column.shortName = Short Name
campaignPropertiesTable.column.displayName = Display Name
-campaignPropertiesTable.column.onStatSheet = Stat Sheet
+campaignPropertiesTable.column.statSheetVisibility = Stat Sheet
campaignPropertiesTable.column.gmStatSheet = GM
campaignPropertiesTable.column.ownerStatSheet = Owner
campaignPropertiesTable.column.defaultValue = Default
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_uk_UA.properties b/src/main/resources/net/rptools/maptool/language/i18n_uk_UA.properties
index a4b066ccaf..6c4f55689a 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_uk_UA.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_uk_UA.properties
@@ -916,7 +916,7 @@ campaignProperties.macroEditDialog.default.title = Default value for Property
campaignPropertiesTable.column.name = Name
campaignPropertiesTable.column.shortName = Short Name
campaignPropertiesTable.column.displayName = Display Name
-campaignPropertiesTable.column.onStatSheet = Stat Sheet
+campaignPropertiesTable.column.statSheetVisibility = Stat Sheet
campaignPropertiesTable.column.gmStatSheet = GM
campaignPropertiesTable.column.ownerStatSheet = Owner
campaignPropertiesTable.column.defaultValue = Default
diff --git a/src/main/resources/net/rptools/maptool/language/i18n_zh_CN.properties b/src/main/resources/net/rptools/maptool/language/i18n_zh_CN.properties
index 88fbc2959b..47711e8780 100644
--- a/src/main/resources/net/rptools/maptool/language/i18n_zh_CN.properties
+++ b/src/main/resources/net/rptools/maptool/language/i18n_zh_CN.properties
@@ -916,7 +916,7 @@ campaignProperties.macroEditDialog.default.title = Default value for Property
campaignPropertiesTable.column.name = Name
campaignPropertiesTable.column.shortName = Short Name
campaignPropertiesTable.column.displayName = Display Name
-campaignPropertiesTable.column.onStatSheet = Stat Sheet
+campaignPropertiesTable.column.statSheetVisibility = Stat Sheet
campaignPropertiesTable.column.gmStatSheet = GM
campaignPropertiesTable.column.ownerStatSheet = Owner
campaignPropertiesTable.column.defaultValue = Default
diff --git a/src/test/java/net/rptools/maptool/client/ui/campaignproperties/CampaignPropertiesDialogTest.java b/src/test/java/net/rptools/maptool/client/ui/campaignproperties/CampaignPropertiesDialogTest.java
index a5df6ea590..d850fe262b 100644
--- a/src/test/java/net/rptools/maptool/client/ui/campaignproperties/CampaignPropertiesDialogTest.java
+++ b/src/test/java/net/rptools/maptool/client/ui/campaignproperties/CampaignPropertiesDialogTest.java
@@ -22,10 +22,17 @@
import javax.swing.JComboBox;
import javax.swing.SwingUtilities;
import net.rptools.maptool.client.AppConstants;
+import net.rptools.maptool.client.swing.table.*;
import net.rptools.maptool.language.I18N;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class CampaignPropertiesDialogTest {
+ @BeforeAll
+ public static void register() {
+ com.jidesoft.utils.Lm.verifyLicense(
+ "Trevor Croft", "rptools", "5MfIVe:WXJBDrToeLWPhMv3kI2s3VFo");
+ }
@Test
public void importPredefinedButton() throws InterruptedException, InvocationTargetException {
@@ -71,7 +78,6 @@ public void predefinedPropertiesComboBox_twoFiles()
new CampaignPropertiesDialog() {
@Override
protected File[] getPredefinedPropertyFiles(File propertyDir) {
-
return new File[] {new File(one), new File(two)};
}
};
diff --git a/src/test/java/net/rptools/maptool/model/TokenPropertiesTest.java b/src/test/java/net/rptools/maptool/model/TokenPropertiesTest.java
index 09a477e6f4..8731165261 100644
--- a/src/test/java/net/rptools/maptool/model/TokenPropertiesTest.java
+++ b/src/test/java/net/rptools/maptool/model/TokenPropertiesTest.java
@@ -37,21 +37,16 @@ public class TokenPropertiesTest {
@BeforeEach
public void setUp() {
propsList = new ArrayList<>();
- propsList.add(new TokenProperty("prop1", null, true, false, false, "10"));
- propsList.add(new TokenProperty("prop2", null, true, false, false, "{prop2=prop1}"));
+ propsList.add(new TokenProperty("prop1", null, null, true, false, Permissions.NONE, "10"));
+ propsList.add(new TokenProperty("prop2", null, null, false, Permissions.ALL, "{prop2=prop1}"));
propsList.add(
new TokenProperty(
- "jsonObj1",
- null,
- true,
- false,
- false,
- "{\"sampleKey\": 5, \"otherKey\": \"theValue\"}"));
- propsList.add(new TokenProperty("jsonObj2", null, true, false, false, "{\"prop3\"=other}"));
- propsList.add(new TokenProperty("jsonObj3", null, true, false, false, "{prop3:other}"));
- propsList.add(new TokenProperty("jsonArr1", null, true, false, false, "[4, 3]"));
- propsList.add(new TokenProperty("plainStr1", null, true, false, false, "justAString"));
- propsList.add(new TokenProperty("badJson", null, true, false, false, "{\"a\": 1}{\"b\": 2}"));
+ "jsonObj1", null, Permissions.ALL, "{\"sampleKey\": 5, \"otherKey\": \"theValue\"}"));
+ propsList.add(new TokenProperty("jsonObj2", null, Permissions.ALL, "{\"prop3\"=other}"));
+ propsList.add(new TokenProperty("jsonObj3", null, Permissions.ALL, "{prop3:other}"));
+ propsList.add(new TokenProperty("jsonArr1", null, Permissions.ALL, "[4, 3]"));
+ propsList.add(new TokenProperty("plainStr1", null, Permissions.ALL, "justAString"));
+ propsList.add(new TokenProperty("badJson", null, Permissions.ALL, "{\"a\": 1}{\"b\": 2}"));
MapTool.getCampaign().putTokenType("testType", propsList);
testToken = new Token();