Storing and Tracking Palette States

Palettes can be configured to remember their previous states across front end sessions. This is useful for palettes containing multiple expandable sections, tab views, checkboxes, and other control objects. For example, a user who has gone to the trouble of closing nine of 10 opener views would not expect to have to do so again every time he or she opens the palette.

There are several ways the Wolfram System can store and track control object states. The first method involves dynamic variables and the second, (RefLink[TaggingRules, paclet:ref/TaggingRules]). Dynamic variables tracked by (RefLink[DynamicModule, paclet:ref/DynamicModule]) are stored in the notebook expression and therefore require that the palette notebook be savable in order to save the changes. (RefLink[TaggingRules, paclet:ref/TaggingRules]), on the other hand, is cached by the front end in a separate file for palette notebooks located on (RefLink[PalettePath, paclet:ref/PalettePath]). This means that the palette notebook itself need not be savable for the state to be stored. Furthermore, if the (RefLink[TaggingRules, paclet:ref/TaggingRules]) are stored as options at the notebook level, the values can be accessed from other notebooks using expressions similar to (RefLink[Options, paclet:ref/Options])[(RefLink[Get, paclet:ref/Get])["filepath"],(RefLink[TaggingRules, paclet:ref/TaggingRules])].

Dynamic Variables

As described in "Advanced Dynamic Functionality", (RefLink[DynamicModule, paclet:ref/DynamicModule]) maintains the values of dynamic variables in the output of an evaluation. However, (RefLink[DynamicModule, paclet:ref/DynamicModule]) is inappropriate for storing values in palettes that should persist across sessions. In order for (RefLink[DynamicModule, paclet:ref/DynamicModule]) values to persist, the document containing the (RefLink[DynamicModule, paclet:ref/DynamicModule]) must be saved, and palettes may be stored in locations where saving them is absolutely forbidden.

(RefLink[DynamicModule, paclet:ref/DynamicModule]) is perfectly acceptable for storing values that should be reset each time a palette is opened. Here is an example of two control objects and two dynamic variables.

CreatePalette[DynamicModule[{checkbox1=True,checkbox2},
Column[{Labeled[Checkbox[Dynamic[checkbox1]],"First Checkbox",Right],Labeled[Checkbox[Dynamic[checkbox2]],"Second Checkbox",Right]}]],WindowTitle->"Palette"];

In this next palette, both checkboxes can be checked or unchecked by clicking either the plus or minus button. The buttons set new dynamic variable values for the control object states.

CreatePalette[DynamicModule[{checkbox1=True,checkbox2=True},
Column[{Labeled[Checkbox[Dynamic[checkbox1]],"First Checkbox",Right],Labeled[Checkbox[Dynamic[checkbox2]],"Second Checkbox",Right],ButtonBar[{"+":>(checkbox1=True;checkbox2=True),"-":>(checkbox1=False;checkbox2=False)}]}]],WindowTitle->"Palette"];

Two control objects can be controlled with the same dynamic variable so that both check and uncheck simultaneously.

CreatePalette[DynamicModule[{checkbox1=True},
Column[{Labeled[Checkbox[Dynamic[checkbox1]],"First Checkbox",Right],Labeled[Checkbox[Dynamic[checkbox1]],"Second Checkbox",Right]}]],WindowTitle->"Palette"];

TaggingRules

(RefLink[TaggingRules, paclet:ref/TaggingRules]) is an option for cells, notebooks, and the front end that provides a way of storing additional information with a selection. This is useful when storing metadata with a cell or attaching values to a cell to call during a notebook search, evaluation, or other operation. (RefLink[TaggingRules, paclet:ref/TaggingRules]) can be a list of strings or rules.

Here are two basic examples of (RefLink[TaggingRules, paclet:ref/TaggingRules]) at the notebook level; the first is a rule and the second is a list of rules.

Notebook[TextCell["Lorem ipsum dolor sit amet, consectetur adipiscing elit"],TaggingRules->{"value"->"Example content removed 02142004"}];

Notebook[TextCell["Lorem ipsum dolor sit amet, consectetur adipiscing elit"],TaggingRules->{"value1"->False,"value2"->{"subvalue1"->12,"subvalue2"->"US"}}];

(RefLink[TaggingRules, paclet:ref/TaggingRules]) can also be used to store values for control object states in palette notebooks saved on (RefLink[PalettePath, paclet:ref/PalettePath]). The front end automatically caches the current values of the tagging rules in the global option PalettesMenuSettings for use in later front end sessions.

CurrentValue[$FrontEnd,System`PalettesMenuSettings]

This means that palettes with explicit tagging rules are guaranteed to remember their control object states across different front end sessions. For example, if a user opened an opener tracked by a tagging rule and then closed the front end, the opener would still be open when the user re-opened the palette during a later front end session.

Tagging rules for palette notebooks are implemented as a rule or nested rules and the control object states set these tagging rule values using dynamic (RefLink[CurrentValue, paclet:ref/CurrentValue]) statements similar to the following example for the tagging rule "TagName".

{Checkbox[Dynamic[CurrentValue[EvaluationNotebook[],{TaggingRules,"TagName"}]]],Dynamic[CurrentValue[EvaluationNotebook[],{TaggingRules,"TagName"}]]}

"TagName" returns (RefLink[Inherited, paclet:ref/Inherited]) unless initial values have been provided. In the next example, the state of the (RefLink[Checkbox, paclet:ref/Checkbox]) is tracked by "TagName2" with the initial value of (RefLink[True, paclet:ref/True]). Checking or unchecking the (RefLink[Checkbox, paclet:ref/Checkbox]) will change the value of the (RefLink[TaggingRules, paclet:ref/TaggingRules]) to (RefLink[True, paclet:ref/True]) or (RefLink[False, paclet:ref/False]).

SetOptions[EvaluationNotebook[],TaggingRules->{"TagName2"->True}];

{Checkbox[Dynamic[CurrentValue[EvaluationNotebook[],{TaggingRules,"TagName2"}]]],Dynamic[CurrentValue[EvaluationNotebook[],{TaggingRules,"TagName2"}]]}

Multiple control object states can be tracked at the same time. Below, the states of the two openers are tracked by "MyOpenerValue1" and "MyOpenerValue2" with initial values.

CreatePalette[Column[{OpenerView[{"First Opener","text"},Dynamic[CurrentValue[EvaluationNotebook[],{TaggingRules,"MyOpenerValue1"}]]],OpenerView[{"Second Opener","text"},Dynamic[CurrentValue[EvaluationNotebook[],{TaggingRules,"MyOpenerValue2"}]]]}],TaggingRules->{"MyOpenerValue1"->True,"MyOpenerValue2"->False},WindowTitle->"Palette"];

Once tagging values have been assigned to control object states, buttons or other controls can be created to modify groups of these values. Here, both openers can be expanded or closed by clicking either the plus or minus button. The buttons set new option values for the control object states, which dynamically update.

CreatePalette[Column[{OpenerView[{"First Opener","text"},Dynamic[CurrentValue[EvaluationNotebook[],{TaggingRules,"MyOpenerValue1"}]]],OpenerView[{"Second Opener","text"},Dynamic[CurrentValue[EvaluationNotebook[],{TaggingRules,"MyOpenerValue2"}]]],ButtonBar[{"+":>SetOptions[EvaluationNotebook[],TaggingRules->{"MyOpenerValue1"->True,"MyOpenerValue2"->True}],"-":>SetOptions[EvaluationNotebook[],TaggingRules->{"MyOpenerValue1"->False,"MyOpenerValue2"->False}]}]}],TaggingRules->{"MyOpenerValue1"->False,"MyOpenerValue2"->False},WindowTitle->"Palette"];

Two control objects can be tagged with the same (RefLink[TaggingRules, paclet:ref/TaggingRules]) value so that both open and close simultaneously.

CreatePalette[Column[{OpenerView[{"First Opener","text"},Dynamic[CurrentValue[EvaluationNotebook[],{TaggingRules,"MyOpenerValue"}]]],OpenerView[{"Second Opener","text"},Dynamic[CurrentValue[EvaluationNotebook[],{TaggingRules,"MyOpenerValue"}]]]}],TaggingRules->{"MyOpenerValue"->True},WindowTitle->"Palette"];

Tagging rules can be organized so that expanding one opener on a palette automatically closes the other. In this example, the second argument of (RefLink[Dynamic, paclet:ref/Dynamic]) is used to control the state of the opposite opener.

CreatePalette[DynamicModule[{openerSettingFunc},
openerSettingFunc[group_][val_,expr_]:=(CurrentValue[EvaluationNotebook[],{TaggingRules,"MyOpenerValue"}]=If[TrueQ[val],group,0]);Column[{OpenerView[{"First Opener","text"},Dynamic[CurrentValue[EvaluationNotebook[],{TaggingRules,"MyOpenerValue"}]==1,openerSettingFunc[1]]],OpenerView[{"Second Opener","text"},Dynamic[CurrentValue[EvaluationNotebook[],{TaggingRules,"MyOpenerValue"}]==2,openerSettingFunc[2]]]}]],TaggingRules->{"MyOpenerValue"->True},WindowTitle->"Palette"];

(RefLink[TaggingRules, paclet:ref/TaggingRules]) can also be inspected from outside the notebook if you have the (RefLink[NotebookObject, paclet:ref/NotebookObject]) corresponding to the notebook you wish to inspect. (RefLink[CreatePalette, paclet:ref/CreatePalette]) always returns a (RefLink[NotebookObject, paclet:ref/NotebookObject]) that can be used for this purpose.

nb=CreatePalette[Column[{OpenerView[{"First Opener","text"},Dynamic[CurrentValue[EvaluationNotebook[],{TaggingRules,"MyOpenerValue1"}]]],OpenerView[{"Second Opener","text"},Dynamic[CurrentValue[EvaluationNotebook[],{TaggingRules,"MyOpenerValue2"}]]]}],TaggingRules->{"MyOpenerValue1"->True,"MyOpenerValue2"->False},WindowTitle->"Palette"];

inspect[]:=Grid[{{"Opener 1 Value","Opener 2 Value"},{CurrentValue[nb,{TaggingRules,"MyOpenerValue1"}],CurrentValue[nb,{TaggingRules,"MyOpenerValue2"}]}},Frame->All,Background->{None,{LightGreen,LightRed}}]

inspect[]

Values can also be set from outside the notebook by simply assigning a value to the appropriate (RefLink[CurrentValue, paclet:ref/CurrentValue]) expression as if it were a variable. This can be useful for debugging purposes or for interfaces where multiple palettes or notebooks interact with each other.

CurrentValue[nb,{TaggingRules,"MyOpenerValue2"}]=True;
inspect[]