changeset 8208:f6ca8ff51818

[mq]: graphics-backend
author John W. Eaton <jwe@octave.org>
date Fri, 10 Oct 2008 11:07:53 -0400
parents 60b4c75287a1
children 2abbc8036f6a
files scripts/ChangeLog scripts/image/__img__.m scripts/plot/__go_draw_axes__.m scripts/plot/__go_draw_figure__.m scripts/plot/colorbar.m scripts/plot/legend.m scripts/plot/newplot.m scripts/plot/pareto.m scripts/plot/plotyy.m scripts/plot/subplot.m src/ChangeLog src/graphics.cc src/graphics.h.in
diffstat 13 files changed, 576 insertions(+), 277 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/ChangeLog	Fri Oct 10 16:44:21 2008 +0200
+++ b/scripts/ChangeLog	Fri Oct 10 11:07:53 2008 -0400
@@ -1,3 +1,25 @@
+2008-10-10  David Bateman  <dbateman@free.fr>
+
+	* image/__img__.m: Manually set the limits of th eimage
+	* plot/__go_draw_axes__.m: Base window position in the axis
+	position property and not the outerposition property. Remove
+	colorbar code based on the gnuplot colorbox. Allow images to be a
+	vector to support image based colorbars. Also check labelmode for
+	manual tics.
+	* plot/__go_draw_figure__.m: Remove gnuplot colorbox based
+	colorbar code.
+	* plot/colorbar.m: Rewrite to use an image and callbacks to link
+	it to the principal axis.
+	* plot/legend.m: Support an axis handle as the first
+	argument. Support hggroups.
+	* plot/pareto.m: Don't support an axis handle as the first
+	argument as the plotyy command in fact needs two axis handles.
+	* plot/plotyy.m: Rewrite to use listeners and callbacks to
+	synchronize the two axes.
+	* plot/subplot.m: Also skip axes that are tagged as being a
+	colorbar. Don't break in search of overlapping axes to delete. Set
+	both the position and the outerposition.
+	
 2008-10-09  Ben Abbott <bpabbott@mac.com>
 
 	* plot/__axis_label__.m: Inherit font properties from axes.
--- a/scripts/image/__img__.m	Fri Oct 10 16:44:21 2008 +0200
+++ b/scripts/image/__img__.m	Fri Oct 10 11:07:53 2008 -0400
@@ -65,7 +65,8 @@
     endif
   endif
 
-  set (ca, "view", [0, 90]);
+  set (ca, "view", [0, 90], "xlimmode", "manual", "ylimmode", "manual",
+       "xlim", xlim, "ylim", ylim);
 
   if (strcmp (get (ca, "nextplot"), "replace"))
     set (ca, "ydir", "reverse");
--- a/scripts/plot/__go_draw_axes__.m	Fri Oct 10 16:44:21 2008 +0200
+++ b/scripts/plot/__go_draw_axes__.m	Fri Oct 10 11:07:53 2008 -0400
@@ -28,27 +28,17 @@
 
     parent_figure_obj = get (axis_obj.parent);
 
-    ## Set axis properties here?
-    pos = [0, 0, 1, 1];
-    if (strcmpi (axis_obj.activepositionproperty, "outerposition"))
-      ymirror = true;
-      if (! isempty (axis_obj.outerposition))
-	pos = axis_obj.outerposition;
-      endif
-    else
+    pos = axis_obj.position;
+    fprintf (plot_stream, "set tmargin 0;\n");
+    fprintf (plot_stream, "set bmargin 0;\n");
+    fprintf (plot_stream, "set lmargin 0;\n");
+    fprintf (plot_stream, "set rmargin 0;\n");
+
+    ## Set to false for plotyy axes.
+    if (strcmp (axis_obj.tag, "plotyy"))
       ymirror = false;
-      if (! isempty (axis_obj.position))
-	pos = axis_obj.position;
-	fprintf (plot_stream, "set tmargin 0;\n");
-	fprintf (plot_stream, "set bmargin 0;\n");
-	fprintf (plot_stream, "set lmargin 0;\n");
-	fprintf (plot_stream, "set rmargin 0;\n");
-      endif
-    endif
-
-    if (! strcmpi (get (h, "__colorbar__"), "none"))
-      [pos, cbox_orient, cbox_size, cbox_origin, cbox_mirror] = ...
-	  gnuplot_position_colorbox (pos, get (h, "__colorbar__"), axis_obj);
+    else
+      ymirror = true;
     endif
 
     fprintf (plot_stream, "set origin %.15g, %.15g;\n", pos(1), pos(2));
@@ -342,12 +332,16 @@
 	    if (x_dim > 1)
 	      dx = abs (img_xdata(2)-img_xdata(1))/(x_dim-1);
 	    else
-	      dx = 1;
+	      x_dim = 2;
+	      img_data = [img_data, img_data];
+	      dx = abs (img_xdata(2)-img_xdata(1));
 	    endif
 	    if (y_dim > 1)
 	      dy = abs (img_ydata(2)-img_ydata(1))/(y_dim-1);
 	    else
-	      dy = 1;
+	      y_dim = 2;
+	      img_data = [img_data; img_data];
+	      dy = abs (img_ydata(2)-img_ydata(1));
 	    endif
 	    x_origin = min (img_xdata);
 	    y_origin = min (img_ydata);
@@ -962,6 +956,8 @@
 
     have_data = (! (isempty (data) || all (cellfun (@isempty, data))));
 
+    ## Note we don't use the [xy]2range of gnuplot as we don't use the
+    ## dual axis plotting features of gnuplot
     if (isempty (xlim))
       return;
     endif
@@ -970,8 +966,7 @@
     else
       xdir = "noreverse";
     endif
-    fprintf (plot_stream, "set %srange [%.15e:%.15e] %s;\n",
-	     xaxisloc, xlim, xdir);
+    fprintf (plot_stream, "set xrange [%.15e:%.15e] %s;\n", xlim, xdir);
 
     if (isempty (ylim))
       return;
@@ -981,8 +976,7 @@
     else
       ydir = "noreverse";
     endif
-    fprintf (plot_stream, "set %srange [%.15e:%.15e] %s;\n",
-	     yaxisloc, ylim, ydir);
+    fprintf (plot_stream, "set yrange [%.15e:%.15e] %s;\n", ylim, ydir);
 
     if (nd == 3)
       if (isempty (zlim))
@@ -1126,16 +1120,8 @@
       fwrite (plot_stream, [1:cmap_sz; cmap.'], "float32");
       fwrite (plot_stream, "\n");
     endif
-	    
-    if (strcmpi (get (h, "__colorbar__"), "none"))
-      fputs (plot_stream, "unset colorbox;\n");
-    else
-      ## FIXME If cbox_mirror is true we want to invert the tic labels
-      ## but gnuplot doesn't allow that
-      fputs (plot_stream, 
-	     sprintf ("set colorbox %s user origin %f,%f size %f,%f;\n",
-		      cbox_orient, cbox_origin, cbox_size));
-    endif
+
+    fputs (plot_stream, "unset colorbox;\n");
 
     if (have_data)
       if (nd == 2)
@@ -1491,8 +1477,8 @@
   else
     fprintf (plot_stream, "set format %s \"%%g\";\n", ax);
     if (mirror)
-      fprintf (plot_stream, "set %stics %s %s %s;\n", ax, axispos, tickdir,
-	       colorspec);
+      fprintf (plot_stream, "set %stics %s %s mirror %s;\n", ax, 
+	       axispos, tickdir, colorspec);
     else
       fprintf (plot_stream, "set %stics %s %s nomirror %s;\n", ax, 
 	       tickdir, axispos, colorspec);
@@ -1871,105 +1857,6 @@
   sym.int = '{/Symbol \362}';
 endfunction
 
-function [pos, orient, sz, origin, mirr] = gnuplot_position_colorbox (pos, cbox, obj)
-  ## This is an emprically derived function that attempts to find a good
-  ## size for the colorbox even for subplots and strange aspect ratios.
-
-  if (strncmp (cbox, "north", 5) || strncmp (cbox, "south", 5))
-    scl = pos([2,4]);
-  else
-    scl = pos([1,3]);
-  endif
-
-  if (length(cbox) > 7 && strncmp (cbox(end-6:end), "outside", 7))
-    scl(2) -= 0.2 * scl(2);
-    if (strncmp (cbox, "west", 4) || strncmp (cbox, "south", 5))
-      scl(1) += 0.2 * scl(2);
-    endif
-  endif
-
-  if (strcmpi (obj.dataaspectratiomode, "manual"))
-    sz = min(pos(3:4))([1,1]);
-    r = obj.dataaspectratio;
-    if (pos(3) > pos(4))
-      switch (cbox)
-	case {"north", "northoutside"}
-	  off = 4 / 3 * [(pos(3) - pos(4)) ./ (r(2)/r(1)), pos(4) / pos(3) / 2];
-	  sz = 2 * sz / 3;
-	case {"south", "southoutside"}
-	  off = 4 / 3 * [(pos(3) - pos(4)) ./ (r(2)/r(1)), 0];
-	  sz = 2 * sz / 3;
-	otherwise
-	  off = [(pos(3) - pos(4)) ./ (r(2)/r(1)), 0];	  
-      endswitch
-    else
-      switch (cbox)
-	case {"north", "northoutside"}
-	  off = 1.5 * [0, (pos(4) - pos(3)) ./ (r(1) / r(2))];
-	case {"south", "southoutside"}
-	  off = 0.5 * [0, (pos(4) - pos(3)) ./ (r(1) / r(2))];
-	otherwise
-	  off = [0, (pos(4) - pos(3)) ./ (r(1) / r(2))];
-      endswitch
-    endif
-    off = off / 2;
-  else
-    sz = pos(3:4);
-    off = 0;
-  endif
-  switch (cbox)
-    case "northoutside"
-      sz = sz - 0.08;
-      origin = [0.05, 0.06] + [0.00, 0.88] .* sz + pos(1:2) + off;
-      mirr = true;
-      orient = "horizontal";
-    case "north"
-      sz = sz - 0.16;
-      origin = [0.09, 0.09] + [0.00, 0.94] .* sz + pos(1:2) + off;
-      mirr = false;
-      orient = "horizontal";
-    case "southoutside"
-      sz = sz - 0.08;
-      origin = [0.05, 0.06] + [0.00, 0.00] .* sz + pos(1:2) + off;
-      mirr = false;
-      orient = "horizontal";
-    case "south"
-      sz = sz - 0.16;
-      origin = [0.08, 0.09] + [0.03, 0.05] .* sz + pos(1:2) + off;
-      mirr = true;
-      orient = "horizontal";
-    case "eastoutside"
-      sz = sz - 0.08;
-      origin = [0.00, 0.06] + [0.94, 0.00] .* sz + pos(1:2) + off;
-      mirr = false;
-      orient = "vertical";
-    case "east"
-      sz = sz - 0.16;
-      origin = [0.09, 0.10] + [0.91, 0.01] .* sz + pos(1:2) + off;
-      mirr = true;
-      orient = "vertical";
-    case "westoutside"
-      sz = sz - 0.08;
-      origin = [0.00, 0.06] + [0.06, 0.00] .* sz + pos(1:2) + off;
-      mirr = true;
-      orient = "vertical";
-    case "west"
-      sz = sz - 0.16;
-      origin = [0.06, 0.09] + [0.04, 0.03] .* sz + pos(1:2) + off;
-      mirr = false;
-      orient = "vertical";
-  endswitch
-
-  if (strncmp (cbox, "north", 5) || strncmp (cbox, "south", 5))
-    sz = sz .* [1, 0.07];
-    pos([2,4]) = scl;
-  else
-    sz = sz .* [0.07, 1];
-    pos([1,3]) = scl;
-  endif
-
-endfunction
-
 function retval = __do_enhanced_option__ (enhanced, obj)
   retval = "";
   if (enhanced)
--- a/scripts/plot/__go_draw_figure__.m	Fri Oct 10 16:44:21 2008 +0200
+++ b/scripts/plot/__go_draw_figure__.m	Fri Oct 10 11:07:53 2008 -0400
@@ -37,10 +37,6 @@
 	  switch (obj.type)
 	    case "axes"
 	      axes_count++;
-	      ## Force multiplot with a colorbar to ensure colorbar on the page
-	      if (!strcmpi (obj.__colorbar__, "none"))
-		axes_count++;
-	      endif
 	  endswitch
 	endfor
 
--- a/scripts/plot/colorbar.m	Fri Oct 10 16:44:21 2008 +0200
+++ b/scripts/plot/colorbar.m	Fri Oct 10 11:07:53 2008 -0400
@@ -1,4 +1,4 @@
-## Copyright (C) 2007 David Bateman
+## Copyright (C) 2008 David Bateman
 ##
 ## This file is part of Octave.
 ##
@@ -49,48 +49,304 @@
 
 ## PKG_ADD: mark_as_command colorbar
 
-function colorbar (varargin)
-  
-  if (nargin > 0 && strcmpi (varargin{1}, "peer"))
-    if (nargin > 1)
-      ax = varargin{2};
-      if (!isscalar (ax) || ! ishandle (ax)
-	  || strcmp (get (ax, "type"), "axes"))
-	error ("colorbar: expecting an axes handle following 'peer'");
-      endif
-    else
-      error ("colorbar: misisng axes handle after 'peer'");
-    endif
-  else
-    ax = gca ();
-  endif
+function h = colorbar (varargin)
+  ax = [];
+  loc = "eastoutside";
+  args = {};
+  deleting = false;
+
+  i = 1;
+  while (i <= nargin)
+    arg = varargin {i++};
 
-  pos = "eastoutside";
-  for i = 1 : length (varargin)
-    arg = varargin{i};
-    if (length(arg) < 1)
-      pos = "eastoutside";
-    elseif (ischar (arg))
-      if (strcmpi (arg, "off") || strcmpi (arg, "none"))
-	pos = "none";
+    if (ischar(arg))
+      if (strcmpi (arg, "peer"))
+	if (i > nargin)
+	  error ("colorbar: missing axes handle after 'peer'");
+	else
+	  ax = vargin{i++}
+	  if (!isscalar (ax) || ! ishandle (ax)
+	      || strcmp (get (ax, "type"), "axes"))
+	    error ("colorbar: expecting an axes handle following 'peer'");
+	  endif
+	endif
       elseif (strcmpi (arg, "north") || strcmpi (arg, "south")
 	      || strcmpi (arg, "east") || strcmpi (arg, "west")
 	      || strcmpi (arg, "northoutside") || strcmpi (arg, "southoutside")
 	      || strcmpi (arg, "eastoutside") || strcmpi (arg, "westoutside"))
-	pos = arg;
+	loc = arg;
+      elseif (strcmpi (arg, "off") || strcmpi (arg, "none"))
+	deleting = true;
       else
-	error ("colorbar: unrecognized position argument");
+	args{end+1} = arg;
+      endif
+    else
+      args{end+1} = arg;
+    endif
+  endwhile
+
+  if (isempty (ax))
+    ax = gca ();
+  endif
+  obj = get (ax);
+
+  if (deleting)
+    objs = findobj (get (ax, "parent"), "type", "axes");
+    for i = 1 : length (objs)
+      if (strcmp (get (objs(i), "tag"), "colorbar") &&
+	  get (objs(i), "axes") == ax)
+	delete (objs(i));
+      endif
+    endfor
+    else
+    position = obj.position;
+    clen = rows (get (get (ax, "parent"), "colormap"));
+    cext = get (ax, "clim");
+    cdiff = (cext(2) - cext(1)) / clen / 2;
+    cmin = cext(1) + cdiff;
+    cmax = cext(2) - cdiff;
+
+    orig_pos = obj.position;
+    orig_opos = obj.outerposition;
+    [pos, cpos, vertical, mirror, aspect] =  ...
+	__position_colorbox__ (loc, obj, ancestor (ax, "figure"));
+    set (ax, "activepositionproperty", "position", "position", pos);
+
+    cax = __go_axes__ (get (ax, "parent"), "tag", "colorbar", 
+		       "handlevisibility", "off", 
+		       "activepositionproperty", "position", 
+		       "position", cpos);
+    addproperty ("location", cax, "radio",
+		 "eastoutside|east|westoutside|west|northoutside|north|southoutside|south",
+		 loc);
+    addproperty ("axes", cax, "handle", ax);
+
+    if (vertical)
+      hi = image (cax, [0,1], [cmin, cmax], [1 : clen]');
+      if (mirror)
+	set (cax, "xtick", [], "xdir", "normal", "ydir", "normal",
+	     "ylim", cext, "ylimmode", "manual",
+	     "yaxislocation", "right", args{:});
+      else
+	set (cax, "xtick", [], "xdir", "normal", "ydir", "normal",
+	     "ylim", cext, "ylimmode", "manual",
+	     "yaxislocation", "left", args{:});
       endif
     else
-      error ("colorbar: expecting string arguments");
+      hi = image (cax, [cmin, cmax], [0,1], [1 : clen]);
+      if (mirror)
+	set (cax, "ytick", [], "xdir", "normal", "ydir", "normal",
+	     "xlim", cext, "xlimmode", "manual",
+	     "xaxislocation", "top", args{:});
+      else
+	set (cax, "ytick", [], "xdir", "normal", "ydir", "normal",
+	     "xlim", cext, "xlimmode", "manual",
+	     "xaxislocation", "bottom", args{:});
+      endif
+    endif
+
+    if (! isnan (aspect))
+      set (cax, "dataaspectratio", aspect);
+    endif
+
+    ctext = text (0, 0, "", "tag", "colorbar","visible", "off", 
+		  "handlevisibility", "off", "xliminclude", "off",  
+		  "yliminclude", "off", "zliminclude", "off",
+		  "deletefcn", {@deletecolorbar, cax, orig_pos, orig_opos});
+
+    set (cax, "deletefcn", {@resetaxis, orig_pos, orig_opos});
+
+    addlistener (ax, "clim", {@update_colorbar_clim, hi, vertical})
+    addlistener (ax, "dataaspectratio", {@update_colorbar_axis, cax})
+    addlistener (ax, "position", {@update_colorbar_axis, cax})
+
+  endif
+
+  if (nargout > 0)
+    h = cax;
+  endif
+endfunction
+
+function deletecolorbar (h, d, hc, pos, opos)
+  ## Don't delete the colorbar and reset the axis size if the
+  ## parent figure is being deleted.
+  if (ishandle (hc) && strcmp (get (hc, "type"), "axes") && 
+      (isempty (gcbf()) || strcmp (get (gcbf(), "beingdeleted"),"off")))
+    if (strcmp (get (hc, "beingdeleted"), "off"))
+      delete (hc);
+    endif
+    if (!isempty (ancestor (h, "axes")) &&
+	strcmp (get (ancestor (h, "axes"), "beingdeleted"), "off"))
+      set (ancestor (h, "axes"), "position", pos, "outerposition", opos);
+    endif
+  endif
+endfunction
+
+function resetaxis (h, d, pos, opos)
+  if (ishandle (h) && strcmp (get (h, "type"), "axes") && 
+      (isempty (gcbf()) || strcmp (get (gcbf(), "beingdeleted"),"off")) &&
+      ishandle (get (h, "axes")))
+    #set (get (h, "axes"), "position", pos, "outerposition", opos);
+  endif
+endfunction
+
+function update_colorbar_clim (h, d, hi, vert)
+  if (ishandle (h) && strcmp (get (h, "type"), "image") && 
+      (isempty (gcbf()) || strcmp (get (gcbf(), "beingdeleted"),"off")))
+    clen = rows (get (get (h, "parent"), "colormap"));
+    cext = get (h, "clim");
+    cdiff = (cext(2) - cext(1)) / clen / 2;
+    cmin = cext(1) + cdiff;
+    cmax = cext(2) - cdiff;
+
+    if (vert)
+      set (hi, "ydata", [cmin, cmax]);
+      set (get (hi, "parent"), "ylim", cext);
+    else
+      set (hi, "xdata", [cmin, cmax]);
+      set (get (hi, "parent"), "xlim", cext);
+    endif
+  endif
+endfunction
+
+function update_colorbar_axis (h, d, cax)
+  if (ishandle (cax) && strcmp (get (cax, "type"), "axes") && 
+      (isempty (gcbf()) || strcmp (get (gcbf(), "beingdeleted"),"off")))
+    loc = get (cax, "location");
+    obj = get (h);
+    [pos, cpos, vertical, mirror, aspect] =  ...
+	__position_colorbox__ (loc, obj, ancestor (h, "figure"));
+
+    if (vertical)
+      if (mirror)
+	set (cax, "xtick", [], "xdir", "normal", "ydir", "normal",
+	     "yaxislocation", "right", "position", cpos);
+      else
+	set (cax, "xtick", [], "xdir", "normal", "ydir", "normal",
+	     "yaxislocation", "left", "position", cpos);
+      endif
+    else
+      if (mirror)
+	set (cax, "ytick", [], "xdir", "normal", "ydir", "normal",
+	     "xaxislocation", "top", "position", cpos);
+      else
+	set (cax, "ytick", [], "xdir", "normal", "ydir", "normal",
+	     "xaxislocation", "bottom", "position", cpos);
+      endif
+    endif
+
+    if (! isnan (aspect))
+      aspect
+      set (cax, "dataaspectratio", aspect);
     endif
-  endfor
+  endif
+endfunction
+
+function [pos, cpos, vertical, mirr, aspect] = __position_colorbox__ (cbox, obj, cf)
+
+  pos = obj.position;
+  sz = pos(3:4);
+
+  off = 0;
+  if (strcmpi (obj.dataaspectratiomode, "manual"))
+    r = obj.dataaspectratio;
+    if (pos(3) > pos(4))
+      switch (cbox)
+	case {"east", "eastoutside", "west", "westoutside"}
+	  off = [(pos(3) - pos(4)) ./ (r(2) / r(1)), 0];	  
+      endswitch
+    else
+      switch (cbox)
+	case {"north", "northoutside", "south", "southoutside"}
+	  off = [0, (pos(4) - pos(3)) ./ (r(1) / r(2))];
+	  ## This shouldn't be here except that gnuplot doesn't have a
+	  ## square window and so a square aspect ratio is not square.
+	  ## The corrections are empirical.
+	  if (strcmp (get (cf, "__backend__"), "gnuplot"))
+	    if (length (cbox) > 7 && strcmp (cbox(end-6:end),"outside"))
+	      off = off / 2;
+	    else
+	      off = off / 1.7;
+	    endif
+	  endif
+      endswitch
+    endif
+    off = off / 2;
+  endif
 
-  set (ax, "__colorbar__", pos);
+  switch (cbox)
+    case "northoutside"
+      origin = pos(1:2) + [0., 0.9] .* sz + [1, -1] .* off;
+      sz = sz .* [1.0, 0.06];
+      pos(4) = 0.8 * pos(4);
+      mirr = true;
+      vertical = false;
+    case "north"
+      origin = pos(1:2) + [0.05, 0.9] .* sz + [1, -1] .* off;
+      sz = sz .* [1.0, 0.06] * 0.9;
+      mirr = false;
+      vertical = false;
+    case "southoutside"
+      origin = pos(1:2) + off;
+      sz = sz .* [1.0, 0.06];
+      pos(2) = pos(2) + pos(4) * 0.2;
+      pos(4) = 0.8 * pos(4);
+      mirr = false;
+      vertical = false;
+    case "south"
+      origin = pos(1:2) + [0.05, 0.05] .* sz + off;
+      sz = sz .* [1.0, 0.06] * 0.9;
+      mirr = true;
+      vertical = false;
+    case "eastoutside"
+      origin = pos(1:2) + [0.9, 0] .* sz + [-1, 1] .* off;
+      sz = sz .* [0.06, 1.0];
+      pos(3) = 0.8 * pos(3);
+      mirr = true;
+      vertical = true;
+    case "east"
+      origin = pos(1:2) + [0.9, 0.05] .* sz + [-1, 1] .* off;
+      sz = sz .* [0.06, 1.0] * 0.9;
+      mirr = false;
+      vertical = true;
+    case "westoutside"
+      origin = pos(1:2) + off;
+      sz = sz .* [0.06, 1.0];
+      pos(1) = pos(1) + pos(3) * 0.2;
+      pos(3) = 0.8 * pos(3);
+      mirr = false;
+      vertical = true;
+    case "west"
+      origin = pos(1:2) + [0.05, 0.05] .* sz + off;
+      sz = sz .* [0.06, 1.0] .* 0.9;
+      mirr = true;
+      vertical = true;
+  endswitch
+
+  cpos = [origin, sz];
+
+  if (strcmpi (obj.dataaspectratiomode, "manual"))
+    r = obj.dataaspectratio;
+
+    if (pos(3) > pos(4))
+      if (vertical)
+	aspect = [1, 0.21, 1];
+      else
+	aspect = [0.21, 1, 1];
+      endif
+    else
+      if (vertical)
+	aspect = [1, 0.21, 1];
+      else
+	aspect = [0.21, 1, 1];
+      endif
+    endif
+  else
+    aspect = NaN;
+  endif
 
 endfunction
 
-
 %!demo
 %! hold off;
 %! close all;
--- a/scripts/plot/legend.m	Fri Oct 10 16:44:21 2008 +0200
+++ b/scripts/plot/legend.m	Fri Oct 10 11:07:53 2008 -0400
@@ -80,10 +80,9 @@
 
 function legend (varargin)
 
+  [ca, varargin, nargin] = __plt_get_axis_arg__ ("legend", varargin{:});
   nargs = nargin;
 
-  ca = gca ();
-
   if (nargs > 0)
     pos = varargin{nargs};
     if (isnumeric (pos) && isscalar (pos) && round (pos) == pos)
@@ -132,12 +131,25 @@
 	  case "boxoff"
 	    set (ca, "keybox", "off");
 	  otherwise
-	    while (k <= nkids && ! strcmp (get (kids(k), "type"), "line"))
+	    typ = get (kids (k), "type");
+	    while (k <= nkids && ! strcmp (typ, "line") &&
+		   ! strcmp (typ, "hggroup"))
 	      k++;
 	    endwhile
 	    if (k <= nkids)
 	      turn_on_legend = true;
-	      set (kids(k), "keylabel", arg);
+	      if (strcmp (typ, "hggroup"))
+		hgkids = get (kids(k), "children");
+		for j = 1 : length (hgkids)
+		  hgobj = get (hgkids (j));
+		  if (isfield (hgobj, "keylabel"))
+		    set (hgkids(j), "keylabel", arg);
+		    break;
+		  endif
+		endfor
+	      else
+		set (kids(k), "keylabel", arg);
+	      endif
 	    else
 	      warning ("legend: ignoring extra labels");
 	    endif
@@ -160,7 +172,8 @@
     for i = 1:nkids
       if (strcmp (get (kids(k), "type"), "line")
 	  || strcmp (get (kids(k), "type"), "surface")
-	  || strcmp (get (kids(k), "type"), "patch"))
+	  || strcmp (get (kids(k), "type"), "patch")
+	  || strcmp (get (kids(k), "type"), "hggroup"))
 	have_data = true;
 	break;
       endif
@@ -177,11 +190,23 @@
       while (k <= nkids
 	     && ! (strcmp (get (kids(k), "type"), "line")
 		   || strcmp (get (kids(k), "type"), "surface")
-		   || strcmp (get (kids(k), "type"), "patch")))
+		   || strcmp (get (kids(k), "type"), "patch")
+		   || strcmp (get (kids(k), "type"), "hggroup")))
 	k++;
       endwhile
       if (k <= nkids)
-	set (kids(k), "keylabel", arg);
+	if (strcmp (get (kids(k), "type"), "hggroup"))
+	  hgkids = get (kids(k), "children");
+	  for j = 1 : length (hgkids)
+	    hgobj = get (hgkids (j));
+	    if (isfield (hgobj, "keylabel"))
+	      set (hgkids(j), "keylabel", arg);
+	      break;
+	    endif
+	  endfor
+	else
+	  set (kids(k), "keylabel", arg);
+	endif
 	turn_on_legend = true;
 	k++;
       elseif (! warned)
@@ -189,6 +214,9 @@
 	warning ("legend: ignoring extra labels");
       endif
     else
+      arg
+      get(kids(k),"type")
+      k
       error ("legend: expecting argument to be a character string");
     endif
   endfor
--- a/scripts/plot/newplot.m	Fri Oct 10 16:44:21 2008 +0200
+++ b/scripts/plot/newplot.m	Fri Oct 10 11:07:53 2008 -0400
@@ -49,8 +49,13 @@
       otherwise
 	error ("newplot: unrecognized nextplot property for current axes");
     endswitch
+
+    yt = get (ca, "ylabel");
+    if (! strcmp (get (yt, "type"), "text"))
+      disp("BAD!!!!!");
+      get(yt)
+    endif
   else
     print_usage ();
   endif
-
 endfunction
--- a/scripts/plot/pareto.m	Fri Oct 10 16:44:21 2008 +0200
+++ b/scripts/plot/pareto.m	Fri Oct 10 11:07:53 2008 -0400
@@ -55,8 +55,6 @@
 
 function h = pareto (varargin)
 
-  [ax, varargin, nargin] = __plt_get_axis_arg__ ("pareto", varargin{:});
-
   if (nargin != 1 && nargin != 2)
     print_usage ();
   endif
@@ -84,7 +82,7 @@
   cdf95 = cdf - 0.95;
   idx95 = find(sign(cdf95(1:end-1)) != sign(cdf95(2:end)))(1);
 
-  [ax, hbar, hline] = plotyy (ax, 1 : idx95, x (1 : idx95), 
+  [ax, hbar, hline] = plotyy (1 : idx95, x (1 : idx95), 
 			      1 : length(cdf), 100 .* cdf, 
 			      @bar, @plot);
 
--- a/scripts/plot/plotyy.m	Fri Oct 10 16:44:21 2008 +0200
+++ b/scripts/plot/plotyy.m	Fri Oct 10 11:07:53 2008 -0400
@@ -77,10 +77,14 @@
       ax(2) = axes ();
     else
       ax = get (f, "children");
-      for i = 3 : length (ax)
-	delete (ax (i));
-      endfor
-      ax = ax(1:2);
+      if (length (ax) > 2)
+	for i = 3 : length (ax)
+	  delete (ax (i));
+	endfor
+	ax = ax(1:2);
+      elseif (length (ax) == 1)
+        ax(2) = axes ();
+      endif
     endif
     if (nargin < 2)
       varargin = {};
@@ -125,8 +129,6 @@
   h1 = feval (fun1, x1, y1);
 
   set (ax(1), "ycolor", getcolor (h1(1)));
-  set (ax(1), "position", [0.11 0.13 0.78 0.73]);
-  set (ax(1), "activepositionproperty", "position");
   set (ax(1), "xlim", xlim);
 
   cf = gcf ();
@@ -141,9 +143,68 @@
   set (ax(2), "yaxislocation", "right");
   set (ax(2), "ycolor", getcolor (h2(1)));
   set (ax(2), "position", get (ax(1), "position"));
-  set (ax(2), "activepositionproperty", "position");
   set (ax(2), "xlim", xlim);
   set (ax(2), "color", "none");
+
+  ## Add invisible text objects that when destroyed, 
+  ## also remove the other axis
+  t1 = text (0, 0, "", "parent", ax(1), "tag", "plotyy", 
+	     "handlevisibility", "off", "visible", "off",
+	     "xliminclude", "off", "yliminclude", "off");
+  t2 = text (0, 0, "", "parent", ax(2), "tag", "plotyy", 
+	     "handlevisibility", "off", "visible", "off",
+	     "xliminclude", "off", "yliminclude", "off");
+
+  set (t1, "deletefcn", {@deleteplotyy, ax(2), t2});
+  set (t2, "deletefcn", {@deleteplotyy, ax(1), t1});
+
+  addlistener (ax(1), "position", {@update_position, ax(2)});
+  addlistener (ax(2), "position", {@update_position, ax(1)});
+  addlistener (ax(1), "view", {@update_position, ax(2)});
+  addlistener (ax(2), "view", {@update_position, ax(1)});
+
+  ## Tag the plotyy axes, so we can use that information
+  ## not to mirror the y axis tick marks
+  set (ax, "tag", "plotyy")
+
+endfunction
+
+%!demo
+%! x = 0:0.1:2*pi; 
+%! y1 = sin (x);
+%! y2 = exp (x - 1);
+%! ax = plotyy (x, y1, x - 1, y2, @plot, @semilogy);
+%! xlabel ("X");
+%! ylabel (ax(1), "Axis 1");
+%! ylabel (ax(2), "Axis 2");
+
+function deleteplotyy (h, d, ax2, t2)
+  if (ishandle (ax2) && strcmp (get (ax2, "type"), "axes") && 
+      (isempty (gcbf()) || strcmp (get (gcbf(), "beingdeleted"),"off")) &&
+      strcmp (get (ax2, "beingdeleted"), "off"))
+    set (t2, "deletefcn", []);
+    delete (ax2);
+  endif
+endfunction
+
+function update_position (h, d, ax2)
+  persistent recursion = false;
+
+  ## Don't allow recursion
+  if (! recursion)
+    unwind_protect
+      recursion = true;
+      position = get (h, "position");
+      view = get (h, "view");
+      oldposition = get (ax2, "position");
+      oldview = get (ax2, "view");
+      if (! (isequal (position, oldposition) && isequal (view, oldview)))
+	set (ax2, "position", position, "view", view);
+      endif
+    unwind_protect_cleanup
+      recursion = false;
+    end_unwind_protect
+  endif  
 endfunction
 
 function color = getcolor (ax)
@@ -159,11 +220,3 @@
   endif
 endfunction
 
-%!demo
-%! x = 0:0.1:2*pi; 
-%! y1 = sin (x);
-%! y2 = exp (x - 1);
-%! ax = plotyy (x, y1, x - 1, y2, @plot, @semilogy);
-%! xlabel ("X");
-%! ylabel (ax(1), "Axis 1");
-%! ylabel (ax(2), "Axis 2");
--- a/scripts/plot/subplot.m	Fri Oct 10 16:44:21 2008 +0200
+++ b/scripts/plot/subplot.m	Fri Oct 10 11:07:53 2008 -0400
@@ -126,8 +126,9 @@
       continue;
     endif
     if (strcmp (get (child, "type"), "axes"))
-      ## Skip legend objects.
-      if (strcmp (get (child, "tag"), "legend"))
+      ## Skip legend and colorbar objects.
+      if (strcmp (get (child, "tag"), "legend") || 
+	  strcmp (get (child, "tag"), "colorbar"))
         continue;
       endif
       objpos = get (child, "outerposition");
@@ -136,7 +137,6 @@
 	## existing axes object, use the existing axes.
 	found = true;
 	tmp = child;
-	break;
       else
 	## If the new axes overlap an old axes object, delete the old
 	## axes.
@@ -154,7 +154,9 @@
   if (found)
     set (cf, "currentaxes", tmp);
   else
-    tmp = axes ("outerposition", pos);
+    border = [0.130, 0.110, 0.225, 0.185] .* [xsize, ysize, xsize, ysize];
+    pos2 = [pos(1:2) + border(1:2), pos(3:4) - border(1:2) - border(3:4)];
+    tmp = axes ("outerposition", pos, "position", pos2);
   endif
 
   if (nargout > 0)
--- a/src/ChangeLog	Fri Oct 10 16:44:21 2008 +0200
+++ b/src/ChangeLog	Fri Oct 10 11:07:53 2008 -0400
@@ -1,3 +1,37 @@
+2008-10-10  John W. Eaton  <jwe@octave.org>
+
+	* graphics.cc (gh_manager::do_free): Set the beingdeleted
+	property, then delete the children, then execute the deletefcn.
+	(axes::properties::set_defaults, axes::properties::remove_child,
+	axes::properites::delete_children): Call delete_text_child to
+	manage the title, xlabel, ylabel, and zlabel properties.
+	(axes::properties::get_title, axes::properties::get_xlabel,
+	axes::properties::get_ylabel, axes::properties::get_zlabel): Delete.
+
+	* graphics.h.in (axes::properites::title,
+	axes::properites::xlabel, axes::properites::ylabel,
+	axes::properites::zlabel): No longer mutable.  Don't generate
+	factory default values or custom get functions for these
+	properties.
+
+	* graphics.cc (axes::properties::delete_text_child): New function.
+	* graphics.h.in: Provide decl.
+
+	* graphics.h.in (graphics_object::type): New function.
+
+2008-10-10  David Bateman  <dbateman@free.fr>
+
+	* graphics.h.in (radio_property axes::properties::__colorbar__):
+	Delete.
+	* graphics.cc (void axes::properties::sync_positions (void)):
+	Disable code synchronizing outerposiiton and position.
+	(void axes::properties::set_defaults (base_graphics_object&,
+	const std::string&): Remove __colorbar__.
+	(F__go_delete__): Don't delete if already being deleting to avoid
+	recursion in callback functions.
+	(F__go_axes_init__): Flag error if handle is deleted during
+	initialization due.
+	
 2008-10-07  David Bateman  <dbateman@free.fr>
 
 	* graphics.cc (F__go_delete__): Allow arrays of graphic handles.
--- a/src/graphics.cc	Fri Oct 10 16:44:21 2008 +0200
+++ b/src/graphics.cc	Fri Oct 10 11:07:53 2008 -0400
@@ -1291,19 +1291,25 @@
 
 	  if (p != handle_map.end ())
 	    {
-	      // FIXME: should we explicitely free all children first?
-	      //        => call delete_children () ?
-
-	      p->second.get_properties ().set_beingdeleted (true);
-	      p->second.get_properties ().execute_deletefcn ();
+	      base_properties& bp = p->second.get_properties ();
+	      
+	      bp.set_beingdeleted (true);
+
+	      bp.delete_children ();
+
+	      octave_value val = bp.get_deletefcn ();
+
+	      bp.execute_deletefcn ();
 
 	      // notify backend
 	      graphics_backend backend = p->second.get_backend ();
 	      if (backend)
                 backend.object_destroyed (p->second);
-                 // note - this will be valid only for first explicitly deleted object.
-                 // All his children will have unknown backend then.
-                 
+
+	      // Note: this will be valid only for first explicitly 
+	      // deleted object.  All its children will then have an
+	      // unknown backend.
+
 	      handle_map.erase (p);
 
 	      if (h.value () < 0)
@@ -1737,8 +1743,15 @@
 {
   octave_idx_type n = children.numel ();
 
+  // A callback function might have already deleted the child,
+  // so check before deleting
   for (octave_idx_type i = 0; i < n; i++)
-    gh_manager::free (children(i));
+    {
+      graphics_object go = gh_manager::get_object (children(i));
+
+      if (go.valid_object ())
+	gh_manager::free (children(i));
+    }
 }
 
 graphics_backend
@@ -2104,8 +2117,12 @@
 void 
 axes::properties::sync_positions (void)
 {
+#if 0
   // FIXME -- this should take font metrics into consideration,
-  // for now we'll just make it position 90% of outerposition
+  // and also the fact that the colorbox leaves the outerposition
+  // alone but alters the position. For now just don't adjust the
+  // positions relative to each other.
+
   if (activepositionproperty.is ("outerposition"))
     {
       Matrix outpos = outerposition.get ().matrix_value ();
@@ -2126,6 +2143,7 @@
       pos(3) *= 1.1;
       outerposition = pos;
     }
+#endif
 
   update_transform ();
 }
@@ -2182,7 +2200,8 @@
 axes::properties::set_defaults (base_graphics_object& obj,
 				const std::string& mode)
 {
-  title = graphics_handle ();
+  delete_text_child (title);
+
   box = "on";
   key = "off";
   keybox = "off";
@@ -2206,9 +2225,11 @@
   ylimmode = "auto";
   zlimmode = "auto";
   climmode = "auto";
-  xlabel = graphics_handle ();
-  ylabel = graphics_handle ();
-  zlabel = graphics_handle ();
+
+  delete_text_child (xlabel);
+  delete_text_child (ylabel);
+  delete_text_child (zlabel);
+
   xgrid = "off";
   ygrid = "off";
   zgrid = "off";
@@ -2289,7 +2310,6 @@
     }
 
   activepositionproperty = "outerposition";
-  __colorbar__  = "none";
 
   delete_children ();
 
@@ -2300,53 +2320,34 @@
   override_defaults (obj);
 }
 
-graphics_handle
-axes::properties::get_title (void) const
-{
-  if (! title.handle_value ().ok ())
-    title = gh_manager::make_graphics_handle ("text", __myhandle__);
-
-  return title.handle_value ();
-}
-
-graphics_handle
-axes::properties::get_xlabel (void) const
+void
+axes::properties::delete_text_child (handle_property& hp)
 {
-  if (! xlabel.handle_value ().ok ())
-    xlabel = gh_manager::make_graphics_handle ("text", __myhandle__);
-
-  return xlabel.handle_value ();
-}
-
-graphics_handle
-axes::properties::get_ylabel (void) const
-{
-  if (! ylabel.handle_value ().ok ())
-    ylabel = gh_manager::make_graphics_handle ("text", __myhandle__);
-
-  return ylabel.handle_value ();
-}
-
-graphics_handle
-axes::properties::get_zlabel (void) const
-{
-  if (! zlabel.handle_value ().ok ())
-    zlabel = gh_manager::make_graphics_handle ("text", __myhandle__);
-
-  return zlabel.handle_value ();
+  graphics_handle h = hp.handle_value ();
+
+  if (h.ok ())
+    {
+      graphics_object go = gh_manager::get_object (h);
+
+      if (go.valid_object ())
+	gh_manager::free (h);
+    }
+
+  if (! is_beingdeleted ())
+    hp = gh_manager::make_graphics_handle ("text", __myhandle__);
 }
 
 void
 axes::properties::remove_child (const graphics_handle& h)
 {
   if (title.handle_value ().ok () && h == title.handle_value ())
-    title = gh_manager::make_graphics_handle ("text", __myhandle__);
+    delete_text_child (title);
   else if (xlabel.handle_value ().ok () && h == xlabel.handle_value ())
-    xlabel = gh_manager::make_graphics_handle ("text", __myhandle__);
+    delete_text_child (xlabel);
   else if (ylabel.handle_value ().ok () && h == ylabel.handle_value ())
-    ylabel = gh_manager::make_graphics_handle ("text", __myhandle__);
+    delete_text_child (ylabel);
   else if (zlabel.handle_value ().ok () && h == zlabel.handle_value ())
-    zlabel = gh_manager::make_graphics_handle ("text", __myhandle__);
+    delete_text_child (zlabel);
   else
     base_properties::remove_child (h);
 }
@@ -2356,10 +2357,11 @@
 {
   base_properties::delete_children ();
 
-  gh_manager::free (title.handle_value ());
-  gh_manager::free (xlabel.handle_value ());
-  gh_manager::free (ylabel.handle_value ());
-  gh_manager::free (zlabel.handle_value ());
+  delete_text_child (title);
+
+  delete_text_child (xlabel);
+  delete_text_child (ylabel);
+  delete_text_child (zlabel);
 }
 
 inline Matrix
@@ -4431,20 +4433,28 @@
 		{
 		  graphics_object obj = gh_manager::get_object (h);
 
-		  graphics_handle parent_h = obj.get_parent ();
-
-		  graphics_object parent_obj = 
-		    gh_manager::get_object (parent_h);
-
-		  // NOTE: free the handle before removing it from its parent's
-		  //       children, such that the object's state is correct
-		  //       when the deletefcn callback is executed
-
-		  gh_manager::free (h);
-
-		  parent_obj.remove_child (h);
-
-		  Vdrawnow_requested = true;
+		  // Don't do recursive deleting, due to callbacks
+		  if (! obj.get_properties ().is_beingdeleted ())
+		    {
+		      graphics_handle parent_h = obj.get_parent ();
+
+		      graphics_object parent_obj = 
+			gh_manager::get_object (parent_h);
+
+		      // NOTE: free the handle before removing it from its
+		      //       parent's children, such that the object's 
+		      //       state is correct when the deletefcn callback
+		      //       is executed
+
+		      gh_manager::free (h);
+
+		      // A callback function might have already deleted 
+		      // the parent
+		      if (parent_obj.valid_object ())
+			parent_obj.remove_child (h);
+
+		      Vdrawnow_requested = true;
+		    }
 		}
 	      else
 		{
@@ -4500,6 +4510,10 @@
 	      graphics_object obj = gh_manager::get_object (h);
 
 	      obj.set_defaults (mode);
+
+	      h = gh_manager::lookup (val);
+	      if (! h.ok ())
+		error ("__go_axes_init__: axis deleted during initialization (= %g)", val);
 	    }
 	  else
 	    error ("__go_axes_init__: invalid graphics object (= %g)", val);
--- a/src/graphics.h.in	Fri Oct 10 16:44:21 2008 +0200
+++ b/src/graphics.h.in	Fri Oct 10 11:07:53 2008 -0400
@@ -2030,6 +2030,8 @@
 
   bool valid_object (void) const { return rep->valid_object (); }
 
+  std::string type (void) const { return rep->type (); }
+
   operator bool (void) const { return rep->valid_object (); }
 
   // FIXME -- these functions should be generated automatically by the
@@ -2530,12 +2532,14 @@
     Matrix x_zlim;
     std::list<octave_value> zoom_stack;
 
+    void delete_text_child (handle_property& h);
+
     // See the genprops.awk script for an explanation of the
     // properties declarations.
 
     BEGIN_PROPERTIES(axes)
       array_property position u , default_axes_position ()
-      mutable handle_property title GSO , graphics_handle ()
+      handle_property title SOf , gh_manager::make_graphics_handle ("text", __myhandle__)
       bool_property box , "on"
       bool_property key , "off"
       bool_property keybox , "off"
@@ -2554,9 +2558,9 @@
       radio_property zlimmode al , "{auto}|manual"
       radio_property climmode al , "{auto}|manual"
       radio_property alimmode    , "{auto}|manual"
-      mutable handle_property xlabel GSO , graphics_handle ()
-      mutable handle_property ylabel GSO , graphics_handle ()
-      mutable handle_property zlabel GSO , graphics_handle ()
+      handle_property xlabel SOf , gh_manager::make_graphics_handle ("text", __myhandle__)
+      handle_property ylabel SOf , gh_manager::make_graphics_handle ("text", __myhandle__)
+      handle_property zlabel SOf , gh_manager::make_graphics_handle ("text", __myhandle__)
       bool_property xgrid , "off"
       bool_property ygrid , "off"
       bool_property zgrid , "off"
@@ -2595,7 +2599,6 @@
       radio_property nextplot , "add|replace_children|{replace}"
       array_property outerposition u , default_axes_outerposition ()
       radio_property activepositionproperty , "{outerposition}|position"
-      radio_property __colorbar__ h , "{none}|north|south|east|west|northoutside|southoutside|eastoutside|westoutside"
       color_property ambientlightcolor , color_values (1, 1, 1)
       array_property cameraposition m , Matrix (1, 3, 0.0)
       array_property cameratarget m , Matrix (1, 3, 0.0)