"======================================================================
|
|   Smalltalk GUI generic inspectors
|
|   $Revision: 1.7.5$
|   $Date: 2000/05/28 16:56:52$
|   $Author: pb$
|
 ======================================================================"


"======================================================================
|
| Copyright 1988-92, 1994-95, 1999, 2000 Free Software Foundation, Inc.
| Written by Brad Diller.
|
| This file is part of GNU Smalltalk.
|
| GNU Smalltalk is free software; you can redistribute it and/or modify it
| under the terms of the GNU General Public License as published by the Free
| Software Foundation; either version 2, or (at your option) any later version.
| 
| GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
| FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
| details.
| 
| You should have received a copy of the GNU General Public License along with
| GNU Smalltalk; see the file COPYING.  If not, write to the Free Software
| Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
|
 ======================================================================
"

GuiData subclass:  #GenericInspector
     instanceVariableNames: 'theObject instVars currentVariable '
     classVariableNames: ''
     poolDictionaries: ''
     category: 'Graphics-Browser'
!

!GenericInspector methodsFor: 'initializing'!

inspectMenu: listView
"Initialize menu for variable list pane"
    ^((PopupMenu new: listView label: '')
	selectors: #(('Inspect' evalAndInspectResult: listView))
	receiver: self
	argument: listView).
!

openOn: anObject
    | topView pane |
    topView := BrowserShell new: ('%1 for %2'
	bindWith: self species name
	with: (anObject class nameIn: Namespace current)).

    topView data: self.
    topView blox x: 0 y: 330 width: 300 height: 100.

    pane := Form new: 'forms' in: topView.
    topView addChildView: pane.

    self openOn: anObject in: pane.
    topView display.
!

openOn: anObject in: pane
    "Initialize Inspector and open an Inspector window on anObject"
    | listView textView  container |
    "Initialize instance variable, instVars, which governs display of
     variable list pane"

    instVars := self instVarsSortBlock
	ifNil: [ OrderedCollection new ]
	ifNotNil: [ :block | SortedCollection sortBlock: block ].

    self setInstanceVars: anObject.
    "Create a Form manager which will contain the variable and text pane"
    container := pane blox. 
    "Create a text window and position it in first third of window"
    pane addChildView: ( (listView := PList new: 'InstanceVars' in: pane)
			     initialize;
			     data: self;
			     stateChange: #variableList; 
			     handleUserChange: #variable:; 
			     listMsg: #variableList;
	   hiliteItemInitMsg: #variableName;
			     menuInit: (self inspectMenu: listView);
			     yourself).
    listView blox xPixels: 2; yPixels: 2.
    listView blox height: pane blox height; heightOffset: -4.
    listView blox width: 100; widthOffset: -4.
    "Create text pane and position it in right 2/3s of window"
    pane addChildView: ((textView := PText new: pane)
			    data: self;
			    stateChange: #text;
			    handleUserChange: #setArg:from:;
			    textMsg: #text;
			    canBeDirty: false;
			    yourself).
    textView blox yPixels: 2; xOffset: 2.
    textView blox height: pane blox height; heightOffset: -4.
    textView blox width: pane blox width - 100; widthOffset: -4.
    textView blox posHoriz: (listView blox).
    "Initialize popup for text pane"
    textView menuInit: ((PopupMenu new: textView label: '') 
			    selectors:  
       #(('Cut' gstCut) ('Copy' gstCopy) ('Paste' gstPaste) 
	() ('Clear' gstClear) () ('Line...' line) ('Find...' find) ())
			    receiver: textView
			    argument: nil;
			    selectors: #(('Do it' eval: textView) 
	('Print it' evalAndPrintResult: textView) ('Inspect' inspectValue: textView))
			    receiver: self
			    argument: textView;
			    selectors: #(() ('Accept' compileIt) 
	('Cancel' revert) () ('Close' close))
			    receiver: textView
			    argument: nil; yourself).
    self changeState: #variableList.
    Primitive updateViews.
! !

!GenericInspector methodsFor: 'private'!

currentVariableValue
    self subclassResponsibility
!

currentVariable: obj
    self subclassResponsibility
!

instVarsSortBlock
    "nil = use OrderedCollection, else a block to be used as instVars'
     sort block."
    ^nil
!

setInstanceVars: anObject
"Store a string representation of the inspected object, anObject, in instVars. 
The first string is self.  The subsequent values are the object's complete set
of instance variables names.  If the object is a variable class, append 
numerical indices from one to number of indexed variables"
    self subclassResponsibility
! !

!GenericInspector methodsFor: 'accessing'!

text
"Return string representation of currently selected instance or indexed 
variable"
    ^currentVariable == 0
	ifTrue: [ '' ]
	ifFalse: [ self currentVariableValue printString ]
!

variable: assoc
"Set variable list index to 'index'. Record state change and update windows"
    currentVariable := assoc key.
    self changeState: #text.
    Primitive updateViews.
!

variableName
    ^currentVariable
!

variableList
"Return list of variable names displayed in the variable list pane"
    ^instVars
! !

!GenericInspector methodsFor: 'text view menu'!

eval: aView
    "Invoked from text pane popup.  Evaluate selected expression in text pane"
    | pos aStream text |
    text := aView blox getSelection.
    (text isNil or: [text size = 0]) ifTrue: [^aView beep].
    aStream := WriteStream on: (String new: 0).
    theObject class evaluate: text to: theObject.
!

inspectValue: aView
    "Open an inspector for evaluated selected expression.  If selected expression 
    contains parsing error(s), the error description is selected and printed at end
    of selection"
    | obj text |
    text := aView blox getSelection.
    (text isNil or: [text size = 0]) ifTrue: [^aView beep].
    obj := theObject class evaluate: text to: theObject ifError: 
	[:fname :lineNo :errorString |
	    aView displayError: errorString. ^nil].

    obj inspect.
!

evalAndPrintResult: aView
    "Print result of evaluation of selected expression to its right"
    | pos result text |
    text := aView blox getSelection.
    (text isNil or: [text size = 0]) ifTrue: [^aView beep].
    result := theObject class evaluate:  text to: theObject ifError: 
	[:fname :lineNo :errorString | errorString ].

    aView blox insertTextSelection: result printString.
!

setArg: aString from: aView
    "Store result of evaluation of selected expression in selected instance or 
    indexed variable"
    | obj |

    (aString isNil or: [aString size = 0]) ifTrue: [^aView beep].
    (currentVariable isNil or: [currentVariable <= 1])
	ifTrue: [^aView beep].

    "Evaluate selected expression.  If expression contains a parsing error, the 
    description is output at end of expression and nil is returned"
    obj := theObject class evaluate: aString to: theObject ifError: 
	[:fname :lineNo :errorString |
	    aView displayError: errorString at: lineNo. ^nil ].

    self currentVariable: obj.
! !

!GenericInspector methodsFor: 'variable list menu'!

evalAndInspectResult: listView
    currentVariable isNil ifTrue: [ ^listView beep ].
    self currentVariableValue inspect.
! !

GenericInspector subclass:  #Inspector
     instanceVariableNames: 'base '
     classVariableNames: ''
     poolDictionaries: ''
     category: 'Graphics-Browser'
!

!Inspector methodsFor: 'private'!

currentVariableValue
    currentVariable == 0
	ifTrue: [ ^nil ].
    currentVariable == 1
	ifTrue: [ ^theObject ].
    ^currentVariable > base
	ifTrue: [ (theObject basicAt: currentVariable - base) ]
	ifFalse: [ (theObject instVarAt: currentVariable - 1) ].
!

currentVariable: obj
    currentVariable > base
	ifTrue: [ theObject basicAt: (currentVariable - base) put: obj]
	ifFalse: [theObject instVarAt: (currentVariable - 1) put: obj].
!

setInstanceVars: anObject
"Store a string representation of the inspected object, anObject, in instVars. 
The first string is self.  The subsequent values are the object's complete set
of instance variables names.  If the object is a variable class, append 
numerical indices from one to number of indexed variables"
    | instVarNames |
    theObject := anObject.
    currentVariable := nil.
    instVars add: 'self'.
    instVarNames := theObject class allInstVarNames.
    1 to: instVarNames size do:
	[ :x | instVars add: (instVarNames at: x) asString ].
    base := instVars size.
    theObject class isVariable
	ifTrue: [ 1 to: theObject validSize do:
		      [ :x | instVars add: x printString]].
! !

! Object methodsFor: 'debugging' !

basicInspect
    "Open an Inspector window on self"
    Inspector new openOn: self
!

inspect
    "Open an inspection window on self -- by default, the same Inspector used
     in #basicInspect."
    Inspector new openOn: self
!

inspect: pane
    "Open an Inspector window on self"
    Inspector new openOn: self in: pane
! !

