[Date Prev][Date Next][Thread Prev][Thread Next][Author Index][Date Index][Thread Index]

PATCH: (0.55) Copy and clone entire selections



The patch subsumes the changes I sent in my patch of Thu, 29 Oct 1998
02:14:49 -0500, which introduced shear operations and replaced `mark'
with `select'.  It contains all the changes from that patch, plus the
following:

I. Selections can be cloned.

      1. The `atcursor_clone' function used to clone the accursed
         cell.  Now it only clones the accursed cell if there is no
         active selection.  

         If there is an active selection, all the selected cells are
         cloned, and any links between them are copied so that the
         cloned structure preserves as much as possible of the old
         structure.

         Example:

            A1 -- B1            +---> d.1
            |                   |
            |                   |
            A2 -- A3            V d.2

         A1, A2, and A3 are selected.  The user presses `t'.  Three
         new cells are created, one linked to each of A1, A2, A3 in
         the d.clone dimension.  Also, the clone of A1 is linked
         +d.2ward to the clone of A2, and the clone of A2 is linked
         +d.1ward to the clone of A3.

         The clone of A1 does not have a +d.1 link the way A1 did,
         because B1 was not cloned:

         Clones:

            A1                  +---> d.1
            |                   |
            |                   |
            A2 -- A3            V d.2


II. New `copy' function for groups of cells.

      1. There is now a `copy' function for cells.  It is useful when
         you want a new group of cells to have a structure similar to
         an old group, but different contents.  

      2. Copying is just like cloning, except that the new cells are
         not linked to the old cells in the d.clone direction.  Also
         the new cells have contents `Copy of XXX'.

      3. The meta-t and meta-T keys are like the t and T keys, but
         they copy instead of cloning.

      4. To support this, the function `atcursor_clone' now accepts an
         optional second argument which specifies which operation it
         will perform.  The second argument can be either `clone' or
         `copy'.  For compatibility with older code, the  default is
         `clone'. 

      5. A new function, `atcursor_copy', is a wrapper for the copy
         semantics of `atcursor_clone'.

III.  Changes in selection semantics

      1. `is_selected' now returns true when the specified cell is
         part of any selection.  It formerly returned true only when
         the cell was in the active selection.

      2. `is_active_selected' takes over the old functionality of
         reporting whether or not a cell is part of the active
         selection.

      3. As before, a cell is inly displayed on the screen as
         `selected' if it belongs to the active selection.

      4. `get_selection' accepts a number $n and returns a list of the
         cells in selection #$n. `get_selection(0)' returns a list of
         the cells in the active selection.  I expect that in the
         future, selections will have user-specified names, and
         `get_selection' will get selections by name instead of by
         number. 

      5. `get_active_selection' returns a list of the cells in the
         active selection.

      6. Given a cell, `get_which_selection' returns the headcell of
         the selection group of which it is a member, or undef it is
         not part of any selection.

      7. Bug fix in `atcursor_select':  Formerly, if a cell was
         linked to others in the d.mark dimension, but was not part of
         any selection, the select and deselect commands would do
         nothing.  This is now fixed.

IV.  Miscallaneous

      1. `cursor_jump' is now more generic.  It accepts two
         arguments: A cursor and a destination cell, and jumps the
         cursor to the specified destination.  The old functionality
         was to examine the input buffer, jump to the cell named in
         the input buffer if there was input, and to cell 0 otherwise.
         This functionality has moved to `cursor_jump_input', which is
         a wrapper for `cursor_jump'.

      2. The key for refreshing the screen is now control-L instead of
         control-R.  This is not mnemonic, but it is consistent with
         every other curses program.

      3. `get_distance' returns the distance from one cell to another in a
         (directed) dimension $dim.  If the second cell cannot be
         reached from the first along the specified dimension, it
         returns undef.

      4. Given a list of cells, `get_links_from' returns a list, each
         of whose members is a list of all the links from the
         corresponding cell in the input list.

         This function is very inefficient.  It has to examine every
         item in the space.  It gets the links from many cells at once
         because that does not take significantly longer than getting
         the links from one cell.

      5. The `cell2' argument to `link_break' is now optional.  If
         omitted, it is inferred from the `cell1' and `dim' arguments.
         If it is supplied, and it is not the same as the cell implied
         by the `cell1' and `dim' arguments, a fatal exception is thrown.

      6. If `cells_row' is asked to find the cells in a rank starting
         from the undefined value instead of from a real cell, it now
         returns the empty list.

         This is useful in a case like this:
             ---> +d.d

             HEADCELL -- cell1 -- cell2 -- cell3

         You want a list of cells linked +d.dward from HEADCELL, but
         not including HEADCELL itself.  You can now write:

             &cells_row($ZZ{"HEADCELL+d.d"}, "+d.d")

         which will work even if the list of cells would have been empty.



Herewith the complete patch:


--- zigzag.55	Tue Oct 27 22:55:17 1998
+++ zigzag.copyselect	Fri Oct 30 16:02:46 1998
@@ -29,7 +29,7 @@
 #
 # $Log: zigzag,v $
 # Revision 0.55  1998/10/26 05:37:32  xanni
-# Change default file extension and incorporate EDITOR selection patch
+# Change default file extension and incorporate $EDITOR selection patch
 # from Mark-Jason Dominus <mjd@xxxxxxxxxx>
 #
 # Revision 0.54  1998/10/08 03:56:21  xanni
@@ -59,6 +59,7 @@
 # Older revisions are listed in the CHANGES file
 #
 
+
 use integer;
 use strict;
 use Curses;        # Available at http://www.cpan.org/CPAN.html#curses
@@ -88,6 +89,7 @@
 my $CELLS_PER_WIN = 5;           # Number of cells displayed across each window
 my $CURSOR_HOME = 10;            # NOTE!  This assumes it stays fixed!
 my $DELETE_HOME = 99;            # NOTE!  This assumes it stays fixed!
+my $SELECT_HOME = 21;            # NOTE!  This assumes it stays fixed!
 my $EDITOR = choose_editor();    
 my $FILENAME = "zigzag.zz";      # Default filename for initial slice
 my $TEMP_FILE = "/tmp/zigzag-$<-$^T"; # Filename used for external editing
@@ -164,22 +166,26 @@
    &meta_key("k")     => 'atcursor_edit(1);',
    "\cK"              => 'atcursor_edit(1);',
    "b"                => '@_ = input_get_direction(); atcursor_break_link(@_) if $_[0];',
-   "G"                => 'cursor_jump(get_cursor(0));',
-   "g"                => 'cursor_jump(get_cursor(1));',
-   &KEY_HOME          => 'cursor_jump(get_cursor(1));',
+   "G"                => 'cursor_jump_input(get_cursor(0));',
+   "g"                => 'cursor_jump_input(get_cursor(1));',
+   &KEY_HOME          => 'cursor_jump_input(get_cursor(1));',
    "h"                => '@_ = input_get_direction(); atcursor_hop(@_) if $_[0];',
+   "\cL"	      => 'display_refresh(curscr()); $@ = "";',
    "N"                => 'cell_create(0);',
    &KEY_IC            => 'cell_create(0);',
    "n"                => 'cell_create(1);',
-   "M"                => 'atcursor_mark(0);',
-   "m"                => 'atcursor_mark(1);',
+   "M"                => 'atcursor_select(0);',
+   "m"                => 'atcursor_select(1);',
+   &meta_key("m")     => 'rotate_selection()',
+   &meta_key("M")     => 'push_selection()',
    "Q"                => 'view_quadrant_toggle(0);',
    "q"                => 'view_quadrant_toggle(1);',
    "R"		      => 'view_reset(0);',
    "r"		      => 'view_reset(1);',
-   "\cR"	      => 'display_refresh(&curscr); $@ = "";',
    "T"                => 'atcursor_clone(0);',
    "t"                => 'atcursor_clone(1);',
+   &meta_key("T")     => 'atcursor_copy(0);',
+   &meta_key("t")     => 'atcursor_copy(1);',
    "V"                => 'view_raster_toggle(0);',
    "v"                => 'view_raster_toggle(1);',
    "\cV"	      => 'status_draw(&version()); $StatusTimer = 20;',
@@ -236,6 +242,7 @@
    10 =>        "Cursor home",
       "10+d.1" =>	1,
       "10+d.2" =>	11,
+      "10-d.1" =>	21,
    11 =>        "Action",
       "11+d.1" =>	12,
       "11-d.2" =>	10,
@@ -268,6 +275,10 @@
       "19+d.1" =>	20,
    20 =>        "I",
       "20-d.1" =>	19,
+   21 => "Selection",
+      "21+d.1" =>       10, 
+      "21+d.2" =>       21, 
+      "21-d.2" =>       21, 
    30 =>        "#Edit\natcursor_edit(1);",
       "30+d.1" =>	35,
       "30-d.2" =>	0,
@@ -313,9 +324,12 @@
       "55+d.1" =>	56,
    56 =>        "#O-break\natcursor_break_link(1, 'O');",
       "56-d.1" =>	55,
-   60 =>        "#Mark\natcursor_mark(1);",
+   60 =>        "#Select\natcursor_select(1);",
       "60-d.2" =>	50,
       "60+d.2" =>	70,
+      "60+d.1" =>       61,
+   61 =>        "#Rot.Selection\nrotate_selection();",
+      "61-d.1" =>       60,
    70 =>        "#L-Hop\natcursor_hop(1, 'L');",
       "70+d.1" =>	71,
       "70-d.2" =>	60,
@@ -334,9 +348,18 @@
       "74+d.1" =>	75,
    75 =>        "#O-Hop\natcursor_hop(1, 'O');",
       "75-d.1" =>	74,
-   80 =>        "#Shear",
+   80 =>        "#Shear -^\natcursor_shear(1, 'D', 'L')",
       "80-d.2" =>	70,
       "80+d.2" =>	85,
+      "80+d.1" =>       81,
+   81 =>        "#Shear -v\natcursor_shear(1, 'U', 'L')",
+      "81-d.1" =>       80,
+      "81+d.1" =>       82,
+   82 =>        "#Shear ^+\natcursor_shear(1, 'D', 'R')",
+      "82-d.1" =>       81,
+      "82+d.1" =>       83,
+   83 =>        "#Shear v+\natcursor_shear(1, 'U', 'R')",
+      "83-d.1" =>       82,
    85 =>        "#Chug",
       "85-d.2" =>	80,
       "85+d.2" =>	90,
@@ -427,10 +450,21 @@
    return (defined($ZZ{"$cell-d.clone"}) || defined($ZZ{"$cell+d.clone"}));
 }
 
-sub is_marked($)
+sub is_selected($)
+{
+  my $cell = shift;
+  my $headcell = get_lastcell($cell, '-d.mark');
+  return 0 if $headcell == $cell; # It's actually a selection head!
+  return 1 if defined &get_distance($headcell, "+d.2", $SELECT_HOME);
+  return 0;
+}
+
+sub is_active_selected($)
 {
   my $cell = shift;
-  return (defined($ZZ{"$cell-d.mark"}) || defined($ZZ{"$cell+d.mark"}));
+  my $headcell = get_lastcell($cell, '-d.mark');
+  return 1 if $headcell != $cell && $headcell == $SELECT_HOME;
+  return 0;
 }
 
 
@@ -454,6 +488,45 @@
 # Retrieving Information
 # Named get_*
 #
+
+
+sub get_accursed($)
+# Get the cell that is accursed in the specified window
+# or by the specified cursor
+{
+  my $n = shift;
+  &get_lastcell(&get_cursor($n), "-d.cursor");
+}
+
+sub get_active_selection()
+{
+  &get_selection(0);
+}
+
+sub get_selection($)
+# Get the $nth selection and return a list of cells
+{
+  my ($n) = @_;
+  my $sel;
+  for ($sel = $SELECT_HOME;
+       $n && defined($sel);
+       $n--
+      ) {
+    $sel = $ZZ{"$sel+d.2"};
+  }
+
+  &cells_row($ZZ{"$sel+d.mark"}, "+d.mark");
+}
+
+sub get_which_selection($) 
+# Given a cell, get the headcell of the selection it is in, or undef
+# if it is not part of a selection
+{
+  my $cell = shift;
+  return undef unless defined $ZZ{"$cell-d.mark"};
+  get_lastcell($cell, "-d.mark");
+}
+
 sub get_lastcell($$)
 # Find the last cell along a given dimension
 {
@@ -466,6 +539,27 @@
   return $cell;
 }
 
+sub get_distance($$$)
+# Given cell A a direction, and cell B,
+# find out how far B is from A in the specified direction.
+# return undef if B cannot be reach ed from A in that direction
+{
+  my ($start, $dir, $end) = @_;
+  my $cell;
+
+  return 0 if $start == $end;
+
+  my $dist = 1;
+  for ($cell = $ZZ{"$start$dir"};
+       defined $cell && $cell != $end && $cell != $start;
+       $cell = $ZZ{"$cell$dir"}
+      ) {
+    $dist++;
+  }
+  return undef if ! defined($cell) || $cell == $start;
+  return $dist;
+}
+
 sub get_outline_parent($)
 # Find the "outline parent" (first cell -d.1 along -d.2) of a cell
 {
@@ -625,16 +719,129 @@
   return @_;
 }
 
+sub get_links_from(@) 
+# Given a list of cells, find all the links from those cells.
+# Return a list of lists.  It would be simpler to have this function
+# accept a single cell and return just the links from that cell, but
+# because of implementation details, it is cery inefficient to do that,
+# and doesn't take any longer to get the links for many cells at once.
+# So the interface here is supposed to encourage use of parallelism.
+{
+  my @result;
+  my %interesting;
+  @interesting{@_} = (0 .. $#_);
+
+  my $k;
+  # Grovel over the entire database
+  while ($k = each %ZZ) {
+    my ($cell, $dir) = $k =~ /^(\d+)(.*)$/;
+    next unless defined $cell;
+    next if $dir eq '';
+    next unless exists $interesting{$cell};
+    push @{$result[$interesting{$cell}]}, $dir;
+  }
+
+  @result;
+}
+
+#
+# Multiple-cell operations
+# Named do_*
+#
+sub do_shear($$$;$$) 
+# Given a row of cells starting at $first_cell,
+# move them all $n cells in the $dir direction.
+# Cells that were linked in the $link direction
+# have their links broken and relinked to new cells.
+#
+# Before: do_shear(A1, d.1, d.2, 1);
+# 
+# ---> d.1
+# V d.2
+#
+# A1 --- B1 --- C1 --- D1 --- E1
+# |      |             |      |
+# A2     B2     C2     D2     E2
+#
+# After:
+#
+#        A1 --- B1 --- C1 --- D1 --- E1
+#        |      |             |
+# A2     B2     C2     D2     E2
+#
+# Optional fourth argument $n defaults to 1.
+# Optional fifth argument $hang says whether the cell on the end
+# should be linked back at the beginning or whether it should just
+# be left hanging.  $hang=false: Back at beginning.  
+# $hang = true: Leave hanging.  Default: false.
+{
+  my ($first_cell, $dir, $link, $n, $hang) = @_;
+  $n = 1 unless defined $n;
+  $hang = 0 unless defined $hang;
+
+  my $cell;
+  my ($prev_cell, $prev_linked);
+  my $first_linked = $ZZ{"$first_cell$link"};
+
+  my @shear_cells = &cells_row($first_cell, $dir);
+  my @linked_cells = map {$ZZ{"$_$link"}} @shear_cells;
+
+  my @new_link = @linked_cells;
+  # Move some of these from the beginning
+  my @x = splice(@new_link, 0, $n);
+  # And put them back at the end.
+  push @new_link, @x;
+
+  my $i;
+  my $linkno = 0;
+  my $last_linked;
+  # Break all the links
+  for ($i=0; $i < @shear_cells; $i++) {
+    my $old_link = $linked_cells[$i];
+    next unless defined $old_link;
+    my $shear_cell = $shear_cells[$i];
+    link_break($shear_cell, $old_link, $link);
+  }
+
+  $linkno = 0;
+  for ($i=0; $i < @shear_cells; $i++) {
+    my $new_link = $new_link[$i];
+    next unless defined $new_link;
+    next if $i == $#shear_cells && $hang;
+    my $shear_cell = $shear_cells[$i];
+    link_make($shear_cell,  $new_link, $link);
+  }
+
+  foreach (@Window_Dirty)
+  {
+     $_ = $TRUE;
+  }
+}
 
 #
 # Functions that operate on links between cells
 # Named link_*
 #
-sub link_break($$$)
+sub link_break($$;$)
 # Break a link between two cells in a given dimension.
 # This should be the only way links are ever broken to ensure consistency.
+# Second argument is optional.  If present, it must be linked from cell 1
+# in the approprate dimension.
 {
-  my ($cell1, $cell2, $dim) = @_;
+  my ($cell1, $cell2, $dim);
+  if (@_ == 3) {
+    ($cell1, $cell2, $dim) = @_;
+  } else {
+    ($cell1, $dim) = @_;
+  }
+  my ($linked_cell) = $ZZ{"$cell1$dim"};
+  if (defined $cell2) {
+    die "$cell1 is not linked to $cell2 in dimension $dim"
+      unless $cell2 == $linked_cell;
+  } else {
+    $cell2 = $linked_cell; # Infer second argument
+  }
+
   die "No cell $cell1" unless defined $ZZ{"$cell1"};
   die "No cell $cell2" unless defined $ZZ{"$cell2"};
   die "Invalid direction $dim" unless ($dim =~ /^[+-]/);
@@ -761,29 +968,36 @@
   }
 }
 
-sub cursor_jump($)
-# Jump cursor to $Input_Buffer
+sub cursor_jump_input($)
+# Jump specified cursor to $Input_Buffer
+# Or to 0 if no input buffer.
+{
+  my $dest = defined($Input_Buffer) ? $Input_Buffer : 0;
+  cursor_jump($_[0], $dest);
+  undef $Input_Buffer;
+}
+
+sub cursor_jump($$)
+# Jump cursor to specified destination
 {
-  my $curs = $_[0];
-  $Input_Buffer = 0 if !defined $Input_Buffer;
+  my ($curs, $dest) = @_;
 
   # Must jump to a valid non-cursor cell
-  if (!defined $ZZ{$Input_Buffer} || defined $ZZ{"$Input_Buffer-d.cursor"})
+  if (!defined $ZZ{$dest} || defined $ZZ{"$dest-d.cursor"})
   {
-     &user_error(3, $Input_Buffer);
+     &user_error(3, $dest);
   }
   else
   {
     # Move the cursor
     &cell_excise($curs, "d.cursor");
-    &cell_insert($curs, &get_lastcell($Input_Buffer, "+d.cursor"), "+d.cursor");
+    &cell_insert($curs, &get_lastcell($dest, "+d.cursor"), "+d.cursor");
 
     foreach (@Window_Dirty)
     {
        $_ = $TRUE;
     }
   }
-  undef $Input_Buffer;
   $Window_Dirty[0] = $TRUE;
 }
 
@@ -824,16 +1038,60 @@
   return 1;
 }
 
-sub atcursor_clone($)
-# Create a new clone cell at a given cursor
+sub atcursor_clone($;$)
+# Clone all the cells in the current selection
+# If the current selection is empty, clone just the accursed cell.
+# Move the given cursor to the first of the new clones.
+# Optional second argument specifies which of several clone-ish operations
+# to perform.  Presently supported:  
+#  clone: as before
+#  copy: copy cells instead of cloning, and copy links also.
 {
   my $curs = &get_cursor($_[0]);
-  my $cell = &get_lastcell($curs, "-d.cursor");
+  my $op = $_[1] || 'clone';
+  my $last_new_cell;
 
-  my $new = $ZZ{"n"}++;
-  $ZZ{$new} = "Clone of $cell";
-  &cell_insert($new, $cell, "+d.clone");
-  &cursor_move_dimension($curs, "+d.clone");
+  my @selection = &get_active_selection;
+  if (@selection == 0) {
+    @selection = (&get_lastcell($curs, "-d.cursor"));
+  }
+
+  my $cell;
+  my %new;
+  my %selected;
+  foreach $cell (@selection) {
+    my $new = $ZZ{"n"}++;
+    $selected{$cell} = 'yup';
+    $new{$cell} = $new;
+    if ($op eq 'clone') {
+      $ZZ{$new} = "Clone of $cell" ;
+      &cell_insert($new, $cell, "+d.clone") ;
+    } elsif ($op eq 'copy') {
+      $ZZ{$new} = "Copy of " . $ZZ{$cell};
+    }
+    $last_new_cell = $new;
+  }
+
+  # duplicate exactly those links that are from one selected
+  # cell to another.  
+  my @cell_links = get_links_from(@selection);
+
+  my $i;
+  for ($i = 0; $i < @cell_links; $i++) {
+    my @links = @{$cell_links[$i]};
+    my $old = $selection[$i];
+    my $new = $new{$old};
+    my $dim;
+    foreach $dim (@links) {
+      # If the linked cell is selected, then copy the link
+      if ($selected{$ZZ{"$old$dim"}}) {
+	$ZZ{"$new$dim"} = $new{$ZZ{"$old$dim"}};
+	# Warning:  Doesn't use `link_make'.
+      }
+    }
+  }
+
+  &cursor_jump($curs, $last_new_cell);
 
   foreach (@Window_Dirty)
   {
@@ -841,18 +1099,33 @@
   }
 }
 
-sub atcursor_mark($)
-# mark the current cell
+sub atcursor_copy($)
+# Convenience routine: See atcursor_clone
+{
+  &atcursor_clone($_[0], 'copy');
+}
+
+sub atcursor_select($)
+# select or unselect the current cell
 {
   my $curs = &get_cursor($_[0]);
   my $cell = &get_lastcell($curs, "-d.cursor");
+  my $already_selected = &is_selected($cell);
+  my $which_selection =  &get_which_selection($cell) if $already_selected;
 
-  my $new = $ZZ{"n"}++;
-  $ZZ{$new} = "mark";
-  &cell_insert($new, $cell, "+d.mark");
-  &status_draw("Marked cell $cell");
-  #&cursor_move_dimension($curs, "+d.mark");
+  &cell_excise($cell, "d.mark");
 
+  # If it wasn't already selected in the active selection, deselect it.
+  # (The excise did this already, so we need only print a message.)
+  # Otherwise add it to the active selection and deliver a message.
+  if ($already_selected
+      && $which_selection == $SELECT_HOME) {
+    &status_draw("Deselected cell $cell");      
+  } else {    
+    &cell_insert($cell, $SELECT_HOME, "+d.mark");
+    &status_draw("Selected cell $cell");
+  }
+  #&cursor_move_dimension($curs, "+d.mark");
 
   foreach (@Window_Dirty)
   {
@@ -860,6 +1133,31 @@
   }
 }
 
+sub rotate_selection () 
+# Exchange the current selection with one of the saved selections.  If
+# there's an input buffer, it holds the number of the desired
+# selection, so shear all the selections +d.2ward by that many.
+# (Selection #0 is currently active.)  Otherwise, just shear by 1.
+{
+  my $shear_count = (defined($Input_Buffer) ? $Input_Buffer : 1);
+#  my $num_selections = &cells_row($SELECT_HOME, "+d.2");
+  
+  do_shear($SELECT_HOME, '-d.2', '+d.mark', $shear_count);
+}
+
+sub push_selection()
+# Push the current selection onto the selection stack.
+# All saved selections move +d.2ward one step.
+# The current selection is now empty.
+{  
+  my $new_sel = $ZZ{"n"}++;
+  my $num_selections = &cells_row($SELECT_HOME, "+d.2");
+  $ZZ{$new_sel} = "Selection #$num_selections";
+  cell_insert($new_sel, $SELECT_HOME, "+d.2");
+  do_shear($SELECT_HOME, "+d.2", "+d.mark", 1);
+}
+
+
 sub atcursor_insert($$)
 # Insert a new cell at a given cursor in a given direction
 {
@@ -988,6 +1286,25 @@
   }
 }
 
+sub atcursor_shear($$$)
+# Arguments: $curs = cursor/window number
+# $sheardir = direction and +/-
+# $linkdir = direction and +/-
+# directions name axes.  They get turned into dimensions
+# for the do_shear call.
+# Head cell of the shear is the accursed cell
+{
+  my ($number, $shear_dir, $link_dir) = @_;
+
+  my $cursor = &get_cursor($number);
+  my $shear_dim = &get_dimension($cursor, $shear_dir);
+  my $link_dim  = &get_dimension($cursor, $link_dir);
+
+  my $headcell = &get_accursed($number);
+
+  &do_shear($headcell, $shear_dim, $link_dim, 1);
+}
+
 sub atcursor_edit($)
 # Invoke an external text editor to edit the cell under a given cursor
 {
@@ -1156,6 +1473,27 @@
   unlink $TEMP_FILE;
 }
 
+sub cells_row($$) 
+# Final all the cells in the row starting from $cell
+# in the $dir dimension.  Return a list or a count
+# depending on calling context.
+{
+  my ($cell1, $dir) = @_;
+
+  if (not defined $cell1) {
+    return;
+  }
+
+  my $cell;
+  my @result = ($cell1);
+  for ($cell = $ZZ{"$cell1$dir"}; 
+       defined($cell) && $cell != $cell1; 
+       $cell = $ZZ{"$cell$dir"}
+      ) {
+    push @result, $cell;
+  }
+  @result;
+}
 
 #
 # Functions that operate on the whole screen
@@ -1299,8 +1637,8 @@
   if (!$LOTS_OF_COLOURS && &is_clone($cell))
   { addch($win, $row - 1, $col, "c"); }
 
-  # Display marked-cell flag if we aren't using lots of colours
-  if (!$LOTS_OF_COLOURS && &is_marked($cell))
+  # Display selected-cell flag if we aren't using lots of colours
+  if (!$LOTS_OF_COLOURS && &is_active_selected($cell))
   { addch($win, $row - 1, $col, "m"); }
 
   # Display dimension guide
@@ -1509,9 +1847,9 @@
   # Clone cells get a special colour in high-colour mode
   attron($win, COLOR_PAIR(2) | A_BOLD) 
     if $LOTS_OF_COLOURS && &is_clone($cell);
-  # Ditto marked cells
+  # Ditto selected cells
   attron($win, COLOR_PAIR(3) | A_BOLD) 
-    if $LOTS_OF_COLOURS && &is_marked($cell);
+    if $LOTS_OF_COLOURS && &is_selected($cell);
 
   # Colour/highlight the cursors
   attron($win, $Display_Has_Colour ? COLOR_PAIR(6) : A_UNDERLINE) if $number == 1;
@@ -1907,4 +2245,3 @@
 #
 # End.
 #
-