Model subclass: #Spreadsheet
        instanceVariableNames: 'value sheetType '
        classVariableNames: ''
        poolDictionaries: ''
        category: 'SpreadSheets'!


!Spreadsheet methodsFor: 'accessing'!

sheetType
        "Return my sheetType"

        ^sheetType.!

sheetType: aType
        "Set the sheetType to aType"

        sheetType := aType.! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

Spreadsheet class
        instanceVariableNames: ''!


!Spreadsheet class methodsFor: 'demos'!

demo

| ss1 ss2 ss3 ss4 ssArray|
ss1 := Spreadsheet type: #sum.
ss1 cell: 1 put: 15.     "Puts 15 into cell 1"
ss1 cell: 2 put: 30.
ss1 cell: 3 put: 75.2.
Transcript cr; show: ss1 value printString.  "Prints the sum (120.2) to the Transcript"
Transcript cr; show: (ss1 cell: 1) printString. "Prints 15 to the Transcript"

ss2 := Spreadsheet type: #average.
ss2 cells: #(24 76.4 19 37 89).  "Fill five cells of the spreadsheets with these values"
Transcript cr; show: ss2 value printString. "Prints the average (49.08) to the Transcript"

"Create a spreadsheet that sums the values of two Spreadsheets"
ssArray := Array with: ss1 with: ss2.
ss3 := Spreadsheet type: #sum with: ssArray.
Transcript cr; show: ss3 value printString. "Prints the sum 120.2 + 49.08 to the Transcript"
ss1 cell: 4 put: 89.9.  "Add a fourth cell to ss1."
Transcript cr; show: ss3 value printString. "Prints the updated values of ss1 and ss2"

"Create a fourth spreadsheet whose value is the average of ss1 cell 2 and ss2 cell 3"
ss4 := Spreadsheet type: #average with: ss1 cell: 2 with: ss2 cell: 3.
Transcript cr; show: ss4 value printString. "Prints the average of 30 and 19 (24.5)"! !

!Spreadsheet class methodsFor: 'instance-creation'!

type: aType
        "Call CellSheet with aType. Return what it returns"

        ^CellSheet type: aType.!

type: aType with: anArray
        "Call SpreadsheetSumSheet with aType and anArray. Return what it returns"

        ^SpreadsheetSumSheet type: aType with: anArray.!

type: aType with: sheet1 cell: cell1 with: sheet2 cell: cell2
        "Call CellAverageSheet with aType sheet1, cell1, sheet2, cell2.
        Return what it returns"

        ^CellAverageSheet type: aType with: sheet1 cell: cell1 with: sheet2 cell: cell2.! !

Spreadsheet subclass: #SpreadsheetSumSheet
        instanceVariableNames: 'sheets '
        classVariableNames: ''
        poolDictionaries: ''
        category: 'SpreadSheets'!


!SpreadsheetSumSheet methodsFor: 'accessing'!

update: anAspect
        "Recalculate my value when I receive the message #value"

        ( anAspect == #value )
                ifTrue: [ self calculate ].!

value
        "Return my value"

        ^value.!

value: aValue
        "Set my value to aValue. Announce change of value to my dependents"

        value := aValue.
        self changed: #value.! !

!SpreadsheetSumSheet methodsFor: 'initialize-release'!

with: anArray
        "Add all the sheets in anArray to sheets, notify each sheet of my dependence
        calculate my value"

        sheets := OrderedCollection new.
        anArray do:
                [ :each | sheets add: each.
                        each addDependent: self ].
        self calculate.! !

!SpreadsheetSumSheet methodsFor: 'enumerating'!

calculate
        "calculate my value, sum the values of all spreadsheets"

        | total |
        total := 0.
        sheets do: [ :each | total := total + each value ].
        self value: total.! !

!SpreadsheetSumSheet methodsFor: 'view access'!

cellCollection
        "Return the values of each of the spreadsheets"

        ^sheets collect: [ :each | each value ].! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

SpreadsheetSumSheet class
        instanceVariableNames: ''!


!SpreadsheetSumSheet class methodsFor: 'instance-creation'!

type: aType with: anArray
        "Create a new one of me, initialize it, set my type, open a window for myself, return myself"

        | aSheet |
        aSheet := super new with: anArray.
        aSheet sheetType: aType.
        SpreadsheetViewContainer new openOn: aSheet labeled: aType.
        ^aSheet.! !

Spreadsheet subclass: #CellAverageSheet
        instanceVariableNames: 'cells '
        classVariableNames: ''
        poolDictionaries: ''
        category: 'SpreadSheets'!


!CellAverageSheet methodsFor: 'initialize-release'!

with: sheet1 cell: cell1 with: sheet2 cell: cell2
        "Add cell1 & cell2 to cells, notify each cell of my dependence, calculate my value"

        cells := OrderedCollection new.
        cells add: (sheet1 aCell: cell1).
        ( sheet1 aCell: cell1 ) addDependent: self.
        cells add: (sheet2 aCell: cell2).
        ( sheet2 aCell: cell2 ) addDependent: self.
        self calculate.! !

!CellAverageSheet methodsFor: 'testing'!

size
        "Return the size of collection cells"
        ^cells size.! !

!CellAverageSheet methodsFor: 'enumerating'!

calculate
        "calculate my value, total the values of all the cells, find their average"

        | total |
        total := 0.
        cells do: [ :each | total := total + each value ].
        self value: total / self size.! !

!CellAverageSheet methodsFor: 'accessing'!

update: anAspect
        "Recalculate my value when I receive the message #value"

        ( anAspect == #value )
                ifTrue: [ self calculate ].!

value
        "Return my value as a float"

        ^value asFloat.!

value: aValue
        "Set my value to aValue. Announce change of value to my dependents"

        value := aValue.
        self changed: #value.! !

!CellAverageSheet methodsFor: 'view access'!

cellCollection
        "Return the values of each of the cells"

        ^cells collect: [ :each | each value ].! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

CellAverageSheet class
        instanceVariableNames: ''!


!CellAverageSheet class methodsFor: 'instance-creation'!

type: aType with: sheet1 cell: cell1 with: sheet2 cell: cell2
        "Create a new one of me, initialize it, set my type, open a window for myself, return myself"

        | aSheet |
        aSheet := super new with: sheet1 cell: cell1 with: sheet2 cell: cell2.
        aSheet sheetType: aType.
        SpreadsheetViewContainer new openOn: aSheet labeled: aType.
        ^aSheet.! !
        
ModelValueViewContainer subclass: #SpreadsheetViewContainer
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Model Views'!


!SpreadsheetViewContainer methodsFor: 'view creation'!

buildViewOn: aSpreadsheet
        "Build a window for aSpreadsheet with the following ViewOn"

        self addCellListViewOn: aSpreadsheet.
        self addValueViewOn: aSpreadsheet.
        self addTypeViewOn: aSpreadsheet.!

makeListOn: aSpreadsheet showing: getListMsg updateFor: aspect
        "Make a list with the following properties for aSpreadsheet"
        | listView |
        listView := SelectionInListView
                on: aSpreadsheet
                printItems: true
                oneItem: false
                aspect: aspect
                change: nil
                list: getListMsg
                menu: nil
                initialSelection: nil
                useIndex: true.
        ^listView.! !

!SpreadsheetViewContainer methodsFor: 'view layout'!

addCellListViewOn: aSpreadsheet
        "Add a ViewOn that displays the value of the Spreadsheet"

        | itemView listArea |
        itemView := self
                makeListOn: aSpreadsheet
                showing: #cellCollection
                updateFor: #value.
        listArea := self constraintFrame: 0 @ (1/10) corner: 1 @ (9/10).
        self addListView: itemView in: listArea.
        ^itemView.!

addListView: listView in: area
        "Create a wrapper with scroll bars"

        | wrapper |
        wrapper := BorderDecorator on: listView.
        self add: wrapper in: area.!

addTypeViewOn: aSpreadsheet
        "Add a ViewOn that displays the sheetType of the Spreadsheet"

        | itemView itemArea |
        itemView := self makeDisplayBoxOn: aSpreadsheet for: #sheetType.
        itemArea := self constraintFrame: 0 @ 0 corner: 1 @ (1/10).
        self addView: itemView in: itemArea.!

addValueViewOn: aSpreadsheet
        "Add a ViewOn that displays the values of each Spreadsheet cell in a list"
        | itemView itemArea |
        itemView := self makeDisplayBoxOn: aSpreadsheet for: #value.
        itemArea := self constraintFrame: 0 @ (9/10) corner: 1 @ 1.
        self addView: itemView in: itemArea.! !