Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

Is there a way to show maybe a slightly different color, or change the cell bracketing color of a Mathematica cell when it has been modified?

enter image description here

Ideally it would be nice to follow the standard of using asterisks but cells don't really have names. Just to clarify a cell is modified when it has been edited but hasn't been run.

Code In Progress: This code basically is supposed to check to see if CellChangeTime changes on a set interval. It doesn't work currently but it is getting there.

share|improve this question
1  
The answer is almost certainly no. –  Eric Brown Jun 22 '13 at 1:11
1  
The previous cell content is a, then I changed content to b, it was modified, but when I changed it to a again, it was modified too? CellChangeTime is a good choice but does not deal with the case I said. –  HyperGroups Jun 23 '13 at 2:24
1  
This is for inline cells. Something related. mathematica.stackexchange.com/questions/27117/… Use one customized CellStyle with events could be one choice. –  HyperGroups Jun 23 '13 at 2:28
    
@HyperGroups Considering the user has only one undo, I would consider that to be an special case that IMO should merit a running of the cell box to update the Output statements –  Liam William Jun 24 '13 at 3:36
add comment

2 Answers 2

up vote 3 down vote accepted

To show a "modified" indicator, we should also choose an appropriate reference/origin from which it is tracked. A reasonable choice would either be the last notebook save or the time the cell was last evaluated. Since your intention is to track all modifications and not only modified + evaluated, I'll use the last save point as the reference.

The following is a proof of concept of a "cell modified" indicator. I'll discuss possible improvements and current limitations after the code:

1. Action when cell is modified

As mentioned above, I've chosen the time when the file was last saved (modification time on disk) as the reference point. The following function sets a cell's background to a light shade of gray if the cell modification time (as given by CellChangeTimes) is after the file modification time.

modifiedCell := With[{
    fileMod = "FileModificationTime" /. NotebookInformation[EvaluationNotebook[]], 
    cellMod = If[# != {}, Last@Flatten@#, -Infinity] &@CurrentValue[CellChangeTimes]},

    Switch[fileMod,
        "FileModificationTime", 
         None,

        _?NumericQ,
        If[fileMod < cellMod || cellMod === {},
            SetOptions[EvaluationCell[], Background -> GrayLevel[0.95]],
            SetOptions[EvaluationCell[], Background -> White]]
        ]
    ]

2. Action when notebook is saved

Given the choice of our reference time, it is necessary to reset the cell backgrounds (here, the "modified indicator") when the notebook is saved. The following function sets the background of all input cells in the notebook to White (you can also use your default background, if it's different or Background -> None).

notebookSaved := SetOptions[#, Background -> White] & /@ 
    Cells[EvaluationNotebook[], CellStyle -> "Input"]

Thanks to Michael for the tip re: using Cells directly instead of Cells + NotebookFind.

3. Setting up event handlers & piecing it together

All that remains is to set up appropriate event handlers — CellEventActions for when a cell is modified ("modified" here means a keypress in that cell) and NotebookEventActions for when the notebook is saved — and make the appropriate changes. I prefer to modify the stylesheet and add the changes to the "Input" style for the notebook. If you want other cells to also exhibit this behaviour, you'll have to add it yourself.

SetOptions[EvaluationNotebook[], {
    StyleDefinitions -> Notebook[{
        Cell[StyleData[StyleDefinitions -> "Default.nb"]], 
        Cell[StyleData["Input"], 
            CellEventActions -> {
                "KeyDown" :> modifiedCell,
                PassEventsDown -> True
            }, 
            Background -> GrayLevel[1]
        ]}, 
        Saveable -> True, StyleDefinitions -> "PrivateStylesheetFormatting.nb"
    ],
    NotebookEventActions -> {
        {"MenuCommand", "Save"} :> notebookSaved,
        PassEventsDown -> True
    }
}]

4. Current limitations

CellEventActions is triggered before the cell is actually modified. This means that the first change after a save point cannot trigger the change in background, because the CellChangeTimes still refers to the previous state (or in the case of a new cell, CellChangeTimes is empty). While this is a wee bit limiting, in practice, I've never had to make just 1 key press worth of change (without even pressing a modifier/arrow key in the process), so it might not be all that bad.

share|improve this answer
1  
+1 btw, in the definition for notebookSaved, is it possible to just strip out the Background option so the style defined in stylesheet can be automatically applied? –  Silvia Jun 23 '13 at 23:01
    
@Silvia Good question! I don't know if that can be done just by setting/unsetting an option though. One way would be to retrieve the background color from the base stylesheet at definition time and use that. Also, in point 3, one should use the custom stylesheet instead of Default.nb –  rm -rf Jun 24 '13 at 2:16
    
@rm-rf I could be wrong but it appears this solutions doesn't work whenever you have an Untitled notebook open, therefore the notebook hasn't been saved. –  Liam William Jun 24 '13 at 3:22
1  
@Liam Yes, that is obvious since it uses the "Last modified time" (on disk) as reference time, so just save the notebook. –  rm -rf Jun 24 '13 at 4:48
    
I encountered a similar problem but with the WindowTitle option. I added it to a Notebook and couldn't find a decent way to strip it out. So I have to manipulate the nb-expression and re-put it. –  Silvia Jun 24 '13 at 18:11
show 2 more comments

enter image description here

It would be nice to see if there is a way to use a regular cell box, but I am going to try a different technique instead. This example uses an InputField to display the content.

There are 2 issues currently 1) there appears to be no way to keep ShiftEnter from bubbling up and therefore producing a new cell 2) the formatter doesn't work.

DynamicModule[{color = RGBColor[1, 1, 1]}, EventHandler[
  InputField["a", String,
   Background -> Dynamic[color],
   ImageSize -> {Dynamic[(WindowSize /.
          Options[EvaluationNotebook[], WindowSize])[[1]]] - 112,
     Automatic}],
  {
   "KeyDown" :> (color = RGBColor[0.85`, 0.85`, 0.85`]),
   "ReturnKeyDown" :>
    FrontEndExecute[{NotebookWrite[EvaluationNotebook[], "\n",
       After]}],
   {"MenuCommand", "HandleShiftReturn"} :> {color = RGBColor[1, 1, 1]}
   }, PassEventsDown -> True
  ]
 ]
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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