UIHelpTool.st
changeset 1669 e6ea33c843a0
parent 1665 f6427f12f173
child 1670 72af7609bb08
--- a/UIHelpTool.st	Sat Dec 21 14:11:41 2002 +0100
+++ b/UIHelpTool.st	Wed Jan 08 13:16:05 2003 +0100
@@ -1,6 +1,6 @@
 "
  COPYRIGHT (c) 1995 by eXept Software AG
-              All Rights Reserved
+	      All Rights Reserved
 
  This software is furnished under a license and may be used
  only in accordance with the terms of that license and with the
@@ -10,25 +10,36 @@
  hereby transferred.
 "
 
-
-
 "{ Package: 'stx:libtool2' }"
 
 ToolApplicationModel subclass:#UIHelpTool
-	instanceVariableNames:'specClass specSelector dictionary dictionaries modifiedHolder
-		modified listOfKeys listOfKeysModel listOfClasses
-		listOfClassesModel contentsModifiedChannel editModel editTextView'
+	instanceVariableNames:'specClass specSelector classItemList classItemModel keyItemModel
+		helpTextView modifiedHolder contentsModifiedChannel editModel'
 	classVariableNames:''
 	poolDictionaries:''
 	category:'Interface-UIPainter'
 !
 
+HierarchicalItem subclass:#ClassItem
+	instanceVariableNames:'theClass list modified'
+	classVariableNames:''
+	poolDictionaries:''
+	privateIn:UIHelpTool
+!
+
+HierarchicalItem subclass:#KeyItem
+	instanceVariableNames:'helpKey helpText modified'
+	classVariableNames:''
+	poolDictionaries:''
+	privateIn:UIHelpTool
+!
+
 !UIHelpTool class methodsFor:'documentation'!
 
 copyright
 "
  COPYRIGHT (c) 1995 by eXept Software AG
-              All Rights Reserved
+	      All Rights Reserved
 
  This software is furnished under a license and may be used
  only in accordance with the terms of that license and with the
@@ -37,8 +48,6 @@
  other person.  No title to or ownership of the software is
  hereby transferred.
 "
-
-
 !
 
 documentation
@@ -52,18 +61,19 @@
     is enabled, an active help bubble is shown at the widget's view.
 
     [instance variables:]
-        specClass       <Symbol>        class implementing the help spec
-        specSelector    <Symbol>        selector returning the help spec
-        dictionary      <Dictionary>    dictionary containing pairs of help keys/texts
-        dictionaries    <Dictionary>    dictionary containing pairs of help spec classes/help dictionaries
-        listSelection   <String>        current selected help key
-        maxCharsPerLine <Integer>       maximum number of allowed characters per text line
-        modifiedHolder  <ValueHolder>   value holder for setting as modified
-        modified        <Boolean>       flag whether the help spec was modified
+	specClass       <Symbol>        class implementing the help spec
+	specSelector    <Symbol>        selector returning the help spec
+	classItemList                   the list of classItems
+	classItemModel                  keeps the selected class
+	keyItemModel                    keeps the selected helpKey
+	modifiedHolder  <ValueHolder>   true if the editField or contents changed
+	editModel                       keeps the current helpKey
+	helpTextView                    the view which shows the helpText
+	contentsModifiedChannel         true if the helpText is modified
 
     [author:]
-        Claus Atzkern, eXept Software AG
-        Thomas Zwick, eXept Software AG
+	Claus Atzkern, eXept Software AG
+	Thomas Zwick,  eXept Software AG
 "
 ! !
 
@@ -71,48 +81,25 @@
 
 open
     ^ self openOnClass:nil.
-
-"/    |className cls|
-"/
-"/    className := Dialog request:(ClassResources string:'Open on which class ?').
-"/    className size > 0 ifTrue:[
-"/        cls := Smalltalk at:className asSymbol.
-"/        cls notNil ifTrue:[
-"/            ^ self openOnClass:cls.
-"/        ].
-"/        self warn:(ClassResources string:'No such class').
-"/    ].
-"/
-"/    "Created: / 20.5.1998 / 00:55:05 / cg"
-"/
-
-    "Modified: / 20.5.1998 / 01:06:07 / cg"
 !
 
 openOnClass:aClass
-    "opens a Help Tool on aClass"
-
-    ^ self openOnClass:aClass andSelector:#helpSpec
-
+    "opens a Help Tool on aClass
     "
-     UIHelpTool openOnClass:self
-    "
-
-    "Modified: / 20.5.1998 / 01:06:14 / cg"
+    ^ self openOnClass:aClass andSelector:#helpSpec
 !
 
 openOnClass:aClass andSelector: aSelector
-    "opens a Help Tool on aClass and aSelector"
-
-    ^self new openOnClass:aClass andSelector: aSelector
-  
+    "opens a Help Tool on aClass and aSelector
+    "
+    ^ self new openOnClass:aClass andSelector:aSelector
 ! !
 
 !UIHelpTool class methodsFor:'constants'!
 
 label
-    "returns the label; used if embedded as sub canvas in the GUI Painter or Menu Editor"
-
+    "returns the label; used if embedded as sub canvas in the GUI Painter or Menu Editor
+    "
     ^'Help'
 ! !
 
@@ -131,7 +118,7 @@
 
     <resource: #help>
 
-    ^super helpSpec addPairsFrom:#(
+    ^ super helpSpec addPairsFrom:#(
 
 #addHelpTextKey
 'Adds the key to the help spec.'
@@ -188,114 +175,101 @@
 
     ^ 
      #(#FullSpec
-        #name: #windowSpec
-        #window: 
+	#name: #windowSpec
+	#window: 
        #(#WindowSpec
-          #label: 'Help Tool'
-          #name: 'Help Tool'
-          #min: #(#Point 10 10)
-          #max: #(#Point 1160 870)
-          #bounds: #(#Rectangle 31 306 317 577)
-        )
-        #component: 
+	  #label: 'UIHelpTool'
+	  #name: 'UIHelpTool'
+	  #min: #(#Point 10 10)
+	  #max: #(#Point 1024 768)
+	  #bounds: #(#Rectangle 30 292 491 585)
+	)
+	#component: 
        #(#SpecCollection
-          #collection: #(
-           #(#VariableVerticalPanelSpec
-              #name: 'VariableVerticalPanel'
-              #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 0 1.0)
-              #component: 
-             #(#SpecCollection
-                #collection: #(
-                 #(#VariableHorizontalPanelSpec
-                    #name: 'VariableHorizontalPanel'
-                    #component: 
-                   #(#SpecCollection
-                      #collection: #(
-                       #(#SelectionInListModelViewSpec
-                          #name: 'listOfKeys'
-                          #activeHelpKey: #listOfHelpTexts
-                          #model: #listOfKeysModel
-                          #menu: #listOfKeysMenu
-                          #hasHorizontalScrollBar: true
-                          #hasVerticalScrollBar: true
-                          #miniScrollerHorizontal: true
-                          #listModel: #listOfKeys
-                          #useIndex: false
-                          #highlightMode: #line
-                        )
-                       #(#ViewSpec
-                          #name: 'Box'
-                          #component: 
-                         #(#SpecCollection
-                            #collection: #(
-                             #(#InputFieldSpec
-                                #name: 'helpKeyInputField'
-                                #layout: #(#LayoutFrame 2 0.0 2 0 -2 1.0 25 0)
-                                #activeHelpKey: #currentHelpTexts
-                                #tabable: true
-                                #model: #editModel
-                                #immediateAccept: true
-                                #acceptOnReturn: false
-                                #acceptOnTab: false
-                                #acceptOnPointerLeave: false
-                              )
-                             #(#SelectionInListModelViewSpec
-                                #name: 'listOfClasses'
-                                #layout: #(#LayoutFrame 0 0.0 27 0 0 1.0 0 1.0)
-                                #activeHelpKey: #listOfClasses
-                                #model: #listOfClassesModel
-                                #hasHorizontalScrollBar: true
-                                #hasVerticalScrollBar: true
-                                #miniScrollerHorizontal: true
-                                #miniScrollerVertical: true
-                                #listModel: #listOfClasses
-                                #useIndex: false
-                                #highlightMode: #line
-                              )
-                             )
+	  #collection: #(
+	   #(#VariableVerticalPanelSpec
+	      #name: 'PanelVrt'
+	      #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 0 1.0)
+	      #component: 
+	     #(#SpecCollection
+		#collection: #(
+		 #(#VariableHorizontalPanelSpec
+		    #name: 'PanelHrz'
+		    #component: 
+		   #(#SpecCollection
+		      #collection: #(
+		       #(#HierarchicalListViewSpec
+			  #name: 'keyItemModel'
+			  #model: #keyItemModel
+			  #menu: #keyItemMenu
+			  #hasHorizontalScrollBar: true
+			  #hasVerticalScrollBar: true
+			  #miniScrollerHorizontal: true
+			  #miniScrollerVertical: false
+			  #listModel: #keyItemListHolder
+			  #useIndex: false
+			  #highlightMode: #label
+			  #showLines: false
+			  #showIndicators: false
+			  #showLeftIndicators: false
+			  #useDefaultIcons: false
+			  #autoScrollHorizontal: false
+			)
+		       #(#ViewSpec
+			  #name: 'classItemList'
+			  #component: 
+			 #(#SpecCollection
+			    #collection: #(
+			     #(#InputFieldSpec
+				#name: 'editModel'
+				#layout: #(#LayoutFrame 0 0.0 2 0 -1 1.0 25 0)
+				#activeHelpKey: #currentHelpTexts
+				#model: #editModel
+				#immediateAccept: true
+				#acceptOnReturn: false
+				#acceptOnTab: false
+				#acceptOnLostFocus: false
+				#acceptOnPointerLeave: false
+			      )
+			     #(#SelectionInListModelViewSpec
+				#name: 'classItemModel'
+				#layout: #(#LayoutFrame 0 0.0 27 0.0 0 1.0 0 1.0)
+				#model: #classItemModel
+				#hasHorizontalScrollBar: true
+				#hasVerticalScrollBar: true
+				#miniScrollerHorizontal: true
+				#miniScrollerVertical: true
+				#autoHideScrollBars: false
+				#listModel: #classItemListHolder
+				#useIndex: false
+				#highlightMode: #label
+			      )
+			     )
                            
-                          )
-                        )
-                       )
+			  )
+			)
+		       )
                      
-                    )
-                    #handles: #(#Any 0.5 1.0)
-                  )
-                 #(#ViewSpec
-                    #name: 'Box1'
-                    #component: 
-                   #(#SpecCollection
-                      #collection: #(
-                       #(#MenuPanelSpec
-                          #name: 'helpTextMenu'
-                          #layout: #(#LayoutFrame 0 0.0 0 0 0 1.0 24 0)
-                          #menu: #helpTextMenu
-                          #textDefault: true
-                        )
-                       #(#TextEditorSpec
-                          #name: 'helpTextView'
-                          #layout: #(#LayoutFrame 0 0.0 24 0.0 0 1.0 0 1.0)
-                          #activeHelpKey: #helpTextView
-                          #tabable: true
-                          #hasHorizontalScrollBar: true
-                          #hasVerticalScrollBar: true
-                          #miniScrollerHorizontal: true
-                          #miniScrollerVertical: true
-                          #modifiedChannel: #contentsModifiedChannel
-                          #postBuildCallback: #postBuildTextView:
-                        )
-                       )
-                     
-                    )
-                  )
-                 )
+		    )
+		    #handles: #(#Any 0.607375 1.0)
+		  )
+		 #(#ArbitraryComponentSpec
+		    #name: 'helpTextView'
+		    #hasHorizontalScrollBar: true
+		    #hasVerticalScrollBar: true
+		    #miniScrollerHorizontal: true
+		    #miniScrollerVertical: true
+		    #hasBorder: false
+		    #component: #helpTextView
+		  )
+		 )
                
-              )
-              #handles: #(#Any 0.5 1.0)
-            )
-           )
+	      )
+	      #handles: #(#Any 0.679181 1.0)
+	    )
+	   )
          
-        )
+	)
       )
 !
 
@@ -315,33 +289,39 @@
 
     ^ 
      #(#FullSpec
-        #name: #windowSpecForStandAlone
-        #window: 
+	#name: #windowSpecForStandAlone
+	#window: 
        #(#WindowSpec
-          #label: 'Help Tool'
-          #name: 'Help Tool'
-          #min: #(#Point 300 300)
-          #max: #(#Point 1152 900)
-          #bounds: #(#Rectangle 83 333 796 896)
-          #menu: #menu
-        )
-        #component: 
+	  #label: 'Help Tool'
+	  #name: 'Help Tool'
+	  #min: #(#Point 300 300)
+	  #max: #(#Point 1152 900)
+	  #bounds: #(#Rectangle 11 332 509 761)
+	  #menu: #menu
+	)
+	#component: 
        #(#SpecCollection
-          #collection: #(
-           #(#UISubSpecification
-              #name: 'windowSpec'
-              #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 -26 1.0)
-              #minorKey: #windowSpec
-            )
-           #(#UISubSpecification
-              #name: 'windowSpecForInfoBar'
-              #layout: #(#LayoutFrame 0 0 -24 1 0 1 0 1)
-              #majorKey: #ToolApplicationModel
-              #minorKey: #windowSpecForInfoBar
-            )
-           )
+	  #collection: #(
+	   #(#UISubSpecification
+	      #name: 'windowSpec'
+	      #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 -50 1.0)
+	      #minorKey: #windowSpec
+	    )
+	   #(#UISubSpecification
+	      #name: 'windowSpecForCommit'
+	      #layout: #(#LayoutFrame 0 0.0 -50 1.0 0 1.0 -24 1.0)
+	      #majorKey: #ToolApplicationModel
+	      #minorKey: #windowSpecForCommit
+	    )
+	   #(#UISubSpecification
+	      #name: 'windowSpecForInfoBar'
+	      #layout: #(#LayoutFrame 0 0 -24 1 0 1 0 1)
+	      #majorKey: #ToolApplicationModel
+	      #minorKey: #windowSpecForInfoBar
+	    )
+	   )
          
-        )
+	)
       )
 ! !
 
@@ -363,24 +343,53 @@
 
     ^ 
      #(#Menu
-        #(
-         #(#MenuItem
-            #activeHelpKey: #commitOK
-            #enabled: #contentsModifiedChannel
-            #label: 'Accept'
-            #itemValue: #accept
-            #translateLabel: true
-          )
-         #(#MenuItem
-            #activeHelpKey: #commitCancel
-            #enabled: #contentsModifiedChannel
-            #label: 'Cancel'
-            #itemValue: #cancel
-            #translateLabel: true
-          )
-         )
-        nil
-        nil
+	#(
+	 #(#MenuItem
+	    #activeHelpKey: #commitOK
+	    #enabled: #contentsModifiedChannel
+	    #label: 'Accept'
+	    #itemValue: #accept
+	    #translateLabel: true
+	  )
+	 #(#MenuItem
+	    #activeHelpKey: #commitCancel
+	    #enabled: #contentsModifiedChannel
+	    #label: 'Cancel'
+	    #itemValue: #cancel
+	    #translateLabel: true
+	  )
+	 )
+	nil
+	nil
+      )
+!
+
+keyItemMenu
+    "This resource specification was automatically generated
+     by the MenuEditor of ST/X."
+
+    "Do not manually edit this!! If it is corrupted,
+     the MenuEditor may not be able to read the specification."
+
+    "
+     MenuEditor new openOnClass:UIHelpTool andSelector:#keyItemMenu
+     (Menu new fromLiteralArrayEncoding:(UIHelpTool keyItemMenu)) startUp
+    "
+
+    <resource: #menu>
+
+    ^ 
+     #(#Menu
+	#(
+	 #(#MenuItem
+	    #activeHelpKey: #deleteHelpTextKey
+	    #label: 'Delete'
+	    #itemValue: #doDelete
+	    #translateLabel: true
+	  )
+	 )
+	nil
+	nil
       )
 !
 
@@ -400,16 +409,16 @@
 
     ^ 
      #(#Menu
-        #(
-         #(#MenuItem
-            #activeHelpKey: #deleteHelpTextKey
-            #label: 'Delete'
-            #itemValue: #doDelete
-            #translateLabel: true
-          )
-         )
-        nil
-        nil
+	#(
+	 #(#MenuItem
+	    #activeHelpKey: #deleteHelpTextKey
+	    #label: 'Delete'
+	    #itemValue: #doDelete
+	    #translateLabel: true
+	  )
+	 )
+	nil
+	nil
       )
 !
 
@@ -429,164 +438,138 @@
 
     ^ 
      #(#Menu
-        #(
-         #(#MenuItem
-            #label: 'File'
-            #translateLabel: true
-            #submenu: 
-           #(#Menu
-              #(
-               #(#MenuItem
-                  #activeHelpKey: #fileLoad
-                  #label: 'Load...'
-                  #itemValue: #doLoad
-                  #translateLabel: true
-                )
-               #(#MenuItem
-                  #activeHelpKey: #fileSave
-                  #label: 'Save'
-                  #itemValue: #doSave
-                  #translateLabel: true
-                )
-               #(#MenuItem
-                  #label: '-'
-                )
-               #(#MenuItem
-                  #activeHelpKey: #fileExit
-                  #label: 'Exit'
-                  #itemValue: #closeRequest
-                  #translateLabel: true
-                )
-               )
-              nil
-              nil
-            )
-          )
-         #(#MenuItem
-            #label: 'Edit'
-            #translateLabel: true
-            #submenu: 
-           #(#Menu
-              #(
-               #(#MenuItem
-                  #activeHelpKey: #deleteHelpTextKey
-                  #label: 'Delete'
-                  #itemValue: #doDelete
-                  #translateLabel: true
-                )
-               )
-              nil
-              nil
-            )
-          )
-         #(#MenuItem
-            #activeHelpKey: #help
-            #label: 'Help'
-            #translateLabel: true
-            #startGroup: #right
-            #submenuChannel: #menuHelp
-          )
-         )
-        nil
-        nil
+	#(
+	 #(#MenuItem
+	    #label: 'File'
+	    #translateLabel: true
+	    #submenu: 
+	   #(#Menu
+	      #(
+	       #(#MenuItem
+		  #activeHelpKey: #fileLoad
+		  #label: 'Load...'
+		  #itemValue: #doLoad
+		  #translateLabel: true
+		)
+	       #(#MenuItem
+		  #activeHelpKey: #fileSave
+		  #label: 'Save'
+		  #itemValue: #doSave
+		  #translateLabel: true
+		)
+	       #(#MenuItem
+		  #label: 'New'
+		  #itemValue: #doNew
+		  #translateLabel: true
+		)
+	       #(#MenuItem
+		  #label: '-'
+		)
+	       #(#MenuItem
+		  #activeHelpKey: #fileExit
+		  #label: 'Exit'
+		  #itemValue: #closeRequest
+		  #translateLabel: true
+		)
+	       )
+	      nil
+	      nil
+	    )
+	  )
+	 #(#MenuItem
+	    #activeHelpKey: #history
+	    #label: 'History'
+	    #translateLabel: true
+	    #submenuChannel: #menuHistory
+	  )
+	 #(#MenuItem
+	    #label: 'Edit'
+	    #translateLabel: true
+	    #submenuChannel: #keyItemMenu
+	    #keepLinkedMenu: true
+	  )
+	 #(#MenuItem
+	    #activeHelpKey: #help
+	    #label: 'Help'
+	    #translateLabel: true
+	    #startGroup: #right
+	    #submenuChannel: #menuHelp
+	  )
+	 )
+	nil
+	nil
       )
 ! !
 
 !UIHelpTool methodsFor:'accessing'!
 
-dictionaries
-    "returns the dictionary of the help dictionaries of the classes having help specs"
-
-    ^dictionaries
-!
-
-dictionaries:aDictionaryOfDictionaries
-    "sets a dictionary of the help dictionaries of the classes having help specs"
-
-    (dictionaries := aDictionaryOfDictionaries) isNil ifTrue:[
-        dictionaries := Dictionary new.
-    ].
-    self updateList.
-!
+helpKey
+    "returns the helpKey as symbol or nil
+    "
+    |key|
 
-dictionary
-    "returns the dictionary of the selected class"
-
-    ^dictionary
-!
+    key := editModel value.
 
-helpKey
-    "returns the help key as symbol or nil
-    "
-    |value|
-
-    value := editModel value.
-
-    value notNil ifTrue:[
-        value := value withoutSeparators.
-        value notEmpty ifTrue:[ ^ value asSymbol ]
+    key size ~~ 0 ifTrue:[
+	key := key withoutSeparators.
+	key notEmpty ifTrue:[ ^ key asSymbol ]
     ].
     ^ nil
 !
 
 helpKey:aKey
-    "sets the help key into the selection channel in order to show the help text"
-
+    "change the helpKey without any change notification (modifiedHolder).
+    "
     |key|
 
-    aKey size ~~ 0 ifTrue:[
-        aKey isSymbol ifTrue:[
-            key := aKey
-        ] ifFalse:[
-            key := aKey withoutSeparators.
-            key notEmpty ifTrue:[ key := key asSymbol ]
-                        ifFalse:[ key := nil ].
-        ]
-    ] ifFalse:[
-        key := nil
+    self withoutModifyDo:[
+	aKey size ~~ 0 ifTrue:[
+	    key := aKey withoutSeparators.
+	    key isEmpty ifTrue:[ key := nil ]
+	] ifFalse:[
+	    key := nil
+	].
+	editModel value:key.
     ].
-    editModel value:key.            
     self cancel.
 !
 
 modified
-    "true if the helpSpec is modified, items are added, deleted or modified
+    "true if any items are added, deleted or modified
     "
-    ^ modified
+    classItemList do:[:aClassItem|
+	aClassItem modified ifTrue:[^ true].
+    ].
+    ^ false
 !
 
-modified: aBoolean
-    "true if the helpSpec is modified, items are added, deleted or modified
+modified:aBoolean
+    "true if any items are added, deleted or modified
     "
-    modified := aBoolean.
+    classItemList do:[:aClassItem| aClassItem modified:aBoolean ].
+!
+
+modifiedHolder
+    "boolean holder which is set to true if the helpKey or contents changed
+    "
+    ^ modifiedHolder
 !
 
 modifiedHolder:aValueHolder
-    "sets the value holder to true in case of modifying attributes"
-
+    "boolean holder which is set to true if the helpKey or contents changed
+    "
     modifiedHolder notNil ifTrue:[
-        modifiedHolder removeDependent:self. 
+	modifiedHolder removeDependent:self. 
     ].
     modifiedHolder := aValueHolder.
 
     modifiedHolder notNil ifTrue:[
-        modifiedHolder addDependent:self.
-    ]
-!
-
-setHelpKey:aKey
-    "set the helpKey without notification
-    "
-    |model|
-
-    model := modifiedHolder.
-    modifiedHolder := nil.
-    self helpKey:aKey.
-    modifiedHolder := model.
+	modifiedHolder addDependent:self.
+    ].
 !
 
 specClass
-    "returns the class on which the help tool works
+    "returns the class on which the help tool works on
     "
     ^ specClass
 !
@@ -599,48 +582,82 @@
 
 !UIHelpTool methodsFor:'aspects'!
 
+classItemListHolder
+    "returns the holder which keeps the class items
+    "
+    |holder|
+
+    holder := builder bindingAt:#classItemListHolder.
+
+    holder isNil ifTrue:[
+	holder := nil asValue.
+	holder value:classItemList.
+	builder aspectAt:#classItemListHolder put:holder.
+    ].
+    ^ holder
+!
+
+classItemModel
+    "returns the holder which keeps the current selected class
+    "
+    ^ classItemModel
+!
+
 contentsModifiedChannel
-    "returns the model which indicates whether the current helpText is modified
+    "boolean holder, which is set to true if the contents assigned to the
+     helpKey changed
     "
     ^ contentsModifiedChannel
 !
 
 editModel
-    "returns the model which keeps the current editing key
+    "string holder, which keeps the current editing helpKey as string
     "
-    ^ editModel
+    ^ editModel.
 !
 
-listOfClasses
-    "returns the list which keeps the classes
+helpTextView
+    "the editView which keeps the current help contents assigned to the key
     "
-    ^ listOfClasses
+    ^ helpTextView
 !
 
-listOfClassesModel
-    "returns the model which keeps the current class selection
-     or nil
+keyItemListHolder
+    "holder, which keeps the current hierarchical list
+     assigned to the selected class item
     "
-    ^ listOfClassesModel
+    |holder|
+
+    holder := builder bindingAt:#keyItemListHolder.
+
+    holder isNil ifTrue:[
+	holder := nil asValue.
+	holder value:(classItemList last list).
+	builder aspectAt:#keyItemListHolder put:holder.
+    ].
+    ^ holder
 !
 
-listOfKeys
-    "returns the list which keeps the current keys
+keyItemModel
+    "model which keeps the current selected helpKey or nil
     "
-    ^ listOfKeys
+    ^ keyItemModel.
 !
 
-listOfKeysModel
-    "returns the model which keeps the current list selection
-     or nil
+valueOfEnablingCommitButtons
+    "returns the enabling of the commit of this tool as value holder
     "
-    ^ listOfKeysModel
+    masterApplication notNil ifTrue:[
+	^ masterApplication valueOfEnablingCommitButtons
+    ].
+    ^ contentsModifiedChannel
 !
 
 valueOfInfoLabel
-
+    "returns the info label as value holder
+    "
     masterApplication notNil ifTrue:[
-        ^ masterApplication valueOfInfoLabel
+	^ masterApplication valueOfInfoLabel
     ].
     ^ super valueOfInfoLabel
 ! !
@@ -648,604 +665,763 @@
 !UIHelpTool methodsFor:'building'!
 
 buildAndMergeFromClass:aClass
-    "class and selector changed; merge old definitions
-     into definitions loaded from a class
-    "
-    |saveDictionary saveHelpKey|
+     "setup a new specClass, merge the current items into
+     "
+     |root mergeItems|
 
-    saveDictionary := dictionary.
-    saveHelpKey    := self helpKey.
+     root := classItemList first.
+
+     root isUnspecified ifTrue:[ mergeItems := root children ]
+		       ifFalse:[ mergeItems := nil ].
+
+     self buildFromClass:aClass.
 
-    self buildFromClass:aClass.
+     mergeItems size ~~ 0 ifTrue:[
+	root := classItemList first.
+
+	mergeItems do:[:anItem| |item hkey|
+	    hkey := anItem helpKey.
+	    item := root detectItemWithKey:hkey.
 
-    saveDictionary notEmpty ifTrue:[
-        dictionary declareAllFrom:saveDictionary.
-        self updateList.
+	    item isNil ifTrue:[
+		item := KeyItem helpKey:hkey helpText:(anItem helpText).
+		root add:item sortBlock:[:a :b| a label < b label ].
+	    ] ifFalse:[
+		item helpText:(anItem helpText).
+	    ]
+	]
     ].
-    self helpKey:saveHelpKey.
-    self modified:true.
 !
 
 buildFromClass:aClass
     "reads the help dictionary from aClass and find remaining classes 
-     'between' aClass and ApplicationModel" 
+     'between' aClass and ApplicationModel
+    " 
+    |lastContents root list resource|
 
-    |list|
-
-    modified  := false.
+    specSelector isNil ifTrue:[ specSelector := #helpSpec ].
     specClass := self getHelpSpecClassFromClass:aClass.
 
-    specClass notNil ifTrue:[   
-        dictionary   := Dictionary new.
-        dictionaries removeAll.
+    list := OrderedCollection new.
+
+    (specClass isClass and:[specClass isLoaded]) ifTrue:[
+	lastContents := nil.
+	resource := specClass name, ' ', specSelector.
+	self addToHistory:(Association key:resource value:#'loadFromMessage:').
+
+	specClass withAllSuperclasses reverse do:[:aClass| |value name|
+	    lastContents isNil ifTrue:[
+		aClass == ApplicationModel ifTrue:[ 
+		    lastContents := IdentityDictionary new
+		].
+	    ] ifFalse:[
+		root := ClassItem onClass:aClass.
+
+		(aClass class includesSelector:specSelector) ifTrue:[
+		    value := aClass perform:specSelector.
+
+		    value keysAndValuesDo:[:k :v| |cval|
+			cval := lastContents at:k ifAbsent:self.
+			cval = v ifFalse:[ root add:(KeyItem helpKey:k helpText:v) ].
+		    ].
+		    lastContents := value.
+		].
+		root sort:[:a :b| a label < b label ].
+		root modified:false.
+		list add:root.
+	    ]
+	]
+    ].
+    list isEmpty ifTrue:[
+	list add:(ClassItem onClass:nil)
     ].
 
-    (specClass isClass and:[specClass isLoaded]) ifFalse:[
-        self updateList.
-        ^ self
-    ].
-    (specClass class includesSelector:specSelector) ifFalse:[
-        dictionaries at:(specClass name) put:dictionary. 
+    self withoutModifyDo:[
+	classItemList contents:list.
+	classItemModel value:(list last)
     ].
-    list := specClass withAllSuperclasses reverse collect:[:cls| cls name ].
-
-    (list includes: #ApplicationModel) ifTrue:[
-        list := list asOrderedCollection.
-        list removeAll:(ApplicationModel withAllSuperclasses collect:[:cls| cls name])
-    ].
-    listOfClasses contents:list.
-    listOfClassesModel triggerValue:(specClass name).
 !
 
 buildFromClass: aClass andSelector: aSelector
-    "sets aSelector and reads the help dictionary from aClass"
-
+    "sets aSelector and reads the help dictionary from aClass
+    "
     specSelector := aSelector.
     self buildFromClass:aClass
-
 !
 
 buildFromHelpTool:aHelpTool
-
-    self doNew.
-
-    dictionaries := aHelpTool dictionaries.
-    dictionary   := aHelpTool dictionary.
+    "build from another helpTool
+    "
+    specClass     := aHelpTool specClass.
+    specSelector  := aHelpTool specSelector.
+    classItemList := aHelpTool classItemListHolder value.
 
-    specSelector := aHelpTool specSelector.
-    specClass    := aHelpTool specClass.
+    self classItemListHolder value:classItemList.
 
-    listOfClasses contents:(aHelpTool listOfClasses).
-    listOfClassesModel triggerValue:(listOfClasses at:1 ifAbsent:nil).
+    classItemModel triggerValue:(classItemList last).
 ! !
 
 !UIHelpTool methodsFor:'change & update'!
 
 editModelChanged
-    "called if the edit model changed
+    "called if the editModel changed
     "
     |key|
 
     key := self helpKey.
-    modifiedHolder notNil ifTrue:[ modifiedHolder value:true ].
-
-    (key notNil and:[(dictionary at:key ifAbsent:nil) isNil]) ifTrue:[
-        listOfClasses do:[:name| |dir|
-            dir := self dictionaryForClassNamed:name.
-
-            (dir includesKey:key) ifTrue:[
-                "/ setup new class
-                listOfKeysModel setValue:key.
-                listOfClassesModel value:name.
-                ^ self
-            ].
-        ].
-        key := nil.
-    ].
-    listOfKeysModel value:key.
-!
 
-listOfClassesModelChanged
-    "called if the class selection changed
-    "
-    |clsName|
-
-    clsName := listOfClassesModel value.
-    clsName isNil ifTrue:[^ self].
+    modifiedHolder notNil ifTrue:[
+	modifiedHolder value:true
+    ].
 
-    dictionary := self dictionaryForClassNamed:clsName.
-    self updateList.
-!
-
-listOfKeysModelChanged
-    "called if the selection of the key list changed
-    "
-    |key txt|
-
-    key := listOfKeysModel value.
+    contentsModifiedChannel value:false.
 
     key notNil ifTrue:[
-        key := key asSymbol.
-        txt := dictionary at:key ifAbsent:nil.
+	keyItemModel value = key ifTrue:[^ self].
+
+	classItemList reverseDo:[:root| |item|
+	    item := root detectItemWithKey:key.
 
-        txt isNil ifTrue:[
-            listOfKeysModel value:nil.
-            ^ self
-        ].
-        editModel value ~= key ifTrue:[
-            editModel value:key withoutNotifying:self.
-            modifiedHolder notNil ifTrue:[ modifiedHolder value:true ].
-        ].
+	    item notNil ifTrue:[
+		classItemModel value:root.
+		keyItemModel   value:item.
+		^ self.
+	    ].
+	].
+
+	masterApplication isNil ifTrue:[
+	    "entered a new helpKey
+	    "
+	    self valueOfEnablingCommitButtons value:true.
+	].
     ].
-
-    contentsModifiedChannel value ifFalse:[
-        self cancel
-    ].
+    keyItemModel value:nil.
 !
 
 update:something with:aParameter from:changedObject
-    "Invoked when an object that I depend upon sends a change notification."
-
-    editModel == changedObject ifTrue:[
-        self editModelChanged.
-        ^ self
-    ].
+    "Invoked when an object that I depend upon sends a change notification.
+    "
+    |root item list|
 
-    listOfKeysModel == changedObject ifTrue:[
-        self listOfKeysModelChanged.
-        ^ self
-    ].
+    changedObject == keyItemModel ifTrue:[
+	item := keyItemModel value.
 
-    listOfClassesModel == changedObject ifTrue:[
-        self listOfClassesModelChanged.
-        ^ self
+	item notNil ifTrue:[
+	    editModel value:(item helpKey).
+	].
+	self cancel.
+	^ self
     ].
 
-    contentsModifiedChannel == changedObject ifTrue:[
-        contentsModifiedChannel value ifTrue:[
-            modifiedHolder notNil ifTrue:[modifiedHolder value:true].
-        ].
-        ^ self
+    changedObject == classItemModel ifTrue:[
+	root := classItemModel value.
+
+	root notNil ifTrue:[
+	    item := root detectItemWithKey:(self helpKey).
+	    list := root list.
+	] ifFalse:[
+	    list := item := nil.
+	].
+
+	item notNil ifTrue:[
+	    keyItemModel value:nil withoutNotifying:self.
+	].
+	self keyItemListHolder value:list.
+	keyItemModel value:item.
+	^ self
     ].
 
-    super update:something with:aParameter from:changedObject
-! !
-
-!UIHelpTool methodsFor:'help'!
+    changedObject == editModel ifTrue:[
+	self editModelChanged.
+	^ self
+    ].
 
-openDocumentation
-    "opens the documentation file of the Help Tool"
+    changedObject == contentsModifiedChannel ifTrue:[
+	modifiedHolder notNil ifTrue:[
+	    modifiedHolder value:true
+	].
+	^ self
+    ].
+    super update:something with:aParameter from:changedObject
+!
 
-    self openHTMLDocument: 'tools/uipainter/HelpTool.html'
+withoutModifyDo:aBlock
+    "discard modifications; trigger not the modifiedHolder during
+     the action is active.
+    "
+    |holder|
+
+    holder := modifiedHolder.
+
+    holder isNil ifTrue:[
+	^ aBlock value
+    ].
+    ^ aBlock valueNowOrOnUnwindDo:[ modifiedHolder := holder ]
 ! !
 
 !UIHelpTool methodsFor:'private'!
 
 askForModification
-    "asks for modification"
+    "asks for modification; launch a dialog if something is modified;
+     returns true if the modifications are accepted by user otherwise
+     false.
+    "
+    |dialog|
 
-    modified ifTrue:[
-        ( (YesNoBox title: 'List was modified!!')        
-                   noText:'Cancel';
-                  yesText:'Waste it and proceed';
-            showAtPointer;
-            accepted
-        ) ifFalse:[
-            ^ false
-        ].
-        modified := false
+    self modified ifTrue:[
+	dialog := YesNoBox title:(resources string:'List was modified !!')
+			 yesText:(resources string:'Forget it and proceed')
+			  noText:(resources string:'Cancel').
+
+	dialog showAtPointer.
+	dialog accepted ifFalse:[^ false].
+	self modified:false.
     ].
     ^ true
 !
 
-dictionaryForClassNamed:clsName
-    "returns the directory assigned to a class name
+extractResourceFrom:aString
+    "extracts class and selector from a resource string. On success
+     an association with the key a class and the selector as value
+     is returned. Otherwise nil is returned
     "
-    ^ dictionaries at:clsName
-          ifAbsentPut:[ self extractHelpSpecForClass: (Smalltalk at:clsName) ].
-!
+    |words newClass newSel|
 
-extractHelpSpecForClass: aClass
-    "extracts the help dictionary of aClass, it current and return it"
-
-    |helpSpecSuperClass superHelpSpecKeys helpSpec|
+    aString size ~~ 0 ifTrue:[
+	words := aString asCollectionOfWords.
 
-    helpSpec := Dictionary new.
+	words size == 2 ifTrue:[
+	    newClass := self resolveName:(words first).
 
-    ((aClass class includesSelector: specSelector)
-    and: [(helpSpecSuperClass := aClass allSuperclasses detect: [:cls| cls class includesSelector: specSelector] ifNone: nil) notNil])
-    ifTrue:[                  
-        superHelpSpecKeys := (helpSpecSuperClass perform:specSelector) keys.
+	    (newClass isClass and:[newClass isLoaded]) ifTrue:[
+		newSel := words last asSymbol.
 
-        (aClass perform:specSelector) keysAndValuesDo:[:key :value |
-            (superHelpSpecKeys includes:key) ifFalse: [
-                helpSpec at:key put:value
-            ]
-        ].          
+		(newClass class includesSelector:newSel) ifTrue:[
+		    ^ Association key:newClass value:newSel            
+		].
+	    ].
+	].
     ].
-    ^ helpSpec 
+    ^ nil
 !
 
 getHelpSpecClassFromClass:aClass
-    "returns application class keeping the associated help text or nil"
-
+    "oops
+    "
     |cls|
 
-    ((cls := self resolveName:aClass) notNil and:[cls respondsTo: #helpSpecClass]) ifTrue:[
-        ^cls helpSpecClass
-    ].
-    ^cls
+    aClass notNil ifTrue:[
+	cls := self resolveName:aClass.
 
-
-!
+	cls notNil ifTrue:[
+	    cls := cls perform:#helpSpecClass ifNotUnderstood:cls.
 
-getUnformattedHelpText: aHelpText
-    "unformats aHelpText and returns it"
-
-    |helpText|
-
-    helpText := aHelpText asString copyReplaceAll:(Character cr) with:(Character space).
-    (helpText endsWith:(Character space)) ifTrue:[
-        helpText := helpText copyWithoutLast:1
+	    (cls isClass and:[cls isLoaded]) ifTrue:[
+		^ cls
+	    ].
+	].
     ].
-    ^ helpText
-
-    "Modified: / 20.7.1998 / 13:17:52 / cg"
+    ^ nil
 !
 
-installHelpSpecOnClass:aClass
-    "saves the help dicts in aClass which is subclass of ApplicationModel"
-
-    |cls src helpSpec|
-
-    cls := self getHelpSpecClassFromClass: aClass.
-
-    cls isNil ifTrue:[
-        self information:'No application class defined!!'.
-        ^nil
-    ].
-
-    (cls isSubclassOf: ApplicationModel) ifFalse:[
-        self information: 'Cannot save help spec into class ', cls name asBoldText, ',\because it is not a subclass of ApplicationModel!!' withCRs.
-        ^nil
-    ].
-
-    helpSpec := dictionaries 
-        at: cls name 
-        ifAbsent: [specClass notNil 
-            ifTrue:  [dictionaries at: aClass put: (self extractHelpSpecForClass: (Smalltalk at: aClass))]
-            ifFalse: [dictionary size > 0 ifTrue: [dictionary] ifFalse: [Dictionary new]]].
-
-    helpSpec associationsDo:
-    [:h|
-        helpSpec at: h key put: (self getUnformattedHelpText: h value)
-    ].
-
-    (cls class includesSelector: specSelector) 
-    ifTrue: 
-    [
-        |superclassHelpKeys implementedHelpSpec hasChanged|
-        implementedHelpSpec := Dictionary new.
-        superclassHelpKeys := (cls superclass respondsTo: specSelector)
-            ifTrue:  [(cls superclass perform:specSelector) keys]
-            ifFalse: [Array new].
-
-        (cls perform:specSelector) associationsDo: [:h| (superclassHelpKeys includes: h key) 
-            ifFalse: [implementedHelpSpec at: h key put: h value]].
+loadFromMessage:aString
+    "Set and rebuild the specClass and specSelector from a resource string.
+     On success true is returned otherwise false. If the current spec is
+     modified, a dialog is launched.
+    "
+    |association|
 
-        hasChanged := false.
-        implementedHelpSpec associationsDo: [:h| (helpSpec            includesAssociation: h) ifFalse: [hasChanged := true]].
-        helpSpec            associationsDo: [:h| (implementedHelpSpec includesAssociation: h) ifFalse: [hasChanged := true]].
-
-        (implementedHelpSpec notEmpty and: [hasChanged and:
-        [DialogBox confirm: 'Class ', cls name asBoldText, ' already implements\a help spec!!\\Do only replace, if you have removed\help keys in an existing help spec.\' withCRs yesLabel: ' Merge ' noLabel: ' Replace ']])
-        ifTrue:
-        [      
-             implementedHelpSpec associationsDo: [:h| (helpSpec includesKey: h key) 
-                ifFalse: [helpSpec at: h key put: h value]].
-        ]
-    ].
-
-    helpSpec isEmpty ifTrue:[
-        ^(cls superclass respondsTo: specSelector) ifTrue: [cls class removeSelector: specSelector].
-    ].
-
-    src  := '' writeStream.
+    association := self extractResourceFrom:aString.
 
-    src nextPutAll:
-        specSelector, '\' withCRs,
-        (ResourceSpecEditor codeGenerationCommentForClass: UIHelpTool) withCRs,
-    '\\' withCRs,
-    '    "\' withCRs,
-    '     UIHelpTool openOnClass:', cls name asString ,'    
-    "
-
-    <resource: #help>
-
-    ^super ', specSelector, ' addPairsFrom:#(
-
-'.
-
-    helpSpec keys asSortedCollection do:
-    [:key|
-        src nextPutLine: key storeString.
-        src nextPutLine: (helpSpec at: key) storeString; cr.
+    association notNil ifTrue:[
+	self askForModification ifTrue:[
+	    self buildFromClass:(association key) andSelector:(association value).
+	    ^ true
+	].
     ].
-    src nextPutLine:')'.
-
-    Compiler 
-        compile:(src contents)
-        forClass:cls class 
-        inCategory:'help specs'.
+    ^ false
 !
 
-installHelpSpecsOnClass:aClass
-    "saves the help dicts on aClass and its superclasses which are subclasses of ApplicationModel"
-
-    |cls helpSpecClasses|
-
-    cls := self getHelpSpecClassFromClass:aClass.
-
-    cls isNil ifTrue: [
-        self information:'No application class defined!!'.
-        ^ self  
-    ].
-
-    modified ifFalse:[
-        masterApplication isNil ifTrue: [self information:'Nothing was modified!!'].
-        ^ self
-    ].
-    helpSpecClasses := listOfClasses copy.
-
-    helpSpecClasses notEmpty ifTrue:[
-        (helpSpecClasses includes: cls name) ifFalse: [helpSpecClasses add: cls name].
-
-        helpSpecClasses do:[:clsName|
-            (self installHelpSpecOnClass: clsName) isNil ifTrue:[
-                modified := false.
-                ^ self
-            ]
-        ].
-    ]
-    ifFalse:
-    [      
-        self installHelpSpecOnClass: cls
-    ].
-
-    modified := false.
-!
+resourceMessage:aString
+    "Set the specClass and specSelector from a resource string. On
+     success true is returned otherwise false.
+    "
+    |association|
 
-resourceMessage: aString
-    "extracts from aString the specClass and the specSelector"
+    association := self extractResourceFrom:aString.
 
-    (aString notNil and: [self askForModification]) 
-    ifTrue:
-    [            
-        |msg cls sel|
-        msg := aString asCollectionOfWords.
-        (msg size == 2 and:
-        [(cls := self resolveName:(msg at:1)) notNil])
-        ifTrue:
-        [
-            specClass := cls name.
-            specSelector := (msg at: 2) asSymbol.
-            ^true
-        ]
+    association notNil ifTrue:[
+	specClass    := association key.
+	specSelector := association value.
+	^ true
     ].
-    ^false
-
-!
-
-updateList
-    "updates the list of keys
-    "
-    |key|
-
-    listOfKeysModel setValue:nil.
-    listOfKeys contents:(dictionary keys asSortedCollection).
-
-    key := self helpKey.
-
-    (key notNil and:[listOfKeys includes:key]) ifFalse:[
-        key := nil.
-    ].
-    listOfKeysModel triggerValue:key
+    ^ false
 ! !
 
-!UIHelpTool methodsFor:'startup / release'!
+!UIHelpTool methodsFor:'startup & release'!
 
 closeRequest
-    "asks for permission before closing"
-
-    (self masterApplication isNil and:[self askForModification]) ifTrue:[
-        super closeRequest
-    ]
+    "asks for permission before closing
+    "
+    (masterApplication isNil and:[self askForModification]) ifTrue:[
+	super closeRequest.
+    ].
 !
 
 initialize
-    "initializes instance variables"
+    "setup default attributes
+    "
+    super initialize.
+    self createBuilder.
 
-    super initialize.
+    specSelector   := #helpSpec.
+
+    classItemList  := List new.
 
-    specSelector := #helpSpec.
-    dictionary   := Dictionary new.
-    dictionaries := Dictionary new.
-    modified     := false.
+    classItemModel := nil asValue.
+    classItemModel addDependent:self.
+
+    keyItemModel := nil asValue.
+    keyItemModel addDependent:self.
+
+    contentsModifiedChannel := false asValue.
+    contentsModifiedChannel addDependent:self.
+
+    helpTextView := EditTextView new.
+    helpTextView acceptAction:[:dummy| self accept ].
+    helpTextView modifiedChannel:contentsModifiedChannel.
 
     editModel := nil asValue.
     editModel addDependent:self.
 
-    listOfKeys   := List new.
-    listOfKeysModel := nil asValue.
-    listOfKeysModel addDependent:self.
-
-    listOfClasses := List new.
-    listOfClassesModel := nil asValue.
-    listOfClassesModel addDependent:self.
-
-    contentsModifiedChannel := false asValue.
-    contentsModifiedChannel addDependent:self.
-!
-
-loadFromMessage:aString
-    "loads a help spec by evaluating aString"
-
-    (aString notNil and: [self askForModification]) 
-    ifTrue:
-    [            
-        |msg cls sel|
-        msg := aString asCollectionOfWords.
-        (msg size == 2 and:
-        [(cls := self resolveName:(msg at:1)) notNil and:
-        [cls class includesSelector: (sel := (msg at: 2) asSymbol)]])
-        ifTrue:
-        [               
-            self buildFromClass: (specClass := cls name) andSelector: (specSelector := sel).
-            ^true
-        ]
-    ].
-    ^false
+    self buildFromClass:nil.
 !
 
 openInterface:aSymbol
-    "do not open as stand alone"
-
-
-   
+    "do not open as stand alone
+    "
 !
 
 openOnClass:aClass
-    "opens the UIHelpTool on aClass"
-
+    "opens the UIHelpTool on aClass
+    "
     self openOnClass:aClass andSelector:nil
-
-    "
-     self openOnClass:NewLauncher
-    "
 !
 
 openOnClass:aClass andSelector: aSelector
     "opens the UIHelpTool on aClass and aSelector"
 
-    super openInterface: #windowSpecForStandAlone.
+    super openInterface:#windowSpecForStandAlone.
 
     builder window label: 'Help Tool'.
     self buildFromClass:aClass andSelector:aSelector
-!
-
-postBuildTextView:aView
-
-    editTextView := aView scrolledView.
-    editTextView acceptAction:[:dummy| self accept ].
 ! !
 
 !UIHelpTool methodsFor:'user actions'!
 
 accept
-    "accepts the help text"
+    "accepts the help text
+    "
+    |helpKey helpItem root|
 
-    |key txt|   
-
-    key := self helpKey.
-    key isNil ifTrue:[^ self].
+    helpKey := self helpKey.
+    helpKey isNil ifTrue:[^ self].
 
-    contentsModifiedChannel value ifFalse:[
-        (dictionary includes:key) ifTrue:[^ self].   
-    ].
-    contentsModifiedChannel value:false.
+    root := classItemModel value.
+    root isNil ifTrue:[^ self].
+
+    helpItem := root detectItemWithKey:helpKey.
 
-    txt := editTextView contents ? ''.
-    txt := txt asString.
-
-    dictionary at:key put:txt.
-
-    listOfKeys detect:[:el| el = key ] ifNone:[ |idx|
-        idx := listOfKeys findFirst:[:el| el > key ].
-        idx == 0 ifTrue:[ listOfKeys add:key ]
-                ifFalse:[ listOfKeys add:key beforeIndex:idx]
+    helpItem isNil ifTrue:[
+	helpItem := KeyItem helpKey:helpKey helpText:(helpTextView contents).
+	root add:helpItem sortBlock:[:a :b| a label < b label ].
+    ] ifFalse:[
+	helpItem helpText:(helpTextView contents).
     ].
 
-    listOfKeysModel value:key withoutNotifying:self.
-
-    modified := true.
-    modifiedHolder notNil ifTrue:[ modifiedHolder value:true ].
+    contentsModifiedChannel value:false.
+    keyItemModel triggerValue:helpItem.
 !
 
 cancel
-    |key txt|
+    "cancel modifications, reload helpText
+    "
+    |item contents modified|
 
-    editTextView notNil ifTrue:[
-        key := listOfKeysModel value.
+    item := keyItemModel value.
+    modified := false.
 
-        key notNil ifTrue:[
-            txt := dictionary at:key ifAbsent:nil.
-        ] ifFalse:[
-            txt := nil
-        ].
-        editTextView contents:txt.
+    item notNil ifTrue:[
+	contents := item helpText.
+    ] ifFalse:[
+	contents := nil.
+
+	modifiedHolder isNil ifTrue:[
+	    modified := self helpKey notNil
+	]
     ].
-    contentsModifiedChannel value:false.
+    helpTextView contents:contents.
+    contentsModifiedChannel value:modified.
 !
 
 doDelete
     "deletes the selected help key
     "
-    |key|
-
-    key := listOfKeysModel value.
-
-    key isNil ifTrue:[
-        self warn:'No key selected !!'.
-        ^ self
-    ].
+    |item|
 
-    listOfKeysModel value:nil.
-
-    key := key asSymbol.
-    listOfKeys remove:key ifAbsent:nil.
-
-    (dictionary removeKey:key ifAbsent:nil) notNil ifTrue:[
-        modified := true.
-        modifiedHolder notNil ifTrue: [modifiedHolder value:true].
-    ].
+    item := keyItemModel value.
+    item notNil ifTrue:[ item remove ].    
+    editModel value:nil.
 !
 
 doLoad
-    "opens a Resource Selection Browser in order to get a resource message"
-
+    "opens a Resource Selection Browser in order to get a resource message
+    "
     self loadFromMessage: 
-        (ResourceSelectionBrowser
-            request: 'Load Help Spec From Class'
-            onSuperclass: nil
-            andClass: specClass
-            andSelector: specSelector ? #help
-            withResourceTypes: (Array with: #help)).
+	(ResourceSelectionBrowser
+	    request: 'Load Help Spec From Class'
+	    onSuperclass: nil
+	    andClass: specClass
+	    andSelector: specSelector ? #help
+	    withResourceTypes: (Array with: #help)).
 
     self updateInfoLabel
-
 !
 
 doNew
-    "resets the help tool"
-
-    specClass := nil.
-
-    editModel          value:nil withoutNotifying:self.
-    listOfKeysModel    value:nil.
-    listOfClassesModel value:nil.
-
-    listOfKeys    removeAll.
-    listOfClasses removeAll.
-    dictionaries  removeAll.
-
-    dictionary   := Dictionary new.
-    modified     := false.
+    "reset all to empty
+    "
+    contentsModifiedChannel value:false.
+    self helpKey:nil.
+    self buildFromClass:nil.
 !
 
 doSave
-    "saves the help dictionaries on specClass"
+    "save the help spec to the spec-class(es)
+    "
+    specClass isNil ifTrue:[
+        self information:(resources string:'No class specified !!').
+        ^ nil
+    ].
+    (specClass isSubclassOf:ApplicationModel) ifFalse:[
+        self information:(resources string:'Cannot save help into none Application class').
+        ^ nil
+    ].
+
+    classItemList do:[:aClassItem| aClassItem createHelpMethodNamed:specSelector ].
+!
+
+openDocumentation
+    "opens the documentation file of the Help Tool
+    "
+    self openHTMLDocument: 'tools/uipainter/HelpTool.html'
+! !
+
+!UIHelpTool::ClassItem class methodsFor:'instance creation'!
+
+onClass:aClass
+    |root|
+
+    root := self new.
+    root onClass:aClass.
+    ^ root
+! !
+
+!UIHelpTool::ClassItem methodsFor:'accessing'!
+
+list
+    "returns the hierarchical list assigned to the classItem; the
+     list contains the keyItems
+    "
+    list isNil ifTrue:[
+	list := HierarchicalList new.
+	list showRoot:false.
+	list root:self.
+    ].
+    ^ list
+!
+
+theClass
+    "returns the class or nil if unspecified
+    "
+    ^ theClass
+! !
+
+!UIHelpTool::ClassItem methodsFor:'change & update'!
+
+helpTextChangedFor:anItem
+    "called if an helpKey changed its contents
+    "
+    self model notNil ifTrue:[
+	self   modified:true.
+	anItem modified:true.
+    ].
+! !
+
+!UIHelpTool::ClassItem methodsFor:'code generation'!
+
+createHelpMethodNamed:aMethodName
+    |stream|
+
+    (modified and:[theClass notNil]) ifFalse:[
+        ^ self
+    ].
+    stream := '' writeStream.
+
+    stream nextPutAll:
+        aMethodName, '\' withCRs,
+        (ResourceSpecEditor codeGenerationCommentForClass:UIHelpTool) withCRs,
+    '\\' withCRs,
+    '    "\' withCRs,
+    '     UIHelpTool openOnClass:', theClass name asString ,'    
+    "
+
+    <resource: #help>
+
+    ^ super ', aMethodName, ' addPairsFrom:#(
+
+'.
+
+    self do:[:aKeyItem| |helpText|
+        helpText := aKeyItem helpText.
+        helpText isNil ifTrue:[ helpText := '' ].
+
+        stream nextPutLine:(aKeyItem helpKey storeString).
+        stream nextPutLine:(helpText storeString); cr.
+    ].
+    stream nextPutLine:')'.
+
+    Compiler 
+        compile:(stream contents)
+        forClass:theClass class 
+        inCategory:'help specs'.
+
+    self modified:false.
+! !
+
+!UIHelpTool::ClassItem methodsFor:'displaying'!
+
+icon
+    "returns the display icon (always nil)
+    "
+    ^ nil
+!
+
+label
+    "returns the display label
+    "
+    |label|
+
+    theClass notNil ifTrue:[ label := theClass name ]
+		   ifFalse:[ label := '** not yet defined **' ].
+
+    modified ifTrue:[
+	label := Text string:label color:(Color red).
+    ].
+    ^ label
+! !
+
+!UIHelpTool::ClassItem methodsFor:'instance creation'!
+
+initialize
+    "setup defaults
+    "
+    super initialize.
+
+    children   := OrderedCollection new.
+    isExpanded := true.
+    modified   := false.
+!
+
+onClass:aClass
+    "the class the ys are assigned to; if the class is nil,
+     the class is not yet specified.
+    "
+    theClass := aClass.
+! !
+
+!UIHelpTool::ClassItem methodsFor:'private'!
+
+basicAdd:aChild sortBlock:aBlock
+    "catch low-level add to update the modification flag
+    "
+    self   modified:true.
+    aChild modified:true.
+
+    ^ super basicAdd:aChild sortBlock:aBlock.
+!
+
+basicAddAll:aList beforeIndex:anIndex
+    "catch low-level add to update the modification flag
+    "
+    self modified:true.
+
+    aList do:[:el| el modified:true ].
+    ^ super basicAddAll:aList beforeIndex:anIndex.
+!
 
-    self installHelpSpecsOnClass:specClass
+basicRemoveFromIndex:startIndex toIndex:stopIndex
+    "catch low-level remove to update the modification flag
+    "
+    self isUnspecified ifFalse:[
+	self modified:true.
+    ].
+    ^ super basicRemoveFromIndex:startIndex toIndex:stopIndex
+! !
+
+!UIHelpTool::ClassItem methodsFor:'queries'!
+
+isUnspecified
+    "true if the class is unspecified
+    "
+    ^ theClass isNil
+!
+
+modified
+    "true, if any item is modified, created or deleted
+    "
+    ^ modified
+!
+
+modified:aBoolean
+    "true, if any item is modified, created or deleted
+    "
+    modified ~~ aBoolean ifTrue:[
+	modified := aBoolean.
+
+	modified ifFalse:[
+	    self do:[:el| el modified:false ].
+	].
+    ].
+! !
+
+!UIHelpTool::ClassItem methodsFor:'searching'!
+
+detectItemWithKey:aKey
+    "returns the item assigned to a helpKey or nil
+    "
+    |key|
+
+    aKey size ~~ 0 ifTrue:[
+	key := aKey asSymbol.
+
+	self do:[:anItem|
+	    anItem helpKey == key ifTrue:[ ^ anItem ].
+	]
+    ].
+    ^ nil
+! !
+
+!UIHelpTool::KeyItem class methodsFor:'instance creation'!
+
+helpKey:aKey helpText:aText
+    |key|
+
+    key := self new.
+    key helpKey:aKey helpText:aText.
+    ^ key
+! !
+
+!UIHelpTool::KeyItem methodsFor:'accessing'!
+
+helpKey
+    "returns the helpKey, a symbol
+    "
+    ^ helpKey
+!
+
+helpText
+    "returns the contents assigned to the helpKey or nil
+    "
+    ^ helpText
+!
+
+helpText:aText
+    "set the contents assigned to the helpKey; if the cxontents changed,
+     a notification is raised.
+    "
+    |text|
+
+    text := self formatText:aText.
+
+    text ~= helpText ifTrue:[
+	helpText := text.
+
+	(modified or:[parent isNil]) ifFalse:[
+	    parent helpTextChangedFor:self.
+	]
+    ].
+! !
+
+!UIHelpTool::KeyItem methodsFor:'displaying'!
+
+icon
+    "returns the display icon (always nil)
+    "
+    ^ nil
+!
+
+label
+    "returns the display label
+    "
+    modified ifTrue:[
+	^ Text string:helpKey color:(Color red)
+    ].
+    ^ helpKey
+! !
+
+!UIHelpTool::KeyItem methodsFor:'instance creation'!
+
+helpKey:aKey helpText:aText
+    "set the key and contents without a change notification
+    "
+    helpKey  := aKey asSymbol.
+    helpText := self formatText:aText.
+!
+
+initialize
+    "setup defaults
+    "
+    super initialize.
+    children := #().
+    modified := false.
+! !
+
+!UIHelpTool::KeyItem methodsFor:'private'!
+
+formatText:aText
+    "format the text, replace carriage return by spaces and compress spaces
+    "
+    |text result|
+
+    aText size ~~ 0 ifTrue:[
+	text := aText asString asCollectionOfWords.
+
+	text notEmpty ifTrue:[
+	    result := text first.
+
+	    text from:2 do:[:t| result := result, ' ', t ].
+	    ^ result
+       ].
+    ].
+    ^ nil
+! !
+
+!UIHelpTool::KeyItem methodsFor:'queries'!
+
+modified
+    "returns true if the helpText is modified
+    "
+    ^ modified
+!
+
+modified:aBoolean
+    "set the modification flag
+    "
+    aBoolean == modified ifFalse:[
+	modified := aBoolean.
+	self changed:#redraw.
+    ].
 ! !
 
 !UIHelpTool class methodsFor:'documentation'!