11
$\begingroup$

Because I find myself relocating frequently within a long notebook divided into sections, subsections, & subsubsections, I would like to display the identifying information for the section, etc. that I'm in at any point in time. I have in mind something similar to a readily visible header or footer that one might display within a word processing document or textbook. For example, if I am working somewhere within a section titled "4.2) Geometry of Matrix Transformations of the Plane", I would like that section identifier displayed conveniently and updated when my further relocating within the notebook takes me into a different section. My aim is not to compose a textbook/article/etc.; it's the far more humble purpose of helping myself as I work my way through an assigned textbook, making notes, computations, plots, and so forth and jumping this way and that throughout the notebook as the semester progresses. Suggestions for someone who is not a programmer? Thx!

$\endgroup$
  • 2
    $\begingroup$ This is easy to set up for printing page headers etc. but just to be clear you want this in a working environment not on a printed page right? $\endgroup$ – Mike Honeychurch Sep 29 '13 at 1:26
8
$\begingroup$

It isn't a simple question. My current solution is

SetOptions[EvaluationNotebook[], DockedCells -> Cell[#, "Output", CellMargins -> 0]] &@
 BoxData@ToBoxes@
  With[{GetTOC = 
     Cases[NotebookGet@SelectedNotebook[], 
      Cell[name_, style : "Section" | "Subsection" | "Subsubsection", ___] :> {style, name},
       Infinity] /. {"Subsubsection", x_} :> x[] //. 
     {x___, {"Subsection", y_}, z : Except[_List] ..., 
       w : PatternSequence[{_, _}, ___] | PatternSequence[]} :> {x, y[z], w} //. 
     {x___, {"Section", y_}, z : Except[_List] ..., 
        w : PatternSequence[{_, _}, ___] | PatternSequence[]} :> {x, y[z], w} &}, 
    DynamicModule[{TOC = GetTOC[]}, Row@
      {Button["Update", TOC = GetTOC[]],
        Dynamic@Refresh[" " <> StringJoin @@ (ToString[#] <> "." & /@ #) <> 
        " " <> If[Length[#] == 0, "", 
         Head@Catch@Fold[If[Length[#] >= #2, #[[#2]], Throw[#]] &, TOC, #]] &[
      CurrentValue[NotebookSelection[], {"CounterValue", #}] & /@ \
{"Section", "Subsection", "Subsubsection"} /. {x___, 0 ...} :> {x}], 
     UpdateInterval -> 1]}]]

enter image description here

It shows the DockedCell with the number and the name of the section (subsection, subsubsection) in which the pointer is located.

It updates every second. The button "Update" is needed only if you change the names of the sections.

The number of the current section can be easily obtained by CurrentValue[NotebookSelection[], {"CounterValue", "Section"}]. But as far as I know there is no standard option to obtain the names of the sections. So I parse the current notebook to obtain the tree of the TOC (table of contents).

| improve this answer | |
$\endgroup$
5
$\begingroup$

I will present a stylesheet approach, using a Dynamic object in a DockedCell.

There are two main issues: How to figure out where the current selection is, so that the previous "Section" or "Subsection" cell can be found, and how to make changes in the current selection ticklish to Dynamic.

  1. Current selection

The first possibility to find the current cell is to use SelectedCells[] or Cells[NotebookSelection[]], but neither work when the cell insertion point is not in a cell. What ended up working is to use FrontEnd`CellInformation. For example:

Button["press", Print @ FrontEndExecute @ FrontEnd`CellInformation[NotebookSelection[]]]

enter image description here

{"Style" -> "Text", "ContentData" -> TextData, "ContentDataForm" -> TextForm, "Evaluating" -> False, "Rendering" -> False, "NeedsRendering" -> False, "CursorPosition" -> {0, 4}, "FirstCellInGroup" -> False, "CellSerialNumber" -> 13990, "Formatted" -> True, "ExpressionUUID" -> "a4103240-2c81-4d64-90fc-deb0941ca4a0"}

When the cell insertion point is between cells, the cell information returned will be for the last selected cell. So, when using the up key, the cell information will be for the cell after the insertion point. The only issue is when using the mouse to select a cell in a new section, in which case the cell information does not get updated, and will still reference the old section. If multiple cells are selected, than information for multiple cells is returned.

The key piece of information returned by FrontEnd`CellInformation is the ExpressionUUID, which uniquely identifies a cell. Given an ExpressionUUID we can find the corresponding CellObject using Cells. For example:

current = First @ Cells[ExpressionUUID -> "a4103240-2c81-4d64-90fc-deb0941ca4a0"]

CellObject[231937]

If you have the CellObject, then you can find the previous "Section" cell using PreviousCell:

PreviousCell[current, CellStyle->"Section"]

CellObject[111489]

The above pieces of code are enough to find the contents of the current "Section" cell.

  1. Making Dynamic ticklish:

Just using Dynamic with CellInformation doesn't cause the output to change when the selection changes:

Dynamic[FrontEndExecute @ FrontEnd`CellInformation[NotebookSelection[]]]

{"Style" -> "Input", "ContentData" -> BoxData, "ContentDataForm" -> StandardForm, "Evaluating" -> False, "Rendering" -> False, "NeedsRendering" -> False, "CursorPosition" -> "CellBracket", "FirstCellInGroup" -> True, "CellSerialNumber" -> 14068, "Formatted" -> True, "ExpressionUUID" -> "0406fe6a-3df3-425d-acbb-0d7ea25f9879"}

The above Dynamic object doesn't update even when different cells are selected. To get the Dynamic output to change one can include the CurrentValue of the selection as well:

Dynamic[{
    CurrentValue[NotebookSelection[], CellStyle],
    FrontEndExecute @ FrontEnd`CellInformation[NotebookSelection[]]
}]

{{"Output"}, {"Style" -> "Output", "ContentData" -> BoxData, "ContentDataForm" -> StandardForm, "Evaluating" -> False, "Rendering" -> False, "NeedsRendering" -> False, "CursorPosition" -> "CellBracket", "FirstCellInGroup" -> False, "CellSerialNumber" -> 14085, "Formatted" -> True, "ExpressionUUID" -> "300de976-3d90-42c3-85df-eca9422c0e1c"}}

The above dynamic content changes as the selection is changed.

We now have the pieces needed to create the desired DockedCells:

SetOptions[
    EvaluationNotebook[],
    StyleDefinitions -> Notebook[
        {
        Cell[StyleData[StyleDefinitions->"Default.nb"]],
        Cell[StyleData["Notebook"],
            DockedCells->Cell[BoxData @ ToBoxes @ Dynamic[
            Row@{
                CurrentValue[NotebookSelection[],{"CounterValue","Section"}],
                Replace[CurrentValue[NotebookSelection[],{"CounterValue","Subsection"}],{0->Sequence[],n_->Sequence[".",n]}],
                ": ",
                Replace[Cell[a_,__]:>RawBoxes@Cell[a]] @ With[
                    {cell=First[Cells[ExpressionUUID->OptionValue[FrontEndExecute@FrontEnd`CellInformation[FrontEnd`SelectionObject],ExpressionUUID]],{}]},
                    Which[
                        cell==={},
                        "",
                        MemberQ[CurrentValue[cell,CellStyle],"Section"|"Subsection"],
                        NotebookRead[cell],
                        True,
                        NotebookRead[PreviousCell[cell,CellStyle->{"Section","Subsection"}]]
                    ]
                ]
            }
            ],
            TextAlignment->Center
            ]
        ]
        },
        StyleDefinitions->"PrivateStylesheetFormatting.nb"
    ]
]

A short animation showing it working follows, although you'll see that the code is erratic when moving the insertion point with the mouse instead of the up/down keys.

enter image description here

| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.