001    // This file is part of the program FRYSK.
002    //
003    // Copyright 2005, Red Hat Inc.
004    //
005    // FRYSK is free software; you can redistribute it and/or modify it
006    // under the terms of the GNU General Public License as published by
007    // the Free Software Foundation; version 2 of the License.
008    //
009    // FRYSK is distributed in the hope that it will be useful, but
010    // WITHOUT ANY WARRANTY; without even the implied warranty of
011    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012    // General Public License for more details.
013    // 
014    // You should have received a copy of the GNU General Public License
015    // along with FRYSK; if not, write to the Free Software Foundation,
016    // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
017    // 
018    // In addition, as a special exception, Red Hat, Inc. gives You the
019    // additional right to link the code of FRYSK with code not covered
020    // under the GNU General Public License ("Non-GPL Code") and to
021    // distribute linked combinations including the two, subject to the
022    // limitations in this paragraph. Non-GPL Code permitted under this
023    // exception must only link to the code of FRYSK through those well
024    // defined interfaces identified in the file named EXCEPTION found in
025    // the source code files (the "Approved Interfaces"). The files of
026    // Non-GPL Code may instantiate templates or use macros or inline
027    // functions from the Approved Interfaces without causing the
028    // resulting work to be covered by the GNU General Public
029    // License. Only Red Hat, Inc. may make changes or additions to the
030    // list of Approved Interfaces. You must obey the GNU General Public
031    // License in all respects for all of the FRYSK code and other code
032    // used in conjunction with FRYSK except the Non-GPL Code covered by
033    // this exception. If you modify this file, you may extend this
034    // exception to your version of the file, but you are not obligated to
035    // do so. If you do not wish to provide this exception without
036    // modification, you must delete this exception statement from your
037    // version and license this file solely under the GPL without
038    // exception.
039    
040    package frysk.gui.srcwin;
041    
042    import java.util.Iterator;
043    import java.util.LinkedList;
044    
045    import org.gnu.gtk.CellRenderer;
046    import org.gnu.gtk.CellRendererText;
047    import org.gnu.gtk.DataColumn;
048    import org.gnu.gtk.DataColumnObject;
049    import org.gnu.gtk.DataColumnString;
050    import org.gnu.gtk.ListStore;
051    import org.gnu.gtk.Menu;
052    import org.gnu.gtk.MenuItem;
053    import org.gnu.gtk.SelectionMode;
054    import org.gnu.gtk.TreeIter;
055    import org.gnu.gtk.TreePath;
056    import org.gnu.gtk.TreeView;
057    import org.gnu.gtk.TreeViewColumn;
058    import org.gnu.gtk.event.MenuItemEvent;
059    import org.gnu.gtk.event.MenuItemListener;
060    import org.gnu.gtk.event.MouseEvent;
061    import org.gnu.gtk.event.MouseListener;
062    import org.gnu.gtk.event.TreeSelectionEvent;
063    import org.gnu.gtk.event.TreeSelectionListener;
064    
065    import frysk.gui.sessions.WatchListListener;
066    import frysk.rt.UpdatingDisplayValue;
067    
068    public class VariableWatchView extends TreeView implements
069            TreeSelectionListener, WatchListListener {
070    
071        public static final String VAR_WATCHES = "variable_watches";
072    
073        public interface WatchViewListener {
074            void variableSelected(UpdatingDisplayValue disp);
075        }
076    
077        private DataColumn[] traceColumns;
078    
079        private LinkedList observers;
080    
081        private SourceView view;
082    
083        private VariableWatchViewListener listener;
084    
085        private ListStore model;
086    
087        private int treeSize = 0;
088    
089        public VariableWatchView() {
090            super();
091    
092            this.setName("varWatchView");
093            this.getAccessible().setName("varWatchView_variableWatchList");
094            this.getAccessible().setDescription(
095                    "A list of all the variables that are being watched");
096    
097            this.observers = new LinkedList();
098    
099            traceColumns = new DataColumn[] { new DataColumnString(),
100                    new DataColumnString(), new DataColumnObject() };
101    
102            this.model = new ListStore(traceColumns);
103    
104            this.setModel(model);
105    
106            TreeViewColumn column = new TreeViewColumn();
107            column.setTitle("Name");
108            CellRenderer renderer = new CellRendererText();
109            column.packStart(renderer, true);
110            column.addAttributeMapping(renderer, CellRendererText.Attribute.TEXT,
111                    traceColumns[0]);
112            this.appendColumn(column);
113    
114            column = new TreeViewColumn();
115            column.setTitle("Value");
116            renderer = new CellRendererText();
117            column.packStart(renderer, true);
118            column.addAttributeMapping(renderer, CellRendererText.Attribute.TEXT,
119                    traceColumns[1]);
120            this.appendColumn(column);
121    
122            this.getSelection().setMode(SelectionMode.SINGLE);
123    
124            this.getSelection().addListener(this);
125            listener = new VariableWatchViewListener();
126            this.addListener(listener);
127        }
128    
129        public void setView(SourceView sv) {
130            this.view = sv;
131        }
132    
133        /**
134         * Adds a listener to this list of observers.
135         * 
136         * @param listener
137         *                The Listener to be added.
138         */
139        public void addObserver(WatchViewListener listener) {
140            this.observers.add(listener);
141        }
142    
143        /**
144         * Notifies all observers of the selected Variable
145         * 
146         * @param disp
147         *                The selected Variable.
148         */
149        private void notifyListeners(UpdatingDisplayValue disp) {
150            Iterator iter = this.observers.iterator();
151    
152            while (iter.hasNext())
153                ((WatchViewListener) iter.next()).variableSelected(disp);
154        }
155    
156        /**
157         * Called when the selection in this TreeView has changed.
158         */
159        public void selectionChangedEvent(TreeSelectionEvent arg0) {
160            TreeIter selected = null;
161    
162            try {
163                selected = this.model
164                        .getIter(this.getSelection().getSelectedRows()[0]);
165            } catch (ArrayIndexOutOfBoundsException aoobe) {
166                return;
167            }
168    
169            this.notifyListeners((UpdatingDisplayValue) this.model.getValue(
170                    selected, (DataColumnObject) this.traceColumns[2]));
171        }
172    
173        /**
174         * Generates a new right-click menu when a row with a Variable is
175         * clicked.
176         * 
177         * @param event
178         *                The click event
179         */
180        public void clickedOnVariable(MouseEvent event) {
181            Menu m = new Menu();
182            MenuItem removeItem = new MenuItem("Remove from Variable Watches",
183                    false);
184            m.append(removeItem);
185            removeItem.setSensitive(true);
186            removeItem.addListener(new MenuItemListener() {
187                public void menuItemEvent(MenuItemEvent arg0) {
188                    handleClick();
189                }
190            });
191    
192            m.showAll();
193            m.popup();
194        }
195    
196        /**
197         * Finds the selected Variable and tells the View view to remove it,
198         * which will then update this TreeView.
199         */
200        private void handleClick() {
201            TreePath[] paths = this.getSelection().getSelectedRows();
202    
203            UpdatingDisplayValue disp = (UpdatingDisplayValue) model.getValue(model
204                    .getIter(paths[0]), (DataColumnObject) traceColumns[2]);
205            this.view.removeDisplay(disp);
206        }
207    
208        /*
209         * Fired whenever a display is added (non-Javadoc)
210         * 
211         * @see frysk.gui.sessions.WatchListListener#variableWatchAdded(frysk.rt.UpdatingDisplayValue)
212         */
213        public void variableWatchAdded(UpdatingDisplayValue disp) {
214            TreeIter iter = this.model.appendRow();
215            this.treeSize++;
216    
217            this.model.setValue(iter, (DataColumnString) this.traceColumns[0], disp
218                    .getName());
219            this.model.setValue(iter, (DataColumnString) this.traceColumns[1], ""
220                    + (disp.isAvailable() ? disp.getValue().toPrint()
221                            : "<unavailable>"));
222            this.model
223                    .setValue(iter, (DataColumnObject) this.traceColumns[2], disp);
224            this.showAll();
225        }
226    
227        /*
228         * Fired when a display updates, provides the display that was updated
229         * (non-Javadoc)
230         * 
231         * @see frysk.gui.srcwin.VariableWatchListener#variableWatchChanged(java.util.Iterator)
232         */
233        public void variableWatchChanged(UpdatingDisplayValue disp) {
234            TreeIter iter = this.model.getFirstIter();
235            // Compare the display against all the displays in the list
236            while (iter != null) {
237                UpdatingDisplayValue stored = (UpdatingDisplayValue) this.model
238                        .getValue(iter, (DataColumnObject) this.traceColumns[2]);
239    
240                // If this is the display that's being updated, refresh it's
241                    // value and
242                // return
243                if (stored.equals(disp)) {
244                    this.model.setValue(iter,
245                            (DataColumnString) this.traceColumns[1], ""
246                                    + (disp.isAvailable() ? disp.getValue()
247                                            .toPrint() : "<unavailable>"));
248                    return;
249                }
250    
251                // Otherwise advance to the next iterator
252                iter = iter.getNextIter();
253            }
254    
255            // We should not get here
256            System.err.println("Error: Recieved an update for a display that was"
257                    + "not already in the watched view");
258        }
259    
260        /*
261         * Fired whenever a watch is deleted (non-Javadoc)
262         * 
263         * @see frysk.gui.sessions.WatchListListener#variableWatchDeleted(frysk.rt.UpdatingDisplayValue)
264         */
265        public void variableWatchDeleted(UpdatingDisplayValue disp) {
266            TreeIter iter = this.model.getFirstIter();
267            while (iter != null) {
268                UpdatingDisplayValue stored = (UpdatingDisplayValue) this.model
269                        .getValue(iter, (DataColumnObject) this.traceColumns[2]);
270    
271                // If this is the display, remove it
272                if (stored.equals(disp)) {
273                    this.model.removeRow(iter);
274                    return;
275                }
276    
277                // Else advance to the next row
278                iter = iter.getNextIter();
279            }
280    
281            // Should not get here
282            System.err.println("Error: Recieved a delete event for a display"
283                    + "that we did not have");
284        }
285    
286        /**
287         * Checks for right-clicks.
288         */
289        private class VariableWatchViewListener implements MouseListener {
290    
291            public boolean mouseEvent(MouseEvent event) {
292                if (!event.isOfType(MouseEvent.Type.BUTTON_PRESS))
293                    return false;
294    
295                if (event.getButtonPressed() == MouseEvent.BUTTON3) {
296                    clickedOnVariable(event);
297                }
298    
299                return false;
300            }
301        }
302    
303    }