This Mathematica notebook, © Copyright 2009 Scientific Arts, LLC, all rights reserved.

Creating a new Stylesheet: an exercise in refinement

Posted on: Tuesday, October 2, 2007 at 03:09 AM UT  Notebook Version

Many things are going into the next version of A WorkLife FrameWork, and I will write about them soon. In fact I have been busy with them and so haven't written to this blog for a while because of that.

Today a question was asked on MathGroup about how to create and save one's own stylesheet.  At first I answered the question in a pretty complex way.  But then I learned some more and posted an update.  Then after that, and after dinner, I sat down to write a function that would do the job automatically.   I decided that it would be easy to do this: to write a function that would create a new StyleSheet for you to reuse as needed based on modifications to style definitions that you have made to a particular notebook.

It wasn't quite a simple as I first thought though, and I will perhaps have to modify it somewhat before releasing it into A WorkLife FrameWork.  Here is the code that I wrote...

Well, hang on a moment.  I really need to make a marketing announcement first...

If you are reading this you are most likely on the website for A WorkLife FrameWork.  Perhaps you have come here from MathGroup.  Whatever circuitous route has brought you here, you really should take a moment to download a trial copy of A WorkLife FrameWork.  It's a great product and will help you with all sorts of things. It extends Mathematica in all sorts of useful directions—and the simple function that follows is one of a huge number of such extensions.

Give it a go.  Please. And if you find it useful, it is quite inexpensive and is like getting several thousand hours of my consulting time  for about a single hour's cost.

Ok, so that's not enough to get you interested?  Then take a look at the screencasts to get a better sense of a small bit of A WorkLife FrameWork's scope and usefulness.

OK, I'm done with my marketing spiel.  Now on to the code—hot off the press with the ink still drying.

Here, first is the code (jump past it for continued discussion; also, you can download a Mathematica notebook with this blog post in it—including the code—by using the "Notebook" link at the end  of the post):

CreateNewStylesheetFromNotebookPrivateStyles::usage="CreateNewStylesheetFromNotebookPrivateStyles[nb] processes the notebook nb and opens a dialog that allows you to install a new Stylesheet into Mathematica based on the private style definitions that you have added to the notebook nb. In the dialog that opens—which is the standard Mathematica Install dialog—you should\n\n\tchoose \"Stylesheet\" for the first menu item;\n\n\tchoose \"From Clipboard\" from the second menu item;\n\n\ttype the name you want your Stylesheet to have in the input field;\n\n\tchoose the installation location using the radio buttons;\n\n\tand then click \"Finish\".\n\n\tThe resulting Stylesheet will then appear in your Mathematica Format>Stylesheet menu. ";

CreateNewStylesheetFromNotebookPrivateStyles[nb_NotebookObject]:=
Catch@Module[{stylenb,nbFrontEndObject,nbObjectID,tempID,tempnb,choices,nbWindowTitle,objects},

nbWindowTitle=WindowTitle/.AbsoluteOptions[nb];

FrontEndExecute[{FrontEnd`FrontEndToken[nb,"EditStyleDefinitions"]}];


tempnb=CreateWindow[Visible->False];

tempID=Last[tempnb];

NotebookClose[tempnb];

nbFrontEndObject=First@nb;

nbObjectID=Last@nb;

objects=NotebookObject[nbFrontEndObject,#]&/@Range[nbObjectID,tempID];

choices=Quiet@Select[objects,((WindowTitle/.AbsoluteOptions[#])===nbWindowTitle)&&#=!=nb&];

If[choices==={},Throw[$Failed]];
If[Length@choices>1,Throw[Message[CreateNewStylesheetFromNotebookPrivateStyles::toomany,nbWindowTitle];$Failed]];

stylenb=First[choices];

SelectionMove[stylenb,All,Notebook];

FrontEndExecute[{FrontEnd`FrontEndToken[stylenb,"Copy"]}];

NotebookClose[stylenb];

FE`InstallDialog`InstallDialogFunc[]

];

CreateNewStylesheetFromNotebookPrivateStyles[]:=CreateNewStylesheetFromNotebookPrivateStyles[EvaluationNotebook[]];

It turns out that this was trickier to do than I first thought.  The problem is not processing the notebook's private Stylesheet.  Rather the problem is figuring out how to get the NotebookObject for that Stylesheet notebook.   It's there, but the Mathematica function Notebooks[] doesn't show Stylesheet notebooks.  This is by design.  So one needs a trick to figure it out. And embedded in this code is a trick that I figured out.

Now this piece of code seems to work fine (well, you should add a check to make sure that the notebook nb is open).

But, I started to write this blog posting and, as I was writing it I realized that I could do this in a much simpler way.  So between the previous two paragraphs I rewrote the code and came up with what follows.  But I decided to leave the previous bit of code above in this blog posting because it shows something that is pedagogically interesting—at least I think so, and I learned something...

So here is the revised version of the code (which also needs a check for whether the notebook, nb, is actually open. (That's easy to do, but the way I do it is to use a function, NotebookOpenQ, that is inA WorkLife FrameWork, but I want to make this posting independent of that.  One way to do it is to evaluate NotebookInformation[nb] and if it doesn't return $Failed then nb is open...)

The function FE`InstallDialog`InstallDialogFunc is not documented in Mathematica, but it does the job that is required. 
  
Normally one would try to use 

FrontEndExecute[FrontEndToken["Install"]]

which is documented.  However this doesn' t work.  This is because, although the "Install" FrontEndToken is documented, it actually is not implemented in Mathematica.  I checked this out with the developers at Wolfram Research and they say that it is a documentation oversight that it is included in the documentation (though I personally think that it should just be implemented) and that the internal function FE`InstallDialog`InstallDialogFunc is what can be used instead.   Of course this makes the code slightly fragile as this function, or its name, may change in future versions of Mathematica.   But for now the function does work ...

Here's the revised code:

CreateNewStylesheetFromNotebookPrivateStyles::usage="CreateNewStylesheetFromNotebookPrivateStyles[nb] processes the notebook nb and opens a dialog that allows you to install a new Stylesheet into Mathematica based on the private style definitions that you have added to the notebook nb. In the dialog that opens—which is the standard Mathematica Install dialog—you should\n\n\tchoose \"Stylesheet\" for the first menu item;\n\n\tchoose \"From Clipboard\" from the second menu item;\n\n\ttype the name you want your Stylesheet to have in the input field;\n\n\tchoose the installation location using the radio buttons;\n\n\tand then click \"Finish\".\n\n\tThe resulting Stylesheet will then appear in your Mathematica Format>Stylesheet menu. ";

CreateNewStylesheetFromNotebookPrivateStyles::nonew="The notebook `1` does not have any private StyleDefinitions. Edit the notebook's styles first before creating a new style sheet from it";

CreateNewStylesheetFromNotebookPrivateStyles[nb_NotebookObject]:=
Catch@Module[{styleDefinitions},

styleDefinitions=StyleDefinitions/.Options[nb,StyleDefinitions];

If[Head[styleDefinitions]=!=Notebook,Throw[Message[CreateNewStylesheetFromNotebookPrivateStyles::nonew,nb];$Failed]];

styleDefinitions=First[styleDefinitions];

NotebookPut[Notebook[styleDefinitions],ClipboardNotebook[]];

FE`InstallDialog`InstallDialogFunc[]

];

CreateNewStylesheetFromNotebookPrivateStyles[]:=CreateNewStylesheetFromNotebookPrivateStyles[EvaluationNotebook[]]

Now, that really is a bit simpler.  I will sit on it a bit, add a bit of functionality, and then add it to the StyleSheetsPalette for A WorkLife FrameWork for easy use.

Perhaps I should have thought a bit more before coding the first version, but I learned something from the process...

So, I hope that you find this useful.

Now, go download the fully functional trial version of A WorkLife FrameWork. It's worth spending some time with...