001/*
002 * Copyright (c) 2009-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2015-08-19 23:46:56 +0000 (Wed, 19 Aug 2015) $' 
007 * '$Revision: 33599 $'
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.frame;
031
032import java.awt.event.ActionEvent;
033import java.awt.event.KeyEvent;
034import java.util.HashSet;
035import java.util.Iterator;
036import java.util.List;
037import java.util.Set;
038
039import org.kepler.gui.state.StateChangeEvent;
040import org.kepler.gui.state.StateChangeListener;
041import org.kepler.gui.state.StateChangeMonitor;
042
043import ptolemy.actor.CompositeActor;
044import ptolemy.actor.gui.PtolemyEffigy;
045import ptolemy.actor.lib.hoc.MultiCompositeActor;
046import ptolemy.actor.lib.hoc.Refinement;
047import ptolemy.gui.ComponentDialog;
048import ptolemy.gui.Query;
049import ptolemy.kernel.CompositeEntity;
050import ptolemy.kernel.Port;
051import ptolemy.kernel.util.IllegalActionException;
052import ptolemy.kernel.util.NameDuplicationException;
053import ptolemy.kernel.util.Nameable;
054import ptolemy.kernel.util.NamedObj;
055import ptolemy.kernel.util.Workspace;
056import ptolemy.moml.LibraryAttribute;
057import ptolemy.moml.MoMLChangeRequest;
058import ptolemy.util.MessageHandler;
059import ptolemy.util.StringUtilities;
060import ptolemy.vergil.toolbox.FigureAction;
061
062//////////////////////////////////////////////////////////////////////////
063//// MultiCompositeTableau
064
065/** An editor tableau for MultiCompositeActors.
066 * 
067 @author Daniel Crawl, Possibly based on CaseGraphTableau by Edward A. Lee.
068 @version $Id: MultiCompositeTableau.java 33599 2015-08-19 23:46:56Z crawl $
069 */
070public abstract class MultiCompositeTableau extends TabbedKeplerGraphTableau implements StateChangeListener {
071    /** Create a new multicomposite editor tableau with the specified
072     *  container and name.
073     *  @param container The container.
074     *  @param name The name.
075     *  @exception IllegalActionException If the model associated with
076     *   the container effigy is not an instance of CompositeEntity.
077     *  @exception NameDuplicationException If the container already
078     *   contains an object with the specified name.
079     */
080    public MultiCompositeTableau(PtolemyEffigy container, String name)
081            throws IllegalActionException, NameDuplicationException {
082        this(container, name, null);
083    }
084
085    /** Create a new multicomposite editor tableau with the specified
086     *  container, name, and default library.
087     *  @param container The container.
088     *  @param name The name.
089     *  @param defaultLibrary The default library, or null to not specify one.
090     *  @exception IllegalActionException If the model associated with
091     *   the container effigy is not an instance of CompositeEntity.
092     *  @exception NameDuplicationException If the container already
093     *   contains an object with the specified name.
094     */
095    public MultiCompositeTableau(PtolemyEffigy container, String name,
096            LibraryAttribute defaultLibrary) throws IllegalActionException,
097            NameDuplicationException {
098        super(container, name);
099    }
100
101    ///////////////////////////////////////////////////////////////////
102    ////                         public methods                    ////
103
104    /** Clone the object into the specified workspace. */
105    @Override
106    public Object clone(Workspace workspace) throws CloneNotSupportedException {
107        MultiCompositeTableau newObject = (MultiCompositeTableau) super.clone(workspace);
108        newObject._multiCompositeActor = null;
109        return newObject;
110    }
111    
112    @Override
113    public void createGraphFrame(CompositeEntity model,
114           LibraryAttribute defaultLibrary)
115    {
116       TabbedKeplerGraphFrame frame = null;
117
118       frame = _addCompositeToExistingFrame(model, defaultLibrary);
119       _createdNewFrame = false;
120               
121       _configureFrame(frame, model);
122       
123    }
124
125    /** React to changes in the selected tab in a TabbedKeplerGraphFrame. */
126    @Override
127    public void handleStateChange(StateChangeEvent event) {
128        
129        if(event instanceof TabbedKeplerGraphFrameEvent) {
130            
131            TabbedKeplerGraphFrameEvent frameEvent = (TabbedKeplerGraphFrameEvent)event;
132            
133            // see if the event is from the frame that this tableau created
134            // and the event is for a tab change.
135            if(frameEvent.getFrame() == _frame &&
136                    frameEvent.getChangedState().equals(TabbedKeplerGraphFrameEvent.CHANGE_TAB)) {
137                
138                NamedObj model = frameEvent.getReference();
139                
140                boolean multiCompositeIsNowSelected = 
141                        (model == _multiCompositeActor ||
142                        model.getContainer() == _multiCompositeActor);
143                
144                // see if the tab containing our MultiCompositeActor is now selected
145                if(!_multiCompositeIsSelected && multiCompositeIsNowSelected) {
146                    _multiCompositeSelected(true);
147                }
148                // see if the tab containing our MultiCompositeActor was selected
149                // and now is not selected.
150                else if(_multiCompositeIsSelected && !multiCompositeIsNowSelected) {
151                    _multiCompositeSelected(false);
152                }
153            }
154        }
155    }
156    
157    /** Set the container for this tableau. If the tab for the MultiCompositeActor
158     *  is closed, the container will be null. In this case, stop listening for
159     *  tab changes, and the tab for the MultiCompositeActor is no longer selected.
160     */
161    @Override
162    public void setContainer(CompositeEntity container) throws IllegalActionException, NameDuplicationException {
163        
164        // see if the graph was closed.
165        if(container == null) {
166            
167            // stop listening for when the graph's tab is selected
168            StateChangeMonitor.getInstance().
169                removeStateChangeListener(TabbedKeplerGraphFrameEvent.CHANGE_TAB, this);
170            
171            // see if the graph is currently selected
172            if(_multiCompositeIsSelected) {
173                _multiCompositeSelected(false);
174            }
175        }
176        
177        super.setContainer(container);
178    }
179
180    ///////////////////////////////////////////////////////////////////
181    ////                         protected methods                 ////
182
183
184    /** Configure the frame for the opened multi composite model. */
185    @Override
186    protected void _configureFrame(TabbedKeplerGraphFrame frame, CompositeEntity model) {
187
188        super._configureFrame(frame, model);
189        
190        _multiCompositeActor = model;
191        _frame = frame;
192        
193        // listen for state change events so we know then the tab is changed.
194        StateChangeMonitor.getInstance().
195            addStateChangeListener(TabbedKeplerGraphFrameEvent.CHANGE_TAB, this);
196        
197        // the tab has just been created and is selected for the multi composite
198        _multiCompositeSelected(true);
199    }
200
201    
202    /** This method is called whenever the tab containing the MultiCompositeActor
203     *  is selected or unselected in the frame.
204     */
205    protected void _multiCompositeSelected(boolean isSelected) {
206        _multiCompositeIsSelected = isSelected;
207    }
208
209    ///////////////////////////////////////////////////////////////////
210    ////                         protected variables               ////
211
212    /** The frame displaying the MultiCompositeActor. */
213    protected TabbedKeplerGraphFrame _frame;
214
215    ///////////////////////////////////////////////////////////////////
216    ////                         private variables                 ////
217
218    /** The MultiCompositeActor associated with this tableau. */
219    private CompositeEntity _multiCompositeActor;
220
221    /** If true, the tab displaying the MultiCompositeActor is selected. */
222    private boolean _multiCompositeIsSelected;
223      
224    ///////////////////////////////////////////////////////////////////
225    ////                         inner classes                     ////
226
227    /** Class implementing the Add Refinement menu command. */
228    public abstract class AddRefinementAction extends FigureAction {
229
230        /** Create an action. 
231         * @param title the window title
232         * @param refinementTypeName the type of sub-workflow to add
233         * @param nameQuery the string displayed in the dialog
234         */
235        public AddRefinementAction(String title, String refinementTypeName,
236                String nameQuery) {
237            super(title);
238            _title = title;
239            _refinementTypeName = refinementTypeName;
240            _nameQuery = nameQuery;
241            putValue(MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_A));
242        }
243
244        ///////////////////////////////////////////////////////////////////////////////
245        ////                            public methods                             ////
246
247        /** Perform the action. */
248        @Override
249        public void actionPerformed(ActionEvent e) {
250            super.actionPerformed(e);
251            // Dialog to ask for a refinement name.
252            Query query = new Query();
253            query.addLine(_refinementTypeName, _nameQuery, "");
254            ComponentDialog dialog = new ComponentDialog(_frame,
255                    _title, query);
256            if (dialog.buttonPressed().equals("OK")) {
257                final String pattern = query.getStringValue(_refinementTypeName);
258                // NOTE: We do not use a TransitionRefinement because we don't
259                // want the sibling input ports that come with output ports.
260                
261                // FIXME: refinementClassName() should be a method of MultiCompositeActor
262                //String refinementClassName = _case.refinementClassName();
263                String refinementClassName = "ptolemy.actor.lib.hoc.Refinement";
264                
265                String moml = "<entity name=\""
266                        + StringUtilities.escapeForXML(pattern) + "\" class=\""
267                        + refinementClassName + "\"/>";
268
269                // The following is, regrettably, copied from ModalTransitionController.
270                MoMLChangeRequest change = new MoMLChangeRequest(this, _multiCompositeActor,
271                        moml) {
272                    @Override
273                    protected void _execute() throws Exception {
274                        super._execute();
275
276                        
277                        // Mirror the ports of the container in the refinement.
278                        // Note that this is done here rather than as part of
279                        // the MoML because we have set protected variables
280                        // in the refinement to prevent it from trying to again
281                        // mirror the changes in the container.
282                        Refinement entity = (Refinement) _multiCompositeActor
283                                .getEntity(pattern);
284
285                        // Get the initial port configuration from the container.
286                        Iterator<?> ports = ((CompositeActor)entity.getContainer()).portList().iterator();
287                        Set<Port> portsToMirror = new HashSet<Port>();
288                        while (ports.hasNext()) {
289                            Port port = (Port) ports.next();
290                            
291                            // see if we should mirror the port
292                            if(mirrorPort(port)) {
293                                portsToMirror.add(port);
294                            }
295                        }
296                        
297                        MultiCompositeActor.mirrorContainerPortsInRefinement(entity, portsToMirror);
298
299                        //JGraph jgraph = _addTabbedPane(entity, true);
300                        _frame.addSubComposite(entity);
301                        //((TabbedKeplerController) _controller)._addHotKeys(jgraph);
302                    }
303                };
304
305                _multiCompositeActor.requestChange(change);
306            }
307        }
308        
309        /** Returns true if port should be mirrored. */
310        public abstract boolean mirrorPort(Port port);
311        
312        ///////////////////////////////////////////////////////////////////
313        ////                         private variables                 ////
314        
315        /** The type of the sub-workflow. */
316        private String _refinementTypeName;
317        
318        /** The string displayed in the dialog. */
319        private String _nameQuery;
320
321        /** The title of the window. */
322        private String _title;
323    }
324
325    /** Class implementing the Remove Refinement menu command. */
326    public class RemoveRefinementAction extends FigureAction {
327
328        /** Create a tabbed composite action with a label. */
329        public RemoveRefinementAction(String refinementTypeName,
330                String refinementTypePluralName) {
331            super("Remove " + refinementTypeName);
332            _refinementTypeName = refinementTypeName;
333            _refinementTypePluralName = refinementTypePluralName;
334            putValue(MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_R));
335        }
336        
337        ///////////////////////////////////////////////////////////////////////////////
338        ////                            public methods                             ////
339
340        /** Perform the action. */
341        @Override
342        public void actionPerformed(ActionEvent e) {
343            super.actionPerformed(e);
344            // Dialog to ask for a refinement name.
345            Query query = new Query();
346            List<?> refinements = _multiCompositeActor.entityList(Refinement.class);
347            if (refinements.size() < 2) {
348                MessageHandler.error("No " + _refinementTypePluralName + " to remove.");
349            } else {
350                String[] refinementNames = new String[refinements.size() - 1];
351                Iterator<?> refinementIterator = refinements.iterator();
352                int i = 0;
353                while (refinementIterator.hasNext()) {
354                    String name = ((Nameable) refinementIterator.next()).getName();
355                    if (!name.equals("default")) {
356                        refinementNames[i] = name;
357                        i++;
358                    }
359                }
360                String title = "Remove " + _refinementTypeName;
361                query.addChoice(_refinementTypeName, title, refinementNames,
362                        refinementNames[0]);
363                ComponentDialog dialog = new ComponentDialog(
364                        _frame, title, query);
365                if (dialog.buttonPressed().equals("OK")) {
366                    final String name = query.getStringValue(_refinementTypeName);
367                    String moml = "<deleteEntity name=\""
368                            + StringUtilities.escapeForXML(name) + "\"/>";
369
370                    // The following is, regrettably, copied from ModalTransitionController.
371                    MoMLChangeRequest change = new MoMLChangeRequest(this,
372                            _multiCompositeActor, moml) {
373                        @Override
374                        protected void _execute() throws Exception {
375                            super._execute();
376                            _frame.updateTabsForComposite(_multiCompositeActor);
377                            /*
378                            // Find the tabbed pane that matches the name and remove it.
379                            int count = _multiRefinementTabbedPane.getTabCount();
380                            for (int i = 0; i < count; i++) {
381                                if (name.equals(_multiRefinementTabbedPane.getTitleAt(i))) {
382                                    _multiRefinementTabbedPane.remove(i);
383                                    break;
384                                }
385                            }
386                            */
387                        }
388                    };
389                    _multiCompositeActor.requestChange(change);
390                }
391            }
392        }
393
394        ///////////////////////////////////////////////////////////////////
395        ////                         private variables                 ////
396
397        /** The type of sub-workflow. */
398        private String _refinementTypeName;
399        
400        /** The plural of the type of sub-workflow. */
401        private String _refinementTypePluralName;
402
403    }
404    
405}