001/*
002 * Copyright (c) 2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2015-08-24 22:44:14 +0000 (Mon, 24 Aug 2015) $' 
007 * '$Revision: 33630 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.kepler.gui;
031
032import java.awt.CardLayout;
033import java.awt.Component;
034import java.awt.Container;
035import java.awt.event.ActionEvent;
036import java.awt.event.ActionListener;
037import java.util.List;
038import java.util.Vector;
039import java.util.WeakHashMap;
040
041import javax.swing.JComboBox;
042import javax.swing.JComponent;
043import javax.swing.JLabel;
044import javax.swing.JPanel;
045import javax.swing.JTabbedPane;
046
047import org.apache.commons.logging.Log;
048import org.apache.commons.logging.LogFactory;
049import org.kepler.configuration.ConfigurationManager;
050import org.kepler.configuration.ConfigurationProperty;
051import org.kepler.gui.state.StateChangeMonitor;
052import org.kepler.gui.state.ViewStateChangeEvent;
053
054import ptolemy.actor.gui.PtolemyFrame;
055import ptolemy.actor.gui.TableauFrame;
056import ptolemy.kernel.util.NamedObj;
057
058/**
059 * The ViewManager keeps track of configured ViewPanes.
060 * 
061 * @author Aaron Schultz
062 * 
063 */
064public class ViewManager {
065
066        private static final Log log = LogFactory.getLog(ViewManager.class
067                        .getName());
068        private static final boolean isDebugging = log.isDebugEnabled();
069
070        //_viewPanes needs to be WeakReference because some of its elements might be GCed during Kepler GUI start.
071        //see bug: https://projects.ecoinformatics.org/ecoinfo/issues/5901
072        protected Vector<ViewPane> _viewPanes; 
073        protected WeakHashMap<TableauFrame, JPanel> _viewAreas;
074        protected WeakHashMap<TableauFrame, JComboBox> _viewComboBoxes;
075
076        /**
077         * Constructor.
078         */
079        public ViewManager() {
080                _viewPanes = new Vector<ViewPane>();
081                _viewAreas = new WeakHashMap<TableauFrame, JPanel>();
082                _viewComboBoxes = new WeakHashMap<TableauFrame, JComboBox>();
083        }
084
085        /**
086         * Instantiate all of the ViewPanes that are specified in configuration.xml
087         * 
088         * @param parent
089         */
090        public void initializeViews(TableauFrame parent) {
091                try {
092                        ViewPaneFactory VPfactory = (ViewPaneFactory) parent
093                                        .getConfiguration().getAttribute("ViewPaneFactory");
094                        if (VPfactory == null) {
095                                VPfactory = new ViewPaneFactory(parent.getConfiguration(),
096                                                "ViewPaneFactory");
097                        }
098
099                        if (VPfactory != null) {
100                                boolean success = VPfactory.createViewPanes(parent);
101                                if (!success) {
102                                        System.out
103                                                        .println("error: ViewPane is null.  "
104                                                                        + "This "
105                                                                        + "problem can be fixed by adding a viewPaneFactory "
106                                                                        + "property in the configuration.xml file.");
107                                }
108                        } else {
109                                System.out.println("error: ViewPane is " + "null.  This "
110                                                + "problem can be fixed by adding a viewPaneFactory "
111                                                + "property in the configuration.xml file.");
112                        }
113                } catch (ptolemy.kernel.util.NameDuplicationException nde) {
114
115                } catch (Exception e) {
116                        System.out.println("Could not create the ViewPaneFactory: "
117                                        + e.getMessage());
118                        e.printStackTrace();
119                        return;
120                }
121
122                // Create the view area and add all the viewpanes to it
123                JPanel viewArea = new JPanel(new CardLayout());
124                Vector<ViewPane> frameViews = getFrameViews(parent);
125                String[] viewsList = new String[frameViews.size()];
126                for (int i = 0; i < frameViews.size(); i++) {
127                        ViewPane vp = frameViews.elementAt(i);
128                        viewArea.add((Component) vp, vp.getViewName());
129                        viewsList[i] = vp.getViewName();
130                        if (isDebugging)
131                                log.debug("add one element to viewsList:" + viewsList[i]);
132                }
133
134                try {
135                        addConfiguredTabPanes(parent);
136                } catch (Exception e) {
137                        e.printStackTrace();
138                }
139
140                // while( e. hasMoreElements() ){
141                // TableauFrame tableFrame = (TableauFrame)(e.nextElement());
142                // System.out.println("getContainer in _viewAreas:"+
143                // tableFrame.getTableau().getContainer());
144                // System.out.println("isMaster in _viewAreas:"+
145                // tableFrame.getTableau().isMaster());
146                // if (tableFrame.getTableau().getContainer()==null &&
147                // tableFrame.getTableau().isMaster())
148                // {
149                // _viewAreas.remove(tableFrame);
150                // System.out.println("one element is in _viewAreas removed");
151                // _viewComboBoxes.remove(tableFrame);
152                // System.out.println("one element is in _viewComboBoxes removed");
153                // }
154                //
155                // }
156
157                _viewAreas.put(parent, viewArea);
158                if (isDebugging) {
159                        log.debug("_viewAreas:" + _viewAreas.size());
160                        log.debug("_viewAreas key set:" + _viewAreas.keySet());
161                }
162                
163                
164
165                JComboBox viewComboBox = new JComboBox(viewsList);
166                if (viewComboBox != null && viewComboBox.getItemCount() > 0) {
167                        viewComboBox.setSelectedIndex(0);
168                        viewComboBox.addActionListener(new ViewComboBoxListener());
169                        _viewComboBoxes.put(parent, viewComboBox);
170                        if (isDebugging)
171                                log.debug("_viewComboBoxes:" + _viewComboBoxes.size());
172                }
173
174        }
175
176        public JPanel getViewArea(TableauFrame parent) {
177                return _viewAreas.get(parent);
178        }
179
180        /**
181         * Method to return a JComponent as a view selector. If there is only one
182         * view for the specified frame then an empty JLabel is returned otherwise a
183         * JComboBox is returned.
184         * 
185         * @param parent
186         * @return JComponent
187         */
188        public JComponent getViewSelector(TableauFrame parent) {
189                JComboBox c = getViewComboBox(parent);
190                if (c.getItemCount() == 1) {
191                        // return new JLabel( c.getItemAt(0).toString() );
192                        return new JLabel("");
193                } else {
194                        return c;
195                }
196        }
197
198        /**
199         * Return a combo box that contains all of the views for the specified
200         * parent frame.
201         * 
202         * @param parent
203         * @return JComboBox
204         */
205        public JComboBox getViewComboBox(TableauFrame parent) {
206                return _viewComboBoxes.get(parent);
207        }
208
209        /**
210         * Set the JComboBox for a specific TableauFrame
211         * 
212         * @param parent
213         * @param jcb
214         */
215        public void setViewComboBox(TableauFrame parent, JComboBox jcb) {
216                _viewComboBoxes.put(parent, jcb);
217        }
218
219        public void showView(TableauFrame parent, String viewName) {
220                JPanel viewArea = _viewAreas.get(parent);
221                CardLayout cl = (CardLayout) (viewArea.getLayout());
222                cl.show(viewArea, viewName);
223                NamedObj reference = ((PtolemyFrame) parent).getModel();
224                ViewPane viewPane = getViewPane(parent, viewName);
225                StateChangeMonitor.getInstance().notifyStateChange(
226                                new ViewStateChangeEvent((Component) viewPane,
227                                                ViewStateChangeEvent.SHOW_VIEW, reference, viewName));
228        }
229
230        /**
231   *
232   */
233        public void addCanvasToLocation(Component canvas, TableauFrame parent)
234                        throws Exception {
235                ConfigurationProperty commonProperty = ConfigurationManager
236                                .getInstance().getProperty(
237                                                ConfigurationManager.getModule("common"));
238                // get //canvasViewPaneLocation/viewPane
239                List<ConfigurationProperty> viewPaneList = commonProperty
240                                .getProperties("canvasViewPaneLocation.viewPane");
241                // for each viewPane
242                for (int i = 0; i < viewPaneList.size(); i++) {
243                        ConfigurationProperty viewPaneProp = (ConfigurationProperty) viewPaneList
244                                        .get(i);
245                        // get viewPane.name
246                        String viewPaneName = viewPaneProp.getProperty("name").getValue();
247
248                        // ViewPane theViewPane = getViewPane(parent, viewPane.name);
249                        ViewPane theViewPane = getViewPane(parent, viewPaneName);
250                        // if theViewPane == null, throw exception
251                        if (theViewPane == null) {
252                                // Check for the case where the ViewPane *is* actually defined
253                                // in configuration.xml, but it was not parsed from that file
254                                // because it was specified along with a tableau filter, which
255                                // the current tableau does not pass.
256                                if (isInConfigurationFile(viewPaneName)) {
257                                        continue;
258                                }
259                                throw new Exception(
260                                                viewPaneName
261                                                                + " ViewPane specified in "
262                                                                + viewPaneProp.getModule()
263                                                                + " configuration.xml"
264                                                                + " was not found in the ViewManager."
265                                                                + " Make sure you have specified this in the configuration.xml file.");
266                        }
267                        List<ConfigurationProperty> viewPaneLocationList = viewPaneProp
268                                        .getProperties("viewPaneLocation");
269                        // for each viewPane.viewPaneLocation
270                        for (int j = 0; j < viewPaneLocationList.size(); j++) {
271                                ConfigurationProperty viewPaneLocationProp = (ConfigurationProperty) viewPaneLocationList
272                                                .get(j);
273                                // get viewPane.viewPaneLocation.name
274                                String viewPaneLocationName = viewPaneLocationProp.getProperty(
275                                                "name").getValue();
276                                // if !theViewPane.hasLocation(viewPane.viewPaneLocation.name)
277                                // throw exception
278                                if (!theViewPane.hasLocation(viewPaneLocationName)) {
279                                        throw new Exception("The ViewPaneLocation, "
280                                                        + viewPaneLocationName
281                                                        + ", is not an available location of ViewPane, "
282                                                        + viewPaneName);
283                                }
284                                // theViewPane.getLocationContainer(viewPane.viewPaneLocation.name).add("Workflow",
285                                // canvas);
286
287                                // see if the tab name for the canvas is specified
288                                String canvasTabPaneName = "Workflow";
289                                ConfigurationProperty tabPaneProp = viewPaneProp
290                                                .getProperty("tabPanename");
291                                if (tabPaneProp != null) {
292                                        canvasTabPaneName = tabPaneProp.getValue();
293                                }
294
295                                canvas.setName(canvasTabPaneName);
296
297                                // add the canvas as the first tab in the view.
298                                theViewPane.getLocationContainer(viewPaneLocationName).add(
299                                                canvas, 0);
300
301                                // if the canvas is part of a tabbed pane, make sure it is
302                                // selected
303                                Component container = canvas.getParent();
304                                if (container instanceof JTabbedPane) {
305                                        ((JTabbedPane) container).setSelectedIndex(0);
306                                }
307                        }
308                }
309        }
310
311        private boolean isInConfigurationFile(String viewPaneName) {
312                if (viewPaneName == null) {
313                        return false;
314                }
315
316                ConfigurationProperty guiProperty = ConfigurationManager.getInstance()
317                                .getProperty(ConfigurationManager.getModule("gui"));
318                List<ConfigurationProperty> paneList = guiProperty
319                                .getProperties("viewPaneFactory.viewPane");
320                for (ConfigurationProperty property : paneList) {
321                        ConfigurationProperty nameProperty = property.getProperty("name");
322                        if (nameProperty != null
323                                        && viewPaneName.equals(nameProperty.getValue())) {
324                                return true;
325                        }
326                }
327                return false;
328        }
329
330        /**
331         * Register a ViewPane with the ViewManager. ViewPanes must be subclasses of
332         * java.awt.Container
333         * 
334         * @param vp
335         * @throws ClassCastException
336         */
337        public void addViewPane(ViewPane vp) throws ClassCastException {
338                if (vp instanceof Container) {
339
340                        // Iterator _viewPaneIt = _viewPanes.iterator();
341                        // while (_viewPaneIt.hasNext()) {
342                        // ViewPane vpEle = (ViewPane)_viewPaneIt.next();
343                        // System.out.println("getContainer in _viewPanes:"+
344                        // vpEle.getParentFrame().getTableau().getContainer());
345                        // System.out.println("isMaster in _viewPanes:"+
346                        // vpEle.getParentFrame().getTableau().isMaster());
347                        // if (vpEle.getParentFrame().getTableau().getContainer()==null &&
348                        // vpEle.getParentFrame().getTableau().isMaster())
349                        // {
350                        // _viewPanes.remove(vpEle);
351                        // System.out.println("one element in _viewPanes is removed");
352                        // }
353                        //
354                        // }
355
356                        // for (ViewPane vpEle : _viewPanes)
357                        // {
358                        // System.out.println("getContainer in _viewPanes:"+
359                        // vpEle.getParentFrame().getTableau().getContainer());
360                        // System.out.println("isMaster in _viewPanes:"+
361                        // vpEle.getParentFrame().getTableau().isMaster());
362                        // if (vpEle.getParentFrame().getTableau().getContainer()==null &&
363                        // vpEle.getParentFrame().getTableau().isMaster())
364                        // {
365                        // _viewPanes.remove(vpEle);
366                        // System.out.println("one element in _viewPanes is removed");
367                        // }
368                        // }
369
370                        _viewPanes.add(vp);
371                        if (isDebugging)
372                                log.debug("_viewPanes:" + _viewPanes.size());
373                } else {
374                        throw new ClassCastException(vp.getViewName()
375                                        + " ViewPane is not a subclass of java.awt.Container");
376                }
377        }
378
379        /**
380         * Return a ViewPane reference for the given TableauFrame and viewName.
381         * 
382         * @param parent
383         * @param viewName
384         * */
385        public ViewPane getViewPane(TableauFrame parent, String viewName) {
386                viewName = viewName.trim();
387                for (int i = 0; i < _viewPanes.size(); i++) {
388                        ViewPane pane = _viewPanes.elementAt(i);
389                        if (pane != null && pane.getParentFrame() == parent) {
390                                if (pane.getViewName().equals(viewName)) {
391                                        if (pane instanceof Container) {
392                                                return pane;
393                                        }
394                                }
395                        } else if (pane == null) {
396                                _viewPanes.removeElementAt(i);
397                                i--;
398                        }
399                }
400                return null;
401        }
402
403        /**
404         * Return a vector of ViewPane objects for the specified TableauFrame.
405         * 
406         * @param parent
407         * */
408        public Vector<ViewPane> getFrameViews(TableauFrame parent) {
409                Vector<ViewPane> frameViewPanes = new Vector<ViewPane>();
410                for (int i = 0; i < _viewPanes.size(); i++) {
411                        ViewPane pane = _viewPanes.elementAt(i);
412                        if (pane != null && pane.getParentFrame() == parent) {
413                                frameViewPanes.add(pane);
414                        } else if (pane == null) {
415                                _viewPanes.removeElementAt(i);
416                                if (isDebugging)
417                                        log.debug("remove pane number " + i + " from _viewPanes because pane == null.");
418                                i--;
419                        }
420
421                }
422                return frameViewPanes;
423        }
424
425        /**
426         * Read in ViewPane locations of TabPanes from Config and add them.
427         * 
428         * &lt;viewPane&gt; &lt;name&gt;example&lt;/name&gt;
429         * &lt;viewPaneLocation&gt; &lt;name&gt;NORTH&lt;/name&gt; &lt;tabPane&gt;
430         * &lt;name&gt;tabname&lt;/name&gt; &lt;/tabPane&gt;
431         * &lt;/viewPaneLocation&gt; &lt;/viewPane&gt;
432         * 
433         * @throws Exception
434         */
435        private void addConfiguredTabPanes(TableauFrame parent) throws Exception {
436                TabManager tabman = TabManager.getInstance();
437                ConfigurationProperty commonProperty = ConfigurationManager
438                                .getInstance().getProperty(
439                                                ConfigurationManager.getModule("common"));
440
441                // get the viewpanes
442                List<ConfigurationProperty> viewPaneList = commonProperty
443                                .getProperties("viewPaneTabPanes.viewPane");
444                // for each viewpane get the name
445                for (int i = 0; i < viewPaneList.size(); i++) {
446                        ConfigurationProperty viewPaneProp = (ConfigurationProperty) viewPaneList
447                                        .get(i);
448                        String viewPaneName = viewPaneProp.getProperty("name").getValue();
449                        // call getViewPane(parent, name)
450                        ViewPane theViewPane = getViewPane(parent, viewPaneName);
451                        // get the viewPaneLocation children of viewpane
452                        List<ConfigurationProperty> viewPaneLocationList = viewPaneProp
453                                        .getProperties("viewPaneLocation");
454                        // for each viewpanelocation of viewpane
455                        for (int j = 0; j < viewPaneLocationList.size(); j++) {
456                                ConfigurationProperty viewPaneLocationProp = (ConfigurationProperty) viewPaneLocationList
457                                                .get(j);
458                                // get viewpanelocation.name
459                                String viewPaneLocationName = viewPaneLocationProp.getProperty(
460                                                "name").getValue();
461                                // check theViewPane.hasLocation(viewpanelocation.name)
462                                if (theViewPane != null
463                                                && !theViewPane.hasLocation(viewPaneLocationName)) {
464                                        throw new Exception("The ViewPaneLocation, "
465                                                        + viewPaneLocationName
466                                                        + ", is not an available location of ViewPane, "
467                                                        + viewPaneName);
468                                } else if (theViewPane != null) {
469                                        // viewPaneContainer =
470                                        // theViewPane.getLocationContainer(viewpanelocation.name)
471                                        Container viewPaneContainer = theViewPane
472                                                        .getLocationContainer(viewPaneLocationName);
473                                        List<ConfigurationProperty> tabPaneList = viewPaneLocationProp
474                                                        .getProperties("tabPane");
475                                        // for each tabpane in viewpanelocation
476                                        for (int k = 0; k < tabPaneList.size(); k++) {
477                                                ConfigurationProperty tabPaneProperty = (ConfigurationProperty) tabPaneList
478                                                                .get(k);
479                                                // get the name of the tabpane
480                                                String tabPaneName = tabPaneProperty
481                                                                .getProperty("name").getValue();
482                                                // System.out.println("getting tab pane: " +
483                                                // tabPaneName);
484                                                TabPane theTabPane = tabman.getTab(parent, tabPaneName);
485                                                if (theTabPane == null) {
486                                                        System.out
487                                                                        .println("ERROR: no tab named "
488                                                                                        + tabPaneName
489                                                                                        + " in the view "
490                                                                                        + viewPaneName
491                                                                                        + ". (Perhaps the tab's getTabName() does not match"
492                                                                                        + " the name given in configuration.xml?)");
493                                                } else {
494                                                        viewPaneContainer.add(theTabPane.getTabName(),
495                                                                        (Component) theTabPane);
496                                                }
497                                        }
498                                }
499                        }
500                }
501        }
502
503        /**
504         * Method for getting an instance of this singleton class.
505         */
506        public static ViewManager getInstance() {
507                return ViewManagerHolder.INSTANCE;
508        }
509
510        /**
511         * Method for remove .
512         */
513        public void removeOpenFrame(TableauFrame parent) {
514
515                Object[] keyArray = _viewComboBoxes.keySet().toArray();
516                for (int i = 0; i < keyArray.length; i++) {
517                        if (keyArray[i] == null) {
518                                if (isDebugging)
519                                        log.debug("keyArray[i] == null");
520                        } else {
521                                TableauFrame tableauFrame = (TableauFrame) keyArray[i];
522                                if (isDebugging) {
523                                        log.debug("getContainer in _viewAreas:"
524                                                        + tableauFrame.getTableau().getContainer());
525                                        log.debug("isMaster in _viewAreas:"
526                                                        + tableauFrame.getTableau().isMaster());
527                                }
528                                // if (tableFrame.getTableau().getContainer()==null &&
529                                // tableFrame.getTableau().isMaster())
530                                if (tableauFrame == parent) {
531                                        _viewAreas.remove(tableauFrame);
532                                        _viewComboBoxes.remove(tableauFrame);
533                                        if (isDebugging) {
534                                                log.debug("one element is in _viewAreas removed:" + i);
535                                                log.debug("one element is in _viewComboBoxes removed:"
536                                                                + i);
537                                        }
538                                }
539                        }
540                }
541
542                if (isDebugging) {
543                        log.debug("the size of _viewAreas after removing:"
544                                        + _viewAreas.size());
545                        log.debug("the size of _viewComboBoxes after removing:"
546                                        + _viewComboBoxes.size());
547                }
548
549                for (int i = 0; i < _viewPanes.size(); i++) {
550                        ViewPane pane = _viewPanes.elementAt(i);
551                        if (pane != null && pane.getParentFrame() == parent) {
552                                pane.setParentFrame(null);
553                                _viewPanes.removeElementAt(i);
554                                if (isDebugging)
555                                        log.debug("one element is in _viewPanes removed:" + i);
556                                i--;
557                        } else if (pane == null) {
558                                _viewPanes.removeElementAt(i);
559                                if (isDebugging)
560                                        log.debug("one element is in _viewPanes removed 2:" + i);
561                                i--;
562                        }
563
564                }
565
566                if (isDebugging)
567                        log.debug("the size of _viewPanes after removing:"
568                                        + _viewPanes.size());
569
570        }
571
572        private static class ViewManagerHolder {
573                private static final ViewManager INSTANCE = new ViewManager();
574        }
575
576        private class ViewComboBoxListener implements ActionListener {
577
578                /** Action for changing the view. */
579                public void actionPerformed(ActionEvent e) {
580                        Object c = e.getSource();
581                        if (c instanceof JComboBox) {
582                                JComboBox jc = (JComboBox) c;
583                                Object s = jc.getSelectedItem();
584                                if (s instanceof String) {
585                                        String viewName = (String) s;
586                                        // NOTE: we need to get the parent TableauFrame here instead
587                                        // of Window, since the detached toolbar is a Window.
588                                        TableauFrame frame = GUIUtil.getParentTableauFrame(jc);
589                                        if (frame == null) {
590                                                System.out
591                                                                .println("ERROR: could not find parent tableau frame.");
592                                        } else {
593                                                showView(frame, viewName);
594                                        }
595                                }
596                        }
597                }
598        }
599}