001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas/
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see https://www.gnu.org/licenses/.
027 */
028package edu.wisc.ssec.mcidasv;
029
030import static edu.wisc.ssec.mcidasv.McIdasPreferenceManager.PROP_HIQ_FONT_RENDERING;
031import static ucar.unidata.util.GuiUtils.makeMenu;
032import static ucar.unidata.util.MenuUtil.MENU_SEPARATOR;
033
034import java.util.ArrayList;
035import java.util.List;
036import java.util.Map;
037
038import edu.wisc.ssec.mcidasv.startupmanager.options.BooleanOption;
039import org.python.util.PythonInterpreter;
040
041import edu.wisc.ssec.mcidasv.startupmanager.options.OptionMaster;
042import edu.wisc.ssec.mcidasv.util.CollectionHelpers;
043
044import ucar.unidata.data.DataSource;
045import ucar.unidata.data.DescriptorDataSource;
046import ucar.unidata.idv.IntegratedDataViewer;
047import ucar.unidata.idv.ui.ImageGenerator;
048import ucar.unidata.idv.ui.JythonShell;
049
050import javax.swing.JMenuItem;
051
052/**
053 * Overrides the IDV's {@link ucar.unidata.idv.JythonManager JythonManager} to 
054 * associate a {@link JythonShell} with a given {@code JythonManager}.
055 */
056public class JythonManager extends ucar.unidata.idv.JythonManager {
057    
058//    /** Trusty logging object. */
059//    private static final Logger logger = LoggerFactory.getLogger(JythonManager.class);
060    
061    /** Associated Jython Shell. May be {@code null}. */
062    private JythonShell jythonShell;
063    
064    /**
065     * Create the manager and call initPython.
066     *
067     * @param idv The IDV.
068     */
069    public JythonManager(IntegratedDataViewer idv) {
070        super(idv);
071    }
072    
073    /**
074     * Create a Jython shell, if one doesn't already exist. This will also 
075     * bring the window {@literal "to the front"} of the rest of the McIDAS-V
076     * session.
077     * 
078     * @return JythonShell object for interactive Jython usage.
079     */
080    public JythonShell createShell() {
081        if (jythonShell == null) {
082            jythonShell = new JythonShell(getIdv());
083            
084        }
085        jythonShell.toFront();
086        return jythonShell;
087    }
088    
089    /** 
090     * Returns the Jython Shell associated with this {@code JythonManager}.
091     * 
092     * @return Jython Shell being used by this manager. May be {@code null}.
093     */
094    public JythonShell getShell() {
095        return jythonShell;
096    }
097    
098    /**
099     * Create and initialize a Jython interpreter.
100     * 
101     * @return Newly created Jython interpreter.
102     */
103    @Override public PythonInterpreter createInterpreter() {
104        PythonInterpreter interpreter = super.createInterpreter();
105        return interpreter;
106    }
107    
108    /**
109     * Removes the given interpreter from the list of active interpreters. 
110     * 
111     * <p>Also attempts to close any Jython Shell associated with the 
112     * interpreter.</p>
113     * 
114     * @param interpreter Interpreter to remove. Should not be {@code null}. 
115     */
116    @Override public void removeInterpreter(PythonInterpreter interpreter) {
117        super.removeInterpreter(interpreter);
118        if ((jythonShell != null) && !jythonShell.isShellResetting() && jythonShell.getInterpreter().equals(interpreter)) {
119            jythonShell.close();
120            jythonShell = null;
121        }
122    }
123
124    /**
125     * Overridden so that McIDAS-V can add an {@code islInterpreter} object
126     * to the interpreter's locals (before executing the contents of {@code}.
127     * 
128     * @param code Jython code to evaluate. {@code null} is probably a bad idea.
129     * @param properties {@code String->Object} pairs to insert into the 
130     * locals. Parameter may be {@code null}.
131     */
132    @SuppressWarnings("unchecked") // dealing with idv code that predates generics.
133    @Override public void evaluateTrusted(String code, Map<String, Object> properties) {
134        if (properties == null) {
135            properties = CollectionHelpers.newMap();
136        }
137        properties.putIfAbsent("islInterpreter", new ImageGenerator(getIdv()));
138        properties.putIfAbsent("_idv", getIdv());
139        properties.putIfAbsent("idv", getIdv());
140        super.evaluateTrusted(code, properties);
141    }
142    
143    /**
144     * Return the list of menu items to use when the user has clicked on a 
145     * formula {@link DataSource}.
146     * 
147     * @param dataSource The data source clicked on.
148     * 
149     * @return {@link List} of menu items.
150     */
151    @SuppressWarnings("unchecked") // dealing with idv code that predates generics.
152    @Override public List doMakeFormulaDataSourceMenuItems(DataSource dataSource) {
153        List menuItems = new ArrayList(100);
154        JMenuItem menuItem;
155
156        menuItem = new JMenuItem("Create Formula");
157        menuItem.addActionListener(e -> showFormulaDialog());
158        menuItems.add(menuItem);
159
160        List editItems;
161        if (dataSource instanceof DescriptorDataSource) {
162            editItems = doMakeEditMenuItems((DescriptorDataSource)dataSource);
163        } else {
164            editItems = doMakeEditMenuItems();
165        }
166        menuItems.add(makeMenu("Edit Formulas", editItems));
167
168        menuItems.add(MENU_SEPARATOR);
169
170        menuItem = new JMenuItem("Jython Library");
171        menuItem.addActionListener(e -> showJythonEditor());
172        menuItems.add(menuItem);
173
174        menuItem = new JMenuItem("Jython Shell");
175        menuItem.addActionListener(e -> createShell());
176        menuItems.add(menuItem);
177
178        menuItems.add(MENU_SEPARATOR);
179
180        menuItem = new JMenuItem("Import");
181        menuItem.addActionListener(e -> importFormulas());
182        menuItems.add(menuItem);
183
184        menuItem = new JMenuItem("Export");
185        menuItem.addActionListener(e -> exportFormulas());
186        menuItems.add(menuItem);
187
188        return menuItems;
189    }
190
191    /**
192     * Determine if the user should be warned about a potential bug that we've been unable to resolve.
193     *
194     * <p>The conditions for the bug to appear are:
195     * <ul>
196     *     <li>In background mode (i.e. running a script).</li>
197     *     <li>Geometry by reference is <b>disabled</b>.</li>
198     *     <li>New font rendering is <b>enabled</b>.</li>
199     * </ul>
200     *
201     * @return {@code true} if the user's configuration has made it possible for bug to manifest.
202     */
203    public static boolean shouldWarnImageCapturing() {
204        boolean backgroundMode = McIDASV.getStaticMcv().getArgsManager().isScriptingMode();
205        boolean shouldWarn = false;
206        OptionMaster optMaster = OptionMaster.getInstance();
207        BooleanOption useGeometryByRef = optMaster.getBooleanOption("USE_GEOBYREF");
208        if (useGeometryByRef != null) {
209            shouldWarn = backgroundMode
210                    && !Boolean.parseBoolean(System.getProperty("visad.java3d.geometryByRef"))
211                    && Boolean.parseBoolean(System.getProperty(PROP_HIQ_FONT_RENDERING, "false"));
212        }
213//        System.out.println("background mode: "+backgroundMode);
214//        System.out.println("geo by ref: "+Boolean.parseBoolean(System.getProperty("visad.java3d.geometryByRef")));
215//        System.out.println("new fonts: "+Boolean.parseBoolean(System.getProperty(PROP_HIQ_FONT_RENDERING, "false")));
216//        System.out.println("shouldWarn: "+shouldWarn);
217//        System.out.println("props:\n"+System.getProperties());
218//        return shouldWarn;
219        return false;
220    }
221
222}