1 /*
  2  * ResourceAction.js
  3  *
  4  * Sweet Home 3D, Copyright (c) 2024 Space Mushrooms <info@sweethome3d.com>
  5  *
  6  * This program is free software; you can redistribute it and/or modify
  7  * it under the terms of the GNU General Public License as published by
  8  * the Free Software Foundation; either version 2 of the License, or
  9  * (at your option) any later version.
 10  *
 11  * This program is distributed in the hope that it will be useful,
 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14  * GNU General Public License for more details.
 15  *
 16  * You should have received a copy of the GNU General Public License
 17  * along with this program; if not, write to the Free Software
 18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 19  */
 20 
 21 // Requires graphics2d.js
 22 // Requires UserPreferences.js
 23 // Requires URLContent.js
 24 
 25 /**
 26  * Creates an action with properties retrieved from a resource bundle
 27  * in which key starts with <code>actionPrefix</code>.
 28  * @param {UserPreferences} preferences   user preferences used to retrieve localized description of the action
 29  * @param {Object} resourceClass the class used as a context to retrieve localized properties of the action
 30  * @param {string} actionPrefix  prefix used in resource bundle to search action properties
 31  * @param {boolean} [enabled] <code>true</code> if the action should be enabled at creation
 32  * @param {Object} controller the controller object holding the method to invoke 
 33  * @param {string} controllerMethod the controller method to invoke
 34  * @param {Object[]} parameters action parameters
 35  * @constructor
 36  * @extends AbstractAction
 37  * @ignore
 38  * @author Emmanuel Puybaret
 39  */
 40 function ResourceAction(preferences, resourceClass, actionPrefix, enabled, controller, controllerMethod, parameters) {
 41   AbstractAction.call(this);
 42   if (enabled === undefined) {
 43     parameters = controllerMethod;
 44     controllerMethod = controller;
 45     controller = enabled;
 46     enabled = false;      
 47   }
 48   this.putValue(ResourceAction.RESOURCE_CLASS, resourceClass);
 49   this.putValue(ResourceAction.RESOURCE_PREFIX, actionPrefix);
 50   this.putValue(ResourceAction.VISIBLE, true);
 51   this.readActionProperties(preferences, resourceClass, actionPrefix);
 52   this.setEnabled(enabled);
 53   this.controller = controller;
 54   this.controllerMethod = controllerMethod;
 55   this.parameters = parameters;
 56   var resourceAction = this;
 57   preferences.addPropertyChangeListener("LANGUAGE", {
 58       propertyChange : function(ev) {
 59         if (resourceAction == null) {
 60           (ev.getSource()).removePropertyChangeListener("LANGUAGE", this);
 61         } else {
 62           resourceAction.readActionProperties(ev.getSource(), 
 63               resourceAction.getValue(ResourceAction.RESOURCE_CLASS), resourceAction.getValue(ResourceAction.RESOURCE_PREFIX));
 64         }
 65       }
 66     });
 67     return this;
 68   };
 69 ResourceAction.prototype = Object.create(AbstractAction.prototype);
 70 ResourceAction.prototype.constructor = ResourceAction;
 71 
 72 /**
 73  * Other property keys for Sweet Home 3D.
 74  */
 75 ResourceAction.RESOURCE_CLASS = "ResourceClass";
 76 ResourceAction.RESOURCE_PREFIX = "ResourcePrefix";
 77 ResourceAction.VISIBLE = "Visible";
 78 ResourceAction.POPUP = "Popup";
 79 ResourceAction.TOGGLE_BUTTON_GROUP = "ToggleButtonGroup";
 80 ResourceAction.TOOL_BAR_ICON = "ToolBarIcon";
 81 
 82 /**
 83  * Reads from the properties of this action.
 84  * @param {UserPreferences} preferences
 85  * @param {Object} resourceClass
 86  * @param {string} actionPrefix
 87  * @private
 88  */
 89 ResourceAction.prototype.readActionProperties = function(preferences, resourceClass, actionPrefix) {
 90   var propertyPrefix = actionPrefix + ".";
 91   this.putValue(AbstractAction.NAME, this.getOptionalString(preferences, resourceClass, propertyPrefix + AbstractAction.NAME, true));
 92   this.putValue(AbstractAction.DEFAULT, this.getValue(AbstractAction.NAME));
 93   this.putValue(ResourceAction.POPUP, this.getOptionalString(preferences, resourceClass, propertyPrefix + ResourceAction.POPUP, true));
 94   this.putValue(AbstractAction.SHORT_DESCRIPTION, this.getOptionalString(preferences, resourceClass, propertyPrefix + AbstractAction.SHORT_DESCRIPTION, false));
 95   this.putValue(AbstractAction.LONG_DESCRIPTION, this.getOptionalString(preferences, resourceClass, propertyPrefix + AbstractAction.LONG_DESCRIPTION, false));
 96   var smallIcon = this.getOptionalString(preferences, resourceClass, propertyPrefix + AbstractAction.SMALL_ICON, false);
 97   if (smallIcon != null) {
 98     this.putValue(AbstractAction.SMALL_ICON, smallIcon);
 99   }
100   var toolBarIcon = this.getOptionalString(preferences, resourceClass, propertyPrefix + ResourceAction.TOOL_BAR_ICON, false);
101   if (toolBarIcon != null) {
102     this.putValue(ResourceAction.TOOL_BAR_ICON, toolBarIcon);
103   }
104   var propertyKey = propertyPrefix + AbstractAction.ACCELERATOR_KEY;
105   var acceleratorKey = this.getOptionalString(preferences, resourceClass, propertyKey + "." + OperatingSystem.getName(), false);
106   if (acceleratorKey == null) {
107     acceleratorKey = this.getOptionalString(preferences, resourceClass, propertyKey, false);
108   }
109   if (acceleratorKey != null) {
110     this.putValue(AbstractAction.ACCELERATOR_KEY, acceleratorKey);
111   }
112   var mnemonicKey = this.getOptionalString(preferences, resourceClass, propertyPrefix + AbstractAction.MNEMONIC_KEY, false);
113   if (mnemonicKey != null) {
114     this.putValue(AbstractAction.MNEMONIC_KEY, mnemonicKey);
115   }
116 }
117 
118 /**
119  * Returns the value of <code>propertyKey</code> in <code>preferences</code>,
120  * or <code>null</code> if the property doesn't exist.
121  * @param {UserPreferences} preferences
122  * @param {Object} resourceClass
123  * @param {string} propertyKey
124  * @param {boolean} label
125  * @return {string}
126  * @private
127  */
128 ResourceAction.prototype.getOptionalString = function(preferences, resourceClass, propertyKey, label) {
129   try {
130     var localizedText = label 
131         ? ResourceAction.getLocalizedLabelText(preferences, resourceClass, propertyKey) 
132         : preferences.getLocalizedString(resourceClass, propertyKey);
133     if (localizedText != null && localizedText.length > 0) {
134       return localizedText;
135     } else {
136       return null;
137     }
138   } catch (ex) {
139     return null;
140   }
141 }
142 
143 /**
144  * Returns a localized text for menus items and labels depending on the system.
145  * @param {UserPreferences} preferences
146  * @param {Object} resourceClass
147  * @param {string} resourceKey
148  * @param {Array} resourceParameters
149  * @return {string}
150  * @private
151  */
152 ResourceAction.getLocalizedLabelText = function(preferences, resourceClass, resourceKey, resourceParameters) {
153   var localizedString = preferences.getLocalizedString(resourceClass, resourceKey, resourceParameters);
154   var language = Locale.getDefault();
155   if (/* OperatingSystem.isMacOSX() 
156       && */ (language.indexOf("zh") == 0 // CHINESE
157           || language.indexOf("ja") == 0 // JAPANESE
158           || language.indexOf("ko") == 0 // KOREAN
159           || language.indexOf("uk") == 0)) { // Ukrainian
160     var openingBracketIndex = localizedString.indexOf('(');
161     if (openingBracketIndex !== -1) {
162       var closingBracketIndex = localizedString.indexOf(')');
163       if (openingBracketIndex === closingBracketIndex - 2) {
164         var c = localizedString.charAt(openingBracketIndex + 1);
165         if (c >= 'A' && c <= 'Z') {
166           localizedString = localizedString.substring(0, openingBracketIndex)
167               + localizedString.substring(closingBracketIndex + 1);
168         }
169       }
170     }
171   }
172   return localizedString;
173 }
174 
175 /**
176  * Returns the URL of the given key for a key matching a URL like an icon.
177  * @param {string} propertyKey
178  */
179 ResourceAction.prototype.getURL = function(propertyKey) {
180   var url = this.getValue(propertyKey);
181   if (url != null && url.indexOf("://") === -1) {
182     url = ZIPTools.getScriptFolder() + url;
183   }
184   return url;
185 }
186 
187 /**
188  * Calls the method on the controller given in constructor.
189  * Unsupported operation. Subclasses should override this method if they want
190  * to associate a real action to this class.
191  * @param {java.awt.event.ActionEvent} ev
192  */
193 ResourceAction.prototype.actionPerformed = function(ev) {
194   if (this.controller != null && this.controllerMethod != null) {
195     return this.controller[this.controllerMethod].apply(this.controller, this.parameters);
196   } else {
197     AbstractAction.prototype.actionPerformed.call(this, ev);
198   }
199 }
200