1"====================================================================== 2| 3| Smalltalk GUI generic inspectors 4| 5| 6 ======================================================================" 7 8"====================================================================== 9| 10| Copyright 1992,94,95,99,2000,2001,2002,2003 Free Software Foundation, Inc. 11| Written by Brad Diller and Paolo Bonzini. 12| 13| This file is part of GNU Smalltalk. 14| 15| GNU Smalltalk is free software; you can redistribute it and/or modify it 16| under the terms of the GNU General Public License as published by the Free 17| Software Foundation; either version 2, or (at your option) any later version. 18| 19| GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT 20| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 21| FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 22| details. 23| 24| You should have received a copy of the GNU General Public License along with 25| GNU Smalltalk; see the file COPYING. If not, write to the Free Software 26| Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 27| 28 ====================================================================== 29" 30 31 32 33GuiData subclass: Inspector [ 34 | listView textView topView fieldList fieldLists diveList | 35 36 <comment: nil> 37 <category: 'Graphics-Browser'> 38 39 text [ 40 "Return string representation of currently selected instance or indexed 41 variable" 42 43 <category: 'accessing'> 44 fieldList currentField == 0 ifTrue: [^'']. 45 ^fieldList currentFieldString 46 ] 47 48 object [ 49 <category: 'accessing'> 50 ^textView object 51 ] 52 53 object: anObject [ 54 <category: 'accessing'> 55 textView object: anObject. 56 fieldLists do: [:each | each value: anObject]. 57 self changeState: #fieldList. 58 self changeState: #text. 59 Primitive updateViews 60 ] 61 62 fields [ 63 "Return list of variable names displayed in the variable list pane" 64 65 <category: 'accessing'> 66 ^fieldList fields 67 ] 68 69 currentField: field [ 70 <category: 'accessing'> 71 fieldList currentField: field. 72 self changeState: #text. 73 Primitive updateViews 74 ] 75 76 currentField [ 77 <category: 'accessing'> 78 ^fieldList currentField 79 ] 80 81 fieldLists [ 82 <category: 'initializing'> 83 ^fieldLists 84 ] 85 86 fieldLists: aCollection [ 87 <category: 'initializing'> 88 fieldLists := aCollection. 89 self fieldList: aCollection first value 90 ] 91 92 fieldList: aFieldList [ 93 <category: 'initializing'> 94 fieldList := aFieldList. 95 fieldList inspector: self. 96 textView isNil 97 ifFalse: 98 [textView object: fieldList value. 99 listView menuInit: ((fieldList inspectMenu: listView) 100 selectors: #(#() #('Dive' #dive) #('Pop' #pop) #('Browse class' #browse) #()) 101 receiver: self 102 argument: nil). 103 self initFieldListsMenu. 104 self changeState: #fieldList. 105 self changeState: #text. 106 Primitive updateViews] 107 ] 108 109 initFieldListsMenu [ 110 <category: 'initializing'> 111 fieldLists do: 112 [:each | 113 listView menu selectors: 114 { 115 {each key. 116 #fieldList:. 117 each value}} 118 receiver: self] 119 ] 120 121 open [ 122 <category: 'initializing'> 123 | pane | 124 topView := BrowserShell 125 new: 'Inspecting %1%2' % 126 {fieldList value isClass 127 ifFalse: [fieldList value class article , ' '] 128 ifTrue: ['']. 129 fieldList value class nameIn: Namespace current}. 130 topView data: self. 131 topView blox 132 x: 20 133 y: 330 134 width: 300 135 height: 100. 136 pane := Form new: 'forms' in: topView. 137 topView addChildView: pane. 138 self openIn: pane menuName: 'Edit'. 139 topView display 140 ] 141 142 openIn: pane [ 143 <category: 'initializing'> 144 self openIn: pane menuName: 'Edit' 145 ] 146 147 openIn: pane menuName: label [ 148 "Initialize Inspector and open an Inspector window on anObject" 149 150 "Initialize instance variable, fields, which governs display of 151 variable list pane" 152 153 <category: 'initializing'> 154 "Create a Form manager which will contain the variable and text pane" 155 156 | listWidth container | 157 container := pane blox. 158 listWidth := pane blox width // 3 min: 100. 159 160 "Create a text window and position it in first third of window" 161 pane addChildView: ((listView := PList new: 'InstanceVars' in: pane) 162 initialize; 163 data: self; 164 stateChange: #fieldList; 165 handleUserChange: #currentField:; 166 listMsg: #fields; 167 selectionMsg: #currentField; 168 yourself). 169 (listView blox) 170 width: listWidth height: pane blox height; 171 inset: 2. 172 173 "Create text pane and position it in right 2/3s of window" 174 pane addChildView: ((textView := PText new: pane) 175 data: self; 176 stateChange: #text; 177 handleUserChange: #setArg:from:; 178 textMsg: #text; 179 canBeDirty: false; 180 setEvaluationKeyBindings; 181 object: fieldList value; 182 yourself). 183 (textView blox) 184 width: pane blox width - listWidth height: pane blox height; 185 inset: 2. 186 textView blox posHoriz: listView blox. 187 "Initialize popup for text pane" 188 textView menuInit: ((PopupMenu new: textView label: label) 189 selectors: #(#('Cut' #gstCut) #('Copy' #gstCopy) #('Paste' #gstPaste) #() #('Clear' #gstClear) #() #('Line...' #line) #('Find...' #find)) 190 receiver: textView 191 argument: nil; 192 selectors: #(#() #('Do it' #eval: #textView) #('Print it' #evalAndPrintResult: #textView) #('Inspect' #inspectValue: #textView)) 193 receiver: self 194 argument: textView; 195 selectors: #(#() #('Accept' #compileIt) #('Cancel' #revert) #() #('Close' #close)) 196 receiver: textView 197 argument: nil; 198 yourself). 199 self fieldLists: self fieldLists. 200 self changeState: #fieldList. 201 Primitive updateViews 202 ] 203 204 browse [ 205 <category: 'list view menu'> 206 ClassBrowser new openOn: self object class asClass 207 ] 208 209 dive [ 210 <category: 'list view menu'> 211 diveList isNil ifTrue: [diveList := OrderedCollection new]. 212 diveList addLast: fieldLists. 213 self fieldLists: fieldList currentFieldValue inspectorFieldLists 214 ] 215 216 pop [ 217 <category: 'list view menu'> 218 diveList isNil ifTrue: [^self]. 219 diveList isEmpty ifTrue: [^self]. 220 self fieldLists: diveList removeLast 221 ] 222 223 eval: aView [ 224 "Invoked from text pane popup. Evaluate selected expression in text pane" 225 226 <category: 'text view menu'> 227 | pos aStream text | 228 text := aView blox getSelection. 229 (text isNil or: [text size = 0]) ifTrue: [^aView beep]. 230 aStream := WriteStream on: (String new: 0). 231 fieldList value class evaluate: text to: fieldList value 232 ] 233 234 evalAndPrintResult: aView [ 235 "Print result of evaluation of selected expression to its right" 236 237 <category: 'text view menu'> 238 | pos result text | 239 text := aView blox getSelection. 240 (text isNil or: [text size = 0]) ifTrue: [^aView beep]. 241 result := fieldList value class 242 evaluate: text 243 to: fieldList value 244 ifError: [:fname :lineNo :errorString | errorString]. 245 aView blox insertTextSelection: result printString 246 ] 247 248 inspectValue: aView [ 249 "Open an inspector for evaluated selected expression. If selected expression 250 contains parsing error(s), the error description is selected and printed at end 251 of selection" 252 253 <category: 'text view menu'> 254 | obj text | 255 text := aView blox getSelection. 256 (text isNil or: [text size = 0]) ifTrue: [^aView beep]. 257 obj := fieldList value class 258 evaluate: text 259 to: fieldList value 260 ifError: 261 [:fname :lineNo :errorString | 262 aView displayError: errorString. 263 ^nil]. 264 obj inspect 265 ] 266 267 setArg: aString from: aView [ 268 "Store result of evaluation of selected expression in selected instance or 269 indexed variable" 270 271 <category: 'text view menu'> 272 | obj | 273 (aString isNil or: [aString size = 0]) ifTrue: [^aView beep]. 274 fieldList currentField <= 1 ifTrue: [^aView beep]. 275 276 "Evaluate selected expression. If expression contains a parsing error, the 277 description is output at end of expression and nil is returned" 278 obj := fieldList value class 279 evaluate: aString 280 to: fieldList value 281 ifError: 282 [:fname :lineNo :errorString | 283 aView displayError: errorString at: lineNo. 284 ^nil]. 285 fieldList currentFieldValue: obj 286 ] 287] 288 289 290 291ValueHolder subclass: InspectorFieldList [ 292 | inspector fields currentField | 293 294 <category: 'Graphics-Browser'> 295 <comment: nil> 296 297 evalAndInspectResult: listView [ 298 <category: 'field list menu'> 299 currentField == 0 ifTrue: [^listView beep]. 300 self currentFieldValue inspect 301 ] 302 303 inspector [ 304 <category: 'private'> 305 ^inspector 306 ] 307 308 inspector: anInspector [ 309 <category: 'private'> 310 inspector := anInspector 311 ] 312 313 inspectMenu: listView [ 314 "Initialize menu for variable list pane" 315 316 <category: 'private'> 317 ^(PopupMenu new: listView) 318 selectors: #(#('Inspect' #evalAndInspectResult: #listView)) 319 receiver: self 320 argument: listView 321 ] 322 323 currentField [ 324 <category: 'private'> 325 ^currentField 326 ] 327 328 currentField: assoc [ 329 "Set variable list index to 'index'." 330 331 <category: 'private'> 332 currentField := assoc key 333 ] 334 335 currentFieldValue: obj [ 336 <category: 'private'> 337 self subclassResponsibility 338 ] 339 340 currentFieldValue [ 341 <category: 'private'> 342 self subclassResponsibility 343 ] 344 345 currentFieldString [ 346 <category: 'private'> 347 ^[self currentFieldValue printString] on: Error 348 do: [:ex | ex return: '[%1 exception raised while printing item]' % {ex class}] 349 ] 350 351 fieldsSortBlock [ 352 "nil = use OrderedCollection, else a block to be used as fields' 353 sort block." 354 355 <category: 'private'> 356 ^nil 357 ] 358 359 fields [ 360 <category: 'private'> 361 ^fields 362 ] 363 364 value: anObject [ 365 <category: 'private'> 366 super value: anObject. 367 fields := self fieldsSortBlock ifNil: [OrderedCollection new] 368 ifNotNil: [:block | SortedCollection sortBlock: block]. 369 currentField := 0. 370 self computeFieldList: anObject 371 ] 372 373 computeFieldList: anObject [ 374 "Store a string representation of the inspected object, anObject, in fields. 375 The first string is self. The subsequent values are the object's complete set 376 of instance variables names. If the object is a variable class, append 377 numerical indices from one to number of indexed variables" 378 379 <category: 'private'> 380 self subclassResponsibility 381 ] 382] 383 384 385 386InspectorFieldList subclass: ObjectInspectorFieldList [ 387 | base | 388 389 <category: 'Graphics-Browser'> 390 <comment: nil> 391 392 currentFieldValue: obj [ 393 <category: 'accessing'> 394 currentField > base 395 ifTrue: [self value basicAt: currentField - base put: obj] 396 ifFalse: [self value instVarAt: currentField - 1 put: obj] 397 ] 398 399 currentFieldValue [ 400 <category: 'accessing'> 401 currentField == 0 ifTrue: [^nil]. 402 currentField == 1 ifTrue: [^self value]. 403 ^currentField > base 404 ifTrue: [self value basicAt: currentField - base] 405 ifFalse: [self value instVarAt: currentField - 1] 406 ] 407 408 computeFieldList: anObject [ 409 "Store a string representation of the inspected object, anObject, in fields. 410 The first string is self. The subsequent values are the object's complete 411 set of instance variables names. If the object is a variable class, 412 append numerical indices from one to number of indexed variables" 413 414 <category: 'accessing'> 415 | instVarNames | 416 fields add: 'self'. 417 instVarNames := anObject class allInstVarNames. 418 1 to: instVarNames size 419 do: [:x | fields add: (instVarNames at: x) asString]. 420 base := fields size. 421 anObject class isVariable 422 ifTrue: [1 to: anObject validSize do: [:x | fields add: x printString]] 423 ] 424] 425 426 427 428ObjectInspectorFieldList subclass: CollectionInspectorFieldList [ 429 | array | 430 431 <category: 'Graphics-Browser'> 432 <comment: nil> 433 434 currentFieldValue: obj [ 435 <category: 'initializing'> 436 (self value isKindOf: SequenceableCollection) not 437 | (self value class == SortedCollection) 438 ifTrue: 439 [(self value) 440 remove: self currentFieldValue ifAbsent: []; 441 add: obj. 442 array := self value asArray. 443 ^self]. 444 self value at: currentField - 1 put: obj. 445 array == self value ifFalse: [array at: currentField - 1 put: obj] 446 ] 447 448 currentFieldValue [ 449 <category: 'initializing'> 450 currentField == 0 ifTrue: [^nil]. 451 currentField == 1 ifTrue: [^self value]. 452 ^array at: currentField - 1 453 ] 454 455 computeFieldList: anObject [ 456 "Use this so that the user doesn't see implementation-dependant details" 457 458 <category: 'initializing'> 459 array := (anObject isKindOf: ArrayedCollection) 460 ifFalse: [anObject asArray] 461 ifTrue: [anObject]. 462 super computeFieldList: array 463 ] 464] 465 466 467 468Object extend [ 469 470 inspectorFieldLists [ 471 <category: 'debugging'> 472 ^{'Basic' -> (BLOX.BLOXBrowser.ObjectInspectorFieldList new value: self)} 473 ] 474 475 basicInspect [ 476 "Open an Inspector window on self" 477 478 <category: 'debugging'> 479 ^(BLOX.BLOXBrowser.Inspector new) 480 fieldLists: 481 {'Basic' -> (BLOX.BLOXBrowser.ObjectInspectorFieldList new value: self)}; 482 open; 483 yourself 484 ] 485 486 inspect [ 487 "Open an inspection window on self -- by default, the same Inspector used 488 in #basicInspect." 489 490 <category: 'debugging'> 491 ^(BLOX.BLOXBrowser.Inspector new) 492 fieldLists: self inspectorFieldLists; 493 open; 494 yourself 495 ] 496 497] 498 499 500 501Collection extend [ 502 503 inspectorFieldLists [ 504 <category: 'debugging'> 505 ^ 506 {'Elements' 507 -> (BLOX.BLOXBrowser.CollectionInspectorFieldList new value: self). 508 'Basic' -> (BLOX.BLOXBrowser.ObjectInspectorFieldList new value: self)} 509 ] 510 511] 512 513