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}