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 }