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

PATCH: Shear function and useful selection operations



The enclosed patch implements the following functions:

I. The `shear' function is implemented.  

   A. I replaced the #Shear action cell with four related shear action
      cells.  They are:

	#Shear -^
	#Shear -v
	#Shear ^+
	#Shear v+

      All shear action cells operate on the vertical rank headed by
      cursor 1.  We'll take ^+ as an example.  The + sign means that
      the rank to be moved will have its Right links detached and
      reattached; if it were a - we would detach the Left links
      instead.  The ^ means that the cells will be moved down one step
      each; if it were v we would move them up instead.  (We'll see
      why up and down are reversed in a minute.)

      Here is an example:

	A1 -- B1
        |
        A2 -- B2
        |
        A3   
        |
        A4 -- B4
        |
        A5 -- B5

      Suppose cursor 1 is on cell A1.  After the ^+ shear, the links
      are like this:

	A1 -- B2
        |    
        A2 
        |       
        A3 -- B4
        |       
        A4 -- B5
        |       
        A5 -- B1
                
      The A1...A5 have all moved down one step.  A1 is linked to the
      cell that A2 used to be linked to.  A2 is linked to the cell
      that A3 used to be linked to (none).  A3 is linked to the cell
      that A4 used to be linked to, etc.  A5 has wrapped around and is
      now linked to the cell that A1 used to be linked to.  (This
      `wrap' behavior is optional.)
	        
      Since the cursor is still on A1, it looks as though the B column
      moved up instead of the A column moving down.  That's why ^+
      means to shear down instead of to shear up:  What you see on
      the screen is an upward movement in the B column, even though
      the real truth is that the A column moved down.

      Caution:  If the cursor had been on A3, only the links from A3,
      A4, and A5 would have changed.  This is because the accursed
      cell is the *head* of the rank that shears.  The result would
      look like this:

	A1 -- B1
        |
        A2 -- B2
        |       
        A3 -- B4   
        |       
        A4 -- B5
        | 
        A5

      We may want to change this behavior.  It is sometimes
      counterintuitive.  For example, if the cursor is on A1 and we
      shear up, nothing will happen, because A1 is already at the top
      of the rank.  It gets disconnected from B1, and then `wraps
      around' and reconnects to B1.

   B. Shearing is implemented internally by the `do_shear' function.
      This inaugurates a new series of functions named do_*.

      do_shear accepts between three and five arguments:

	do_shear($first_cell, $shear_dim, $link_dim, $n, $hang);

      The last two are optional.  $first_cell is the head of the rank
      that is sheared.  $shear_dim is the dimension in which the rank
      extends.  For example, if $shear_dim is +d.1, then all the cells
      +d.1ward from $first_cell will be sheared.

      $link_dim is the dimension of the links that are broken and
      relinked.  If $shear_dim is +d.1 and $link_dim is +d.2, then the
      +d.2 links from all the cells in the sheared rank will be
      broken, and each cell in the rank will be re-linked to the cell
      that was formerly +d.1+d.2 from it, if there was such a cell.

      $n says how many steps to shear.  If absent, it defaults to 1.

      If $hang is a true value, then the last cell in the rank will be
      left unlinked instead of wrapping around to the beginning.


   C. The function `atcursor_shear($number, $sheardir, $linkdir)'
      shears cells in window #$number.  The sheared cells are the
      rank starting at cursor #$number in the $sheardir direction.
      Their $linkdir links are detached and reattached.

      $sheardir and $linkdir must each one of L, R, U, D, I, or O.

II. Cell marking is improved.

    A. ZigZag now supports arbitrarily many `selections'.  A selection
       is a set of marked cells.  Initially, there is one selection,
       called the `active selection' or `selection zero'.  The cells
       that are considered `marked' are those in the active selection
       and are displayed specially.

    B. A cell is in the active selection when it is linked +d.markward
       from the $SELECT_HOME, which is cell 21.  The default
       ZigZagSpace initializes cell 21 to contain the word `Selection'.

    C. The user can select or unselect a cell with the `m' and `M'
       commands as before.  These add the accursed cell in window 1 or
       window 0 to the active selection by inserting it +d.markward
       from $SELECT_HOME.  If the cell is already in the active
       selection, it is removed from the $SELECT_HOME+d.mark rank.

    D. The key sequence `meta-M' creates a new selection and saves the
       active selection in it.  It then makes the active selection
       empty.  The new selection gets a name like `Selection #1' or
       `Selection #2'.

    E. Inactive selections are linked d.2ward from $SELECT_HOME.
       Each has a special cell which heads a +d.markward rank which
       includes all the cells in the selection it represents.  The
       head cells of all the selections form a ring rank in dimension
       d.2.

    F. The key sequence `meta-m' shears the selection space d.2ward so
       that what used to be selection #2 becomes selection #1, what
       used to be selection #1 becomes the active selection, and what
       used to be the active selection becomes some high-numbered
       selection.  In this way, the user can manage several different
       selected sets.  One selection might be the set of all the
       Monarchs in the geneaology, and another might be the set of all
       the cells that represent red things.  The user can make any
       selection active with `meta-m', and can then add cells to it or
       remove cells from it with `m' and `M'.

    G. I expect that we will provide operations that apply to
       selections.  For example ``Intersect these two selections.''
       ``Remove all the cells in selection #X from selection #Y.''
       ``Select all the cells in a certain rank.'' ``Select all the
       cells that can be reached from here.''
       
    H. Because of the way I organized the selection structure, no cell
       can be in two selections at once.  This is a bug.

    I. I renamed all the *_mark functions to *_select.

    J. There is a new function, `rotate_selection', which shears the
       selection space +d.2wards to cycle through the selections.
       It takes no arguments, but it does check the input buffer for a
       number.  If present, the number says how many notches to turn
       the selection wheel; the default is 1.

    K. There is a new function, `push_selection', which creates a new
       selection head and pushes it into the $SELECTION_HOME+d.2 rank.
       After it does this, it shears the selection space so that the
       old active selection is attached to the new head, and the
       active selection head, $SELECT_HOME, is selecting nothing.

    L. There is a new action note, `#Rot.Selection', which invokes the
       `rotate_selection' command.

III. Miscellaneous utilities.

    A. The function `get_accursed($number)' returns the accursed cell
       in the specified window.

    B. The function `cells_row($head, $dimension)' returns a list of
       all the cells in the rank that starts at cell $head and
       continues in the direction specified by $dimension.  It behaves
       properly in the presence of rings.

       In scalar context, it returns the number of cells in the rank.



The patch:

--- zigzag.55	Tue Oct 27 22:55:17 1998
+++ zigzag	Thu Oct 29 01:26:21 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
@@ -88,6 +88,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
@@ -171,13 +172,15 @@
    "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);',
    "T"                => 'atcursor_clone(0);',
    "t"                => 'atcursor_clone(1);',
    "V"                => 'view_raster_toggle(0);',
@@ -236,6 +239,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 +272,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 +321,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 +345,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 +447,11 @@
    return (defined($ZZ{"$cell-d.clone"}) || defined($ZZ{"$cell+d.clone"}));
 }
 
-sub is_marked($)
+sub is_selected($)
 {
   my $cell = shift;
-  return (defined($ZZ{"$cell-d.mark"}) || defined($ZZ{"$cell+d.mark"}));
+  my $headcell = get_lastcell($cell, '-d.mark');
+  return $headcell == $SELECT_HOME && $cell != $SELECT_HOME;
 }
 
 
@@ -454,6 +475,15 @@
 # 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_lastcell($$)
 # Find the last cell along a given dimension
 {
@@ -625,6 +655,79 @@
   return @_;
 }
 
+#
+# 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
@@ -841,25 +944,52 @@
   }
 }
 
-sub atcursor_mark($)
-# mark the current cell
+sub atcursor_select($)
+# select or unselect the current cell
 {
   my $curs = &get_cursor($_[0]);
   my $cell = &get_lastcell($curs, "-d.cursor");
 
-  my $new = $ZZ{"n"}++;
-  $ZZ{$new} = "mark";
-  &cell_insert($new, $cell, "+d.mark");
-  &status_draw("Marked cell $cell");
+  if (&is_selected($cell)) {
+    &cell_excise($cell, "d.mark");
+    &status_draw("Selected cell $cell");
+  } else {
+    &cell_insert($cell, $SELECT_HOME, "+d.mark");
+    &status_draw("Selected cell $cell");
+  }
   #&cursor_move_dimension($curs, "+d.mark");
 
-
   foreach (@Window_Dirty)
   {
      $_ = $TRUE;
   }
 }
 
+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 +1118,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 +1305,22 @@
   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) = @_;
+  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 +1464,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_selected($cell))
   { addch($win, $row - 1, $col, "m"); }
 
   # Display dimension guide
@@ -1509,9 +1674,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 +2072,3 @@
 #
 # End.
 #
-