7
$\begingroup$

One can of course Ctrl + MouseClick to select 1st and 3rd cell.

But how to select non adjacent Cells programmatically?

I was looking for neat examples here or in the documentation but I was not able to find suitable Option / function / FrontEndToken.


The method I've prepared is good enough for many cases so I'm posting it as an answer but I will gladly accept other if it deals with following issues:

  • Using my method, to expand selection one need to rescan all already selected. Not really efficient.

and a minor issues

  • This is ugly big amout of code for such simple operation.
  • I was not able to manipulate CellTags with CurrentValue. Not quite sure why.
$\endgroup$
8
$\begingroup$

So the only way that came to my mind and worked was to use CellTags and NotebookLocate.

SetAttributes[selectCells, HoldRest];

selectCells[cells_, nb_: InputNotebook[]] := With[{
  tag = ToString @ Unique["Tag"]
  },
  (SelectionMove[#, All, Cell, AutoScroll -> False]; 
   FrontEndExecute@FrontEnd`SelectionAddCellTags[nb, tag]
  ) & /@ cells;
  NotebookLocate[tag];
  FrontEndExecute@FrontEnd`SelectionRemoveCellTags[nb, tag]   
];

This only works with current InputNotebook[] so you can use it in the notebook which is a parent to cells you want to select or you can use it from palette.


For testing purposes evaluate:

selectCells @ Cells[][[{1, 3}]]

or

 selectCells @ Cells[][[;; ;; 2]]

and the result should be something like:

enter image description here

$\endgroup$
  • $\begingroup$ Personally I prefer a NotebookFind to a NotebookLocate because I can more easily specify which notebook to so the selection in. $\endgroup$ – b3m2a1 Aug 23 '17 at 21:02
  • $\begingroup$ @b3m2a1 It think your preference is indeed better. I must have missed that commands like NotebookFind[nb, "tag", All, CellTags] work. Quite often my self answers are imperfect as I need stuff to be done and proceed with other duties. Will update this answer soon. $\endgroup$ – Kuba Aug 25 '17 at 22:13
4
$\begingroup$

Instead of selecting cells and then using the FrontEnd`SelectionAddCellTags / FrontEnd`SelectionRemoveCellTags functions, you can just use SetOptions on the CellObject (i.e., no need to select them first). Here is a version of selectCells that does this:

selectCells[cells_List]:=With[
    {
    tmp = CreateUUID[],
    parents = DeleteDuplicates @ Map[ParentNotebook] @ cells
    },
    Internal`WithLocalSettings[
        addCellTag[cells, tmp],
        NotebookFind[#1, tmp, All, CellTags]& /@ parents,
        removeCellTag[cells, tmp]
    ]
]

addCellTag[cells_, tag:_String|{__String}] := With[
    {
    new = Flatten /@ Thread[{CurrentValue[cells, CellTags], Sequence@@tag}]
    },
    MapThread[SetOptions[#1, CellTags->#2]&, {cells, new}]
]

removeCellTag[cells_, tag:_String|{__String}] := With[
    {
    new = DeleteCases[
        Flatten @* List /@ CurrentValue[cells, CellTags],
        Alternatives @@ tag,
        {2}
    ]
    },
    MapThread[SetOptions[#1, CellTags->#2]&, {cells, new}];
]

It uses some of the ideas behind @b3m2a1's answer to essentially the same question, e.g., CreateUUID (to create a unique tag), NotebookFind (so that it works with cells from any notebook) and ParentNotebook (to figure out what notebooks contain the cells).

$\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.