001package jmri.jmrit.display.logixng; 002 003// import java.beans.PropertyChangeEvent; 004// import java.beans.PropertyVetoException; 005import java.awt.Frame; 006import java.beans.*; 007import java.util.*; 008 009import javax.annotation.CheckForNull; 010import javax.annotation.Nonnull; 011 012import jmri.*; 013import jmri.jmrit.logixng.*; 014import jmri.jmrit.logixng.actions.AbstractDigitalAction; 015import jmri.jmrit.logixng.util.LogixNG_SelectEnum; 016import jmri.jmrit.logixng.util.ReferenceUtil; 017import jmri.jmrit.logixng.util.parser.*; 018import jmri.jmrit.logixng.util.parser.ExpressionNode; 019import jmri.jmrit.logixng.util.parser.RecursiveDescentParser; 020import jmri.util.JmriJFrame; 021import jmri.util.ThreadingUtil; 022import jmri.util.TypeConversionUtil; 023 024/** 025 * This action acts on a Window. 026 * 027 * @author Daniel Bergqvist Copyright 2024 028 */ 029public class WindowManagement extends AbstractDigitalAction 030 implements PropertyChangeListener, VetoableChangeListener { 031 032 private String _jmriJFrameTitle; 033 private JmriJFrame _jmriJFrame; 034 private NamedBeanAddressing _addressing = NamedBeanAddressing.Direct; 035 private String _reference = ""; 036 private String _localVariable = ""; 037 private String _formula = ""; 038 private ExpressionNode _expressionNode; 039 040 private boolean _ignoreWindowNotFound = false; 041 042 private final LogixNG_SelectEnum<HideOrShow> _selectEnumHideOrShow = 043 new LogixNG_SelectEnum<>(this, HideOrShow.values(), HideOrShow.DoNothing, this); 044 045 private final LogixNG_SelectEnum<MaximizeMinimizeNormalize> _selectEnumMaximizeMinimizeNormalize = 046 new LogixNG_SelectEnum<>(this, MaximizeMinimizeNormalize.values(), MaximizeMinimizeNormalize.DoNothing, this); 047 048 private final LogixNG_SelectEnum<BringToFrontOrBack> _selectEnumBringToFrontOrBack = 049 new LogixNG_SelectEnum<>(this, BringToFrontOrBack.values(), BringToFrontOrBack.DoNothing, this); 050 051 052 public WindowManagement(String sys, String user) 053 throws BadUserNameException, BadSystemNameException { 054 super(sys, user); 055 } 056 057 @Override 058 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException { 059 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 060 String sysName = systemNames.get(getSystemName()); 061 String userName = userNames.get(getSystemName()); 062 if (sysName == null) sysName = manager.getAutoSystemName(); 063 WindowManagement copy = new WindowManagement(sysName, userName); 064 copy.setComment(getComment()); 065 copy.setJmriJFrame(_jmriJFrameTitle); 066 copy.setAddressing(_addressing); 067 copy.setFormula(_formula); 068 copy.setLocalVariable(_localVariable); 069 copy.setReference(_reference); 070 copy._ignoreWindowNotFound = _ignoreWindowNotFound; 071 _selectEnumHideOrShow.copy(copy._selectEnumHideOrShow); 072 _selectEnumMaximizeMinimizeNormalize.copy(copy._selectEnumMaximizeMinimizeNormalize); 073 _selectEnumBringToFrontOrBack.copy(copy._selectEnumBringToFrontOrBack); 074 return manager.registerAction(copy); 075 } 076 077 public LogixNG_SelectEnum<HideOrShow> getSelectEnumHideOrShow() { 078 return _selectEnumHideOrShow; 079 } 080 081 public LogixNG_SelectEnum<MaximizeMinimizeNormalize> getSelectEnumMaximizeMinimizeNormalize() { 082 return _selectEnumMaximizeMinimizeNormalize; 083 } 084 085 public LogixNG_SelectEnum<BringToFrontOrBack> getSelectEnumBringToFrontOrBack() { 086 return _selectEnumBringToFrontOrBack; 087 } 088 089 public void setJmriJFrame(@CheckForNull String jmriJFrameTitle) { 090 assertListenersAreNotRegistered(log, "setJmriJFrame"); 091 _jmriJFrameTitle = jmriJFrameTitle; 092 _jmriJFrame = null; 093// InstanceManager.turnoutManagerInstance().addVetoableChangeListener(this); 094 } 095 096 public void setJmriJFrame(@CheckForNull JmriJFrame jmriJFrame) { 097 assertListenersAreNotRegistered(log, "setJmriJFrame"); 098 _jmriJFrame = jmriJFrame; 099 _jmriJFrameTitle = jmriJFrame != null ? jmriJFrame.getTitle() : ""; 100// InstanceManager.turnoutManagerInstance().addVetoableChangeListener(this); 101 } 102 103 public JmriJFrame getJmriJFrame() { 104 return _jmriJFrame; 105 } 106 107 public String getJmriJFrameTitle() { 108 return _jmriJFrameTitle; 109 } 110 111 public void setAddressing(NamedBeanAddressing addressing) throws ParserException { 112 _addressing = addressing; 113 parseFormula(); 114 } 115 116 public NamedBeanAddressing getAddressing() { 117 return _addressing; 118 } 119 120 public void setReference(@Nonnull String reference) { 121 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 122 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 123 } 124 _reference = reference; 125 } 126 127 public String getReference() { 128 return _reference; 129 } 130 131 public void setLocalVariable(@Nonnull String localVariable) { 132 _localVariable = localVariable; 133 } 134 135 public String getLocalVariable() { 136 return _localVariable; 137 } 138 139 public void setFormula(@Nonnull String formula) throws ParserException { 140 _formula = formula; 141 parseFormula(); 142 } 143 144 public String getFormula() { 145 return _formula; 146 } 147 148 private void parseFormula() throws ParserException { 149 if (_addressing == NamedBeanAddressing.Formula) { 150 Map<String, Variable> variables = new HashMap<>(); 151 152 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 153 _expressionNode = parser.parseExpression(_formula); 154 } else { 155 _expressionNode = null; 156 } 157 } 158 159 public void setIgnoreWindowNotFound(boolean ignoreWindowNotFound) { 160 _ignoreWindowNotFound = ignoreWindowNotFound; 161 } 162 163 public boolean isIgnoreWindowNotFound() { 164 return _ignoreWindowNotFound; 165 } 166 167 @Override 168 public void vetoableChange(java.beans.PropertyChangeEvent evt) throws java.beans.PropertyVetoException { 169/* 170 if ("CanDelete".equals(evt.getPropertyName())) { // No I18N 171 if (evt.getOldValue() instanceof Turnout) { 172 if (evt.getOldValue().equals(getTurnout().getBean())) { 173 PropertyChangeEvent e = new PropertyChangeEvent(this, "DoNotDelete", null, null); 174 throw new PropertyVetoException(Bundle.getMessage("Turnout_TurnoutInUseTurnoutExpressionVeto", getDisplayName()), e); // NOI18N 175 } 176 } 177 } else if ("DoDelete".equals(evt.getPropertyName())) { // No I18N 178 if (evt.getOldValue() instanceof Turnout) { 179 if (evt.getOldValue().equals(getTurnout().getBean())) { 180 removeTurnout(); 181 } 182 } 183 } 184*/ 185 } 186 187 /** {@inheritDoc} */ 188 @Override 189 public Category getCategory() { 190 return CategoryDisplay.DISPLAY; 191 } 192 193 private void throwErrorJmriJFrameDoesNotExists() throws JmriException { 194 var lng = getLogixNG(); 195 var cng = getConditionalNG(); 196 var m = getModule(); 197 String errorMessage; 198 if (m != null) { 199 errorMessage = Bundle.getMessage( 200 "WindowManagement_ErrorNoJmriJFrame_Module", 201 getLongDescription(), m.getDisplayName(), getSystemName()); 202 } else { 203 errorMessage = Bundle.getMessage( 204 "WindowManagement_ErrorNoJmriJFrame_LogixNG", 205 getLongDescription(), lng.getDisplayName(), cng.getDisplayName(), getSystemName()); 206 } 207 List<String> list = Arrays.asList(errorMessage.split("\n")); 208 throw new JmriException(Bundle.getMessage("WindowManagement_ErrorNoJmriJFrame"), list); 209 } 210 211 /** {@inheritDoc} */ 212 @Override 213 public void execute() throws JmriException { 214 ConditionalNG conditionalNG = getConditionalNG(); 215 216 JmriJFrame jmriJFrame; 217 218// System.out.format("WindowToFront.execute: %s%n", getLongDescription()); 219 220 switch (_addressing) { 221 case Direct: 222 jmriJFrame = this._jmriJFrame; 223 if (jmriJFrame == null && (_jmriJFrameTitle != null && !_jmriJFrameTitle.isBlank())) { 224 jmriJFrame = JmriJFrame.getFrame(_jmriJFrameTitle); 225 if (jmriJFrame == null) { 226 if (_ignoreWindowNotFound) { 227 log.debug("Window is not found"); 228 return; 229 } else { 230 throwErrorJmriJFrameDoesNotExists(); 231 } 232 } 233 } 234 break; 235 236 case Reference: 237 String ref = ReferenceUtil.getReference( 238 conditionalNG.getSymbolTable(), _reference); 239 jmriJFrame = JmriJFrame.getFrame(ref); 240 break; 241 242 case LocalVariable: 243 SymbolTable symbolTable = conditionalNG.getSymbolTable(); 244 jmriJFrame = JmriJFrame.getFrame(TypeConversionUtil 245 .convertToString(symbolTable.getValue(_localVariable), false)); 246 break; 247 248 case Formula: 249 jmriJFrame = _expressionNode != null ? 250 JmriJFrame.getFrame(TypeConversionUtil 251 .convertToString(_expressionNode.calculate( 252 conditionalNG.getSymbolTable()), false)) 253 : null; 254 break; 255 256 default: 257 throw new IllegalArgumentException("invalid _addressing state: " + _addressing.name()); 258 } 259 260// System.out.format("WindowToFront.execute: positionable: %s%n", positionable); 261 262 if (jmriJFrame == null) { 263 log.error("Window is null"); 264 return; 265 } 266 267 HideOrShow hideOrShow = 268 _selectEnumHideOrShow.evaluateEnum(conditionalNG); 269 MaximizeMinimizeNormalize maximizeMinimizeNormalize = 270 _selectEnumMaximizeMinimizeNormalize.evaluateEnum(conditionalNG); 271 BringToFrontOrBack bringToFrontOrBack = 272 _selectEnumBringToFrontOrBack.evaluateEnum(conditionalNG); 273 274 JmriJFrame frame = jmriJFrame; 275 276 ThreadingUtil.runOnGUI(() -> { 277 hideOrShow.run(frame); 278 maximizeMinimizeNormalize.run(frame); 279 bringToFrontOrBack.run(frame); 280 }); 281 } 282 283 @Override 284 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 285 throw new UnsupportedOperationException("Not supported."); 286 } 287 288 @Override 289 public int getChildCount() { 290 return 0; 291 } 292 293 @Override 294 public String getShortDescription(Locale locale) { 295 return Bundle.getMessage(locale, "WindowManagement_Short"); 296 } 297 298 @Override 299 public String getLongDescription(Locale locale) { 300 String jmriJFrameName; 301 302 switch (_addressing) { 303 case Direct: 304 if (this._jmriJFrameTitle != null) { 305 jmriJFrameName = this._jmriJFrameTitle; 306 } else { 307 jmriJFrameName = Bundle.getMessage(locale, "BeanNotSelected"); 308 } 309 jmriJFrameName = Bundle.getMessage(locale, "AddressByDirect", jmriJFrameName); 310 break; 311 312 case Reference: 313 jmriJFrameName = Bundle.getMessage(locale, "AddressByReference", _reference); 314 break; 315 316 case LocalVariable: 317 jmriJFrameName = Bundle.getMessage(locale, "AddressByLocalVariable", _localVariable); 318 break; 319 320 case Formula: 321 jmriJFrameName = Bundle.getMessage(locale, "AddressByFormula", _formula); 322 break; 323 324 default: 325 throw new IllegalArgumentException("invalid _addressing state: " + _addressing.name()); 326 } 327 328 List<Object> strings = new ArrayList<>(); 329 strings.add(jmriJFrameName); 330 if (!_selectEnumHideOrShow.isEnum(HideOrShow.DoNothing)) { 331 strings.add(_selectEnumHideOrShow.getDescription(locale)); 332 } 333 if (!_selectEnumMaximizeMinimizeNormalize.isEnum(MaximizeMinimizeNormalize.DoNothing)) { 334 strings.add(_selectEnumMaximizeMinimizeNormalize.getDescription(locale)); 335 } 336 if (!_selectEnumBringToFrontOrBack.isEnum(BringToFrontOrBack.DoNothing)) { 337 strings.add(_selectEnumBringToFrontOrBack.getDescription(locale)); 338 } 339 340 if (_ignoreWindowNotFound) { 341 strings.add(Bundle.getMessage("WindowManagement_IgnoreWindowNotFound_Descr", 342 Bundle.getMessage("WindowManagement_IgnoreWindowNotFound"))); 343 } else { 344 strings.add(""); 345 } 346 347 return Bundle.getMessage(locale, "WindowManagement_Long_"+Integer.toString(strings.size()), 348 strings.toArray()); 349 } 350 351 /** {@inheritDoc} */ 352 @Override 353 public void setup() { 354 if ((_jmriJFrameTitle != null) && (_jmriJFrame == null)) { 355 setJmriJFrame(_jmriJFrameTitle); 356 } 357 } 358 359 /** {@inheritDoc} */ 360 @Override 361 public void propertyChange(PropertyChangeEvent evt) { 362 getConditionalNG().execute(); 363 } 364 365 /** {@inheritDoc} */ 366 @Override 367 public void registerListenersForThisClass() { 368 } 369 370 /** {@inheritDoc} */ 371 @Override 372 public void unregisterListenersForThisClass() { 373 } 374 375 /** {@inheritDoc} */ 376 @Override 377 public void disposeMe() { 378 } 379 380 private interface FrameAction { 381 void run(JmriJFrame f); 382 } 383 384 public enum HideOrShow { 385 DoNothing(Bundle.getMessage("WindowManagement_HideOrShow_DoNothing"), (f) -> {}), 386 Show(Bundle.getMessage("WindowManagement_HideOrShow_Show"), (f) -> { f.setVisible(true); }), 387 Hide(Bundle.getMessage("WindowManagement_HideOrShow_Hide"), (f) -> { f.setVisible(false); }); 388 389 private final String _text; 390 private final FrameAction _action; 391 392 private HideOrShow(String text, FrameAction action) { 393 this._text = text; 394 this._action = action; 395 } 396 397 public void run(JmriJFrame f) { 398 _action.run(f); 399 } 400 401 @Override 402 public String toString() { 403 return _text; 404 } 405 406 } 407 408 public enum MaximizeMinimizeNormalize { 409 DoNothing(Bundle.getMessage("WindowManagement_MaximizeMinimizeNormalize_DoNothing"), (f) -> {}), 410 Minimize(Bundle.getMessage("WindowManagement_MaximizeMinimizeNormalize_Minimize"), (f) -> { f.setExtendedState(Frame.ICONIFIED); }), 411 Normalize(Bundle.getMessage("WindowManagement_MaximizeMinimizeNormalize_Normalize"), (f) -> { f.setExtendedState(Frame.NORMAL); }), 412 Maximize(Bundle.getMessage("WindowManagement_MaximizeMinimizeNormalize_Maximize"), (f) -> { f.setExtendedState(Frame.MAXIMIZED_BOTH); }); 413 414 private final String _text; 415 private final FrameAction _action; 416 417 private MaximizeMinimizeNormalize(String text, FrameAction action) { 418 this._text = text; 419 this._action = action; 420 } 421 422 public void run(JmriJFrame f) { 423 _action.run(f); 424 } 425 426 @Override 427 public String toString() { 428 return _text; 429 } 430 431 } 432 433 public enum BringToFrontOrBack { 434 DoNothing(Bundle.getMessage("WindowManagement_BringToFrontOrBack_DoNothing"), (f) -> {}), 435 Front(Bundle.getMessage("WindowManagement_BringToFrontOrBack_Front"), (f) -> { f.toFront(); }), 436 Back(Bundle.getMessage("WindowManagement_BringToFrontOrBack_Back"), (f) -> { f.toBack(); }); 437 438 private final String _text; 439 private final FrameAction _action; 440 441 private BringToFrontOrBack(String text, FrameAction action) { 442 this._text = text; 443 this._action = action; 444 } 445 446 public void run(JmriJFrame f) { 447 _action.run(f); 448 } 449 450 @Override 451 public String toString() { 452 return _text; 453 } 454 455 } 456 457 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(WindowManagement.class); 458 459}