Edit Rename Changes History Upload Download Back to Top

Instructions to Port Arbor 2.5 help to 5i help

Arbor Help ￿porting images from Vw2.5 to Vw5i

Background: The full version of Arbor Help is not available as a purchasable item for Vw5i. Vw5i does contain simplified Arbor classes so balloon & status lines will continue to work. This article deals specifically with getting the status lines to work. The Arbor tool which used to assign help messages to widgets is not present in Vw5i. This gives us 2 separate goals, how to port the help (which in 2.5 is not stored in methods, but rather is in data), and secondly how to put this in a form which will allow the user to make updates and changes to the help messages.

Porting goals: Step 1 ￿Disable events Vw5i is event based by default. Vw2.5 is polling based by default. The first step is to change Vw5i back into a polling based system so that the initial port will run. In Vw5i the class/method WindowSpec>>isEventDriven must be changed so that it returns a false instead of true.

Step 2 ￿Get the help data from Vw2.5 We need to both get a string representation of the help, and to change the data structure (yup, its different between 2.5 & 5i). Get the Arbor help data by executing the following: AHSHelpDictionaryManager allInstances first inspect While inspecting the original AHSHelpDictionaryManager with all the class names, select the class to bring across, look at its persistent dictionary, assign it to the global B, and then use the below script. Note that many of the classes which are present in the dictionary will not have any Arbor help associated with them (the only string will be ￿version 2.5￿). | str text array | str := ReadWriteStream on: ''. B keysAndValuesDo: [:key :value | value class name == #AHSTranslationText ifTrue: [text := value string] . value class name == #ByteString ifTrue: [text := value]. value class name == #Array ifTrue: [text := value first string]. text isNil ifTrue: [self error: 'figure out class for text container']. array := key copyWith: text. str nextPutAll: array printString; cr. text := nil. ]. str contents

The string that you get from ￿str contents￿ is a string representation of arrays ready to be used as the text of a method. So now you need a method to plug this code into. In 5i the method ￿ahsHelpData￿ is used on the class side of the ApplicationModel subclasses to hold the help data. To keep this readable, and modifiable I effectively expanded this single method into 4 methods. For the purists out there you can consider it an exercise to reduce it back to a single method which looks like existing ahsHelpData methods. Here are the 4 methods you will need for every class which is to have help data (2 of the methods could be bumped up to ApplicationModel). Remember these go on the class side.

ahsHelpData " self ahsHelpData " | ahsHelpCls pdict | pdict := AHSPersistentDictionary new. self buildAHSHelpData: pdict. ahsHelpCls := AHSHelpClassData languages: #(#English) widgetData: pdict. ^ahsHelpCls


buildAHSHelpData: persistDict "add all the help strings to the AHSPersistentDictionary as AHSHelpData's" | insideDict key helpData | self getHelpStrings do: [:data | helpData := self helpText: (data at: 3). insideDict := AHSPersistentDictionary new. (key := data at: 2) isNil ifTrue: [ key := #nil ]. key isSymbol ifTrue: [insideDict at: key put: helpData] ifFalse: ["we are dealing with an array" key do: [:symbol | insideDict at: symbol put: helpData.] ]. persistDict at: data first put: insideDict. ].


helpText: aString ^AHSHelpData texts: (Array with: aString asText ) outlineLinks: #(nil).


getHelpStrings ￿paste your data here, and make it look basically like below￿ ^#( #(#recapId #default 'Toggle recap view.')
  1. (#exitId #default 'Exit the application.')
  2. (#helpId #default 'Application help options.') )
The last method ￿getHelpStrings￿ is where you paste the string that the script generated. You will have to add the overall array wrapper, and get rid of the double single quotes. When the ahsHelpData is called it will create an object that looks like the following: AHSHelpClassData languages #(#English) widgetData AHSPersistentDictionary
  1. widgetId AHSPersistentDictionary
  2. default AHSHelpData texts #(Text for 'blah') outlineLinks #(nil)
After you have done all your ApplicationModel classes you are interested in, you are ready for the next step.

Step 3 -- alter some methods in 5i Obviously if we are going to use Arbor help it needs to be enabled, so make the following changes. UI.ApplicationModel class>>arborHelpEnabled "Answer true if the Arbor Help System is to be enabled for this application. The default is false." ^true

Step 4 ￿watch out for the alligators Typically in a the initial port the goal will be to get the windows working, and then add in the help. This means some of the code may have been commented out. Where you need to check for this is the following: postOpenWith: --make sure the following line is not commented out: AHSProcessor postOpenEnableHelpFor: self.

postBuildWith: aModel --often this will have help initialization which has been commented out

--Note that AHSProcessor has the helpEnabled: & balloonHelpEnabled: methods.

Step 5 ￿down & dirty The above is all that should be necessary, but if things are not working I found the following snippet useful

G := 0@true. G y ifTrue: [G x: G x + 1. G x > 500 ifTrue: [ G y: false. self halt]]. I used this in AHSWHCProcessor>>helpFor:temps: Assuming the help is running this allows you to trap one particular thread without locking up the image. If help is not working, use this code while VW help is up which uses help. This will allow you to trace typical paths. (the >500 gives you some time to get to the window & area you are interested in).

Updates & changes So lets say you now want to add additional help to some of your widgets. How do you know what the first two symbols are in your help table (what to use in the method ￿getHelpStrings￿)? Or lets say you simply wish to see what help selectors are being used. The following code will print each unique selector once while you are moving the mouse over anything of interest: ALLid := Set new. ￿initialize this global | myId | className = #AHSStorageManager ifFalse: [ myId := (className, '>>' , id, '&', state) asSymbol. (ALLid includes: myId) ifFalse: [ ALLid add: myId. Transcript show: myId; cr].].

Stuff this code into AHSHelpInfo>> helpDataForClassName: widgetID: state: ifAbsent:

The helpDataFor￿ is an interesting method because this is where the help is looked up in the help objects which were created.

To add additional help, just look at what was printed on the transcript when you were over the widget of interest, and this will give you the class name, and the 2 symbols you need in the array. Then you simply add those symbols as an array with the desired text in the appropriate getHelpStrings method (as identified by the className), and you are set. You will see the selectors even if there is no current help data. Note that the help data is cached, so just changing the method is not sufficient to see the new help string on the initial change.


Edit Rename Changes History Upload Download Back to Top