From VisualWorks®, 7.6 of February 8, 2008 on March 3, 2008 at 9:42:45 pm (Dialog confirm: 'You are filing-in a Parcel source file!\\While this is possible it will not have\the same effect as loading the parcel.\None of the Parcel''s prerequisites will\be loaded and none of its load actions\will be performed.\\Are you sure you want to file-in?' withCRs) ifFalse: [self error: 'Parcel file-in abandoned. Choose terminate or close.'] <class> <name>ModularSettingsPageModule</name> <environment>Tools</environment> <super>UI.ApplicationModel</super> <private>false</private> none parent setting valueHolder spec <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.ModularSettingsPageModule
ModularSettingsPageModule is an abstract superclass of *modules*. A module is a reusable subapplication for editing a SettingModel of a particular type. Modules are most often used by ModularSettingsPages--pages whose UI is automatically generated from a group of settings. Subclasses must implement the following messages: private componentWithLabelID Implement this method to return a component of the module UI which should display the label of the setting. Instance Variables: parent <ModularSettingsPage> The page this module belongs to--most often a ModularSettingsPage. However, other (laid out by hand) page classes can reuse modules too. setting <SettingModel> The setting represented by this module. spec <UISpecification> The spec for this module GUI. valueHolder <BufferedValueHolder> A buffered value holder created on the setting. The holder is the model of the widget displaying the value of the setting. All those holders are centrally triggered by the settings manager when it is time to accept settings.
</comment> <class> <name>NumberSettingModule</name> <environment>Tools</environment> <super>Tools.ModularSettingsPageModule</super> <private>false</private> none <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.NumberSettingModule
NumberSettingSpinButtonModule is a subapplication of ModularSettingsPage (a page of the SettingsManager that is automatically generated from a group of settings) that represents a NumberSetting (or an IntegerSetting) as an input field.
</comment> <class> <name>IntegerSettingSpinButtonModule</name> <environment>Tools</environment> <super>Tools.NumberSettingModule</super> <private>false</private> none <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.IntegerSettingSpinButtonModule
IntegerSettingSpinButtonModule is a subapplication of ModularSettingsPage (a page of the SettingsManager that is automatically generated from a group of settings) that represents an IntegerSetting as spin button.
</comment> <class> <name>ExternalSettingModule</name> <environment>Tools</environment> <super>Tools.ModularSettingsPageModule</super> <private>false</private> none <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.ExternalSettingModule
ExternalSettingModule is a module rendering an ExternalSetting as a label with a button that opens the external editor the setting links to.
</comment> <class> <name>BooleanSettingModule</name> <environment>Tools</environment> <super>Tools.ModularSettingsPageModule</super> <private>false</private> none <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.BooleanSettingModule
BooleanSettingModule is a subapplication of ModularSettingsPage (a page of the SettingsManager that is automatically generated from a group of settings) that represents a BooleanSetting as a check box.
</comment> <class> <name>SettingsWriter</name> <environment>Tools</environment> <super>Core.Object</super> <private>false</private> none domain settings <imports> private XML.XMLParser private XML.SAXWriter private XML.Document private XML.Element private XML.Attribute </imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.SettingsWriter
SettingsWriter is responsible for writing settings of SettingsDomain into a stream. Instance Variables: domain <SettingsDomain> The domain to be written. settings <Collection> The settings to be written, by default all settings of the domain.
</comment> <class> <name>DirectorySettingModule</name> <environment>Tools</environment> <super>Tools.ModularSettingsPageModule</super> <private>false</private> none <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.DirectorySettingModule
DirectorySettingModule is a subapplication of ModularSettingsPage (a page of the SettingsManager that is automatically generated from a group of settings) that represents a DirectorySetting as text field with a Browse button. The browse button opens a directory selection dialog.
</comment> <class> <name>SettingType</name> <environment>Tools</environment> <super>Core.Object</super> <private>false</private> none setting <imports> XML.Element XML.Attribute </imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.SettingType
Instances of SettingType are used by SettingModels to describe what objects a setting can contain. A setting type is responsible for rendering the current setting value in XML, recreating it from XML, checking whether a particular object can be a value of a setting of this type, and managing the UI used to create this setting. In other words, SettingType does all the work that depends on the kind of values a setting can hold, while a SettingModel itself is only responsible for knowing where to get that value. Concrete subclasses of SettingType are also responsible for creating instances of SettingModels, because the resulting expressions are more natural than the alternatives. For example, a setting representing a boolean value stored in a shared variable named Foo would typically be created as BooleanSetting on: #{Foo} Subclasses must implement the following messages: accessing fromXML: isValueValid: toXML: parameters xmlTag user interface editorModuleClassIn: Instance Variables: setting <SettingModel> The setting described by this type instance.
</comment> <class> <name>StringSetting</name> <environment>Tools</environment> <super>Tools.SettingType</super> <private>false</private> none meaning <imports></imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.StringSetting
StringSetting is a setting type whose model contains strings. Additionally, the 'meaning' variable can be set to specify if those strings have some meaning. For example, it is possible to have a StringSetting that is expected to contain a name of file (a String, not a Filename). Such meaning is a hint to automatically generated UI how to represent the value. For example, regular StringSettings are represented using a simple edit field, while a StringSetting with the #nameOfFile meaning is represented as an edit field with a Browse button. Instance Variables: meaning <ByteSymbol | nil> See above.
</comment> <class> <name>EnumerationSetting</name> <environment>Tools</environment> <super>Tools.SettingType</super> <private>false</private> none keys choices labels <imports></imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.EnumerationSetting
EnumerationSetting is a setting type whose model contains only objects listed in the collection in the 'choices' instance variable (compared using #=). For each object included in 'choices', the element of 'keys' collection at the same index is expected to be a symbol used to identify that choices in circumstances when using choices themselves is impossible--most importantly, when saving to a file. The element of 'labels' collection at the same index is a String or a UserMessage that will be used by the UI to represent that particular choice. Automatically generated setting pages represent EnumerationSettings as drop-down lists of values, unless the page has been sent the #useRadioButtonsForEnumerations message, in which case each EnumerationSetting is represented as a group of radio buttons. Instance Variables: choices <SequenceableCollection> See the description above. keys <SequenceableCollection of: Symbol> Ditto. labels <SequenceableCollection of: <String | UserMessage>> Ditto.
</comment> <class> <name>InvalidStoredSettingDataError</name> <environment>Tools</environment> <super>Core.Error</super> <private>false</private> none setting data <imports></imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.InvalidStoredSettingDataError
This exception is signalled when XML data for a setting appears to be invalid. This exception can occur when settings are loaded from an XML file, or when unapplied setting snapshots are applied to settings added to the image at a later time. In both cases, unless handled otherwise, the offending setting will be ignored and not restored from the settings. Instance Variables: data <Node> The XML node that was found to be disfigured. setting <GenericSettingDescription> The setting for which the data was intended.
</comment> <class> <name>GenericSettingDescriptor</name> <environment>Tools</environment> <super>UI.Model</super> <private>false</private> none id position definingMethod <imports> XML.Element XML.Attribute </imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.GenericSettingDescriptor
GenericSetting represents a setting in the most general case: an entity that is aware of some interesting information in the system, that may need to be stored externally and restored in the image at a later time. Subclasses of this class define the specifics of where that information is located in the system, how it is represented externally, and how it is applied to the image on loading. This most general superclass defines a setting as something that has an ID and can represent its state as a tree of XML nodes (as an answer to the #asXmlNode message). That tree is used to store the state of the setting externally. At a later time, when those external representations (also called SettingSnapshots) are loaded back into the image, for each snapshot a setting with a matching ID is looked up. If found, the snapshot is passed to the setting (by sending the message #restoreFrom:) so that the setting can update the information it manages according to the state saved in the snapshot. How exactly it does that is, again, up to the concrete subclass to define. An ID of a setting is an array of symbols, for example #(tools workspace showToolbar). If we compare IDs to filenames, the directory-like part of the ID, in this example #(tools workspace), is called an ID prefix. It is expected that IDs of logically related settings would have the same prefixes and be different only in the last element. It is because prefixes are often used by pages of the SettingsManager to select groups of settings. For example, the Workspace page requests the settings it shows by asking for all settings with the prefix #(tools workspace). Subclasses must implement the following methods: 'persistence' addStateXmlElementsTo: anXMLElement restoreFrom: aSettingSnapshot The former of those methods may signal InvalidSettingValueError, if the current setting information in the image cannot be meaningfully represented in XML by the instance. The latter may signal InvalidStoredSettingDataError to indicate that the XML structure found in the snapshot is invalid and/or cannot be applied to the system at this time. Therefore, senders of messages #asXmlNode (which uses #addStateXmlElementsTo:) and #restoreFrom: should expect to handle those exceptions. Instance Variables: id <Array of: Symbol> A unique identification of this setting. position <Integer> Used by some UI tools to order settings on automatically generated pages. If not set, defaults to 0. definingMethod <MethodDefinition> that created the setting, if known. This setting is used by the UI tools.
</comment> <class> <name>SettingsGroup</name> <environment>Tools</environment> <super>Tools.GenericSettingDescriptor</super> <private>false</private> none settings names preSaveBlock postSaveBlock preApplyBlock postApplyBlock <imports></imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.SettingsGroup
SettingsGroup is a simple pluggable "settings organizer". It can be used in situations when saving or loading a group of settings needs to be done in a particular order, and/or a certain action has to be performed before of after saving the values in a file or applying the values loaded from a file to settings in the image. When a setting is added to a group, it is given a name. This name can be used later to retrieve the setting from the group. Instance Variables: settings <OrderedCollection of: GenericSettingDescriptor> The settings that belong to this group. These settings will be saved into a file and applied to the image in the order they appear in this collection. names <Dictionary> mapping symbol names (typically Symbols) to the settings. postApplyBlock <Block | nil> Executed after applying setting values loaded from a file to all settings in the group. postSaveBlock <Block | nil> Executed after saving all settings in the group into a file. preApplyBlock <Block | nil> Executed before applying setting values loaded from a file to all settings in the group preSaveBlock <Block | nil> Executed before saving all settings in the group into a file.
</comment> <class> <name>SettingReader</name> <environment>Tools</environment> <super>Core.Object</super> <private>false</private> none domain snapshots <imports> private XML.XMLParser private XML.SAXWriter private XML.Document private XML.Element private XML.Attribute </imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.SettingReader
SettingsReader is responsible for reading an XML file and creating SettingSnapshots later used to deliver the saved information to the settings currently in the image. Instance Variables: domain <SettingsDomain> The domain the settings are read into; used to verify that the file really belongs to that domain. snapshots <Collection of: SettingSnapshot> This variable contains the snapshots read from the file. After the file has been read, that is.
</comment> <class> <name>SettingsManager</name> <environment>Tools</environment> <super>UI.ApplicationModel</super> <private>false</private> none triggerChannel pageListHolder domainHolder rootPage <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.SettingsManager
SettingsManager is the main setting editing application. It is the model of the settings window. It supports two user interfaces: one, a master settings editor, with a drop-down list of all setting domains in the image, another without, focused on a given domain only. Instance Variables: domainHolder <ValueModel> A ValueHolder with the SettingsDomain currently displayed by the editor. pageListHolder <SelectionInTree> The tree of pages currently displayed. triggerChannel <ValueHolder with: Boolean> A value holder where a true is placed as a signal to all pages and their settings to accept their values, or a false is placed as a signal to reload their displayed values from their models. rootPage <SettingsManagerPageTreeRoot> The root of the page tree of the current domain.
</comment> <class> <name>FilenameSetting</name> <environment>Tools</environment> <super>Tools.SettingType</super> <private>false</private> none mustExist <imports></imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.FilenameSetting
FilenameSetting is a setting type whose model refers to files, using instances of LogicalFilename or PortableFilename to do so. Instance variables: mustExist <Boolean> true if the filename referenced by the setting must exist for the setting to be considered valid.
</comment> <class> <name>SettingsManagerPageTreeRoot</name> <environment>Tools</environment> <super>Core.Object</super> <private>false</private> none domain children <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.SettingsManagerPageTreeRoot
An instance of SettingsManagerPageTreeRoot is used by SettingsManager as the root of the tree of pages it displays. The instance helps with arranging the pages hierarchically according to their IDs. The instance itself is not displayed anywhere, but its children are the top-level pages we see in the manager. Instance Variables: children <Collection of: SettingsManagerPage> See above. domain <SettingsDomain> The domain from which this instance collects pages to adopt.
</comment> <class> <name>SettingSnapshot</name> <environment>Tools</environment> <super>Core.Object</super> <private>false</private> none id stateNodes <imports> XML.Element XML.Attribute </imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.SettingSnapshot
I represent information stored by a setting into an external file and later loaded into the image. I have an ID, which is the same as that of the setting that originally stored its value into an external file, and I hold on the tree of XML nodes read from the file for this setting. Instance Variables: id <Array of: Symbol> The ID of the setting that saved this information, used to locate it in the image. stateNodes <Array of: Node> XML data stored for that setting.
</comment> <class> <name>ErrorAsSetting</name> <environment>Tools</environment> <super>Tools.GenericSettingDescriptor</super> <private>false</private> none messageText <imports></imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.ErrorAsSetting
ErrorAsSetting is a setting that represents an error that has occurred while collecting settings or pages. The trick here is that this setting takes place of the original erroneous setting, or becomes a sole setting of a page that takes place of an erroneous page. It gives a message notifying the user that an error has occurred in the page or setting definition, and provides a helpful button to evaluate the method again to catch the error in the debugger. Instance Variables: messageText <Object> See above.
</comment> <class> <name>InvalidSettingValueError</name> <environment>Tools</environment> <super>Core.Error</super> <private>false</private> none setting offendingValue <imports></imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.InvalidSettingValueError
This exception is signalled when there is an attempt to store an invalid value into a setting. In case of SettingModels, an invalid value is whatever the setting type rejects. The expected cases when this exception can occur are: - When a settings file is being written, if the current value of the setting in the setting target is not recognized by the setting type as acceptable. Unless handled otherwise, the setting will be ignored and not written out. - During setting file loading, when a setting value loaded from a file appears to be invalid for the setting as it is now. Unless handled otherwise, the setting value will be ignored and not applied to the setting. - When a SettingModel is used as a value model serving the setting value, and a message #value: is sent with an invalid object. Instance Variables: offendingValue <Object> recognized as invalid for the setting. setting <GenericSettingDescription> the setting that refused the object.
</comment> <class> <name>SettingsPageDecoration</name> <environment>Tools</environment> <super>Tools.GenericSettingDescriptor</super> <private>false</private> none <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.SettingsPageDecoration
SettingPageDecoration is an abstract class for settings of a special "decoration" kind. Those settings are not settings in the true sense of the word. They do not represent values that can be changed by the user and that can be written out to a file to be loaded into another image. Decoration settings are there only to add static decoration elements to a settings page, for example a horizontal separator bar or a text label. However, they are settings in the sense that they are found by the framework and are picked up and rendered by auto-generated settings pages.
</comment> <class> <name>SettingsPageSeparator</name> <environment>Tools</environment> <super>Tools.SettingsPageDecoration</super> <private>false</private> none <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.SettingsPageSeparator
SettingsPageSeparator is a bogus setting whose only purpose is to create a horizontal divider line in an auto-generated page.
</comment> <class> <name>NumberSetting</name> <environment>Tools</environment> <super>Tools.SettingType</super> <private>false</private> none min max <imports></imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.NumberSetting
NumberSetting is a setting whose model contains numbers. A setting type can constrain values of numbers that are allowed to be saved in the setting. Instance Variables: max <Number | nil> If not nil, the setting is not allowed to have a value greater than this attribute. min <Number | nil> If not nil, the setting is not allowed to have a value less than this attribute.
</comment> <class> <name>SettingsDomain</name> <environment>Tools</environment> <super>Core.Object</super> <private>false</private> none cachedSettings cachedPages unappliedSnapshots current <imports></imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.SettingsDomain
A SettingsDomain is a group of settings and setting pages. From the user's perspective, that grouping means that those settings are saved into a file and loaded together, and those pages are shown together in the same tree in the SettingsManager. It is recommended for an application whose settings are to be managed separately from those of other applications to define its own a subclass of this class (in other words, its own domain) to store its settings. For example, all settings related to the VisualWorks development environment are grouped together in the class VisualWorksSettings. An application that extends VisualWorks environment in some way--for example, an image editor--might want to add its settings to the VisualWorksSettings class or to a subclass of it. This will ensure that those settings will be saved and restored with other environment settings. An application whose function is not related to the VisualWorks environment--for example, a Wiki server--might want to define its own settings domain. A settings domain is represented by its class, however internally the data is stored and cached in a singleton instance of the class. Setting and page definitions are lightweight and stateless, in the sense that a setting only knows where the information is manages is stored in the image. It does not hold the information itself, therefore there may be muptile settings representing the same configurable parameter. For this reason and to simplify the framework, the settings are not saved anywhere. They are created from their definition methods on demand each time they are requested. Since this may generate much overhead in the cases when many settings are requrested at once (for example when opening a settings manager tool), a settings domain instance can temporarily cache the current set of settings. This is done by sending a message #withSettingsCachedDo:. The argument of that message is a block. The domain creates and stores the current set of its settings for the duration of the block's execution. Any accesses to the settings during that time use the cached set of settings and are very efficient. After completion of the block evaluation, the cached is always flushed. The same scheme is used to speed up access to the setting tool pages defined in the domain. These two caches are part of the settings domain singleton instance. Another responsibility of the settings domain instance is remembering unapplied snapshots. When saved setting values are loaded from a file, it may happen so that not all the settings whose values have been saved exist in the image at the moment (since some applications that were loaded at the time the settings were saved might not be present in the image when the settings are restored). Such "orphaned" setting values are saved by the domain in a collection 'unappliedSnapshots'. At a later time, whenever the set of settings defined in the domain changes, the domain tries to reapply the saved snapshots, in the hope that some of the newly defined settings might be those for whom the snapshots were intended. Instance variables: cachedSettings <Collection | nil> See above. cachedPages <Collection | nil> See above. unappliedSnapshots <Collection> See above.
</comment> <class> <name>SettingsManagerPage</name> <environment>Tools</environment> <super>UI.ApplicationModel</super> <private>false</private> none manager id position label icon children definingMethod <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.SettingsManagerPage
This is the generic superclass of any page used as a component of the SettingsManager. Subclasses are not required to implement any methods, however they might want to reimplement the following (see each particular method comment for an explanation why): helpText interestingMethods settings importSnapshots: resetToDefault Instance Variables: children <Collection of: SettingsManagerPage> Pages appear as children of this one in the settings manager's tree of pages. definingMethod <MethodDefinition | nil> The method where this page was defined, if known. icon <OpaqueImage> The icon displayed next to this page's label in the tree of pages. id <Array of: Symbol> The unique ID of the page. Page IDs determine the parent-child relationship of pages. For example, a page with an id #(tools) will become a parent of a page with an id #(tools foo). label <String | UserMessage> The label displayed for this page in the page tree in the manager. manager <SettingsManager> The host manager.
</comment> <class> <name>ModularSettingsPage</name> <environment>Tools</environment> <super>Tools.SettingsManagerPage</super> <private>false</private> none settings modules spec preferredModuleClasses enablementTrackers <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.ModularSettingsPage
ModularSettingsPage is a page whose layout is generated automatically from a group of settings. Instance Variables: enablementTrackers <Collection of: Object> modules <Collection of: ModularSettingsPageModule> Modules of this page. preferredModuleClasses <Dictionary> For settings that can be represented by several module classes, for example enumerations, a page keeps track of what class it should use to render such settings. Those classes are kept in this dictionary keyed by a symbol private to this class. settings <Collection of: SettingModel> Settings represented by modules on this page. spec <UISpecification> The composite spec of this page.
</comment> <class> <name>FilenameSettingModule</name> <environment>Tools</environment> <super>Tools.DirectorySettingModule</super> <private>false</private> none <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.FilenameSettingModule
FilenameSettingModule is a subapplication of ModularSettingsPage (a page of the SettingsManager that is automatically generated from a group of settings) that represents a FilenameSetting as text field with a Browse button. The browse button opens a file selection dialog.
</comment> <class> <name>StringSettingModule</name> <environment>Tools</environment> <super>Tools.ModularSettingsPageModule</super> <private>false</private> none <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.StringSettingModule
StringSettingModule is a subapplication of ModularSettingsPage (a page of the SettingsManager that is automatically generated from a group of settings) that represents a StringSetting as an input field.
</comment> <class> <name>SettingModel</name> <environment>Tools</environment> <super>Tools.GenericSettingDescriptor</super> <private>false</private> none type target hasDefault default label helpText <imports></imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.SettingModel
SettingModel is a setting that represents a single Smalltalk object. A SettingModel itself does not store that object, it merely acts as a handle that can knows how to access it and what constraints that object should satisfy. To know how to obtain its value in the image, the setting holds onto a "target": an object supporting a ValueModel interface and set up so that sending #value and #value: messages to it retrieves and sets the value of the setting. To specify what kinds of values are acceptable, a setting holds onto a type: an instance of one of the subclasses of SettingType. The type is responsible for representing a particular value of a setting in XML for writing it, and for other type-specific tasks, such as letting the UI know what kind of an interface should be used to present this setting to the user. Typically, instances of SettingModel are not created directly. Rather, an instance is created by a setting type, when a message is sent to the type to create a setting of that type on a particular target in the image. This is done simply because it leads to expressions that look simpler and more natural. See SettingType class for more detail. Instance Variables: type <SettingType> See the description above. target <ValueModel> Ditto. hasDefault <Boolean> When true, the variable 'default' contains a default value. (A separate flag is used because the usual no-object value, nil, could be the default too). default <Object> This object will be stored in the target if a request is sent to the setting to reset itself. label <String | UserMessage | nil> When this setting is displayed in an automatically generated UI, this is the label it gets. helpText <String | UserMessage | nil> The text displayed to the user when the user requests help for this setting.
</comment> <class> <name>TextSettingPage</name> <environment>Tools</environment> <super>Tools.SettingsManagerPage</super> <private>false</private> none textHolder helpText <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.TextSettingPage
TextSettingPage is a very simple page that displays a text editor with some text, portions of which can be evaluated. The text to display is supplied by sending the message #testHolder: and a passing an object supporting ValueModel protocol holding the text. The object can be either a String wrapped in a ValueHolder, or a SettingModel whose value is a string. Instance Variables: textHolder <ValueHolder with: String> The holder of the contents of the editor. helpText <UserMessage | String | nil> If not nil, the text to display when the user clicks the Help button.
</comment> <class> <name>DirectorySetting</name> <environment>Tools</environment> <super>Tools.FilenameSetting</super> <private>false</private> none <imports></imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.DirectorySetting
DirectorySetting is a setting type whose model contains an instance of Filename pointing to a directory. The difference between this and FilenameSetting is in its UI representation.
</comment> <class> <name>ErrorAsSettingModule</name> <environment>Tools</environment> <super>Tools.ModularSettingsPageModule</super> <private>false</private> none <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.ErrorAsSettingModule
ErrorAsSettingModule is a module that presents an ErrorAsSetting. See that class comment for an explanation of why it is necessary.
</comment> <class> <name>EnumerationSettingRadioModule</name> <environment>Tools</environment> <super>Tools.ModularSettingsPageModule</super> <private>false</private> none <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.EnumerationSettingRadioModule
EnumerationSettingRadioModule is a subapplication of ModularSettingsPage (a page of the SettingsManager that is automatically generated from a group of settings) that represents an EnumerationSetting as a column of radio buttons.
</comment> <class> <name>SettingsPageSeparatorModule</name> <environment>Tools</environment> <super>Tools.ModularSettingsPageModule</super> <private>false</private> none <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.SettingsPageSeparatorModule
SettingsPageSeparatorModule is a module with a horizontal divider rule created at the request of a SettingsPageSeparator. SettingsPageSeparator is a fake setting which has no value, used for the sole purpose of inserting a horizontal separator into a page.
</comment> <class> <name>AbortAcceptRequest</name> <environment>Tools</environment> <super>Core.Exception</super> <private>false</private> none requestor message <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.AbortAcceptRequest
When a settings manager requests all pages to accept their settings, a page may signal AbortAcceptRequest to indicate that there is a problem with the settings on that page that prevent settings from being accepted. The manager will do whatever is possible to prevent changes to those settings from being lost. In particular, if the current accept is caused by the user pressing the OK button, the settings manager will not close. Instance Variables: message <String> A message to the user describing why the page could not be accepted, set by the requestor. requestor <SettingsManagerPage> The page requesting to abort the accept operation.
</comment> <class> <name>BooleanSetting</name> <environment>Tools</environment> <super>Tools.SettingType</super> <private>false</private> none <imports></imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.BooleanSetting
BooleanSetting is a setting type whose model can only have Boolean values.
</comment> <class> <name>IntegerSetting</name> <environment>Tools</environment> <super>Tools.NumberSetting</super> <private>false</private> none <imports></imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.IntegerSetting
IntegerSetting is a specialization of NumberSetting that further constrains the allowed values to be integers.
</comment> <class> <name>SequenceSetting</name> <environment>Tools</environment> <super>Tools.SettingType</super> <private>false</private> none elementType smalltalkClassBinding <imports></imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.SequenceSetting
SequenceSetting is a compound setting type representing a sequence of values. The type of an individual value is defined by the type contained in the elementType instance variable. When a value of such a setting is loaded from an XML file, it is represented by an instance of Smalltalk collection class referenced by the smalltalkClassBinding instance variable. Instance Variables: elementType <SettingType> See above. smalltalkClassBinding <GeneralBindingReference> See above.
</comment> <class> <name>ColorValueSetting</name> <environment>Tools</environment> <super>Tools.SettingType</super> <private>false</private> none <imports></imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.ColorValueSetting
ColorValueSetting is a setting type whose model contains instances of ColorValue.
</comment> <class> <name>ExternalSetting</name> <environment>Tools</environment> <super>Tools.GenericSettingDescriptor</super> <private>false</private> none label helpText editorOpeningBlock <imports></imports> <category>Tools-Settings</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.ExternalSetting
ExternalEditorSetting represents a setting that links to a separate tool. When such a setting appears on a settings page, it is represented as a button that opens the tool. Instance Variables: label <Text> Description of the external editor. editorOpeningBlock <BlockClosure> Evaluated to open the external editor.
</comment> <class> <name>EnumerationSettingModule</name> <environment>Tools</environment> <super>Tools.ModularSettingsPageModule</super> <private>false</private> none <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.EnumerationSettingModule
EnumerationSettingModule is a subapplication of ModularSettingsPage (a page of the SettingsManager that is automatically generated from a group of settings) that represents an EnumerationSetting as a drop-down list.
</comment> <class> <name>SequenceSettingPage</name> <environment>Tools</environment> <super>Tools.SettingsManagerPage</super> <private>false</private> none listHolder workHolder helpText displayBlock acceptBlock postAcceptBlock isChangeOnly validationBlock hasChanges <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.SequenceSettingPage
This page shows either a SequenceSetting or a list of strings created from a collection stored in the image. In the latter case, the collection can be changed using the tool, but the changed state cannot be persisted outside the image. Instance Variables: listHolder <SequenceSetting | ValueHolder with: SequenceableCollection> The model we display in this page. workHolder <ValueHolder with: <List of: String>> A sequence of string representations of the values contained in the collection in the listHolder. helpText <UserMessage | String | nil> If not nil, the text to display when the user clicks the Help button. displayBlock <BlockClosure> Used to transform a collection of items in listHolder to a collection of strings in workHolder. acceptBlock <BlockClosure> Used to transform strings in workHolder back into items of listHolder. Can be 1- or 2-argument. If 1-argument, it is called with the string from workHolder for each string in it. If 2-argument, it is called both with the string from the workHolder and with the original item from the listHolder. The latter case is only allowed for settings pages configured to disallow additions and deletions. postAcceptBlock <BlockClosure> Evaluated after accepting all the items. isChangeOnly <Boolean> Indicates whether the page should prohibit adding and removing items. validationBlock <BlockClosure> Used to check whether new values entered by the user are allowed. hasChanges <Boolean> True if there are changes to be accepted.
</comment> <class> <name>ColorValueSettingModule</name> <environment>Tools</environment> <super>Tools.ModularSettingsPageModule</super> <private>false</private> none <imports></imports> <category>Tools-Settings-UI</category> <attributes> <package>Tools-Settings</package> </attributes> </class> <comment> Tools.ColorValueSettingModule
ColorValueSettingModule is a subapplication of ModularSettingsPage (a page of the SettingsManager that is automatically generated from a group of settings) that represents a ColorValueSetting as a colored swatch with a button opening a color picker.
</comment> <name>DebugSettingsErrors</name> <environment>Tools.SettingsDomain</environment> <private>false</private> <constant>false</constant> <category>settings</category> <initializer>true</initializer> <attributes> <package>Tools-Settings</package> </attributes> <methods> Tools.ModularSettingsPageModule <category>interface opening</category>
postBuildWith: aBuilder (aBuilder componentAt: (self componentWithLabelID ifNil: [^self])) widget labelString: self formattedLabel
spec spec isNil ifTrue: [spec := self interfaceSpecFor: #windowSpec]. ^spec
</methods> <methods> Tools.ModularSettingsPageModule <category>accessing</category>
clientKey "Answer an ID unique to this module, used by the UI spec generating machinery." ^(setting id inject: String new into: [:a :b | a, '-', b]) asSymbol
formattedLabel "Answer the label retrieved from the setting and formatted whatever way is appropriate for this module. For most modules, a trailing semicolon is appropriate as a label of an input filed that follows." ^self ensureTrailingColon: self label
helpText ^setting helpText
id "Answer the ID of the setting this module represents." ^setting id
importSnapshot: aSnapshot "To load setting values into the tool and let the user review the settings before accepting, module provide this method. It loads the value from the snapshot into the buffered value holder instead of the setting itself." valueHolder value: (setting type fromSnapshot: aSnapshot)
interestingMethods "Answer the methods relevant to this module, to allow browsing." ^setting isNil ifTrue: [Array new] ifFalse: [Array with: setting definingMethod]
label setting label ifNotNil: [:label | ^label asString]. setting id ifNotNil: [:idArray | ^idArray last asString]. ^nil
resetUIStateToDefault "To reset setting values to the default in the tool and let the user review the default values before accepting, modules provide this method. It load the default value into the buffered value holder instead of the setting itself." setting hasDefault ifTrue: [valueHolder value: setting default]
setting ^setting.
</methods> <methods> Tools.ModularSettingsPageModule <category>initialize-release</category>
initialize: aSetting "This is the first initialization stage: a module is being created by a setting, but the page it is intended for is not yet known." setting := aSetting. self setupValueHolder
page: parentPage "This is the final initialization stage; after a module has been passed to the page. We can now finish setting up the buffered value holder we use to store the setting value." parent := parentPage. self valueHolder triggerChannel: parent triggerChannel; onChangeSend: #tickle to: parent
</methods> <methods> Tools.ModularSettingsPageModule <category>private</category>
componentWithLabelID self subclassResponsibility
ensureTrailingColon: aString | result | result := aString trimBlanks. ^result last = $: ifTrue: [result] ifFalse: [result copyWith: $:]
setupValueHolder valueHolder := BufferedValueHolder new subject: setting
</methods> <methods> Tools.ModularSettingsPageModule <category>aspects</category>
valueHolder ^valueHolder
</methods> <methods> Tools.ModularSettingsPageModule class <category>interface specs</category>
windowSpec self subclassResponsibility
</methods> <methods> Tools.ModularSettingsPageModule class <category>instance creation</category>
on: aSetting ^self new initialize: aSetting
</methods> <methods> Tools.NumberSettingModule <category>private</category>
componentWithLabelID ^#label
</methods> <methods> Tools.NumberSettingModule <category>validation</category>
validateEntry: aController | newValue | newValue := aController view editValue. ^(setting type isValueValid: newValue) ifTrue: [true] ifFalse: [aController view flash. false]
</methods> <methods> Tools.ExternalSettingModule <category>private</category>
componentWithLabelID ^#label
</methods> <methods> Tools.ExternalSettingModule <category>actions</category>
edit setting openEditor
</methods> <methods> Tools.BooleanSettingModule <category>private</category>
componentWithLabelID ^#checkBox
</methods> <methods> Tools.BooleanSettingModule <category>accessing</category>
formattedLabel ^self label
</methods> <methods> Tools.SettingsWriter <category>private</category>
addNodeFor: each to: root "A setting may signal an invalid setting value error when trying to generate XML to itself, if the current value of that setting does not match its description. For example, when a Boolean setting value is nil. If that is the case, we ignore the setting altogether." [root addNode: each asXmlNode] on: InvalidSettingValueError do: [:ex | ex return]
createRootElement ^Element tag: 'settings' attributes: (Array with: (XML.Attribute name: 'domain' value: domain name)) elements: nil
settingsDocument | document root | document := Document new. root := self createRootElement. settings do: [:each | each hasPersistableState ifTrue: [self addNodeFor: each to: root]]. document addNode: root. ^document
</methods> <methods> Tools.SettingsWriter <category>accessing</category>
writeTo: aStream aStream nextPutAll: '<?xml version="1.0"?>'; cr. self settingsDocument printOn: aStream
</methods> <methods> Tools.SettingsWriter <category>initialize-release</category>
domain: aSettingDomainOrClass domain := aSettingDomainOrClass current. settings := domain settings
domain: aSettingDomain settings: aCollection domain := aSettingDomain. settings := aCollection
</methods> <methods> Tools.SettingsWriter class <category>instance creation</category>
on: aSettingsDomain ^self new domain: aSettingsDomain
on: aSettingsDomain settings: aCollection ^self new domain: aSettingsDomain settings: aCollection
</methods> <methods> Tools.DirectorySettingModule <category>private</category>
componentWithLabelID ^#label
createFilenameAdaptor ^(PluggableAdaptor on: setting) getBlock: [:m | m value asString] putBlock: [:m :v | m value: v asFilename asLogicalFileSpecification] updateBlock: [:m :a :p | true]
setupValueHolder "Since this module can work on both real DirectorySettings and also on String settings, the adaptor to translate between strings and filenames may not be needed." | model | model := setting type isStringSetting ifTrue: [setting] ifFalse: [self createFilenameAdaptor]. valueHolder := BufferedValueHolder new subject: model
</methods> <methods> Tools.DirectorySettingModule <category>actions</category>
browseAction | dialog | dialog := Tools.FileTools.DirectorySelectionDialog new. [dialog initialDirectory: valueHolder value asFilename] on: Error do: [:ex | ex return]. dialog open ifTrue: [valueHolder value: dialog selection asString]
</methods> <methods> Tools.DirectorySettingModule <category>initialize-release</category>
postBuildWith: aBuilder super postBuildWith: aBuilder. setting type isStringSetting ifTrue: [(self controllerAt: #inputField) continuousAccept: true]
</methods> <methods> Tools.SettingType <category>accessing</category>
fromSnapshot: aSnapshot aSnapshot stateNodes size ~= 1 ifTrue: [self reportInvalidStoredData: aSnapshot]. ^self fromXML: aSnapshot stateNodes first.
fromXML: anXMLNode "Subclasses must redefine this to instantiate an object described by the argument XML node, or signal InvalidSettingFileError if the node is junk." self subclassResponsibility
isValueValid: anObject "Subclasses must redefine this to check if the passed object is acceptable for this type of setting, and answer a boolean." self subclassResponsibility
messageTextForInvalidValue: anObject ^#ThisValueIsInvalidForSettingsOfThisType << #dialogs >> 'This value is invalid for settings of this type'
setting ^setting
toXML: anObject "Subclasses must redefine this to generate an XML node somehow representing the object passed as the arugment, or signal InvalidSettingValueError if the object is inappropriate for this type of nodes." self subclassResponsibility
validate: anObject "Signal an error if the argument is not of a type acceptable for this kind of value." (self isValueValid: anObject) ifFalse: [self reportInvalidValue: anObject]
</methods> <methods> Tools.SettingType <category>parameters</category>
xmlTag "Answer a string used as a tag of XML elements representing values of this type." self subclassResponsibility
</methods> <methods> Tools.SettingType <category>private</category>
reportInvalidStoredData: anObject ^InvalidStoredSettingDataError report: anObject in: setting
reportInvalidValue: anObject ^InvalidSettingValueError report: anObject in: setting
verifyStoredDataTag: aNode aNode tag type = self xmlTag ifFalse: [self reportInvalidStoredData: aNode].
</methods> <methods> Tools.SettingType <category>testing</category>
isStringSetting ^false
</methods> <methods> Tools.SettingType <category>initialize-release</category>
initialize
setting: anObject setting := anObject
</methods> <methods> Tools.SettingType <category>SettingModel creation</category>
on: aValueModel "Create a setting with the receiver as the type and value is contained by the object supporting ValueModel protocol passed as the argument." ^SettingModel type: self on: aValueModel
on: anObject aspect: aSymbol "Create a setting with the receiver as the type and value obtained by sending to anObject a message with aSymbol as the selector, and set by sending it that selector with a colon appended." ^SettingModel type: self on: anObject aspect: aSymbol
on: anObject atKey: aSymbol "Create a setting with the receiver as the type and value obtained by sending to anObject the message #at: with aSymbol as the argument, and set by sending it the message #at:put: with aSymbol and the value." ^SettingModel type: self on: anObject atKey: aSymbol
onUISetting: aSymbol "Create a setting with the receiver as the type and value obtained by sending to UI.UISettings a message #preferenceFor:, with aSymbol as the argument, and set by sending it #preferenceFor:put: with aSymbol and the value. This is provided for easy access to the settings stored in the old VW settings framework." ^SettingModel type: self onUISetting: aSymbol
</methods> <methods> Tools.SettingType <category>user interface</category>
editorModuleClassIn: aPage self subclassResponsibility
hasModularEditor ^false
</methods> <methods> Tools.SettingType class <category>instance creation</category>
new ^super new initialize
</methods> <methods> Tools.SettingType class <category>setting creation</category>
on: aValueModel "Create a setting with the receiver instance as the type and value is contained by the object supporting ValueModel protocol passed as the argument." ^self new on: aValueModel
on: anObject aspect: aSymbol "Create a setting with the receiver instance as the type and value obtained by sending to anObject a message with aSymbol as the selector, and set by sending it that selector with a colon appended." ^self new on: anObject aspect: aSymbol
on: anObject atKey: aSymbol "Create a setting with the receiver instance as the type and value obtained by sending to anObject the message #at: with aSymbol as the argument, and set by sending it the message #at:put: with aSymbol and the value." ^self new on: anObject atKey: aSymbol
onUISetting: aSymbol "Create a setting with the receiver instance as the type and value obtained by sending to UI.UISettings a message #preferenceFor:, with aSymbol as the argument, and set by sending it #preferenceFor:put: with aSymbol and the value. This is provided for easy access to the settings stored in the old VW settings framework." ^self new onUISetting: aSymbol
</methods> <methods> Tools.StringSetting <category>accessing</category>
fromXML: aNode self verifyStoredDataTag: aNode. ^aNode children isEmpty ifTrue: [String new] ifFalse: [aNode children first text]
isValueValid: anObject ^anObject isString
toXML: anObject self validate: anObject. ^Element tag: self xmlTag elements: (Array with: (XML.Text text: anObject))
</methods> <methods> Tools.StringSetting <category>parameters</category>
xmlTag ^'string'
</methods> <methods> Tools.StringSetting <category>testing</category>
isPassword ^meaning = #password
isStringSetting ^true
</methods> <methods> Tools.StringSetting <category>user interface</category>
acceptBlockForStringRepresentation ^[:string | string]
displayBlockForStringRepresentation ^[:string | string]
editorModuleClassIn: aPage meaning isNil ifTrue: [^StringSettingModule]. #password = meaning ifTrue: [^StringSettingModule]. #nameOfFile = meaning ifTrue: [^FilenameSettingModule]. #nameOfDirectory = meaning ifTrue: [^DirectorySettingModule]. self error: (#InvalidStringSetting << #dialogs >> 'invalid string setting meaning')
hasModularEditor ^true
validationBlockForStringRepresentation ^[:string | true]
</methods> <methods> Tools.StringSetting <category>initialize-release</category>
beNameOfDirectory "This is string setting, but it really is a directory name. This affects how a setting is rendered by automatically generated pages." meaning := #nameOfDirectory
beNameOfFile "This is string setting, but it really is a file name. This affects how a setting is rendered by automatically generated pages." meaning := #nameOfFile
bePassword "This is string that contains a password. It should be displayed in the regular password style (all asterisks instead of characters)." meaning := #password
</methods> <methods> Tools.StringSetting class <category>instance creation</category>
forNameOfDirectory ^self new beNameOfDirectory
forNameOfFile ^self new beNameOfFile
forPassword ^self new bePassword
</methods> <methods> Tools.EnumerationSetting <category>accessing</category>
choices ^choices
fromXML: aNode | key | self verifyStoredDataTag: aNode. key := aNode children first text asSymbol. ^self choiceAtKey: key ifAbsent: [self reportInvalidStoredData: aNode]
isValueValid: anObject ^choices includes: anObject
keyIn: aSetting "Answer the key corresponding to the current value of aSetting." ^self keyOfChoice: aSetting value
keyIn: aSetting put: anObject "Change the value of aSetting to be the one corresponding to the key anObject." aSetting value: (self choiceAtKey: anObject). ^anObject
keys ^keys
labels ^labels
toXML: anObject self validate: anObject. ^Element tag: self xmlTag elements: (Array with: (XML.Text text: (self keyOfChoice: anObject) asString))
</methods> <methods> Tools.EnumerationSetting <category>initialize-release</category>
keys: keysSequence choices: choicesSequence labels: labelsSequence (keysSequence size ~= choicesSequence size or: [choicesSequence size ~= labelsSequence size]) ifTrue: [self error: (#equalSizeSequencesRequired << #dialogs >> 'invalid setting attribute - key and choice sequence sizes do not match')]. (keysSequence allSatisfy: [:each | each isSymbol]) ifFalse: [self error: (#keysMustAllBeSymbols << #dialogs >> 'invalid setting attribute - keys should all be symbols')]. keys := keysSequence. choices := choicesSequence. labels := labelsSequence
</methods> <methods> Tools.EnumerationSetting <category>parameters</category>
xmlTag ^'choice-key'
</methods> <methods> Tools.EnumerationSetting <category>private</category>
choiceAtKey: anObject ^self choiceAtKey: anObject ifAbsent: [KeyNotFoundError raiseSignal]
choiceAtKey: anObject ifAbsent: aBlock ^choices at: (keys indexOf: anObject ifAbsent: aBlock)
keyOfChoice: anObject ^self keyOfChoice: anObject ifAbsent: [ValueNotFoundError raiseSignal]
keyOfChoice: anObject ifAbsent: aBlock ^keys at: (choices indexOf: anObject ifAbsent: [^aBlock value])
labelAtKey: anObject ^self labelAtKey: anObject ifAbsent: [KeyNotFoundError raiseSignal]
labelAtKey: anObject ifAbsent: aBlock ^labels at: (keys indexOf: anObject ifAbsent: aBlock)
</methods> <methods> Tools.EnumerationSetting <category>user interface</category>
editorModuleClassIn: aPage ^aPage preferredModuleClassForEnumeration: self
hasModularEditor ^true
</methods> <methods> Tools.EnumerationSetting class <category>instance creation</category>
choices: choicesSequence labels: labelsSequence "This instance creation method assumes that the choices sequence contains all symbols and can therefore double as the keys sequence." (choicesSequence allSatisfy: [:each | each isSymbol]) ifFalse: [self error: (#choicesAreNotValidKeys << #dialogs >> 'these choices cannot be used as keys')]. ^self new keys: choicesSequence choices: choicesSequence labels: labelsSequence
keys: keysSequence choices: choicesSequence "The instance creation method that does not requre the label sequence, assuming that plain displayStrings of the choices will do." ^self new keys: keysSequence choices: choicesSequence labels: (choicesSequence collect: [:each | each displayString])
keys: keysSequence choices: choicesSequence labels: labelsSequence "The most generic instance creation method--create an instance initialized with three sequences of equal size. The first sequence is the key sequence, symbols used to identify the current value of the setting in a stored file. The second sequence are the choices, the actual objects stored in the setting. The third one are labels, Strings or UserMessages displayed to the user to explain the choices." ^self new keys: keysSequence choices: choicesSequence labels: labelsSequence
</methods> <methods> Tools.InvalidStoredSettingDataError <category>accessing</category>
data ^data
data: anObject data := anObject
setting ^setting
setting: anObject setting := anObject
</methods> <methods> Tools.InvalidStoredSettingDataError class <category>signaling utilities</category>
data: anObject in: aSetting ^self new data: anObject; setting: aSetting
report: anObject in: aSetting ^(self data: anObject in: aSetting) raiseSignal
</methods> <methods> Tools.InvalidStoredSettingDataError class <category>properties</category>
mayResume ^true
</methods> <methods> Tools.GenericSettingDescriptor <category>persistence</category>
addStateXmlElementsTo: anElement "Subclasses with persistable state must redefine this method to store their state in the XML element passed as the argument. The argument is the <state> child of the <setting> node created to represent this setting. Subclasses in which #hasPersistableState answers false do not need to redefine it." self subclassResponsibility
asXmlNode "This is the message sent by the SettingsWriter to obtain an XML representation of the current state of this setting, to be saved into a file." | root | root := Element tag: 'setting'. root addNode: self idAsXmlNode; addNode: self stateAsXmlNode. ^root
hasPersistableState "Answer whether this setting should be written out to an XML file when setting values are saved. Most settings should be written out, so this default implementation answers true. A subclass may redefine this method if the setting only makes sense in a particular image and should not be transferred to other images, or if a setting is not a meaningful setting at all, for example if it is a SettingPageDecoration. Classes for which the answer is true must redefine #addStateXMLElementsTo: and #restoreFrom: subclass responsibilities. Those for which it is false do not need to." ^true
restoreFrom: aSettingSnapshot "Subclasses with persistable state must redefine this to restore the state of this setting from XML Elements saved in the SettingSnapshot passed as the argument, or signal InvalidStoredSettingDataError if XML Elements are invalid or the value it represents cannot be applied to the image at this time. Subclasses in which #hasPersistableState answers false do not need to redefine it." self subclassResponsibility
</methods> <methods> Tools.GenericSettingDescriptor <category>private</category>
idAsXmlNode | root | root := Element tag: 'id'. id collect: [:each | | key | key := Element tag: 'key'. key addNode: (XML.Text text: each asString). root addNode: key]. ^root
reportInvalidStoredData InvalidStoredSettingDataError raiseSignal
stateAsXmlNode | root | root := Element tag: 'state'. self addStateXmlElementsTo: root. ^root
</methods> <methods> Tools.GenericSettingDescriptor <category>testing</category>
hasModularEditor "Answer true if there is an editor for this included in an auto-generated settings page (ModularSettingsPage)." ^false
hasPrefix: symbolArray "Returns true if the prefix of this page's ID is the same as the argument." ^id size = (symbolArray size + 1) and: [self prefix = symbolArray]
hasSameIdAs: aThingWithId ^id = aThingWithId id
</methods> <methods> Tools.GenericSettingDescriptor <category>comparing</category>
= anObject ^self class = anObject class and: [id = anObject id]
hash ^id hash
</methods> <methods> Tools.GenericSettingDescriptor <category>accessing</category>
definingMethod "The MethodDefinition identifying the method where this setting was defined." ^definingMethod
helpText "Default help text." ^(#NoHelpAvailable << #dialogs >> 'No help available.') asString
id "The ID of the setting, an Array of Symbols uniquely identifying a setting for the purpose of finding it when saved setting values are loaded from a file." ^id
interestingMethods "This message is sent by UI tools that want to get a list of methods somehow relevant to this setting. The defining method is one of them, and composite settings such as SettingGroup would also include interesting methods of the settings they contain." ^definingMethod isNil ifTrue: [Array new] ifFalse: [Array with: definingMethod]
label "Most normal pages return something (aString or a aText) a little more helpful. Some subclasses (decoration for example), there is no label." ^nil
localId "The 'real' name of the setting, that is the last element of its ID. The sequence of the preceding elements is called the prefix." ^id last
position "See the description in the class comment." ^position
prefix "A setting prefix is an array of all the elements of its ID except the last. It is typically used as a grouping utility. For example, all settings to be picked up by an automatically generated page usually have the same prefix." ^id copyFrom: 1 to: id size - 1
reset "Settings that support the concept of a default value should redefine this method to reset their value to default."
</methods> <methods> Tools.GenericSettingDescriptor <category>initialize-release</category>
definingMethod: aMethodDefinition "A method that created this setting can send this message to make itself known to the user interface, so that the method is going to be included in the browser open on a page. Settings created from pragma methods have this attribute set automatically by the pragma processing logic." definingMethod := aMethodDefinition
id: symbolArray "The ID of the setting, a required parameter. For settings generated from pragma methods, it gets set by the pragma processing code and does not require setting in the method body." id := symbolArray
initialize position := 0
position: anObject "A setting may be assigned a position to change its order in pages that automatically order pages. By default, all settings are assigned the position value of 0. Settings with equal position values are ordered in the increasing lexicographic order of their selectors. However, a position value takes precedence over that ordering." position := anObject
</methods> <methods> Tools.GenericSettingDescriptor class <category>instance creation</category>
new ^super new initialize
</methods> <methods> Tools.SettingsGroup <category>private</category>
maybeRun: aBlockOrNil ^aBlockOrNil notNil ifTrue: [aBlockOrNil value]
xmlAttributes ^Array with: (Attribute name: 'class' value: self class name asString)
xmlTag ^'setting-group'
</methods> <methods> Tools.SettingsGroup <category>initialize-release</category>
addSetting: aSetting named: aSymbol "Append the setting to the end of list of settings contained in this group, and use aSymbol to retrieve it from the group later." settings add: aSetting. names at: aSymbol put: aSetting
initialize super initialize. settings := OrderedCollection new. names := Dictionary new
</methods> <methods> Tools.SettingsGroup <category>persistence</category>
addStateXmlElementsTo: anElement | root | self runPreSaveBlock. root := Element tag: self xmlTag attributes: self xmlAttributes elements: nil. settings do: [:each | root addNode: each asXmlNode]. self runPostSaveBlock. anElement addNode: root
restoreFrom: aSettingSnapshot "Individual settings in the group are matched to their saved data by their id. Therefore, it is ok if settings have moved around, or there are some new ones or missing ones." | root snapshots | root := aSettingSnapshot stateNodes first. root tag type ~= self xmlTag ifTrue: [self reportInvalidStoredData]. self runPreApplyBlock. snapshots := Dictionary new. root realElements collect: [:each | | snapshot | snapshot := SettingSnapshot new initializeFromXml: each. snapshots at: snapshot id put: snapshot]. settings do: [:each | | snapshot | snapshot := snapshots at: each id ifAbsent: [nil]. snapshot notNil ifTrue: [each restoreFrom: snapshot]]. self runPostApplyBlock
</methods> <methods> Tools.SettingsGroup <category>accessing-actions</category>
postApplyBlock: anObject "Set the block to be evaluated after applying the values of the settings to the image." postApplyBlock := anObject
postSaveBlock: anObject "Set the block to be evaluated after saving the values of the settings to a file." postSaveBlock := anObject
preApplyBlock: anObject "Set the block to be evaluated before applying the values of the settings to the image." preApplyBlock := anObject
preSaveBlock: anObject "Set the block to be evaluated before saving the values of the settings to a file." preSaveBlock := anObject
runPostApplyBlock ^self maybeRun: postApplyBlock
runPostSaveBlock ^self maybeRun: postSaveBlock
runPreApplyBlock ^self maybeRun: preApplyBlock
runPreSaveBlock ^self maybeRun: preSaveBlock
</methods> <methods> Tools.SettingsGroup <category>accessing</category>
nameOfSetting: aSetting "Answer the name of this setting within the group." ^names keyAtValue: aSetting
reset "Reset all the settings in the group." settings do: [:each | each reset]
settingNamed: aSymbol "Answer the setting with the given name, or fail." ^names at: aSymbol
settingNamed: aSymbol ifAbsent: aBlock "Answer the setting with the given name, or evaluate the block." ^names at: aSymbol ifAbsent: aBlock
settingsDo: aBlock "Perform the block on all the contained settings, in the proper setting order." ^settings do: aBlock
</methods> <methods> Tools.SettingReader <category>accessing</category>
domain ^domain.
readAndSetDomainFrom: aStream "Read settings from the stream. The settings are later accessible by sending the #snapshots message to the class." | parser document | parser := XMLParser new. parser validate: false. document :=parser parse: aStream. document root tag type = 'settings' ifFalse: [self invalidFile]. self applySettingsIn: document.
readFrom: aStream "Read settings from the stream. The settings are later accessible by sending the #snapshots message to the class." | parser document | parser := XMLParser new. parser validate: false. document :=parser parse: aStream. document root tag type = 'settings' ifFalse: [self invalidFile]. self applySettingsIn: document
snapshots "Answer the snapshots that have been read. This message is only meaningful after sending #readFrom: to an instance." ^snapshots
</methods> <methods> Tools.SettingReader <category>private-settings</category>
applySettingsIn: aDocument self verifyDomainOf: aDocument. self loadSnapshotsFrom: aDocument
loadSnapshotsFrom: aDocument snapshots := aDocument root realElements collect: [:each | SettingSnapshot new initializeFromXml: each]
verifyDomainOf: aDocument | domainName | domainName := aDocument root valueOfAttribute: 'domain' ifAbsent: [^self invalidFile]. domain isNil ifTrue: [domain := (domainName asQualifiedReference valueOrDo: [^nil]) current]. domainName = domain name ifFalse: [self invalidFile: (#differentSettingsDomain << #dialogs >> 'different settings domain')]
</methods> <methods> Tools.SettingReader <category>initialize-release</category>
initialize: aSettingDomain domain := aSettingDomain
</methods> <methods> Tools.SettingReader <category>private</category>
invalidFile InvalidStoredSettingDataError raiseSignal
invalidFile: errorMessage InvalidStoredSettingDataError raiseSignal: errorMessage
</methods> <methods> Tools.SettingReader class <category>instance creation</category>
on: aSettingDomain "Create a reader on a particular domain. Note that aSettingDomain can be nil" ^self new initialize: aSettingDomain
</methods> <methods> Tools.SettingsManager <category>aspects</category>
domainHolder "This method was generated by UIDefiner. Any edits made here may be lost whenever methods are automatically defined. The initialization provided below may have been preempted by an initialize method." ^domainHolder isNil ifTrue: [domainHolder := nil asValue] ifFalse: [domainHolder]
pageListHolder "This method was generated by UIDefiner. Any edits made here may be lost whenever methods are automatically defined. The initialization provided below may have been preempted by an initialize method." ^pageListHolder isNil ifTrue: [pageListHolder := SelectionInTree new] ifFalse: [pageListHolder]
</methods> <methods> Tools.SettingsManager <category>actions</category>
applyPressed [self acceptAllPages] on: AbortAcceptRequest do: [:ex | self selectPageWithId: ex requestor id. Dialog warn: ex message]
browsePages | methods | methods := OrderedCollection new. self pageListHolder list root treeDo: [:page | page definingMethod ifNotNil: [:method | methods add: method]]. (#{Refactory.Browser.RefactoringBrowser} valueOrDo: [^self]) openListBrowserOn: methods label: ((#SettingPagesIn1s << #dialogs >> 'Setting Pages in<1s>') expandMacrosWith: self domain name) initialSelection: nil
browseSettingDefinitions self pageListHolder selectionDo: [:page | (#{Refactory.Browser.RefactoringBrowser} valueOrDo: [^self]) openListBrowserOn: page interestingMethods label: ((#SettingsOf1s << #dialogs >> 'Settings of <1s>') expandMacrosWith: page label) initialSelection: nil]
cancelPressed self closeRequest
exportSelected | name stream writer | self pageListHolder selectionDo: [:page | | pageNick | triggerChannel value: true. pageNick := page label asString select: [:any | any isLetter or: [any isDigit]]. name := Dialog requestFileName: (#WriteSettingsToFileNamedC << #dialogs >> 'Write settings to file named:') default: self domain name, '-', pageNick, '.xml'. name isEmpty ifTrue: [^self]. writer := SettingsWriter on: self domain settings: page settings. stream := (name asFilename withEncoding: #'UTF_8') writeStream. [writer writeTo: stream] ensure: [stream close]]
exportSettings | name stream | triggerChannel value: true. name := Dialog requestFileName: (#WriteSettingsToFileNamedC << #dialogs >> 'Write settings to file named:') default: self domain name, '.xml'. name isEmpty ifTrue: [^self]. stream := (name asFilename withEncoding: #'UTF_8') writeStream. [self domain writeTo: stream] ensure: [stream close]
helpPressed | helpViewer | self currentPage ifNotNil: [:page | helpViewer := SimpleHelp helpString: page helpText. SimpleHelp openOn: helpViewer]
importAllSettings | name stream | name := Dialog requestFileName: (#ReadSettingsFromFile << #dialogs >> 'Read settings from file named:') default: self domain name, '.xml'. name isEmpty ifTrue: [^self]. stream := (name asFilename withEncoding: #'UTF_8') readStream. [self domain readFrom: stream] ensure: [stream close]. triggerChannel value: false. self showAcceptedStateUI
importSettingsIntoPage | name stream reader | self pageListHolder selectionDo: [:page | name := Dialog requestFileName: (#ReadSettingsFromFile << #dialogs >> 'Read settings from file named:') default: self domain name, '.xml'. name isEmpty ifTrue: [^self]. stream := (name asFilename withEncoding: #'UTF_8') readStream. reader := SettingReader on: self domain. [reader readFrom: stream] ensure: [stream close]. page importSnapshots: reader snapshots]
okPressed [self acceptAllPages. self closeRequest] on: AbortAcceptRequest do: [:ex | self selectPageWithId: ex requestor id. Dialog warn: ex message]
resetAllToDefault (Dialog confirm: (#ConfirmResetAllSettingsToDefault << #dialogs >> 'Reset all settings on all pages to their default values?')) ifFalse: [^self]. self domain reset. triggerChannel value: false. self showAcceptedStateUI
resetPageToDefault self pageListHolder selectionDo: [:page | page resetUIStateToDefault]
selectPageWithId: symbolArray "Note that even if page with an id #(foo bar) does exist, a page with an id #(foo) might not." | tokens prefix treeModel lastExpanded | tokens := symbolArray readStream. prefix := Array new. treeModel := self pageListHolder list. lastExpanded := nil. [tokens atEnd] whileFalse: [ | page | prefix := prefix copyWith: tokens next. page := treeModel detect: [:some | some id = prefix] ifNone: [nil]. page notNil ifTrue: [treeModel expand: (treeModel indexOf: page). lastExpanded := page]]. self pageListHolder selection: lastExpanded
</methods> <methods> Tools.SettingsManager <category>accessing</category>
currentPage ^self pageListHolder selection
domain ^domainHolder value
pageWithId: symbolArray rootPage treeDo: [:each | each id = symbolArray ifTrue: [^each]]. KeyNotFoundError raiseSignal
tickle self showUnacceptedStateUI
triggerChannel ^triggerChannel
</methods> <methods> Tools.SettingsManager <category>testing</category>
isPageSelected ^self pageListHolder selectionIndex ~= 0
</methods> <methods> Tools.SettingsManager <category>menu building</category>
domainMenu | domains menuBuilder | domains := SettingsDomain allDomains select: [:some | some hasPages]. SequenceableCollectionSorter sort: domains using: [:a :b | a label < b label]. menuBuilder := MenuBuilder new. domains do: [:each | menuBuilder add: each label ->each]. ^menuBuilder menu
pageTreeMenu | menu | menu := self class pageTreeMenu. #{Refactory.Browser.RefactoringBrowser} isDefined ifFalse: [(menu atNameKey: #browseThisPage) hidden: true. (menu atNameKey: #browsePages) hidden: true]. ^menu
</methods> <methods> Tools.SettingsManager <category>private</category>
acceptAllPages [triggerChannel value: true] on: InvalidSettingValueError do: [:ex | ex resume]. self showAcceptedStateUI
refresh | lastSelection | lastSelection := self pageListHolder selection ifNil: [nil] ifNotNil: [:page | page id]. triggerChannel value: false. triggerChannel := false asValue. self release; setNewDomain: self domain. self pageListHolder list expandToLevel: 1. lastSelection notNil ifTrue: [self selectPageWithId: lastSelection] ifFalse: [self pageListHolder selectionIndex: 1]
setNewDomain: aSettingsDomain rootPage := SettingsManagerPageTreeRoot new. rootPage addAllPagesFrom: aSettingsDomain. rootPage treeDo: [:each | each manager: self]. self pageListHolder list: (TreeModel on: rootPage displayRoot: false)
showAcceptedStateUI (self wrapperAt: #applyButton) disable
showUnacceptedStateUI builder ifNotNil: [(self wrapperAt: #applyButton) enable]
</methods> <methods> Tools.SettingsManager <category>initialize-release</category>
domain: aSettingDomain | domain | domain := aSettingDomain isBehavior ifTrue: [aSettingDomain current] ifFalse: [aSettingDomain]. domainHolder value: domain
initialize super initialize. triggerChannel := false asValue. self pageListHolder selectionIndexHolder onChangeSend: #pageSelected to: self. self domainHolder onChangeSend: #domainSelected to: self
postBuildWith: aBuilder super postBuildWith: aBuilder. self showAcceptedStateUI. (self domain isNil and: [#{VisualWorksSettings} isDefined]) ifTrue: [self domain: #{VisualWorksSettings} value current]. self domain notNil ifTrue: [self pageListHolder list expandToLevel: 1. self pageListHolder selectionIndex: 1]. aBuilder window when: #bounds send: #updatePageSubcanvasScrollbar to: self. (self widgetAt: #TreeView) when: #itemCollapsed send: #treeCollapsed to: self. (self widgetAt: #TreeView) when: #itemExpanded send: #treeExpanded to: self.
release self pageListHolder list root ifNotNil: [:eachParent | eachParent treeDo: [:each | each release]]. super release
</methods> <methods> Tools.SettingsManager <category>notifications</category>
domainSelected | newDomain | newDomain := domainHolder value. self release. self setNewDomain: newDomain. (self builder notNil and: [self pageListHolder list notEmpty]) ifTrue: [self pageListHolder list expandToLevel: 1. self pageListHolder selectionIndex: 1]
pageSelected | page | page := self pageListHolder selection. page isNil ifTrue: [^self]. (self widgetAt: #GroupBox) label: ((TreeViewIndentedLabelAndIcon with: page label) icon: page listIcon). "This is the only kind of label with a sane icon positioning." page builder: nil. (self widgetAt: #Subcanvas) client: page spec: page spec builder: self builder newSubBuilder. self updatePageSubcanvasScrollbar
treeCollapsed | treeView index | treeView := self widgetAt: #TreeView. index := treeView selectionIndex. self pageListHolder selectionIndex: index.
treeExpanded | page tree | page := self pageListHolder selection. page isNil ifTrue: [^self]. tree := self pageListHolder list. ((tree isExpandable: page) and: [(tree isExpanded: page) not]) ifTrue: [tree expand: self pageListHolder selectionIndex].
verifyNewSelection: aController ^aController view targetIndex ~= aController view selectionIndex
</methods> <methods> Tools.SettingsManager <category>subcanvas management</category>
pageCanvasWrapper ^self subcanvasWrapper decorator components first
pageSubcanvasScrollbarIsNeeded | pageWrapper | pageWrapper := self pageCanvasWrapper. ^pageWrapper preferredBounds height > pageWrapper bounds height
scrollbarWrapper ^self subcanvasWrapper decorator components last
showPageSubcanvasScrollbar: aBoolean "Be careful to avoid updating the layout if the layout is already what we need, or there will be lots of flickering." | offset | offset := aBoolean ifTrue: [-16] ifFalse: [0]. self pageCanvasWrapper layout rightOffset = offset ifFalse: [self pageCanvasWrapper layout rightOffset: offset. self scrollbarWrapper layout leftOffset: offset. self builder window refresh]
subcanvasWrapper ^self builder componentAt: #Subcanvas
updatePageSubcanvasScrollbar "Errors are possible at some moments due to not all components being there, that is OK." [self showPageSubcanvasScrollbar: self pageSubcanvasScrollbarIsNeeded] on: Error do: [:ex | ex return]
</methods> <methods> Tools.SettingsManager class <category>opening</category>
open "Create and open a 'global' settings manager--that is, a manager with a drop-down list of existing domains to choose from." ^self new openInterface: #multiDomainWindowSpec
open: aSettingsDomain "Create and open a new setting manager, displaying pages from the given domain only." ^self new domain: aSettingsDomain; open
open: aSettingsDomain pageWithId: symbolArray "Compatibility method supporting the old page preselection protocol.." ^self open: aSettingsDomain selectPage: symbolArray
open: aSettingsDomain selectPage: symbolArray "Create and open a new setting manager, displaying pages from the given domain only, with the page having the id equal to symbolArray preselected." ^self new domain: aSettingsDomain; open; selectPageWithId: symbolArray
openOrRaise: settingsDomain "Raise an existing (or open a new) Settings tool and select the page specified by symbolArray." | instances | instances := self instancesOpenOn: settingsDomain current. instances isEmpty ifTrue: [settingsDomain openManager] ifFalse: [| manager | manager := instances asArray first. manager mainWindow raise]
openOrRaise: settingsDomain selectPage: symbolArray "Raise an existing (or open a new) Settings tool and select the page specified by symbolArray." | instances | instances := self instancesOpenOn: settingsDomain current. instances isEmpty ifTrue: [settingsDomain openManagerPage: symbolArray] ifFalse: [| manager | manager := instances asArray first. manager selectPageWithId: symbolArray. manager mainWindow raise]
</methods> <methods> Tools.SettingsManager class <category>accessing</category>
instancesOpenOn: aSettingsDomain | models | models := OrderedCollection new. Screen default allScheduledControllersDo: [:each | | model | model := each model. ((model isKindOf: self) and: [model domain = aSettingsDomain]) ifTrue: [models add: model]]. ^models
refreshInstancesOpenOn: aSettingsDomain "Make all manager windows open on the given domain refresh themselves. This message is typically sent by the domains after changes to their pragma methods defining settings or pages." (self instancesOpenOn: aSettingsDomain) do: [:each | each refresh]
</methods> <methods> Tools.FilenameSetting <category>accessing</category>
fromXML: aNode "The #isText test is to allow loading settings saved by versions prior to 7.1 release candidate. It may be dropped in future, and #filenameFromXml: used unconditionally." | data | self verifyStoredDataTag: aNode. data := aNode realElements first. ^data isText ifTrue: [LogicalFilename named: data text] ifFalse: [self filenameFromXml: data]
isValueValid: anObject ^(anObject isKindOf: LogicalFilename) and: [mustExist not or: [[anObject asFilename exists] on: Error do: [:ex | ex return: false]]]
mustExist ^mustExist
toXML: anObject self validate: anObject. ^Element tag: self xmlTag elements: (Array with: (self filenameAsXml: anObject))
</methods> <methods> Tools.FilenameSetting <category>parameters</category>
xmlTag ^'filename'
</methods> <methods> Tools.FilenameSetting <category>initialize-release</category>
initialize super initialize. mustExist := false
mustExist: aBoolean mustExist := aBoolean
</methods> <methods> Tools.FilenameSetting <category>user interface</category>
acceptBlockForStringRepresentation ^[:string | SystemUtils makeFilenamePortable: string]
displayBlockForStringRepresentation ^[:filename | filename asString]
editorModuleClassIn: aPage ^FilenameSettingModule
hasModularEditor ^true
validationBlockForStringRepresentation ^[:string | | logicalName | [logicalName := SystemUtils makeFilenamePortable: string. mustExist not or: [logicalName asFilename exists]] on: Error do: [:ex | ex return: false]]
</methods> <methods> Tools.FilenameSetting <category>private</category>
filenameAsXml: aLogicalFilename | componentElements | componentElements := aLogicalFilename componentStrings collect: [:each | Element tag: 'path-component' elements: (Array with: (XML.Text text: each))]. ^Element tag: 'logical-filename' elements: componentElements
filenameFromXml: aNode | components | aNode tag type = 'logical-filename' ifFalse: [self reportInvalidStoredData: aNode]. components := (aNode realElements collect: [:each | each tag type = 'path-component' ifFalse: [self reportInvalidStoredData: each]. each children first text]) asOrderedCollection. self rewriteIfOldGoodiesPath: components. ^LogicalFilename fromComponents: components
rewriteIfOldGoodiesPath: componentsCollection "As of VisualWorks 7.4, 'goodies' directory has been renamed to 'contributed' and flattened to get rid of its two subdirectories, 'parc' and 'other'. To avoid problems when loading a settings file saved with an earlier version whose saved parcel paths refer to 'goodies', we quietly rewrite them here to use the new name and discard one level of '*' wildcards to account for the flattening." componentsCollection first = '$(VISUALWORKS)' ifTrue: [ | index | index := componentsCollection indexOf: 'goodies'. index = 0 ifTrue: [^self]. componentsCollection at: index put: 'contributed'. ((componentsCollection last: componentsCollection size - index) allSatisfy: [:each | each = '*']) ifTrue: [componentsCollection removeLast]]
</methods> <methods> Tools.FilenameSetting class <category>instance creation</category>
mustExist "Create an instance of the setting that will only be considered valid if the file referenced by the setting does exist." ^self new mustExist: true
</methods> <methods> Tools.SettingsManagerPageTreeRoot <category>enumerating</category>
treeDo: aBlock children do: [:each | each treeDo: aBlock]
</methods> <methods> Tools.SettingsManagerPageTreeRoot <category>accessing</category>
addAllPagesFrom: aSettingDomain domain := aSettingDomain. domain withPagesCachedDo: [children addAll: (self organizePages: aSettingDomain pages)]
addPage: aSettingsPageApplication children add: aSettingsPageApplication
children ^children
label ^String new
</methods> <methods> Tools.SettingsManagerPageTreeRoot <category>private</category>
findPlaceFor: aPage in: rootPages rootPages do: [:each | (aPage isChildOf: each) ifTrue: [^each findPlaceFor: aPage]]. rootPages add: aPage. rootPages copy do: [:each | (each isChildOf: aPage) ifTrue: [rootPages remove: each. aPage findPlaceFor: each]]
organizePages: aCollection | rootPages | rootPages := OrderedCollection new. aCollection do: [:each | self findPlaceFor: each in: rootPages]. SequenceableCollectionSorter sort: rootPages using: [:a :b | a position = b position ifTrue: [a label asString < b label asString] ifFalse: [a position < b position]]. ^rootPages
</methods> <methods> Tools.SettingsManagerPageTreeRoot <category>initialize-release</category>
initialize children := OrderedCollection new
</methods> <methods> Tools.SettingsManagerPageTreeRoot class <category>instance creation</category>
new ^super new initialize
</methods> <methods> Tools.SettingSnapshot <category>private</category>
idFromXml: anElement | components | components := anElement realElements collect: [:each | | text | text := each children first. text isText ifFalse: [self reportInvalidStoredData]. text text asSymbol]. ^components asArray
reportInvalidStoredData InvalidStoredSettingDataError raiseSignal
stateFromXml: anElement ^anElement realElements
</methods> <methods> Tools.SettingSnapshot <category>initialize-release</category>
initializeFromXml: anElement id := self idFromXml: (anElement elementNamed: 'id'). stateNodes := self stateFromXml: (anElement elementNamed: 'state')
</methods> <methods> Tools.SettingSnapshot <category>accessing</category>
id "Answer the snapshot ID. The ID is an array of symbols that identifies the setting the snapshot is to be applied to." ^id
stateNodes "XML nodes with the setting state information read from the file. The nodes are interpreted by the setting itself; the snapshot knows nothing about them and merely stores them." ^stateNodes
</methods> <methods> Tools.ErrorAsSetting <category>accessing</category>
createModuleFor: aPage ^ErrorAsSettingModule on: self
messageText ^messageText
messageText: anObject messageText := anObject
</methods> <methods> Tools.ErrorAsSetting <category>testing</category>
hasModularEditor ^true
hasPersistableState ^false
</methods> <methods> Tools.InvalidSettingValueError <category>accessing</category>
offendingValue ^offendingValue
offendingValue: anObject offendingValue := anObject
setting ^setting
setting: anObject setting := anObject
</methods> <methods> Tools.InvalidSettingValueError class <category>instance creation/signaling</category>
report: unacceptableObject ^(self value: unacceptableObject) raiseSignal
report: unacceptableObject in: aSetting ^(self value: unacceptableObject in: aSetting) raiseSignal
value: unacceptableObject ^self new offendingValue: unacceptableObject; yourself
value: unacceptableObject in: aSetting ^self new offendingValue: unacceptableObject; setting: aSetting; yourself
</methods> <methods> Tools.InvalidSettingValueError class <category>properties</category>
mayResume ^true
</methods> <methods> Tools.SettingsPageDecoration <category>testing</category>
hasModularEditor "The whole point of any page decoration is placing a modular editor (which is not really an editor of course) on a generated page." ^true
hasPersistableState "Decorations are only there to show up on pages and have no state at all to be persisted." ^false
</methods> <methods> Tools.SettingsPageDecoration <category>private</category>
moduleClass self subclassResponsibility
</methods> <methods> Tools.SettingsPageDecoration <category>accessing</category>
createModuleFor: aPage ^self moduleClass on: self
</methods> <methods> Tools.SettingsPageSeparator <category>private</category>
moduleClass ^SettingsPageSeparatorModule
</methods> <methods> Tools.NumberSetting <category>private</category>
valueInRange: aNumber ^min isNil ifTrue: [self valueInRangeNoMinimum: aNumber] ifFalse: [self valueInRangeWithMinimum: aNumber]
valueInRangeNoMinimum: aNumber ^max isNil ifTrue: [true] ifFalse: [aNumber <= max]
valueInRangeWithMinimum: aNumber ^max isNil ifTrue: [min <= aNumber] ifFalse: [aNumber between: min and: max]
</methods> <methods> Tools.NumberSetting <category>accessing</category>
fromXML: aNode self verifyStoredDataTag: aNode. ^Number readFrom: aNode children first text readStream
isValueValid: anObject ^(anObject isKindOf: Number) and: [self valueInRange: anObject]
max ^max
max: anObject max := anObject
min ^min
min: anObject min := anObject
toXML: anObject self validate: anObject. ^Element tag: self xmlTag elements: (Array with: (XML.Text text: anObject printString))
</methods> <methods> Tools.NumberSetting <category>parameters</category>
xmlTag ^'number'
</methods> <methods> Tools.NumberSetting <category>user interface</category>
editorModuleClassIn: aPage ^NumberSettingModule
hasModularEditor ^true
</methods> <methods> Tools.NumberSetting class <category>instance creation</category>
max: maxValue "Create the type of a numeric setting that can take on values less than or equal to the argument." ^self new max: maxValue
min: minValue "Create the type of a numeric setting that can take on values greater than or equal to the argument." ^self new min: minValue
min: minValue max: maxValue "Create the type of a numeric setting that can take on values between these two numbers (inclusively)." ^self new min: minValue; max: maxValue
</methods> <methods> Tools.SettingsDomain <category>accessing</category>
current "For uniform protocol with the class, so that the class and the instance could be passed around interchangeably, as long as the receiver uses the result of sending #current to the passed object." ^self
hasPages ^(Pragma allNamed: #settingsPage: in: self class class) notEmpty
label "The label is used in various UI tools, unlike the domain name which is machine readable. This method generates a label from the class name. Subclasses may want to redefine this method if the label it produces is not good enough." | result | result := (String new: 50) writeStream. (self name piecesCutWhere: [:p :n | p isLowercase & n isUppercase]) do: [:word | result nextPutAll: word] separatedBy: [result space]. ^result contents
name ^self class name asString
pages ^cachedPages isNil ifTrue: [self class collectPages] ifFalse: [cachedPages]
pageWithId: symbolArray ^self pageWithId: symbolArray ifAbsent: [KeyNotFoundError raiseSignal]
pageWithId: symbolArray ifAbsent: aBlock ^self pages detect: [:some | some id = symbolArray] ifNone: aBlock
reset "Reset the value of all the settings in this domain to the default." self settings do: [:each | each reset]
settings "Answer a collection of all settings in this domain. Note that this (and, transitively, all other setting access messages) is a relatively expensive operation if this message is sent 'out of the blue'. To greatly improve the performance of such access, see #withSettingsCachedDo:." ^cachedSettings notNil ifTrue: [cachedSettings] ifFalse: [self class collectSettings]
settingsWithPrefix: symbolArray "Answer a collection of settings with a given prefix. In other words, the settings all but the last symbols of whose ID are equal to the argument array. For example, querying for prefix #(foo) will find settings with IDs #(foo bar) and #(foo zork). Note the performance warning in the #settings method." ^self settings select: [:some | some hasPrefix: symbolArray]
settingsWithPrefix: symbolArray except: symbolCollection "Answer a collection of settings with a given prefix. In other words, the settings all but the last symbols of whose ID are equal to the argument array, except those whose 'own' name (the last element of the ID) is listed in the symbolCollection. Note the performance warning in the #settings method." | choices | choices := self settings select: [:some | some hasPrefix: symbolArray]. ^choices reject: [:some | symbolCollection includes: some id last]
settingWithId: anArray "Answer the setting with the given ID, or fail. Note the performance warning in the #settings method." ^self settingWithId: anArray ifAbsent: [KeyNotFoundError raise]
settingWithId: anArray ifAbsent: aBlock "Answer the setting with the given ID, or evaluate the block. Note the performance warning in the #settings method." ^self settings detect: [:some | some id = anArray] ifNone: aBlock
withPagesCachedDo: aBlock "See the comment to #withSettingsCachedDo: method." self withSettingsCachedDo: [cachedPages := self class collectPages. aBlock ensure: [cachedPages := nil]]
withSettingsCachedDo: aBlock "Evaluate the block so that the settings that exist at the time of the message send are cached through the block's dynamic extent. In other words, while the block executes, the message #settings sent to this instance always answers the same collection of settings rather than recreating it on each send as it does otherwise. This is a good compromise between not rebuilding settings every time they are requested and maintaining a cache at all times." cachedSettings := self class collectSettings. ^aBlock ensure: [cachedSettings := nil]
</methods> <methods> Tools.SettingsDomain <category>private</category>
domainRoot "See the explanation in the class-side method." ^self class domainRoot current
installSnapshots: aCollection | settingsById | settingsById := Dictionary new. self settings do: [:each | settingsById at: each id put: each]. unappliedSnapshots := OrderedCollection new. aCollection do: [:each | | setting | setting := settingsById at: each id ifAbsent: [nil]. setting isNil ifTrue: [unappliedSnapshots add: each] ifFalse: [self restore: setting from: each]]
restore: setting from: each "I think unacceptable settings and those having invalid entry format can silently be ignored and not considered unaccepted. We'll see if it holds." [setting restoreFrom: each] on: InvalidSettingValueError, InvalidStoredSettingDataError do: [:ex | ex return]
</methods> <methods> Tools.SettingsDomain <category>initialize-release</category>
initialize unappliedSnapshots := Array new
</methods> <methods> Tools.SettingsDomain <category>reading/writing</category>
readFrom: aStream "Read the values of settings from the stream, which should be a stream open on the XML snapshot of setting values previously saved by the same domain." | reader | reader := SettingReader on: self. reader readFrom: aStream. self domainRoot installSnapshots: reader snapshots
writeTo: aStream "Write the current value of the settings to the stream." (SettingsWriter on: self) writeTo: aStream
</methods> <methods> Tools.SettingsDomain <category>notifications</category>
tryInstallingSomeUnappliedSnapshots "Since this is called by #instanceMethodsChanged which may be called often, be careful about doing things efficiently. In particular, do not instantiate settings unless it is necessary, and instead dealing with pragmas directly." | pragmas | pragmas := Dictionary new. (Pragma allNamed: #setting: in: self class class) do: [:each | pragmas at: (each argumentAt: 1) put: each]. (Pragma allNamed: #setting:position: in: self class class) do: [:each | pragmas at: (each argumentAt: 1) put: each]. unappliedSnapshots copy do: [:each | | pragma setting | (pragmas includesKey: each id) ifTrue: [pragma := pragmas at: each id. setting := self class perform: pragma selector. [setting restoreFrom: each] on: InvalidStoredSettingDataError do: [:ex | ex return]. unappliedSnapshots remove: each]]
</methods> <methods> Tools.SettingsDomain class <category>pluggable blocks</category>
filenameMustExistValidationBlock ^[:string | [ | filename | filename := SystemUtils makeFilenamePortable: string. filename asFilename exists] on: Error do: [:ex | ex return: false]]
filenameToStringBlock ^[:filename | filename asString]
filenameValidationBlock ^[:string | [SystemUtils makeFilenamePortable: string. true] on: Error do: [:ex | ex return: false]]
stringToFilenameBlock ^[:string | SystemUtils makeFilenamePortable: string]
</methods> <methods> Tools.SettingsDomain class <category>instance creation</category>
new ^super new initialize
</methods> <methods> Tools.SettingsDomain class <category>accessing</category>
allDomains ^self allSubclasses collect: [:each | each current]
current "Answer the singleton instance of the receiver domain." current isNil ifTrue: [current := self new]. ^current
domainRoot "A settings domain is composed of a tree of classes: one of them is a direct subclass of SettingsDomain, and the rest are subclasses of that direct subclass. We call that direct subclass the domain root. This message, when sent to a class, returns the root of the domain the class belongs to." | hereClass | self == SettingsDomain ifTrue: [self error: (#InappropriateMessageForSettingsDomain << #dialogs >> 'This message is inappropriate for SettingsDomain class, because it is abstract and should not be used to store any settings. It should only be sent to SettingsDomain subclasses.')]. hereClass := self. [hereClass superclass == SettingsDomain] whileFalse: [hereClass := hereClass superclass]. ^hereClass
openManager "Open the settings manager on this domain." ^SettingsManager open: self
openManagerPage: symbolArray "Open the settings manager on this domain, and pre-select a page with the given ID." ^SettingsManager open: self selectPage: symbolArray
settingsWithPrefix: symbolArray "Answer a collection of settings whose prefix (elements up to but not including the last one) are the same as the argument array. For example, a search for settingsWithPrefix: #(foo bar) will select settings with IDs #(foo bar one), #(foo bar two). Note that if a setting with id #(foo bar) will not be selected even if it exists." ^self current settingsWithPrefix: symbolArray
settingsWithPrefix: symbolArray except: symbolCollection "Answer a collection of settings whose prefix (elements of the ID up to but not including the last one) are the same as the argument array, excluding those settings whose last ID element occurs in symbolCollection. For example a search for settingsWithPrefix: #(foo bar) except: #(three four) will find elements with IDs #(foo bar one), #(foo bar two), and #(foo bar five), but not #(foo bar three) or #(foo bar four)." ^self current settingsWithPrefix: symbolArray except: symbolCollection
settingWithId: anArray "Answer a setting with the ID equal to the argument. A KeyNotFoundError will be signalled if there is no such setting." ^self current settingWithId: anArray
settingWithId: anArray ifAbsent: aBlock "Answer a setting with the ID equal to the argument. Evaluate aBlock if there is no such setting." ^self current settingWithId: anArray ifAbsent: aBlock
</methods> <methods> Tools.SettingsDomain class <category>private</category>
addPageFromPragma: aPragma to: aCollection | page | page := [self perform: aPragma selector] on: Error do: [:ex | self reportExceptionCausedBy: aPragma. self shouldDebugSettingsErrors ifFalse: [^self]. ex return: (self pageWithErrorCausedBy: aPragma)]. page ifNil: [^self]. page id: (aPragma argumentAt: 1). aPragma numArgs > 1 ifTrue: [page position: (aPragma argumentAt: 2)]. page definingMethod: aPragma methodDefinition. aCollection add: page
addSettingFromPragma: aPragma to: aCollection | setting | setting := [self perform: aPragma selector] on: Error do: [:ex | self reportExceptionCausedBy: aPragma. self shouldDebugSettingsErrors ifFalse: [^self]. ex return: self createSettingForError]. setting ifNil: [^self]. setting id: (aPragma argumentAt: 1). aPragma numArgs > 1 ifTrue: [setting position: (aPragma argumentAt: 2)]. setting definingMethod: aPragma methodDefinition. aCollection add: setting
createSettingForError ^ErrorAsSetting new messageText: #AnErrorOccurredInSetting << #labels >> 'An error has occurred in this setting definition'
pageWithErrorCausedBy: aPragma | setting page | setting := ErrorAsSetting new. setting messageText: #AnErrorOccurredInPageDefinition << #labels >> 'An error has occurred in this page definition'. aPragma numArgs > 1 ifTrue: [setting position: (aPragma argumentAt: 2)]. setting id: (aPragma argumentAt: 1); definingMethod: aPragma methodDefinition. page :=ModularSettingsPage new. page id: (aPragma argumentAt: 1); label: #OBerrorCB << #labels >> '[Error]'; icon: (ListIconLibrary visualFor: #error). aPragma numArgs > 1 ifTrue: [page position: (aPragma argumentAt: 2)]. page addSetting: setting. ^page
reportExceptionCausedBy: aPragma Transcript show: (#ExceptionIn1P << #dialogs >> '<n>Exception in <1p>' expandMacrosWith: aPragma methodDefinition)
sortSettings: aCollection SequenceableCollectionSorter sort: aCollection using: [:a :b | a position = b position ifTrue: [a definingMethod selector < b definingMethod selector] ifFalse: [a position < b position]]
</methods> <methods> Tools.SettingsDomain class <category>notifications</category>
classMethodsChanged "Class methods containing pragmas--that is, methods defining settings and pages-- have changed. Do the necessary updates. For robustness, only allow exception to happen if there is an outer handler; meaning someone intends to handle it. Otherwise, report to the transcipt but muffle. Run the following code to force a notifier to come up for debugging." "[self classMethodsChanged] on: Error do: [:ex | ex pass]" [self unsafeUpdatePragmaDependents] on: Error do: [:ex | ex isNested ifTrue: [ex pass] ifFalse: [Transcript cr; show: (#ErrorUpdatingSettingsInClass << #dialogs >> 'Exception while updating <1s>: <2p> in <3p>. To debug, evaluate the following expression: "[<4s> classMethodsChanged] on: Error do: [:ex | ex pass]".' expandMacrosWith: self class name with: ex with: ex initialContext sender with: self printString). ex exit]]
</methods> <methods> Tools.SettingsDomain class <category>privileged</category>
collectPages | pages | pages := OrderedCollection new: 20. pages addAll: (self collectPagesForOne: self). self subclasses do: [:each | pages addAll: (each collectPages)]. ^pages
collectPagesForOne: aClass | pages | pages := OrderedCollection new: 20. (Pragma allNamed: #settingsPage: in: aClass class) do: [:each | aClass addPageFromPragma: each to: pages]. (Pragma allNamed: #settingsPage:position: in: aClass class) do: [:each | aClass addPageFromPragma: each to: pages]. ^pages
collectSettings "Answer a collection of settings defined in this domain--that is, in this class and its subclasses." | settings | settings := OrderedCollection new: 20. self withAllSubclasses do: [:each | settings addAll: (self collectSettingsForOne: each)]. self sortSettings: settings. ^settings
collectSettingsForOne: aClass "Answer a collection of settings defined in this class only." | settings | settings := OrderedCollection new: 20. (Pragma allNamed: #setting: in: aClass class) do: [:each | aClass addSettingFromPragma: each to: settings]. (Pragma allNamed: #setting:position: in: aClass class) do: [:each | aClass addSettingFromPragma: each to: settings]. ^settings
unsafeUpdatePragmaDependents "Update anything that depends on the current set of setting or page definitions. The method is unsafe in the sense that it does not try to handle any exceptions, so any problems that may occur will cause walkbacks unless handed by the sender. The abstract root of the domain tree has no settings and should not try to update anything even if pragma methods are added or removed." self == SettingsDomain ifTrue: [^self]. self domainRoot current tryInstallingSomeUnappliedSnapshots. ^#{Tools.SettingsManager} ifDefinedDo: [:managerClass | managerClass refreshInstancesOpenOn: self current]
</methods> <methods> Tools.SettingsDomain class <category>testing</category>
isRuntime "Answer whether this is a runtime image. We assume that a runtime image is the one that includes a RuntimePackager." ^#{RuntimePackager} isDefined
shouldDebugSettingsErrors "Answer whether settings or pages that could not be created due to an exception in their declaration should be replaced with an 'Error occurred, click this button to debug' module in the settings manager. This only happens if this is not a runtime image, and if setting debugging has not been turned off." ^DebugSettingsErrors and: [self isRuntime not]
</methods> <methods> Tools.SettingsDomain class <category>utilities</category>
readFromFile: aFilename "Read all settings from a given file and apply them to itself." "VisualWorksSettings readFromFile: 'settings.xml' asFilename" | stream | stream := aFilename readStream. [self current readFrom: stream] ensure: [stream close]
writeToFile: aFilename "Write all settings from this domain into a file." "VisualWorksSettings writeToFile: 'settings.xml' asFilename" | stream | stream := aFilename writeStream. [self current writeTo: stream] ensure: [stream close]
</methods> <methods> Tools.SettingsManagerPage <category>page tree building</category>
findPlaceFor: aPage children do: [:each | (aPage isChildOf: each) ifTrue: [^each findPlaceFor: aPage]]. children add: aPage. children copy do: [:each | (each isChildOf: aPage) ifTrue: [children remove: each. aPage findPlaceFor: each]]. SequenceableCollectionSorter sort: children using: [:a :b | a position = b position ifTrue: [a label asString < b label asString] ifFalse: [a position < b position]]
isChildOf: anotherPage | anotherId | anotherId := anotherPage id. ^anotherId size < id size and: [(id copyFrom: 1 to: anotherId size) = anotherId]
</methods> <methods> Tools.SettingsManagerPage <category>initialize-release</category>
definingMethod: aMethodDefinition definingMethod := aMethodDefinition
icon: anOpaqueImage icon := anOpaqueImage
id: symbolArray id := symbolArray
initialize super initialize. position := 0. children := OrderedCollection new. icon := ListIconLibrary visualFor: #settingCheck
manager: aSettingsManager manager := aSettingsManager
release self settings do: [:each | each release]. super release
</methods> <methods> Tools.SettingsManagerPage <category>enumeration</category>
treeDo: aBlock aBlock value: self. children do: [:each | each treeDo: aBlock]
</methods> <methods> Tools.SettingsManagerPage <category>accessing</category>
children ^children
children: anObject children := anObject
definingMethod ^definingMethod
helpText "Subclasses might want to redefine this to return a help message. This is the message displayed when the user clicks the Help button." ^#noHelpIsProvidedForThisPage << #dialogs >> 'No help was provided for this page.'
id ^id
interestingMethods "This method should return the collection of MethodDefinitions of methods browsed when the user selects Browse for this page. Redefine it if there are other relevant methods to include them in the result." ^definingMethod isNil ifTrue: [Array new] ifFalse: [Array with: definingMethod]
label ^label asString
label: anObject label := anObject
listIcon ^icon
position ^position
position: anObject position := anObject
settings "Subclasses should redefine this to answer a collection of the settings the page contains, when it does contain any settings, in the sense object that support the GenericSettingDescriptor interface." ^#()
spec "This is the spec for our page; the thing that appears in the right hand part of the settings window when this page is selected. Subclasses may redefine this to answer a different spec name, or a pre-built spec object itself." ^#windowSpec
</methods> <methods> Tools.SettingsManagerPage <category>notifications</category>
tickle "Someone, a widget or a value holder, notifies us that the user has changed some of the displayed information and therefore whoever cares about unaccepted changes should know that there are some." manager tickle
</methods> <methods> Tools.SettingsManagerPage <category>actions</category>
importSnapshots: aCollection "This message is sent when the user imports settings from a settings file into this specific page. A page should reimplement this method properly to apply the saved state *to the widgets*."
resetUIStateToDefault "Subclasses may want to redefine this to set the UI to the state declared by the settings as the default. Note that what is required is resetting the *UI* to the default, not the setting targets - in other words, not sending #reset to the settings."
</methods> <methods> Tools.ModularSettingsPage <category>accessing</category>
builder: aBuilderOrNil "When the builder is niled out (to ensure successful rebuilding with a different one, which the UI framework cannot handle on its own), have the modules do the same. Without that, widgets may not initialize properly on successive visits to a page." super builder: aBuilderOrNil. aBuilderOrNil isNil ifTrue: [modules do: [:each | each builder: nil]]
formattedLabel ^self ensureTrailingColon: self label
helpText | stream helpfulModules | stream := TextStream on: (String new: 100). helpfulModules := modules reject: [:each | each label isNil or: [each helpText isNil]]. helpfulModules do: [:each | stream nextPutAllText: each label asText allBold; nextPutAll: ' - '. stream nextPutAll: each helpText] separatedBy: [stream cr; cr]. ^stream contents
interestingMethods | interestingMethods | interestingMethods := super interestingMethods asOrderedCollection. modules do: [:each | interestingMethods addAll: each interestingMethods]. ^interestingMethods
moduleForSetting: aSetting ^modules detect: [:each | each setting = aSetting] ifNone: [nil].
moduleForSettingWithId: aSettingId ^modules detect: [:each | each setting id = aSettingId] ifNone: [nil].
settings ^settings
triggerChannel ^manager triggerChannel
</methods> <methods> Tools.ModularSettingsPage <category>actions</category>
importSnapshots: aCollection | modulesById | modulesById := Dictionary new. modules do: [:each | modulesById at: each id put: each]. aCollection do: [:each | (modulesById includesKey: each id) ifTrue: [(modulesById at: each id) importSnapshot: each]]
resetUIStateToDefault modules do: [:each | each resetUIStateToDefault]
</methods> <methods> Tools.ModularSettingsPage <category>interface management</category>
enable: aBoolean module: aModule | wrapper | wrapper := self builder componentAt: aModule clientKey. wrapper notNil ifTrue: [wrapper isEnabled: aBoolean]
postBuildWith: aBuilder "Need to fire trackers if there are any so the initial item state gets set properly." super postBuildWith: aBuilder. enablementTrackers do: [:each | each value]
preBuildWith: aBuilder "The builder should be configured so that it can find the right modules and specs to hook up to the subcanvases we've prepared for them in our generated spec." super preBuildWith: aBuilder. modules do: [:each | aBuilder clientAt: each clientKey put: each; subCanvasAt: each clientKey at: #spec put: each spec]
when: aModel valueSatisfies: aBlock enable: aModule | tracker | tracker := [self enable: (aBlock value: aModel value) == true module: aModule]. aModel onChangeSend: #value to: tracker. enablementTrackers add: tracker
when: aModel valueSatisfies: aBlock enableAll: moduleCollection | tracker | tracker := [ | beOn | beOn := (aBlock value: aModel value) == true. moduleCollection do: [:each | self enable: beOn module: each]]. aModel onChangeSend: #value to: tracker. enablementTrackers add: tracker
</methods> <methods> Tools.ModularSettingsPage <category>interface generation</category>
generateComponentSpec "Modules are stacked on top of each other, each allocated the full width or our spec and as many pixels of vertical space as it desires, with a few pixel gap between the modules." | specs top | specs := OrderedCollection new. top := 0. modules do: [:module | | moduleSpec | module page: self. moduleSpec := self subcanvasSpecFor: module top: top. specs add: moduleSpec. top := moduleSpec layout bottomOffset + self intramodularGap]. ^SpecCollection new collection: specs
generateSpec | stub | stub := self class windowSpec decodeAsLiteralArray. stub component: self generateComponentSpec. ^stub
intramodularGap "This many pixels are added between the modules." ^2
spec spec isNil ifTrue: [spec := self generateSpec]. ^spec
subcanvasSpecFor: aModule top: top | holderSpec layout preferredHeight | holderSpec := SubCanvasSpec new. holderSpec name: aModule clientKey; hasBorder: false; clientKey: aModule clientKey; majorKey: aModule clientKey; minorKey: #spec. preferredHeight := aModule spec window bounds height. layout := LayoutFrame leftFraction: 0 offset: 0 topFraction: 0 offset: top rightFraction: 1 offset: 0 bottomFraction: 0 offset: top + preferredHeight. holderSpec layout: layout. ^holderSpec
</methods> <methods> Tools.ModularSettingsPage <category>initialize-release</category>
initialize super initialize. settings := OrderedCollection new. modules := OrderedCollection new. preferredModuleClasses := Dictionary new. enablementTrackers := OrderedCollection new
</methods> <methods> Tools.ModularSettingsPage <category>configuration</category>
addAllSettings: settingCollection settingCollection do: [:each | self addSetting: each]
addModule: aModule modules add: aModule
addSetting: aSetting aSetting hasModularEditor ifTrue: [settings add: aSetting. modules add: (aSetting createModuleFor: self)]
settings: aCollection settings := (aCollection select: [:each | each hasModularEditor]) asOrderedCollection. modules := settings collect: [:each | each createModuleFor: self]
useRadioButtonsForEnumerations preferredModuleClasses at: #Enumeration put: EnumerationSettingRadioModule
useSpinButtonsForIntegers preferredModuleClasses at: #Integer put: IntegerSettingSpinButtonModule
</methods> <methods> Tools.ModularSettingsPage <category>page class choosing</category>
preferredModuleClassForEnumeration: aType ^preferredModuleClasses at: #Enumeration ifAbsent: [EnumerationSettingModule]
preferredModuleClassForInteger: aType ^preferredModuleClasses at: #Integer ifAbsent: [NumberSettingModule]
</methods> <methods> Tools.FilenameSettingModule <category>actions</category>
browseAction | newName | newName := Dialog requestFileName: (#Filename << #dialogs >> 'Filename') default: valueHolder value. newName notEmpty ifTrue: [valueHolder value: newName]
</methods> <methods> Tools.StringSettingModule <category>private</category>
componentWithLabelID ^#label
</methods> <methods> Tools.StringSettingModule <category>initialize-release</category>
postBuildWith: aBuilder super postBuildWith: aBuilder. (self controllerAt: #inputField) continuousAccept: true. setting type isPassword ifTrue: [(self widgetAt: #inputField) displayContents passwordChar: $*]
</methods> <methods> Tools.SettingModel <category>initialize-release</category>
default: anObject "Register anObject as the default value of the setting, that is the value that will be assigned to the setting by the #reset operation." hasDefault := true. default := anObject
helpText: aStringOrUserMessage helpText := aStringOrUserMessage
initialize super initialize. hasDefault := false
label: aStringOrUserMessage label := aStringOrUserMessage
target: aValueModel target := aValueModel.
type: aSettingType type := aSettingType. type setting: self
</methods> <methods> Tools.SettingModel <category>accessing</category>
default "Answer the default value of this setting. Note that if #hasDefault is false, whatever this method answers is not really the default value." ^default
defaultOr: aBlock "Answer the default value of this setting, or the value of the block if the setting does not have a default value." ^hasDefault ifTrue: [default] ifFalse: [aBlock value]
helpText "Answer a String with the help text for this setting. If the help text was specified as a UserMessage, return the actual text from the message." ^helpText ifNotNil: [:stringOrMessage | stringOrMessage asString]
label ^label
reset "Set the value of the setting to the default if one was provided." hasDefault ifTrue: [self value: self default ifInvalid: [^self]. self changed: #value]
target ^target
type ^type
value "Answer the value of the setting, that is the object stored in the target." ^target value
value: anObject "Make an object the current value of the setting. Will signal InvalidSettingValueError if the argument is inacceptable as a value of this setting." type validate: anObject. target value: anObject
value: anObject ifInvalid: aBlock "Make anObject the current value of the setting, but only if it is valid for the setting type. Otherwise evaluate the block." (self isValueValid: anObject) ifTrue: [target value: anObject] ifFalse: [aBlock value]
</methods> <methods> Tools.SettingModel <category>persistence</category>
addStateXmlElementsTo: anElement anElement addNode: (type toXML: self value)
restoreFrom: aSettingSnapshot | newValue | newValue := type fromSnapshot: aSettingSnapshot. self value = newValue ifFalse: [self value: newValue]
</methods> <methods> Tools.SettingModel <category>private</category>
reportInvalidValue: anObject ^(InvalidSettingValueError value: anObject in: self) messageText: (self messageTextForInvalidValue: anObject) asString; raiseSignal
</methods> <methods> Tools.SettingModel <category>validating</category>
isValueValid: anObject "Answer true if anObject can be used as the value of this setting." ^type isValueValid: anObject
messageTextForInvalidValue: anObject "When there is an attempt to store an invalid object into the setting target, the signaled InvalidSettingValueError has the result of this message as the messageText." ^type messageTextForInvalidValue: anObject
</methods> <methods> Tools.SettingModel <category>user interface</category>
createModuleFor: aPage ^(type editorModuleClassIn: aPage) on: self
hasDefault "Answer whether this setting has a default value specified." ^hasDefault
hasModularEditor ^type hasModularEditor
isDefault "Answer whether the current value of the setting is the default one." ^hasDefault and: [self value = self default]
</methods> <methods> Tools.SettingModel class <category>instance creation</category>
type: aSettingType on: aValueModel "Create a setting of the specified type whose value is contained by the object supporting ValueModel protocol passed as the second argument." | typeInstance | typeInstance := aSettingType isBehavior ifTrue: [aSettingType new] ifFalse: [aSettingType]. ^self new type: typeInstance; target: aValueModel
type: aSettingType on: anObject aspect: aSymbol "Create a setting of the specified type whose value is obtained by sending to anObject a message with aSymbol as the selector, and set by sending it that selector with a colon appended." ^self type: aSettingType on: ((AspectAdaptor subject: anObject) forAspect: aSymbol)
type: aSettingType on: anObject atKey: keyObject "Create a setting of the specified type whose value is obtained by sending to anObject the message #at: with aSymbol as the argument, and set by sending it the message #at:put: with aSymbol and the value." ^self type: aSettingType on: ((IndexedAdaptor subject: anObject) forIndex: keyObject)
type: aSettingType onUISetting: aSymbol "Create a setting of the specified type whose value is obtained by sending to UI.UISettings a message #preferenceFor:, with aSymbol as the argument, and set by sending it #preferenceFor:put: with aSymbol and the value. This is provided for easy access to the settings stored in the old VW settings framework." ^self type: aSettingType on: (UISettings preferenceModelOrSubstituteFor: aSymbol)
</methods> <methods> Tools.TextSettingPage <category>accessing</category>
helpText ^(textHolder isKindOf: SettingModel) ifTrue: [textHolder helpText] ifFalse: [helpText ifNil: [super helpText]]
helpText: aStringOrUserMessage helpText := aStringOrUserMessage
settings "Whether we say we have any settings or not depends on whether our text holder is a simple value holder or a setting model (which does support a value holder interface)." ^(textHolder isKindOf: SettingModel) ifTrue: [Array with: textHolder] ifFalse: [super settings]
textHolder ^textHolder
</methods> <methods> Tools.TextSettingPage <category>initialize-release</category>
postBuildWith: aBuilder "The text view's menu should include the usual Smalltalk evaluation operations." super postBuildWith: aBuilder. (aBuilder componentAt: #TextEditor) widget controller initializeMenuForCode
textHolder: anObject textHolder := anObject
</methods> <methods> Tools.DirectorySetting <category>accessing</category>
isValueValid: anObject ^(super isValueValid: anObject) and: [[anObject exists and: [anObject isDirectory]] on: Error do: [:ex | ex return: false]]
</methods> <methods> Tools.DirectorySetting <category>parameters</category>
xmlTag ^'directory-name'
</methods> <methods> Tools.DirectorySetting <category>user interface</category>
editorModuleClassIn: aPage ^DirectorySettingModule
validationBlockForStringRepresentation ^[:string | | filename | [filename := SystemUtils makeFilenamePortable: string. mustExist not or: [filename := filename asFilename. filename exists and: [filename isDirectory]]] on: Error do: [:ex | ex return: false]]
</methods> <methods> Tools.ErrorAsSettingModule <category>private</category>
componentWithLabelID ^#label
formattedLabel ^setting messageText asString
</methods> <methods> Tools.ErrorAsSettingModule <category>actions</category>
debugError | offendingMethod result | offendingMethod := setting definingMethod. result := [offendingMethod implementingClass soleInstance perform: offendingMethod selector] on: Error do: [:ex | ^Notifier debugException: ex]. (Dialog confirm: (#ThisErrorCouldNotBeReproducedInspectQ << #dialogs >> 'The error could not be reproduced.<n>The definition evaluated successfully this time.<n>Inspect the result?') expandMacros) ifTrue: [result inspect]
</methods> <methods> Tools.EnumerationSettingRadioModule <category>interface management</category>
addRadioButtonsTo: aSpecCollection | top | top := self lineGrid + 2. setting type keys with: setting type labels do: [:eachKey :eachLabel | | buttonSpec | buttonSpec := self specForKey: eachKey label: eachLabel top: top. aSpecCollection collection: (aSpecCollection collection copyWith: buttonSpec). top := top + self lineGrid]
adjustBoundsOf: aWindowSpec "The width does not matter since modules are always stretched to fit the modular page. The height does." aWindowSpec bounds: (0@0 extent: 200 @ (setting type choices size + 1 * self lineGrid) asInteger)
generateSpec | stub | stub := self class windowSpec decodeAsLiteralArray. self addRadioButtonsTo: stub component. self adjustBoundsOf: stub window. ^stub
lineGrid "Each radio button gets this many pixels of vertical space." ^22
spec spec isNil ifTrue: [spec := self generateSpec]. ^spec
specForKey: anObject label: aString top: anInteger | radioSpec layout | radioSpec := RadioButtonSpec new. layout := LayoutFrame leftFraction: 0 offset: 20 topFraction: 0 offset: anInteger rightFraction: 1 offset: -10 bottomFraction: 0 offset: anInteger + self lineGrid. radioSpec layout: layout; model: #valueHolder; label: aString; select: anObject. ^radioSpec
</methods> <methods> Tools.EnumerationSettingRadioModule <category>private</category>
componentWithLabelID ^#label
</methods> <methods> Tools.EnumerationSettingRadioModule <category>initialize-release</category>
page: parentPage super page: parentPage. valueHolder := (PluggableAdaptor on: valueHolder) getBlock: [:m | setting type keyOfChoice: m value ifAbsent: [nil]] putBlock: [:m :v | m value: (setting type choiceAtKey: v ifAbsent: [nil])] updateBlock: [:m :a :p | true]
</methods> <methods> Tools.SettingsPageSeparatorModule <category>private</category>
componentWithLabelID "There is no such thing as a label in this module." ^nil
</methods> <methods> Tools.AbortAcceptRequest <category>accessing</category>
message ^message
message: anObject message := anObject
requestor ^requestor
requestor: anObject requestor := anObject
</methods> <methods> Tools.AbortAcceptRequest class <category>signaling</category>
from: aPage message: aString "Create and signal a new exception to stop the settings manager from accepting. The request to stop is coming from aPage, and the the string message argument is included in the message displayed to the user (if the handler chooses to display a message to the user)." ^self new requestor: aPage; message: aString; raiseSignal
</methods> <methods> Tools.BooleanSetting <category>parameters</category>
xmlTag ^'boolean'
</methods> <methods> Tools.BooleanSetting <category>accessing</category>
fromXML: aNode self verifyStoredDataTag: aNode. ^aNode children first text = 'true'
isValueValid: anObject ^true == anObject | (false == anObject)
toXML: anObject self validate: anObject. ^Element tag: self xmlTag elements: (Array with: (XML.Text text: anObject printString))
</methods> <methods> Tools.BooleanSetting <category>user interface</category>
editorModuleClassIn: aPage ^BooleanSettingModule
hasModularEditor ^true
</methods> <methods> Tools.IntegerSetting <category>parameters</category>
xmlTag ^'integer'
</methods> <methods> Tools.IntegerSetting <category>accessing</category>
isValueValid: anObject ^anObject isInteger and: [self valueInRange: anObject]
</methods> <methods> Tools.IntegerSetting <category>user interface</category>
editorModuleClassIn: aPage ^aPage preferredModuleClassForInteger: self
</methods> <methods> Tools.SequenceSetting <category>accessing</category>
elementType ^elementType
elementType: anObject elementType := anObject
fromXML: aNode | elements | self verifyStoredDataTag: aNode. elements := self elementsFromXML: aNode. ^(smalltalkClassBinding notNil and: [smalltalkClassBinding isDefined]) ifTrue: [smalltalkClassBinding value withAll: elements] ifFalse: [elements]
isValueValid: anObject ^anObject isSequenceable and: [anObject allSatisfy: [:each | elementType isValueValid: each]]
smalltalkClass ^smalltalkClassBinding
smalltalkClass: anObject smalltalkClassBinding := anObject
toXML: anObject self validate: anObject. ^Element tag: self xmlTag elements: (self elementsToXML: anObject)
</methods> <methods> Tools.SequenceSetting <category>private</category>
elementsFromXML: aNode "If there is an error, let the outer handler deal with it and if it resumes, go on ignoring the offending element." | elements | elements := OrderedCollection new. aNode realElements do: [:each | [elements add: (elementType fromXML: each)] on: InvalidSettingValueError, InvalidStoredSettingDataError do: [:ex | ex outer; return]]. ^elements
elementsToXML: aCollection "If a bad value is found while converting the collection's elements, let the outer handler deal with it. If the outer handler resumes, just ignore the offending element if the resumption value is nil, or use whatever that value is as a replacement node." | nodes | nodes := OrderedCollection new. aCollection do: [:each | [nodes add: (elementType toXML: each)] on: InvalidSettingValueError do: [:ex | ex outer; return]]. ^nodes
</methods> <methods> Tools.SequenceSetting <category>parameters</category>
xmlTag ^'sequence'
</methods> <methods> Tools.SequenceSetting class <category>instance creation</category>
of: aSettingType "Create a new type identifying settings that have elements of type identified by the argument." ^self new elementType: (aSettingType isBehavior ifTrue: [aSettingType new] ifFalse: [aSettingType])
</methods> <methods> Tools.ColorValueSetting <category>accessing</category>
fromXML: aNode | complain red green blue | self verifyStoredDataTag: aNode. complain := [self reportInvalidStoredData: aNode]. red := Number readFrom: (aNode valueOfAttribute: 'red' ifAbsent: complain) readStream. green := Number readFrom: (aNode valueOfAttribute: 'green' ifAbsent: complain) readStream. blue := Number readFrom: (aNode valueOfAttribute: 'blue' ifAbsent: complain) readStream. ^ColorValue red: red green: green blue: blue
isValueValid: anObject ^anObject isKindOf: ColorValue
toXML: aColorValue self validate: aColorValue. ^Element tag: self xmlTag attributes: (Array with: (Attribute name: 'red' value: aColorValue red printString) with: (Attribute name: 'green' value: aColorValue green printString) with: (Attribute name: 'blue' value: aColorValue blue printString)) elements: nil
</methods> <methods> Tools.ColorValueSetting <category>parameters</category>
xmlTag ^'color-value'
</methods> <methods> Tools.ColorValueSetting <category>user interface</category>
editorModuleClassIn: aPage ^ColorValueSettingModule
hasModularEditor ^true
</methods> <methods> Tools.ExternalSetting <category>initialize-release</category>
editorBlock: aBlock editorOpeningBlock := aBlock
editorClass: aClass self editorBlock: [aClass open]
editorClass: aClass spec: aSymbol self editorBlock: [aClass openWithSpec: aSymbol]
helpText: anObject helpText := anObject
label: aString label := aString
</methods> <methods> Tools.ExternalSetting <category>accessing</category>
createModuleFor: aPage ^ExternalSettingModule on: self
helpText ^helpText ifNotNil: [:stringOrMessage | stringOrMessage asString]
label ^label
openEditor ^editorOpeningBlock value
</methods> <methods> Tools.ExternalSetting <category>testing</category>
hasModularEditor ^true
hasPersistableState ^false
</methods> <methods> Tools.EnumerationSettingModule <category>accessing</category>
importSnapshot: aSnapshot valueHolder value: (setting type keyOfChoice: (setting type fromSnapshot: aSnapshot))
</methods> <methods> Tools.EnumerationSettingModule <category>aspects</category>
menuHolder ^[ | menuBuilder | menuBuilder := MenuBuilder new. setting type labels with: setting type keys do: [:label :key | menuBuilder add: label -> key]. menuBuilder menu]
</methods> <methods> Tools.EnumerationSettingModule <category>private</category>
componentWithLabelID ^#label
</methods> <methods> Tools.EnumerationSettingModule <category>initialize-release</category>
page: parentPage super page: parentPage. valueHolder := (PluggableAdaptor on: valueHolder) getBlock: [:m | setting type keyOfChoice: m value ifAbsent: [nil]] putBlock: [:m :v | m value: (setting type choiceAtKey: v ifAbsent: [nil])] updateBlock: [:m :a :p | true]
</methods> <methods> Tools.SequenceSettingPage <category>initialize-release</category>
initialize super initialize. hasChanges := false. displayBlock := [:value | value]. acceptBlock := [:value | value]. validationBlock := [:value | hasChanges := true. true]. isChangeOnly := false. listHolder := List new asValue. workHolder := ValueHolder with: List new
manager: aSettingsManager super manager: aSettingsManager. manager triggerChannel onChangeSend: #acceptOrResetTriggered to: self
postBuildWith: aBuilder (self widgetAt: #editorSubcanvas) client: self editor spec: (isChangeOnly ifTrue: [#changeOnlyWindowSpec] ifFalse: [#windowSpec]) builder: aBuilder newSubBuilder
</methods> <methods> Tools.SequenceSettingPage <category>configuring</category>
beChangeOnly "Do not allow to add and remove new items, only edit the existing ones. Reordering is not allowed as well." isChangeOnly := true
listHolder: aValueModel displayBlock: aBlock acceptBlock: anotherBlock listHolder := aValueModel. displayBlock := aBlock. acceptBlock := anotherBlock. acceptBlock numArgs = 2 ifTrue: [self beChangeOnly]. self reset
listHolder: aValueModel displayBlock: aBlock acceptBlock: anotherBlock validationBlock: thirdBlock listHolder := aValueModel. displayBlock := aBlock. acceptBlock := anotherBlock. acceptBlock numArgs = 2 ifTrue: [self beChangeOnly]. validationBlock := thirdBlock. self reset
postAcceptBlock: aBlock postAcceptBlock := aBlock
setting: aSettingModel | elementType | elementType := aSettingModel type elementType. listHolder := aSettingModel. displayBlock := elementType displayBlockForStringRepresentation. acceptBlock := elementType acceptBlockForStringRepresentation. validationBlock := elementType validationBlockForStringRepresentation. self reset
</methods> <methods> Tools.SequenceSettingPage <category>accessing</category>
editor ^(SimpleListEditor new) listHolder: workHolder; validationBlock: validationBlock; changedBlock: [hasChanges := true. self tickle]
helpText ^(listHolder isKindOf: SettingModel) ifTrue: [listHolder helpText] ifFalse: [helpText ifNil: [super helpText]]
helpText: aStringOrUserMessage helpText := aStringOrUserMessage
settings ^(listHolder isKindOf: SettingModel) ifTrue: [Array with: listHolder] ifFalse: [super settings]
</methods> <methods> Tools.SequenceSettingPage <category>private</category>
accept acceptBlock numArgs = 2 ifTrue: [listHolder value size = workHolder value size ifFalse: [self error: (#acceptBlockInapplicable << #dialogs >> 'accept block inapplicable')]. listHolder value with: workHolder value do: acceptBlock] ifFalse: [listHolder value: (workHolder value collect: acceptBlock)]. postAcceptBlock notNil ifTrue: [postAcceptBlock value]. hasChanges := false. self reset
reset workHolder value: (listHolder value collect: displayBlock)
</methods> <methods> Tools.SequenceSettingPage <category>notifications</category>
acceptOrResetTriggered "The manager's trigger channel's value has changed requesting accept or reset." manager triggerChannel value ifTrue: [hasChanges ifTrue: [self accept]] ifFalse: [self reset]
</methods> <methods> Tools.SequenceSettingPage <category>actions</category>
importSnapshots: aCollection | savedState | (listHolder isKindOf: SettingModel) ifFalse: [^self]. savedState := aCollection detect: [:some | some id = listHolder id] ifNone: [^self]. listHolder restoreFrom: savedState. self reset
</methods> <methods> Tools.ColorValueSettingModule <category>accessing</category>
colorSwatch ^ColoredArea new model: valueHolder
</methods> <methods> Tools.ColorValueSettingModule <category>private</category>
componentWithLabelID ^#label
</methods> <methods> Tools.ColorValueSettingModule <category>actions</category>
editColor | dialog | dialog := ColorSelectionDialog new. dialog selection: valueHolder value. dialog open. dialog accept value ifTrue: [valueHolder value: dialog selection]
</methods> <methods> Tools.NumberSettingModule class <category>interface specs</category>
windowSpec "Tools.UIPainter new openOnClass: self andSelector: #windowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #UnlabeledCanvas #defaultString: 'Unlabeled Canvas' #catalogID: #labels) #min: #(#{Core.Point} 20 20 ) #max: #(#{Core.Point} 1024 768 ) #bounds: #(#{Graphics.Rectangle} 512 384 812 409 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.InputFieldSpec} #layout: #(#{Graphics.LayoutFrame} 0 0.5 0 0 0 1 25 0 ) #name: #inputField #model: #valueHolder #callbacksSpec: #(#{UI.UIEventCallbackSubSpec} #requestValueChangeSelector: #validateEntry: ) #type: #number ) #(#{UI.LabelSpec} #layout: #(#{UI.AlignmentOrigin} 0 0 25 0 0 1 ) #name: #label #label: #(#{Kernel.UserMessage} #key: #Label #defaultString: 'Label' #catalogID: #labels) ) ) ) )
</methods> <methods> Tools.IntegerSettingSpinButtonModule class <category>interface specs</category>
windowSpec "Tools.UIPainter new openOnClass: self andSelector: #windowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #UnlabeledCanvas #defaultString: 'Unlabeled Canvas' #catalogID: #labels) #min: #(#{Core.Point} 20 20 ) #max: #(#{Core.Point} 1024 768 ) #bounds: #(#{Graphics.Rectangle} 512 384 812 409 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.LabelSpec} #layout: #(#{UI.AlignmentOrigin} 0 0 25 0 0 1 ) #name: #label #label: #(#{Kernel.UserMessage} #key: #Label #defaultString: 'Label' #catalogID: #labels) ) #(#{UI.SpinButtonSpec} #layout: #(#{Graphics.LayoutFrame} 0 0.5 0 0 0 1 0 1 ) #name: #spinButton #model: #valueHolder #callbacksSpec: #(#{UI.UIEventCallbackSubSpec} #requestValueChangeSelector: #validateEntry: ) #isReadOnly: false #type: #number ) ) ) )
</methods> <methods> Tools.ExternalSettingModule class <category>interface specs</category>
windowSpec "Tools.UIPainter new openOnClass: self andSelector: #windowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #UnlabeledCanvas #defaultString: 'Unlabeled Canvas' #catalogID: #labels ) #min: #(#{Core.Point} 20 25 ) #max: #(#{Core.Point} 1024 768 ) #bounds: #(#{Graphics.Rectangle} 512 384 712 409 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.LabelSpec} #layout: #(#{UI.AlignmentOrigin} 0 0 25 0 0 1 ) #name: #label #label: #(#{Kernel.UserMessage} #key: #Label #defaultString: 'Label' #catalogID: #labels) ) #(#{UI.ActionButtonSpec} #layout: #(#{Graphics.LayoutFrame} 0 0.5 0 0 0 1 0 1 ) #name: #editButton #model: #edit #label: #(#{Kernel.UserMessage} #key: #Edit #defaultString: 'Edit...' #catalogID: #labels) #defaultable: true ) ) ) )
</methods> <methods> Tools.BooleanSettingModule class <category>interface specs</category>
windowSpec "Tools.UIPainter new openOnClass: self andSelector: #windowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #UnlabeledCanvas #defaultString: 'Unlabeled Canvas' #catalogID: #labels) #min: #(#{Core.Point} 20 20 ) #max: #(#{Core.Point} 1024 768 ) #bounds: #(#{Graphics.Rectangle} 512 384 912 409 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.CheckBoxSpec} #layout: #(#{UI.AlignmentOrigin} 0 0 25 0 0 1 ) #name: #checkBox #model: #valueHolder #label: #(#{Kernel.UserMessage} #key: #CheckBox #defaultString: 'Check Box' #catalogID: #labels) ) ) ) )
</methods> <methods> Tools.DirectorySettingModule class <category>interface specs</category>
windowSpec "Tools.UIPainter new openOnClass: self andSelector: #windowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #UnlabeledCanvas #defaultString: 'Unlabeled Canvas' #catalogID: #labels) #min: #(#{Core.Point} 20 20 ) #max: #(#{Core.Point} 1024 768 ) #bounds: #(#{Graphics.Rectangle} 512 384 812 434 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.InputFieldSpec} #layout: #(#{Graphics.LayoutFrame} 0 0 25 0 -70 1 50 0 ) #name: #inputField #model: #valueHolder #isReadOnly: false #type: #string ) #(#{UI.ActionButtonSpec} #layout: #(#{Graphics.LayoutFrame} -70 1 25 0 0 1 50 0 ) #name: #browseButton #model: #browseAction #label: #(#{Kernel.UserMessage} #key: #BrowseEllipsis #defaultString: 'Browse...' #catalogID: #labels ) #defaultable: true ) #(#{UI.LabelSpec} #layout: #(#{UI.AlignmentOrigin} 0 0 25 0 0 1 ) #name: #label #label: #(#{Kernel.UserMessage} #key: #Label #defaultString: 'Label' #catalogID: #labels) ) ) ) )
</methods> <methods> Tools.SettingsManager class <category>interface specs</category>
multiDomainWindowSpec "Tools.UIPainter new openOnClass: self andSelector: #multiDomainWindowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #Settings #defaultString: 'Settings' #catalogID: #labels ) #min: #(#{Core.Point} 450 330 ) #max: #(#{Core.Point} 0 0 ) #bounds: #(#{Graphics.Rectangle} 424 348 1024 768 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.MenuButtonSpec} #layout: #(#{Graphics.LayoutFrame} 10 0 10 0 0 0.35 35 0 ) #name: #domainMenu #model: #domainHolder #menu: #domainMenu ) #(#{UI.TreeViewSpec} #layout: #(#{Graphics.LayoutFrame} 10 0 45 0 0 0.35 -45 1 ) #name: #TreeView #flags: 15 #model: #pageListHolder #menu: #pageTreeMenu #useModifierKeys: true #selectionType: #highlight #displayStringSelector: #label #useIcons: #other #iconSelectors: #(#listIcon ) #rootExpander: true ) #(#{UI.ResizingSplitterSpec} #layout: #(#{Graphics.LayoutFrame} 2 0.35 5 0 8 0.35 -45 1 ) #name: #ResizingSplitter1 #horizontal: false #minAboveSize: 30 #minBelowSize: 30 #aboveWidgets: 'TreeView' #belowWidgets: 'Subcanvas GroupBox' ) #(#{UI.GroupBoxSpec} #layout: #(#{Graphics.LayoutFrame} 10 0.35 10 0 -10 1 -45 1 ) #name: #GroupBox ) #(#{UI.ActionButtonSpec} #layout: #(#{Graphics.LayoutFrame} -330 1 -35 1 -250 1 -10 1 ) #name: #okButton #model: #okPressed #label: #(#{Kernel.UserMessage} #key: #OK #defaultString: 'OK' #catalogID: #labels ) #isDefault: true #defaultable: true ) #(#{UI.ActionButtonSpec} #layout: #(#{Graphics.LayoutFrame} -245 1 -35 1 -165 1 -10 1 ) #name: #cancelButton #model: #cancelPressed #label: #(#{Kernel.UserMessage} #key: #Cancel #defaultString: 'Cancel' #catalogID: #labels ) #defaultable: true ) #(#{UI.ActionButtonSpec} #layout: #(#{Graphics.LayoutFrame} -160 1 -35 1 -90 1 -10 1 ) #name: #applyButton #model: #applyPressed #helpText: #(#{Kernel.UserMessage} #key: #SettingsManagerApplyButton #defaultString: 'Apply the yet unapplied settings without closing this window.' #catalogID: #labels ) #label: #(#{Kernel.UserMessage} #key: #_Apply #defaultString: '&Apply' #catalogID: #labels ) #defaultable: true ) #(#{UI.ActionButtonSpec} #layout: #(#{Graphics.LayoutFrame} -80 1 -35 1 -10 1 -10 1 ) #name: #helpButton #model: #helpPressed #helpText: '' #label: #(#{Kernel.UserMessage} #key: #AmpHelp #defaultString: '&Help' #catalogID: #labels ) #defaultable: true ) #(#{UI.SubCanvasSpec} #layout: #(#{Graphics.LayoutFrame} 20 0.35 35 0 -20 1 -55 1 ) #name: #Subcanvas #flags: 1 ) ) ) )
windowSpec "Tools.UIPainter new openOnClass: self andSelector: #windowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #Settings #defaultString: 'Settings' #catalogID: #labels ) #min: #(#{Core.Point} 450 330 ) #max: #(#{Core.Point} 0 0 ) #bounds: #(#{Graphics.Rectangle} 720 450 1320 870 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.TreeViewSpec} #layout: #(#{Graphics.LayoutFrame} 10 0 10 0 0 0.38 -45 1 ) #name: #TreeView #flags: 15 #model: #pageListHolder #callbacksSpec: #(#{UI.UIEventCallbackSubSpec} #requestValueChangeSelector: #verifyNewSelection: ) #menu: #pageTreeMenu #useModifierKeys: true #selectionType: #highlight #displayStringSelector: #label #useIcons: #other #iconSelectors: #(#listIcon ) #rootExpander: true ) #(#{UI.ResizingSplitterSpec} #layout: #(#{Graphics.LayoutFrame} 2 0.38 5 0 8 0.38 -45 1 ) #name: #ResizingSplitter1 #horizontal: false #minAboveSize: 30 #minBelowSize: 30 #aboveWidgets: 'TreeView' #belowWidgets: 'Subcanvas GroupBox' ) #(#{UI.GroupBoxSpec} #layout: #(#{Graphics.LayoutFrame} 10 0.38 10 0 -10 1 -45 1 ) #name: #GroupBox ) #(#{UI.ActionButtonSpec} #layout: #(#{Graphics.LayoutFrame} -330 1 -35 1 -250 1 -10 1 ) #name: #okButton #model: #okPressed #label: #(#{Kernel.UserMessage} #key: #OK #defaultString: 'OK' #catalogID: #labels ) #isDefault: true #defaultable: true ) #(#{UI.ActionButtonSpec} #layout: #(#{Graphics.LayoutFrame} -245 1 -35 1 -165 1 -10 1 ) #name: #cancelButton #model: #cancelPressed #label: #(#{Kernel.UserMessage} #key: #Cancel #defaultString: 'Cancel' #catalogID: #labels ) #defaultable: true ) #(#{UI.ActionButtonSpec} #layout: #(#{Graphics.LayoutFrame} -160 1 -35 1 -90 1 -10 1 ) #name: #applyButton #model: #applyPressed #helpText: #(#{Kernel.UserMessage} #key: #SettingsManagerApplyButton #defaultString: 'Apply the yet unapplied settings without closing this window.' #catalogID: #labels ) #label: #(#{Kernel.UserMessage} #key: #_Apply #defaultString: '&Apply' #catalogID: #labels ) #defaultable: true ) #(#{UI.ActionButtonSpec} #layout: #(#{Graphics.LayoutFrame} -80 1 -35 1 -10 1 -10 1 ) #name: #helpButton #model: #helpPressed #helpText: '' #label: #(#{Kernel.UserMessage} #key: #AmpHelp #defaultString: '&Help' #catalogID: #labels ) #defaultable: true ) #(#{UI.SubCanvasSpec} #layout: #(#{Graphics.LayoutFrame} 20 0.38 35 0 -20 1 -55 1 ) #name: #Subcanvas #flags: 1 ) ) ) )
</methods> <methods> Tools.SettingsManager class <category>resources</category>
pageTreeMenu "Tools.MenuEditor new openOnClass: self andSelector: #pageTreeMenu" <resource: #menu> ^#(#{UI.Menu} #( #(#{UI.MenuItem} #rawLabel: #(#{Kernel.UserMessage} #key: #_LoadDot #defaultString: '&Load...' #catalogID: #menus ) #nameKey: #import #value: #importAllSettings #labelImage: #(#{UI.ResourceRetriever} #{Tools.ToolbarIconLibrary} #fileOpen ) ) #(#{UI.MenuItem} #rawLabel: #(#{Kernel.UserMessage} #key: #_SaveDot #defaultString: '&Save...' #catalogID: #menus ) #nameKey: #export #value: #exportSettings #labelImage: #(#{UI.ResourceRetriever} #{Tools.ToolbarIconLibrary} #fileSave ) ) #(#{UI.MenuItem} #rawLabel: #(#{Kernel.UserMessage} #key: #_ResetAllToDefaultDots #defaultString: '&Reset All to Default...' #catalogID: #menus ) #nameKey: #resetAllToDefault #value: #resetAllToDefault ) #(#{UI.MenuItem} #rawLabel: #(#{Kernel.UserMessage} #key: #_BrowseAllPages #defaultString: '&Browse All Pages' #catalogID: #menus ) #nameKey: #browsePages #value: #browsePages ) #(#{UI.MenuItem} #rawLabel: #(#{Kernel.UserMessage} #key: #LoadPageDots #defaultString: 'L&oad Page...' #catalogID: #menus ) #nameKey: #importPage #value: #importSettingsIntoPage #enablementSelector: #isPageSelected ) #(#{UI.MenuItem} #rawLabel: #(#{Kernel.UserMessage} #key: #SavePageDots #defaultString: 'S&ave Page...' #catalogID: #menus ) #nameKey: #exportSelected #value: #exportSelected #enablementSelector: #isPageSelected ) #(#{UI.MenuItem} #rawLabel: #(#{Kernel.UserMessage} #key: #Reset_PageToDefault #defaultString: 'Reset &Page to Default' #catalogID: #menus ) #nameKey: #resetPageToDefault #value: #resetPageToDefault #enablementSelector: #isPageSelected ) #(#{UI.MenuItem} #rawLabel: #(#{Kernel.UserMessage} #key: #Bro_wsePageAndSettings #defaultString: 'Bro&wse Page and Settings' #catalogID: #menus ) #nameKey: #browseThisPage #value: #browseSettingDefinitions #enablementSelector: #isPageSelected #labelImage: #(#{UI.ResourceRetriever} #{Tools.ToolbarIconLibrary} #systemBrowser ) ) ) #(4 4 ) nil ) decodeAsLiteralArray
</methods> <methods> Tools.SettingsDomain class <category>declarations</category>
settingPragmas "These pragmas are used to mark methods that return setting objects. The first parameter is always a setting ID, and the second, if present, is a numeric positions of the setting. A position matters within an automatically generated page, where settings are ordered in the increasing order of their position values. Settings with equal position values are ordered in the alphabetical order of their method selectors. In other words, they appear in the same order as their methods do in the browser. Settings without an explicitly assigned position value always have a default position value of 0." <pragmas: #class> ^#(setting: setting:position:)
settingsPagePragmas <pragmas: #class> ^#(settingsPage: settingsPage:position:)
</methods> <methods> Tools.ModularSettingsPage class <category>interface specs</category>
windowSpec "Tools.UIPainter new openOnClass: self andSelector: #windowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #UnlabeledCanvas #defaultString: 'Unlabeled Canvas' #catalogID: #labels) #bounds: #(#{Graphics.Rectangle} 512 384 712 584 ) ) #component: #(#{UI.SpecCollection} #collection: #() ) )
</methods> <methods> Tools.FilenameSettingModule class <category>interface specs</category>
windowSpec "Tools.UIPainter new openOnClass: self andSelector: #windowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #UnlabeledCanvas #defaultString: 'Unlabeled Canvas' #catalogID: #labels) #min: #(#{Core.Point} 20 20 ) #max: #(#{Core.Point} 1024 768 ) #bounds: #(#{Graphics.Rectangle} 512 384 812 434 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.InputFieldSpec} #layout: #(#{Graphics.LayoutFrame} 0 0 25 0 -70 1 50 0 ) #name: #inputField #model: #valueHolder #isReadOnly: false #type: #string ) #(#{UI.ActionButtonSpec} #layout: #(#{Graphics.LayoutFrame} -70 1 25 0 0 1 50 0 ) #name: #browseButton #model: #browseAction #label: #(#{Kernel.UserMessage} #key: #BrowseEllipsis #defaultString: 'Browse...' #catalogID: #labels ) #defaultable: true ) #(#{UI.LabelSpec} #layout: #(#{UI.AlignmentOrigin} 0 0 25 0 0 1 ) #name: #label #label: #(#{Kernel.UserMessage} #key: #Label #defaultString: 'Label' #catalogID: #labels) ) ) ) )
</methods> <methods> Tools.StringSettingModule class <category>interface specs</category>
windowSpec "Tools.UIPainter new openOnClass: self andSelector: #windowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #UnlabeledCanvas #defaultString: 'Unlabeled Canvas' #catalogID: #labels) #min: #(#{Core.Point} 20 20 ) #max: #(#{Core.Point} 1024 768 ) #bounds: #(#{Graphics.Rectangle} 512 384 812 409 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.InputFieldSpec} #layout: #(#{Graphics.LayoutFrame} 0 0.5 0 0 0 1 25 0 ) #name: #inputField #model: #valueHolder #type: #string ) #(#{UI.LabelSpec} #layout: #(#{UI.AlignmentOrigin} 0 0 25 0 0 1 ) #name: #label #label: #(#{Kernel.UserMessage} #key: #Label #defaultString: 'Label' #catalogID: #labels) ) ) ) )
</methods> <methods> Tools.TextSettingPage class <category>interface specs</category>
windowSpec "Tools.UIPainter new openOnClass: self andSelector: #windowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #UnlabeledCanvas #defaultString: 'Unlabeled Canvas' #catalogID: #labels) #bounds: #(#{Graphics.Rectangle} 512 384 712 584 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.TextEditorSpec} #layout: #(#{Graphics.LayoutFrame} 0 0 0 0 0 1 0 1 ) #name: #TextEditor #model: #textHolder #tabRequiresControl: true ) ) ) )
</methods> <methods> Tools.ErrorAsSettingModule class <category>interface specs</category>
windowSpec "Tools.UIPainter new openOnClass: self andSelector: #windowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #UnlabeledCanvas #defaultString: 'Unlabeled Canvas' #catalogID: #labels) #min: #(#{Core.Point} 20 20 ) #max: #(#{Core.Point} 1024 768 ) #bounds: #(#{Graphics.Rectangle} 512 384 912 409 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.LabelSpec} #layout: #(#{UI.AlignmentOrigin} 0 0 25 0 0 1 ) #name: #label #colors: #(#{UI.LookPreferences} #setForegroundColor: #(#{Graphics.ColorValue} #red ) ) #label: #(#{Kernel.UserMessage} #key: #messageSentAtRuntime #defaultString: 'this message is set at runtime' #catalogID: #labels) ) #(#{UI.ActionButtonSpec} #layout: #(#{Graphics.LayoutFrame} -80 1 0 0 0 1 0 1 ) #name: #debugButton #model: #debugError #label: #(#{Kernel.UserMessage} #key: #debugIt #defaultString: 'Debug it' #catalogID: #labels ) #defaultable: true ) ) ) )
</methods> <methods> Tools.EnumerationSettingRadioModule class <category>interface specs</category>
windowSpec "Tools.UIPainter new openOnClass: self andSelector: #windowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #UnlabeledCanvas #defaultString: 'Unlabeled Canvas' #catalogID: #labels) #bounds: #(#{Graphics.Rectangle} 512 384 712 584 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.LabelSpec} #layout: #(#{UI.AlignmentOrigin} 0 0 22 0 0 1 ) #name: #label #label: #(#{Kernel.UserMessage} #key: #Label #defaultString: 'Label' #catalogID: #labels) ) ) ) )
</methods> <methods> Tools.SettingsPageSeparatorModule class <category>interface specs</category>
windowSpec "Tools.UIPainter new openOnClass: self andSelector: #windowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #UnlabeledCanvas #defaultString: 'Unlabeled Canvas' #catalogID: #labels) #min: #(#{Core.Point} 20 20 ) #max: #(#{Core.Point} 1024 768 ) #bounds: #(#{Graphics.Rectangle} 512 384 912 409 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.DividerSpec} #layout: #(#{Graphics.LayoutFrame} 0 0 12 0 0 1 14 0 ) #name: #divider ) ) ) )
</methods> <methods> Tools.EnumerationSettingModule class <category>interface specs</category>
windowSpec "Tools.UIPainter new openOnClass: self andSelector: #windowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #UnlabeledCanvas #defaultString: 'Unlabeled Canvas' #catalogID: #labels) #min: #(#{Core.Point} 20 20 ) #max: #(#{Core.Point} 1024 768 ) #bounds: #(#{Graphics.Rectangle} 512 384 812 409 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.MenuButtonSpec} #layout: #(#{Graphics.LayoutFrame} 0 0.5 0 0 0 1 25 0 ) #name: #MenuButton #model: #valueHolder #menu: #menuHolder ) #(#{UI.LabelSpec} #layout: #(#{UI.AlignmentOrigin} 0 0 25 0 0 1 ) #name: #label #label: #(#{Kernel.UserMessage} #key: #Label #defaultString: 'Label' #catalogID: #labels) ) ) ) )
</methods> <methods> Tools.SequenceSettingPage class <category>interface specs</category>
windowSpec "Tools.UIPainter new openOnClass: self andSelector: #windowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #UnlabeledCanvas #defaultString: 'Unlabeled Canvas' #catalogID: #labels) #bounds: #(#{Graphics.Rectangle} 512 384 846 672 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.SubCanvasSpec} #layout: #(#{Graphics.LayoutFrame} 0 0 0 0 0 1 0 1 ) #name: #editorSubcanvas #flags: 0 ) ) ) )
</methods> <methods> Tools.ColorValueSettingModule class <category>interface specs</category>
windowSpec "Tools.UIPainter new openOnClass: self andSelector: #windowSpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: #(#{Kernel.UserMessage} #key: #UnlabeledCanvas #defaultString: 'Unlabeled Canvas' #catalogID: #labels) #min: #(#{Core.Point} 20 20 ) #max: #(#{Core.Point} 1024 768 ) #bounds: #(#{Graphics.Rectangle} 512 384 812 409 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.ArbitraryComponentSpec} #layout: #(#{Graphics.LayoutFrame} 2 0.5 2 0 -28 1 23 0 ) #name: #colorSwatch #flags: 0 #component: #colorSwatch ) #(#{UI.RegionSpec} #layout: #(#{Graphics.LayoutFrame} 0 0.5 0 0 -26 1 25 0 ) #lineWidth: 1 ) #(#{UI.ActionButtonSpec} #layout: #(#{Graphics.LayoutFrame} -25 1 0 0 0 1 25 0 ) #name: #EditButton #model: #editColor #label: '...' #defaultable: true ) #(#{UI.LabelSpec} #layout: #(#{UI.AlignmentOrigin} 0 0 25 0 0 1 ) #name: #label #label: #(#{Kernel.UserMessage} #key: #Label #defaultString: 'Label' #catalogID: #labels) ) ) ) )
</methods> "Imported Classes:" self error: 'Attempting to file-in parcel imports. Choose terminate or close' <class> <name>Object</name> <environment>Core</environment> <super></super> <private>false</private> none <imports></imports> <category>Kernel-Objects</category> <attributes> <package>Kernel-Objects</package> </attributes> </class> <class> <name>Exception</name> <environment>Core</environment> <super>Core.GenericException</super> <private>false</private> none <imports></imports> <category>Kernel-Exception Handling</category> <attributes> <package>Kernel-Exception Handling</package> </attributes> </class> <class> <name>Model</name> <environment>UI</environment> <super>Core.Object</super> <private>false</private> none dependents <imports></imports> <category>Kernel-Objects</category> <attributes> <package>Kernel-Objects</package> </attributes> </class> <class> <name>Error</name> <environment>Core</environment> <super>Core.Exception</super> <private>false</private> none <imports></imports> <category>Kernel-Exception Handling</category> <attributes> <package>Kernel-Exception Handling</package> </attributes> </class> <class> <name>ApplicationModel</name> <environment>UI</environment> <super>UI.Model</super> <private>false</private> none builder uiSession eventHandlers savedWindowInformation <imports></imports> <category>UIBuilder-Framework</category> <attributes> <package>UIBuilder-Framework</package> </attributes> </class>