Skins often tend to modify key elements of the Messenger interface. In addition to changes to the look of the windows, things like buttons, toolbars, links and menus are often modified or removed by them. The main problem with functional changes such as these is that in most cases, no choice is given to the user. Skins that are distributed in static hard-patch packages present themselves as one single entity doing all sorts of things, some of which users may dislike.
Instead of requiring from the developer to decide everything on behalf of his users, Messenger Plus! skins allow the creation of sets of user modifiable options. These options are displayed together in a special window, accessible from the skin menu and the preferences panel, which can also be customized to incorporate things like a banner, additional links to your web site, etc... Messenger Plus! takes care of all the technical details, all you need to do is declare the name of an option and use it the way you deem necessary.
User options are declared in the <Skin>\<Properties> child of the <Options> element. Every property has a name, a type and a default. Additional display elements such as DispLabel and DispHelp are also available to create more user friendly option panels. Here are the two current types of properties offered by Messenger Plus!:
Options only allow users to make a choice between predefined values that you created for them. The Messenger Plus! skinning system does not allow variable options (example: strings entered in an edit box) for security reasons. You can never be sure of what users can specify in a non-controlled environment and this may lead to crashes in Messenger.
All the options you specify in your skininfo file are automatically displayed by Messenger Plus! in a option panel created specifically for your skin. Every option is displayed in the order specified in the file and Messenger Plus! takes care of reading/saving the properties in the Windows' registry. If an option is changed, Messenger Plus! also asks the user if Messenger should be restarted immediately for the options to be applied. A restart is always required for new option values to be applied because of optimizations made in Messenger and Messenger Plus! itself.
Here is an example of how to declare two options for your skin, one boolean and the other one list-based. Note that spaces are not allowed in the Name attributes.
<Options> <Skin> <Properties> <BoolVal Name="ChangeBkg"> <Default>true</Default> </BoolVal> <MultiVal Name="RememberMeTxt"> <Values> <Value>hello</Value> <Value>goodbye</Value> <Value>farewell</Value> </Values> <Default>hello</Default> </MultiVal> </Properties> </Skin> </Options>
That's all you've got to do for Messenger Plus! to recognize and take care of your options!. If you add this code to the Test Skin file and restart Messenger, you'll notice that a "Skin's Options" menu is now available for it.
Here is the window that Messenger Plus! will generate for these options:
As you can see, the default values are properly set in this screen. Because skins are loaded when Messenger starts-up, the options are global to every user registered in a given Windows' account. Options are displayed with their name by default but DispLabel elements and attributes should always be used for added clarity. Using display names will also allow you to change the text displayed in your options window without risking to alter the internal name of your option and breaking some of your skin's code as a result (seen later in this section).
Both BoolVal and MultiVal accept <DispLabel> elements. Values specified in a MultiVal also accept DispElement but as an attribute. If additional information is required to explain the purpose of an option, a <DispHelp> element can be added for tooltip creation. Let's modify the example's code to take advantage of all of this:
<Options> <Skin> <Properties> <BoolVal Name="ChangeBkg"> <DispLabel>Change the background picture</DispLabel> <DispHelp>This option displays a cloudy sky</DispHelp> <Default>true</Default> </BoolVal> <MultiVal Name="RememberMeTxt"> <DispLabel>New "Remember Me" label</DispLabel> <Values> <Value DispLabel="Hello World!">hello</Value> <Value DispLabel="Goodbye Planet!">goodbye</Value> <Value DispLabel="Farewell Kosmos!">farewell</Value> </Values> <Default>hello</Default> </MultiVal> </Properties> </Skin> </Options>
The updated screenshot shows a window that's now appropriate for general public use. The tooltip added by <DispHelp> will be shown only when the mouse cursor is left a couple of seconds hovering above the ChangeBkg checkbox.
Of course, declaring the options is just half of the story. Let's see how to put these variables into good use.
As you saw in Restrictions: How and Why?, resource groups can be paired to blocks of restrictions for conditional loading. User options are one of the restrictions you can use for each resource group. Thanks to them, you can now easily design different resources to match different options.
Options can be specified as restrictions in two different ways that could be called "basic" and "advanced". Several options can be grouped together in a restriction block and the principle is the same as with any other restriction kind: grouping is made with a logical OR so the resources will be loaded as long as one of the option conditions is considered valid. Conditions are specified in what's called an Option Set. With the basic method, each <OptionSet> element specifies a single option and is represented in XML like this:
<Restrictions> <SkinOptions> <OptionSet> <BoolVal Name="ChangeBkg">true</BoolVal> </OptionSet> <OptionSet> <MultiVal Name="RememberMeTxt">goodbye</MultiVal> </OptionSet> </SkinOptions> </Restrictions>
The example above can be read as "Load this groups if ChangeBkg is true OR RememberMeTxt is goodbye". Let's say that you want to use a more elaborate condition such as "ChangeBkg is true AND RememberMeTxt is farewell": to do that you'll need to use the advanced method.
Option sets can use condition strings instead of single option element. These strings allow a mix of AND and OR with an unlimited number of options. Here are a couple of examples:
Condition strings use a simple syntax that recognizes the following operators:
Boolean values are either true or false and strings are enclosed between "" or ''. The \, " and ' characters must be escaped if written in a string (written \\, \", and \'). != stands for 'not equal to'. Parenthesis are not supported: conditions are evaluated from left to right and OR operators have grouping priority over AND. Any number of space characters can be used to separate each entity. Operators are case sensitive but options' names and values are not.
Priority of operators means that the last example in the list above is read by Messenger Plus! as (RememberMeTxt = "hello" OR RememberMeTxt = "hello") AND ChangeBkg = false. The reason why OR conditions are grouped is because to write RememberMeTxt = "hello" OR (RememberMeTxt = "hello" AND ChangeBkg = false) you simply need to add two different option sets. If you're not familiar with programming languages, this may not make much sense to you right now but if you start using condition strings, you'll quickly get used to how they work. For now, let's just see how to rewrite the example above with one condition string:
<OptionSet> <Conditions>ChangeBkg=true OR RememberMeTxt="goodbye"</Conditions> </OptionSet>
Let's use what we've learned here to improve the Test Skin project. This new skin allows the user to enable or disable the new background and offers two alternatives for the "Remember Me" text replacement. To make sure you've understood the content of this section correctly, please try to do the modifications yourself on the original code and compare your solution with the one below.
<SkinInfo> <Information> <Name>Test Skin Revisited</Name> </Information> <Options><Skin> <Properties> <BoolVal Name="ChangeBkg"> <DispLabel>Change the background picture</DispLabel> <Default>true</Default> </BoolVal> <MultiVal Name="RememberMeTxt"> <DispLabel>New "Remember Me" label</DispLabel> <Values> <Value DispLabel="Hello World!">hello</Value> <Value DispLabel="Goodbye Planet!">goodbye</Value> </Values> <Default>hello</Default> </MultiVal> </Properties> </Skin></Options> <MessengerSkin> <!-- background replacement --> <ResGroup> <Restrictions> <MsgVersions> <Version Major="8" Minor="5"/> </MsgVersions> <SkinOptions> <OptionSet> <BoolVal Name="ChangeBkg">true</BoolVal> </OptionSet> </SkinOptions> </Restrictions> <Resources><Replace> <Graphics><Pictures> <Picture Id="20061"> <File>clouds.jpg</File> </Picture> </Pictures></Graphics> </Replace></Resources> </ResGroup> <!-- "hello" alternative to Remember Me --> <ResGroup> <Restrictions> <MsgVersions> <Version Major="8" Minor="5"/> </MsgVersions> <SkinOptions> <OptionSet> <MultiVal Name="RememberMeTxt">hello</MultiVal> </OptionSet> </SkinOptions> </Restrictions> <Resources><Replace> <Strings> <String Id="61288">Hello World!</String> </Strings> </Replace></Resources> </ResGroup> <!-- "goodbye" alternative to Remember Me --> <ResGroup> <Restrictions> <MsgVersions> <Version Major="8" Minor="5"/> </MsgVersions> <SkinOptions> <OptionSet> <Conditions>RememberMeTxt = "goodbye"</Conditions> </OptionSet> </SkinOptions> </Restrictions> <Resources><Replace> <Strings> <String Id="61288">Goodbye Planet!</String> </Strings> </Replace></Resources> </ResGroup> </MessengerSkin> </SkinInfo>
The subject of options cannot be dealt with in a single section of documentation. Much more can be done with what you've just learnt so once you'll have practiced what is shown here, go read about Options for Advanced Users.
Specialized Subjects, Options for Advanced Users.