In this section we will add a special browser to the 'Engi' system. I believe that in order to be a real technical expert you have to be able to make your own design and coding tools. For instance, I do not recommend that you use the standard Smalltalk browser without customizing it for your own purpose.
Before making a custom browser let us first examine the standard browser. The standard browser is composed of seven panes. They are usually called category view, class view, instance button, class button, protocol view, message view, and code view. These panes are made via pluggable MVC. So the interface between the browser model and each pane is handled simply by reading plug in messages for each pane.
In Smalltalk a classes are grouped into categories. The following expression returns the category to which a class belongs.
------------------ aClass category ------------------
Conversely the next massage returns all classes in a category.
----------------------------------------------------------- Smalltalk organization listAtCategoryNamed: categoryName -----------------------------------------------------------
Smalltalk organization listAtCategoryNamed: #'Graphics-Geometry'
returns: #(#Bezier #Circle #EllipticalArc #Geometric #LineSegment #Point #Polyline #Rectangle #Spline)
'Smalltalk organization' returns an instance of 'SystemOrganizer' which is a kind of 'ClassOrganizer' which is a kind of 'Object'.
This expression returns all categories:
Program-20-1: (Smalltalk; organization, categories) ---------------------------------- Smalltalk organization categories ----------------------------------
'ClassOrganizer', located in the inheritance tree between 'Object' and 'SystemOrganizer', manages class message categories. An instance constains categories and method names for a given class. Inspect the following:
Program-20-2: (Point; organization) -------------------- Point organization --------------------
Sending 'listAtCategoryNamed:' to the ClassOrganizer instance returns an array of method names in the given category.
Program-20-3: (Point; listAtCategoryNamed:) ----------------------------------------------------- Point organization listAtCategoryNamed: #'accessing' -----------------------------------------------------
You'll get the array '#(#x #x: #y #y:)'.
This expression returns all message categories of the 'Point' class.
------------------------------ Point organization categories ------------------------------
Source code of methods is also accessible, for example:
Program-20-4: (Point; sourceCodeAt:) ---------------------------- Point sourceCodeAt: #theta ----------------------------
If the system can not find the source code of given message then the de-compiler generates source code from the binary code. The decompiled source code is difficult to read because variable names are meaningless strings starting with 't' and there are no comments. If you want see an example of decompiled source codes, press shift key when you 'inspect' the above expression.
Given these access methods, I think that you can begin to see how a system browser works. To shift from instance mode to class mode, for example, the browser uses 'aClass class organization' rather than 'aClass organization'. So, now that you know how to access various aspects of categories and classes, building a system browser should not be too difficult.
So let us now try to make our own browser for the 'Engi' system. All the classes in the 'Engi' system are returned by:
--------------------- EngiSystem classes ---------------------
The system browser sends the 'categoryList' plug-in message to get items for the category view. By overiding this method, you could provide only 'Engi' class categories.
Also, the system browser sends the 'classList' plug-in message to get items for the class view. So you overide this also to show only 'Engi' classes in the class view.
This is done in the 'Engi' system browser in Appendix 10. 'EngiBrowser' class has 'Browser' as super class and has an instance variable 'engiClasses'. The instance variable caches the response from 'EngiSystem classes' because that call is slow. The cache is flushed when a category or a class is added to or deleted from 'Engi' system.
I think if you examine Appendix 10, you'll understand that overiding 'categoryList' and 'classList' just serves to filter the list. 'categoryMenu' and 'classMenu' also add functionality. These messages are sent when the yellow button is pressed, and the 'Engi' system browser overides the standard system menus only if the shift key is down.
Check this feature by holding down the shift key while pressing the yellow button on the category view or class view of the 'Engi' system browser. You should see a menu which has 'about', 'number of classes', 'number of messages', 'save' etc. Other features are inherited from the standard system browser, so you can do anything you do in the system browser in the same way in 'Engi' system browser.
I hope enjoy constructing custom tools for yourself. Let me describe a little more some example programs that have similar features of the system browser. Evaluate Program 20-5.
Program-20-5: (EngiBrowser, OrderedCollection, PopUpMenu; withAllSuperclasses, instVarNames, instanceVariableNames, asArray, reverseDo:, addAll:) ------------------------------------------------------------- | aClass aCollection instanceVariableNames | aClass := EngiBrowser. aCollection := OrderedCollection new. aClass withAllSuperclasses reverseDo: [:each | instanceVariableNames := each instVarNames. aCollection addAll: instanceVariableNames]. (PopUpMenu labelArray: aCollection asArray) startUp -------------------------------------------------------------
You should see a menu of instance variables of 'EngiBrowser '. Of course it contains inherited instance variables also. Do you know the same feature of the system browser? How about Program 20-6.
Program-20-6: (EngiBrowser, OrderedCollection, PopUpMenu; withAllSuperclasses, instVarNames, instanceVariableNames, asArray, asSortedCollection, reverseDo:, classPool, addAll:) --------------------------------------------------------------- | aClass aCollection classVariableNames | aClass := EngiBrowser. aCollection := OrderedCollection new. aClass withAllSuperclasses reverseDo: [:each | classVariableNames := each classPool keys asSortedCollection. aCollection addAll: classVariableNames]. (PopUpMenu labelArray: aCollection asArray) startUp ---------------------------------------------------------------
Program 20-6 shows a menu of class variables. The system browser also has this feature.
Program-20-7: (EngiBrowser, PopUpMenu; compiledMethodAt:, allSymbolLiterals, asSortedCollection) ----------------------------------------------------------------- | aClass aSymbol aCompiledMethod aCollection | aClass := EngiBrowser. aSymbol := #engiCategoryMenu. aCompiledMethod := aClass compiledMethodAt: aSymbol. aCollection := aCompiledMethod allSymbolLiterals asSortedCollection. (PopUpMenu labelArray: aCollection asArray) startUp -----------------------------------------------------------------
Program 20-7 shows a list of messages that 'engiCategoryMenu' instance method of 'EngiBrowser' sends to itself or other objects. The system browser also has these features. 'CompiledMethod' is binary code for the Smalltalk virtual machine which is translated from the source code you wrote in the code view of the browser.
Finally, here is some code to help you explore what is happening inside the Smalltalk system while you are programming. Program 20-8 creates a 'zzz' message in a dummy category as an instance method of 'EngiBrowser'.
Program-20-8: (EngiBrowser; compile:classified:notifying, compile) ------------------------------------------ | aClass aCategory sourceCode | aClass := EngiBrowser. aCategory := #dummy. sourceCode := 'zzz "This is a dummy method." ^3 + 4'. aClass compile: sourceCode classified: aCategory notifying: nil ------------------------------------------
Inspect Program 20-9, which returns a CompiledMethod, and take a look at the instance variables, for example '- byte codes'.
Program-20-9: (EngiBrowser; compiledMethodAt:, compile) ------------------------------------------ | aClass aSymbol aCompiledMethod | aClass := EngiBrowser. aSymbol := #zzz. aCompiledMethod := aClass compiledMethodAt: aSymbol. ^aCompiledMethod ------------------------------------------
Learning Smalltalk requires imagination, even more so than analysis. Do not try too hard to analyze Smalltalk internals. Instead, use your imagination and visualize what's going on. You'll be rewarded with deeper understanding.