changeset 18551:de76baa76aa1 gui-release

maint: Periodic merge of stable to gui-release.
author John W. Eaton <jwe@octave.org>
date Fri, 07 Mar 2014 12:54:16 -0500
parents 89bd70fae066 (current diff) 5c868bed3f43 (diff)
children b83fca22bb4c 2d5d0d86432e
files configure.ac libgui/src/m-editor/file-editor.cc libgui/src/m-editor/file-editor.h libgui/src/main-window.h
diffstat 8 files changed, 416 insertions(+), 300 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Tue Mar 04 10:49:31 2014 -0500
+++ b/.hgtags	Fri Mar 07 12:54:16 2014 -0500
@@ -86,3 +86,4 @@
 85d31344fb23725791bc687337295d47416b9c17 rc-3-8-1-2
 02ce68d63fba31cd14ac060c087212a032ae3202 rc-3-8-1-3
 dd669c2ae76c167613f54d6f4db0130fa2124cac rc-3-8-1-4
+43cc202335dc4a53a3d8fc9ca90acaa81d2e63d3 release-3-8-1
--- a/libgui/src/m-editor/file-editor.cc	Tue Mar 04 10:49:31 2014 -0500
+++ b/libgui/src/m-editor/file-editor.cc	Fri Mar 07 12:54:16 2014 -0500
@@ -42,6 +42,7 @@
 
 #include "octave-link.h"
 #include "utils.h"
+#include "main-window.h"
 
 file_editor::file_editor (QWidget *p)
   : file_editor_interface (p)
@@ -266,6 +267,23 @@
   return false;
 }
 
+bool
+file_editor::is_editor_console_tabbed ()
+{
+  main_window *w = static_cast<main_window *>(main_win ());
+  QList<QDockWidget *> w_list = w->tabifiedDockWidgets (this);
+  QDockWidget *console =
+    static_cast<QDockWidget *> (w->get_dock_widget_list ().at (0));
+
+  for (int i = 0; i < w_list.count (); i++)
+    {
+      if (w_list.at (i) == console)
+        return true;
+    }
+
+  return false;
+}
+
 void
 file_editor::request_open_files (const QStringList& open_file_names)
 {
@@ -311,8 +329,11 @@
                 emit fetab_do_breakpoint_marker (insert, tab, line);
             }
 
-          emit fetab_set_focus (tab);
-          set_focus ();
+          if (! ((breakpoint_marker || debug_pointer) && is_editor_console_tabbed ()))
+            {
+              emit fetab_set_focus (tab);
+              set_focus ();
+            }
         }
       else
         {
@@ -411,9 +432,12 @@
                 }
             }
 
-          // really show editor and the current editor tab
-          set_focus ();
-          emit file_loaded_signal ();
+          if (! ((breakpoint_marker || debug_pointer) && is_editor_console_tabbed ()))
+            {
+              // really show editor and the current editor tab
+              set_focus ();
+              emit file_loaded_signal ();
+            }
         }
     }
 }
--- a/libgui/src/m-editor/file-editor.h	Tue Mar 04 10:49:31 2014 -0500
+++ b/libgui/src/m-editor/file-editor.h	Fri Mar 07 12:54:16 2014 -0500
@@ -195,6 +195,7 @@
 
 private:
 
+  bool is_editor_console_tabbed ();
   void construct (void);
   void add_file_editor_tab (file_editor_tab *f, const QString& fn);
   void save_file_as (QWidget *fetabID = 0);
--- a/libgui/src/main-window.h	Tue Mar 04 10:49:31 2014 -0500
+++ b/libgui/src/main-window.h	Fri Mar 07 12:54:16 2014 -0500
@@ -198,6 +198,9 @@
   void clipboard_has_changed (QClipboard::Mode);
   void clear_clipboard ();
 
+  // get the dockwidgets
+  QList<octave_dock_widget *> get_dock_widget_list ()
+    { return dock_widget_list (); }
 
 protected:
   void closeEvent (QCloseEvent * closeEvent);
--- a/libinterp/dldfcn/__magick_read__.cc	Tue Mar 04 10:49:31 2014 -0500
+++ b/libinterp/dldfcn/__magick_read__.cc	Fri Mar 07 12:54:16 2014 -0500
@@ -416,6 +416,7 @@
         }
     }
 
+  const octave_idx_type colour_stride = nRows * nCols;
   switch (type)
     {
     case Magick::BilevelType:           // Monochrome bi-level image
@@ -480,6 +481,7 @@
         img = T (dim_vector (nRows, nCols, 3, nFrames));
         P *img_fvec = img.fortran_vec ();
 
+        const octave_idx_type frame_stride  = colour_stride * 3;
         for (octave_idx_type frame = 0; frame < nFrames; frame++)
           {
             const Magick::PixelPacket *pix
@@ -487,10 +489,9 @@
                                                        col_cache, row_cache);
 
             octave_idx_type idx = 0;
-            img_fvec += nRows * nCols * frame;
             P *rbuf   = img_fvec;
-            P *gbuf   = img_fvec + nRows * nCols;
-            P *bbuf   = img_fvec + nRows * nCols * 2;
+            P *gbuf   = img_fvec + colour_stride;
+            P *bbuf   = img_fvec + colour_stride * 2;
 
             for (octave_idx_type col = 0; col < nCols; col++)
               {
@@ -504,6 +505,7 @@
                   }
                 pix -= col_shift;
               }
+            img_fvec += frame_stride;
           }
         break;
       }
@@ -516,6 +518,8 @@
         P *img_fvec = img.fortran_vec ();
         P *a_fvec   = alpha.fortran_vec ();
 
+        const octave_idx_type frame_stride  = colour_stride * 3;
+
         // Unlike the index for the other channels, this one won't need
         // to be reset on each frame since it's a separate matrix.
         octave_idx_type a_idx = 0;
@@ -526,10 +530,9 @@
                                                        col_cache, row_cache);
 
             octave_idx_type idx = 0;
-            img_fvec += nRows * nCols * frame;
             P *rbuf   = img_fvec;
-            P *gbuf   = img_fvec + nRows * nCols;
-            P *bbuf   = img_fvec + nRows * nCols * 2;
+            P *gbuf   = img_fvec + colour_stride;
+            P *bbuf   = img_fvec + colour_stride * 2;
 
             for (octave_idx_type col = 0; col < nCols; col++)
               {
@@ -544,6 +547,7 @@
                   }
                 pix -= col_shift;
               }
+            img_fvec += frame_stride;
           }
         retval(2) = alpha;
         break;
@@ -554,6 +558,7 @@
         img   = T (dim_vector (nRows, nCols, 4, nFrames));
         P *img_fvec = img.fortran_vec ();
 
+        const octave_idx_type frame_stride  = colour_stride * 4;
         for (octave_idx_type frame = 0; frame < nFrames; frame++)
           {
             const Magick::PixelPacket *pix
@@ -561,11 +566,10 @@
                                                        col_cache, row_cache);
 
             octave_idx_type idx = 0;
-            img_fvec += nRows * nCols * frame;
             P *cbuf   = img_fvec;
-            P *mbuf   = img_fvec + nRows * nCols;
-            P *ybuf   = img_fvec + nRows * nCols * 2;
-            P *kbuf   = img_fvec + nRows * nCols * 3;
+            P *mbuf   = img_fvec + colour_stride;
+            P *ybuf   = img_fvec + colour_stride * 2;
+            P *kbuf   = img_fvec + colour_stride * 3;
 
             for (octave_idx_type col = 0; col < nCols; col++)
               {
@@ -580,6 +584,7 @@
                   }
                 pix -= col_shift;
               }
+            img_fvec += frame_stride;
           }
         break;
       }
@@ -592,6 +597,8 @@
         P *img_fvec = img.fortran_vec ();
         P *a_fvec   = alpha.fortran_vec ();
 
+        const octave_idx_type frame_stride  = colour_stride * 4;
+
         // Unlike the index for the other channels, this one won't need
         // to be reset on each frame since it's a separate matrix.
         octave_idx_type a_idx = 0;
@@ -606,11 +613,10 @@
               = imvec[frameidx(frame)].getConstIndexes ();
 
             octave_idx_type idx = 0;
-            img_fvec += nRows * nCols * frame;
             P *cbuf   = img_fvec;
-            P *mbuf   = img_fvec + nRows * nCols;
-            P *ybuf   = img_fvec + nRows * nCols * 2;
-            P *kbuf   = img_fvec + nRows * nCols * 3;
+            P *mbuf   = img_fvec + colour_stride;
+            P *ybuf   = img_fvec + colour_stride * 2;
+            P *kbuf   = img_fvec + colour_stride * 3;
 
             for (octave_idx_type col = 0; col < nCols; col++)
               {
@@ -626,6 +632,7 @@
                   }
                 pix -= col_shift;
               }
+            img_fvec += frame_stride;
           }
         retval(2) = alpha;
         break;
@@ -1062,7 +1069,6 @@
     {
     case Magick::GrayscaleType:
       {
-        octave_idx_type GM_idx = 0;
         for (octave_idx_type frame = 0; frame < nFrames; frame++)
           {
             Magick::Image m_img = init_enconde_image (nCols, nRows, bitdepth,
@@ -1070,6 +1076,7 @@
                                                       Magick::DirectClass);
 
             Magick::PixelPacket *pix = m_img.getPixels (0, 0, nCols, nRows);
+            octave_idx_type GM_idx = 0;
             for (octave_idx_type col = 0; col < nCols; col++)
               {
                 for (octave_idx_type row = 0; row < nRows; row++)
@@ -1091,7 +1098,6 @@
 
     case Magick::GrayscaleMatteType:
       {
-        octave_idx_type GM_idx = 0;
         for (octave_idx_type frame = 0; frame < nFrames; frame++)
           {
             Magick::Image m_img = init_enconde_image (nCols, nRows, bitdepth,
@@ -1099,13 +1105,14 @@
                                                       Magick::DirectClass);
 
             Magick::PixelPacket *pix = m_img.getPixels (0, 0, nCols, nRows);
+            octave_idx_type GM_idx = 0;
             for (octave_idx_type col = 0; col < nCols; col++)
               {
                 for (octave_idx_type row = 0; row < nRows; row++)
                   {
-                    Magick::Color c;
-                    c.redQuantum   (double (*img_fvec) / divisor);
-                    c.alphaQuantum (MaxRGB - (double (*a_fvec) / divisor));
+                    double grey = double (*img_fvec) / divisor;
+                    Magick::Color c (grey, grey, grey,
+                                     MaxRGB - (double (*a_fvec) / divisor));
                     pix[GM_idx] = c;
                     img_fvec++;
                     a_fvec++;
@@ -1125,7 +1132,6 @@
         // The fortran_vec offset for the green and blue channels
         const octave_idx_type G_offset = nCols * nRows;
         const octave_idx_type B_offset = nCols * nRows * 2;
-        octave_idx_type GM_idx = 0;
         for (octave_idx_type frame = 0; frame < nFrames; frame++)
           {
             Magick::Image m_img = init_enconde_image (nCols, nRows, bitdepth,
@@ -1133,6 +1139,7 @@
                                                       Magick::DirectClass);
 
             Magick::PixelPacket *pix = m_img.getPixels (0, 0, nCols, nRows);
+            octave_idx_type GM_idx = 0;
             for (octave_idx_type col = 0; col < nCols; col++)
               {
                 for (octave_idx_type row = 0; row < nRows; row++)
@@ -1149,6 +1156,7 @@
             // Save changes to underlying image.
             m_img.syncPixels ();
             imvec.push_back (m_img);
+            img_fvec += B_offset;
           }
         break;
       }
@@ -1158,7 +1166,6 @@
         // The fortran_vec offset for the green and blue channels
         const octave_idx_type G_offset = nCols * nRows;
         const octave_idx_type B_offset = nCols * nRows * 2;
-        octave_idx_type GM_idx = 0;
         for (octave_idx_type frame = 0; frame < nFrames; frame++)
           {
             Magick::Image m_img = init_enconde_image (nCols, nRows, bitdepth,
@@ -1166,6 +1173,7 @@
                                                       Magick::DirectClass);
 
             Magick::PixelPacket *pix = m_img.getPixels (0, 0, nCols, nRows);
+            octave_idx_type GM_idx = 0;
             for (octave_idx_type col = 0; col < nCols; col++)
               {
                 for (octave_idx_type row = 0; row < nRows; row++)
@@ -1184,6 +1192,7 @@
             // Save changes to underlying image.
             m_img.syncPixels ();
             imvec.push_back (m_img);
+            img_fvec += B_offset;
           }
         break;
       }
@@ -1194,7 +1203,6 @@
         const octave_idx_type M_offset = nCols * nRows;
         const octave_idx_type Y_offset = nCols * nRows * 2;
         const octave_idx_type K_offset = nCols * nRows * 3;
-        octave_idx_type GM_idx = 0;
         for (octave_idx_type frame = 0; frame < nFrames; frame++)
           {
             Magick::Image m_img = init_enconde_image (nCols, nRows, bitdepth,
@@ -1202,6 +1210,7 @@
                                                       Magick::DirectClass);
 
             Magick::PixelPacket *pix = m_img.getPixels (0, 0, nCols, nRows);
+            octave_idx_type GM_idx = 0;
             for (octave_idx_type col = 0; col < nCols; col++)
               {
                 for (octave_idx_type row = 0; row < nRows; row++)
@@ -1219,6 +1228,7 @@
             // Save changes to underlying image.
             m_img.syncPixels ();
             imvec.push_back (m_img);
+            img_fvec += K_offset;
           }
         break;
       }
@@ -1229,7 +1239,6 @@
         const octave_idx_type M_offset = nCols * nRows;
         const octave_idx_type Y_offset = nCols * nRows * 2;
         const octave_idx_type K_offset = nCols * nRows * 3;
-        octave_idx_type GM_idx = 0;
         for (octave_idx_type frame = 0; frame < nFrames; frame++)
           {
             Magick::Image m_img = init_enconde_image (nCols, nRows, bitdepth,
@@ -1238,6 +1247,7 @@
 
             Magick::PixelPacket *pix = m_img.getPixels (0, 0, nCols, nRows);
             Magick::IndexPacket *ind = m_img.getIndexes ();
+            octave_idx_type GM_idx = 0;
             for (octave_idx_type col = 0; col < nCols; col++)
               {
                 for (octave_idx_type row = 0; row < nRows; row++)
@@ -1257,6 +1267,7 @@
             // Save changes to underlying image.
             m_img.syncPixels ();
             imvec.push_back (m_img);
+            img_fvec += K_offset;
           }
         break;
       }
--- a/scripts/image/imshow.m	Tue Mar 04 10:49:31 2014 -0500
+++ b/scripts/image/imshow.m	Fri Mar 07 12:54:16 2014 -0500
@@ -130,7 +130,7 @@
           xdata = [xdata(1) xdata(end)];
         case "ydata";
           ydata = varargin{narg++};
-          if (isvector (xdata))
+          if (! isvector (ydata))
             error ("imshow: expect a vector for ydata")
           endif
           ydata = [ydata(1) ydata(end)];
--- a/scripts/image/imwrite.m	Tue Mar 04 10:49:31 2014 -0500
+++ b/scripts/image/imwrite.m	Fri Mar 07 12:54:16 2014 -0500
@@ -114,7 +114,6 @@
 
 endfunction
 
-
 %% Test input validation
 %!error imwrite ()                            # Wrong # of args
 %!error imwrite (1)                           # Wrong # of args
@@ -126,14 +125,69 @@
 %!error imwrite ([], "filename.jpg")          # Empty img matrix
 %!error imwrite (spones (2), "filename.jpg")  # Invalid sparse img
 
+%!function [r, cmap, a] = write_and_read (varargin)
+%!  filename = [tmpnam() ".tif"];
+%!  unwind_protect
+%!    imwrite (varargin{1}, filename, varargin{2:end});
+%!    [r, cmap, a] = imread (filename, "Index", "all");
+%!  unwind_protect_cleanup
+%!    unlink (filename);
+%!  end_unwind_protect
+%!endfunction
+
+## typical usage with grayscale uint8 images
 %!testif HAVE_MAGICK
-%! imw = randi (255, 100, "uint8");
-%! filename = [tmpnam() ".png"];
-%! unwind_protect
-%!   imwrite (imw, filename);
-%!   imr = imread (filename);
-%! unwind_protect_cleanup
-%!   unlink (filename);
-%! end_unwind_protect
-%! assert (imw, imr)
+%! gray  = randi (255, 10, 10, 1, "uint8");
+%! r  = write_and_read (gray);
+%! assert (r, gray)
+
+## grayscale uint8 images with alpha channel
+%!testif HAVE_MAGICK
+%! gray  = randi (255, 10, 10, 1, "uint8");
+%! alpha = randi (255, 10, 10, 1, "uint8");
+%! [r, ~, a] = write_and_read (gray, "Alpha", alpha);
+%! assert (r, gray)
+%! assert (a, alpha)
+
+## multipage grayscale uint8 images
+%!testif HAVE_MAGICK
+%! gray  = randi (255, 10, 10, 1, 5, "uint8");
+%! r     = write_and_read (gray);
+%! assert (r, gray)
+
+## multipage RGB uint8 images with alpha channel
+%!testif HAVE_MAGICK
+%! gray  = randi (255, 10, 10, 3, 5, "uint8");
+%! alpha = randi (255, 10, 10, 1, 5, "uint8");
+%! [r, ~, a] = write_and_read (gray, "Alpha", alpha);
+%! assert (r, gray)
+%! assert (a, alpha)
 
+## typical usage with RGB uint8 images
+%!testif HAVE_MAGICK
+%! rgb = randi (255, 10, 10, 3, "uint8");
+%! r = write_and_read (rgb);
+%! assert (r, rgb)
+
+## RGB uint8 images with alpha channel
+%!testif HAVE_MAGICK
+%! rgb   = randi (255, 10, 10, 3, "uint8");
+%! alpha = randi (255, 10, 10, 1, "uint8");
+%! [r, ~, a] = write_and_read (rgb, "Alpha", alpha);
+%! assert (r, rgb)
+%! assert (a, alpha)
+
+## multipage RGB uint8 images
+%!testif HAVE_MAGICK
+%! rgb = randi (255, 10, 10, 3, 5, "uint8");
+%! r = write_and_read (rgb);
+%! assert (r, rgb)
+
+## multipage RGB uint8 images with alpha channel
+%!testif HAVE_MAGICK
+%! rgb   = randi (255, 10, 10, 3, 5, "uint8");
+%! alpha = randi (255, 10, 10, 1, 5, "uint8");
+%! [r, ~, a] = write_and_read (rgb, "Alpha", alpha);
+%! assert (r, rgb)
+%! assert (a, alpha)
+
--- a/scripts/testfun/assert.m	Tue Mar 04 10:49:31 2014 -0500
+++ b/scripts/testfun/assert.m	Fri Mar 07 12:54:16 2014 -0500
@@ -63,269 +63,187 @@
   persistent call_depth = -1;
   persistent errmsg;
 
-  call_depth++;
-
-  if (call_depth == 0)
-    errmsg = "";
-  endif
+  unwind_protect
 
-  if (nargin == 1 || (nargin > 1 && islogical (cond) && ischar (varargin{1})))
-    if ((! isnumeric (cond) && ! islogical (cond)) || ! all (cond(:)))
-      call_depth--;
-      if (nargin == 1)
-        ## Perhaps, say which elements failed?
-        argin = ["(" strjoin(cellstr (argn), ",") ")"];
-        error ("assert %s failed", argin);
-      else
-        error (varargin{:});
-      endif
-    endif
-  else
-    expected = varargin{1};
-    if (nargin < 3)
-      tol = 0;
-    elseif (nargin == 3)
-      tol = varargin{2};
-    else
-      print_usage ();
+    call_depth++;
+
+    if (call_depth == 0)
+      errmsg = "";
     endif
 
-    ## Add to list as the errors accumulate.  If empty at end then no errors.
-    err.index = {};
-    err.observed = {};
-    err.expected = {};
-    err.reason = {};
-
-    if (ischar (expected))
-      if (! ischar (cond))
-        err.index{end+1} = ".";
-        err.expected{end+1} = expected;
-        if (isnumeric (cond))
-          err.observed{end+1} = num2str (cond);
-          err.reason{end+1} = "Expected string, but observed number";
+    if (nargin == 1 || (nargin > 1 && islogical (cond) && ischar (varargin{1})))
+      if ((! isnumeric (cond) && ! islogical (cond)) || ! all (cond(:)))
+        if (nargin == 1)
+          ## Perhaps, say which elements failed?
+          argin = ["(" strjoin(cellstr (argn), ",") ")"];
+          error ("assert %s failed", argin);
         else
-          err.observed{end+1} = "O";
-          err.reason{end+1} = ["Expected string, but observed " class(cond)];
+          error (varargin{:});
         endif
-      elseif (! strcmp (cond, expected))
-        err.index{end+1} = "[]";
-        err.observed{end+1} = cond;
-        err.expected{end+1} = expected;
-        err.reason{end+1} = "Strings don't match";
+      endif
+    else
+      expected = varargin{1};
+      if (nargin < 3)
+        tol = 0;
+      elseif (nargin == 3)
+        tol = varargin{2};
+      else
+        print_usage ();
       endif
 
-    elseif (iscell (expected))
-      if (! iscell (cond))
-        err.index{end+1} = ".";
-        err.observed{end+1} = "O";
-        err.expected{end+1} = "E";
-        err.reason{end+1} = ["Expected cell, but observed " class(cond)];
+      ## Add to list as the errors accumulate.  If empty at end then no errors.
+      err.index = {};
+      err.observed = {};
+      err.expected = {};
+      err.reason = {};
+
+      if (ischar (expected))
+        if (! ischar (cond))
+          err.index{end+1} = ".";
+          err.expected{end+1} = expected;
+          if (isnumeric (cond))
+            err.observed{end+1} = num2str (cond);
+            err.reason{end+1} = "Expected string, but observed number";
+          else
+            err.observed{end+1} = "O";
+            err.reason{end+1} = ["Expected string, but observed " class(cond)];
+          endif
+        elseif (! strcmp (cond, expected))
+          err.index{end+1} = "[]";
+          err.observed{end+1} = cond;
+          err.expected{end+1} = expected;
+          err.reason{end+1} = "Strings don't match";
+        endif
+
+      elseif (iscell (expected))
+        if (! iscell (cond))
+          err.index{end+1} = ".";
+          err.observed{end+1} = "O";
+          err.expected{end+1} = "E";
+          err.reason{end+1} = ["Expected cell, but observed " class(cond)];
+        elseif (ndims (cond) != ndims (expected)
+                || any (size (cond) != size (expected)))
+          err.index{end+1} = ".";
+          err.observed{end+1} = ["O(" sprintf("%dx", size(cond))(1:end-1) ")"];
+          err.expected{end+1} = ["E(" sprintf("%dx", size(expected))(1:end-1) ")"];
+          err.reason{end+1} = "Dimensions don't match";
+        else
+          try
+            ## Recursively compare cell arrays
+            for i = 1:length (expected(:))
+              assert (cond{i}, expected{i}, tol);
+            endfor
+          catch
+            err.index{end+1} = "{}";
+            err.observed{end+1} = "O";
+            err.expected{end+1} = "E";
+            err.reason{end+1} = "Cell configuration error";
+          end_try_catch
+        endif
+
+      elseif (is_function_handle (expected))
+        if (! is_function_handle (cond))
+          err.index{end+1} = "@";
+          err.observed{end+1} = "O";
+          err.expected{end+1} = "E";
+          err.reason{end+1} = ["Expected function handle, but observed " class(cond)];
+        elseif (! isequal (cond, expected))
+          err.index{end+1} = "@";
+          err.observed{end+1} = "O";
+          err.expected{end+1} = "E";
+          err.reason{end+1} = "Function handles don't match";
+        endif
+
+      elseif (isstruct (expected))
+        if (! isstruct (cond))
+          err.index{end+1} = ".";
+          err.observed{end+1} = "O";
+          err.expected{end+1} = "E";
+          err.reason{end+1} = ["Expected struct, but observed " class(cond)];
+        elseif (ndims (cond) != ndims (expected)
+                || any (size (cond) != size (expected))
+                || rows (fieldnames (cond)) != rows (fieldnames (expected)))
+
+          err.index{end+1} = ".";
+          err.observed{end+1} = ["O(" sprintf("%dx", size(cond))(1:end-1) ")"];
+          err.expected{end+1} = ["E(" sprintf("%dx", size(expected))(1:end-1) ")"];
+          err.reason{end+1} = "Structure sizes don't match";
+        else
+          try
+            empty = isempty (cond);
+            normal = (numel (cond) == 1);
+            for [v, k] = cond
+              if (! isfield (expected, k))
+                err.index{end+1} = ".";
+                err.observed{end+1} = "O";
+                err.expected{end+1} = "E";
+                err.reason{end+1} = ["'" k "'" " is not an expected field"];
+              endif
+              if (empty)
+                v = {};
+              elseif (normal)
+                v = {v};
+              else
+                v = v(:)';
+              endif
+              ## Recursively call assert for struct array values
+              assert (v, {expected.(k)}, tol);
+            endfor
+          catch
+            err.index{end+1} = ".";
+            err.observed{end+1} = "O";
+            err.expected{end+1} = "E";
+            err.reason{end+1} = "Structure configuration error";
+          end_try_catch
+        endif
+
       elseif (ndims (cond) != ndims (expected)
               || any (size (cond) != size (expected)))
         err.index{end+1} = ".";
         err.observed{end+1} = ["O(" sprintf("%dx", size(cond))(1:end-1) ")"];
         err.expected{end+1} = ["E(" sprintf("%dx", size(expected))(1:end-1) ")"];
         err.reason{end+1} = "Dimensions don't match";
-      else
-        try
-          ## Recursively compare cell arrays
-          for i = 1:length (expected(:))
-            assert (cond{i}, expected{i}, tol);
-          endfor
-        catch
-          err.index{end+1} = "{}";
-          err.observed{end+1} = "O";
-          err.expected{end+1} = "E";
-          err.reason{end+1} = "Cell configuration error";
-        end_try_catch
-      endif
 
-    elseif (isstruct (expected))
-      if (! isstruct (cond))
-        err.index{end+1} = ".";
-        err.observed{end+1} = "O";
-        err.expected{end+1} = "E";
-        err.reason{end+1} = ["Expected struct, but observed " class(cond)];
-      elseif (ndims (cond) != ndims (expected)
-              || any (size (cond) != size (expected))
-              || rows (fieldnames (cond)) != rows (fieldnames (expected)))
-
-        err.index{end+1} = ".";
-        err.observed{end+1} = ["O(" sprintf("%dx", size(cond))(1:end-1) ")"];
-        err.expected{end+1} = ["E(" sprintf("%dx", size(expected))(1:end-1) ")"];
-        err.reason{end+1} = "Structure sizes don't match";
-      else
-        try
-          empty = isempty (cond);
-          normal = (numel (cond) == 1);
-          for [v, k] = cond
-            if (! isfield (expected, k))
-              err.index{end+1} = ".";
-              err.observed{end+1} = "O";
-              err.expected{end+1} = "E";
-              err.reason{end+1} = ["'" k "'" " is not an expected field"];
-            endif
-            if (empty)
-              v = {};
-            elseif (normal)
-              v = {v};
-            else
-              v = v(:)';
-            endif
-            ## Recursively call assert for struct array values
-            assert (v, {expected.(k)}, tol);
-          endfor
-        catch
-          err.index{end+1} = ".";
-          err.observed{end+1} = "O";
-          err.expected{end+1} = "E";
-          err.reason{end+1} = "Structure configuration error";
-        end_try_catch
-      endif
-
-    elseif (ndims (cond) != ndims (expected)
-            || any (size (cond) != size (expected)))
-      err.index{end+1} = ".";
-      err.observed{end+1} = ["O(" sprintf("%dx", size(cond))(1:end-1) ")"];
-      err.expected{end+1} = ["E(" sprintf("%dx", size(expected))(1:end-1) ")"];
-      err.reason{end+1} = "Dimensions don't match";
-
-    else  # Numeric comparison
-      if (nargin < 3)
-        ## Without explicit tolerance, be more strict.
-        if (! strcmp (class (cond), class (expected)))
-          err.index{end+1} = "()";
-          err.observed{end+1} = "O";
-          err.expected{end+1} = "E";
-          err.reason{end+1} = ["Class " class(cond) " != " class(expected)];
-        elseif (isnumeric (cond))
-          if (issparse (cond) != issparse (expected))
+      else  # Numeric comparison
+        if (nargin < 3)
+          ## Without explicit tolerance, be more strict.
+          if (! strcmp (class (cond), class (expected)))
             err.index{end+1} = "()";
             err.observed{end+1} = "O";
             err.expected{end+1} = "E";
-            if (issparse (cond))
-              err.reason{end+1} = "sparse != non-sparse";
-            else
-              err.reason{end+1} = "non-sparse != sparse";
-            endif
-          elseif (iscomplex (cond) != iscomplex (expected))
-            err.index{end+1} = "()";
-            err.observed{end+1} = "O";
-            err.expected{end+1} = "E";
-           if (iscomplex (cond))
-              err.reason{end+1} = "complex != real";
-            else
-              err.reason{end+1} = "real != complex";
+            err.reason{end+1} = ["Class " class(cond) " != " class(expected)];
+          elseif (isnumeric (cond))
+            if (issparse (cond) != issparse (expected))
+              err.index{end+1} = "()";
+              err.observed{end+1} = "O";
+              err.expected{end+1} = "E";
+              if (issparse (cond))
+                err.reason{end+1} = "sparse != non-sparse";
+              else
+                err.reason{end+1} = "non-sparse != sparse";
+              endif
+            elseif (iscomplex (cond) != iscomplex (expected))
+              err.index{end+1} = "()";
+              err.observed{end+1} = "O";
+              err.expected{end+1} = "E";
+             if (iscomplex (cond))
+                err.reason{end+1} = "complex != real";
+              else
+                err.reason{end+1} = "real != complex";
+              endif
             endif
           endif
         endif
-      endif
 
-      if (isempty (err.index))
-
-        A = cond;
-        B = expected;
-
-        ## Check exceptional values.
-        errvec = (  isna (real (A)) != isna (real (B))
-                  | isna (imag (A)) != isna (imag (B)));
-        erridx = find (errvec);
-        if (! isempty (erridx))
-          err.index(end+1:end+length (erridx)) = ...
-            ind2tuple (size (A), erridx);
-          err.observed(end+1:end+length (erridx)) = ...
-            strtrim (cellstr (num2str (A(erridx) (:))));
-          err.expected(end+1:end+length (erridx)) = ...
-            strtrim (cellstr (num2str (B(erridx) (:))));
-          err.reason(end+1:end+length (erridx)) = ...
-            repmat ({"'NA' mismatch"}, length (erridx), 1);
-        endif
-        errseen = errvec;
-
-        errvec = (  isnan (real (A)) != isnan (real (B))
-                  | isnan (imag (A)) != isnan (imag (B)));
-        erridx = find (errvec & !errseen);
-        if (! isempty (erridx))
-          err.index(end+1:end+length (erridx)) = ...
-            ind2tuple (size (A), erridx);
-          err.observed(end+1:end+length (erridx)) = ...
-            strtrim (cellstr (num2str (A(erridx) (:))));
-          err.expected(end+1:end+length (erridx)) = ...
-            strtrim (cellstr (num2str (B(erridx) (:))));
-          err.reason(end+1:end+length (erridx)) = ...
-            repmat ({"'NaN' mismatch"}, length (erridx), 1);
-        endif
-        errseen |= errvec;
+        if (isempty (err.index))
 
-        errvec =   ((isinf (real (A)) | isinf (real (B))) ...
-                    & (real (A) != real (B)))             ...
-                 | ((isinf (imag (A)) | isinf (imag (B))) ...
-                    & (imag (A) != imag (B)));
-        erridx = find (errvec & !errseen);
-        if (! isempty (erridx))
-          err.index(end+1:end+length (erridx)) = ...
-            ind2tuple (size (A), erridx);
-          err.observed(end+1:end+length (erridx)) = ...
-            strtrim (cellstr (num2str (A(erridx) (:))));
-          err.expected(end+1:end+length (erridx)) = ...
-            strtrim (cellstr (num2str (B(erridx) (:))));
-          err.reason(end+1:end+length (erridx)) = ...
-            repmat ({"'Inf' mismatch"}, length (erridx), 1);
-        endif
-        errseen |= errvec;
+          A = cond;
+          B = expected;
 
-        ## Check normal values.
-        ## Replace exceptional values already checked above by zero.
-        A_null_real = real (A);
-        B_null_real = real (B);
-        exclude = errseen | ! isfinite (A_null_real) & ! isfinite (B_null_real);
-        A_null_real(exclude) = 0;
-        B_null_real(exclude) = 0;
-        A_null_imag = imag (A);
-        B_null_imag = imag (B);
-        exclude = errseen | ! isfinite (A_null_imag) & ! isfinite (B_null_imag);
-        A_null_imag(exclude) = 0;
-        B_null_imag(exclude) = 0;
-        A_null = complex (A_null_real, A_null_imag);
-        B_null = complex (B_null_real, B_null_imag);
-        if (isscalar (tol))
-          mtol = tol * ones (size (A));
-        else
-          mtol = tol;
-        endif
-
-        k = (mtol == 0);
-        erridx = find ((A_null != B_null) & k);
-        if (! isempty (erridx))
-          err.index(end+1:end+length (erridx)) = ...
-            ind2tuple (size (A), erridx);
-          err.observed(end+1:end+length (erridx)) = ...
-            strtrim (cellstr (num2str (A(erridx) (:))));
-          err.expected(end+1:end+length (erridx)) = ...
-            strtrim (cellstr (num2str (B(erridx) (:))));
-          err.reason(end+1:end+length (erridx)) = ...
-            ostrsplit (deblank (sprintf ("Abs err %.5g exceeds tol %.5g\n",...
-            [abs(A_null(erridx) - B_null(erridx))(:) mtol(erridx)(:)]')), "\n");
-        endif
-
-        k = (mtol > 0);
-        erridx = find ((abs (A_null - B_null) > mtol) & k);
-        if (! isempty (erridx))
-          err.index(end+1:end+length (erridx)) = ...
-            ind2tuple (size (A), erridx);
-          err.observed(end+1:end+length (erridx)) = ...
-            strtrim (cellstr (num2str (A(erridx) (:))));
-          err.expected(end+1:end+length (erridx)) = ...
-            strtrim (cellstr (num2str (B(erridx) (:))));
-          err.reason(end+1:end+length (erridx)) = ...
-            ostrsplit (deblank (sprintf ("Abs err %.5g exceeds tol %.5g\n",...
-            [abs(A_null(erridx) - B_null(erridx))(:) mtol(erridx)(:)]')), "\n");
-        endif
-
-        k = (mtol < 0);
-        if (any (k(:)))
-          ## Test for absolute error where relative error can't be calculated.
-          erridx = find ((B_null == 0) & abs (A_null) > abs (mtol) & k);
+          ## Check exceptional values.
+          errvec = (  isna (real (A)) != isna (real (B))
+                    | isna (imag (A)) != isna (imag (B)));
+          erridx = find (errvec);
           if (! isempty (erridx))
             err.index(end+1:end+length (erridx)) = ...
               ind2tuple (size (A), erridx);
@@ -334,14 +252,64 @@
             err.expected(end+1:end+length (erridx)) = ...
               strtrim (cellstr (num2str (B(erridx) (:))));
             err.reason(end+1:end+length (erridx)) = ...
-              ostrsplit (deblank (sprintf ("Abs err %.5g exceeds tol %.5g\n",
-              [abs(A_null(erridx) - B_null(erridx)) -mtol(erridx)]')), "\n");
+              repmat ({"'NA' mismatch"}, length (erridx), 1);
+          endif
+          errseen = errvec;
+
+          errvec = (  isnan (real (A)) != isnan (real (B))
+                    | isnan (imag (A)) != isnan (imag (B)));
+          erridx = find (errvec & !errseen);
+          if (! isempty (erridx))
+            err.index(end+1:end+length (erridx)) = ...
+              ind2tuple (size (A), erridx);
+            err.observed(end+1:end+length (erridx)) = ...
+              strtrim (cellstr (num2str (A(erridx) (:))));
+            err.expected(end+1:end+length (erridx)) = ...
+              strtrim (cellstr (num2str (B(erridx) (:))));
+            err.reason(end+1:end+length (erridx)) = ...
+              repmat ({"'NaN' mismatch"}, length (erridx), 1);
           endif
-          ## Test for relative error
-          Bdiv = Inf (size (B_null));
-          Bdiv(k & (B_null != 0)) = B_null(k & (B_null != 0));
-          relerr = abs ((A_null - B_null) ./ abs (Bdiv));
-          erridx = find ((relerr > abs (mtol)) & k);
+          errseen |= errvec;
+
+          errvec =   ((isinf (real (A)) | isinf (real (B))) ...
+                      & (real (A) != real (B)))             ...
+                   | ((isinf (imag (A)) | isinf (imag (B))) ...
+                      & (imag (A) != imag (B)));
+          erridx = find (errvec & !errseen);
+          if (! isempty (erridx))
+            err.index(end+1:end+length (erridx)) = ...
+              ind2tuple (size (A), erridx);
+            err.observed(end+1:end+length (erridx)) = ...
+              strtrim (cellstr (num2str (A(erridx) (:))));
+            err.expected(end+1:end+length (erridx)) = ...
+              strtrim (cellstr (num2str (B(erridx) (:))));
+            err.reason(end+1:end+length (erridx)) = ...
+              repmat ({"'Inf' mismatch"}, length (erridx), 1);
+          endif
+          errseen |= errvec;
+
+          ## Check normal values.
+          ## Replace exceptional values already checked above by zero.
+          A_null_real = real (A);
+          B_null_real = real (B);
+          exclude = errseen | ! isfinite (A_null_real) & ! isfinite (B_null_real);
+          A_null_real(exclude) = 0;
+          B_null_real(exclude) = 0;
+          A_null_imag = imag (A);
+          B_null_imag = imag (B);
+          exclude = errseen | ! isfinite (A_null_imag) & ! isfinite (B_null_imag);
+          A_null_imag(exclude) = 0;
+          B_null_imag(exclude) = 0;
+          A_null = complex (A_null_real, A_null_imag);
+          B_null = complex (B_null_real, B_null_imag);
+          if (isscalar (tol))
+            mtol = tol * ones (size (A));
+          else
+            mtol = tol;
+          endif
+
+          k = (mtol == 0);
+          erridx = find ((A_null != B_null) & k);
           if (! isempty (erridx))
             err.index(end+1:end+length (erridx)) = ...
               ind2tuple (size (A), erridx);
@@ -350,26 +318,74 @@
             err.expected(end+1:end+length (erridx)) = ...
               strtrim (cellstr (num2str (B(erridx) (:))));
             err.reason(end+1:end+length (erridx)) = ...
-              ostrsplit (deblank (sprintf ("Rel err %.5g exceeds tol %.5g\n",
-              [relerr(erridx)(:) -mtol(erridx)(:)]')), "\n");
+              ostrsplit (deblank (sprintf ("Abs err %.5g exceeds tol %.5g\n",...
+              [abs(A_null(erridx) - B_null(erridx))(:) mtol(erridx)(:)]')), "\n");
+          endif
+
+          k = (mtol > 0);
+          erridx = find ((abs (A_null - B_null) > mtol) & k);
+          if (! isempty (erridx))
+            err.index(end+1:end+length (erridx)) = ...
+              ind2tuple (size (A), erridx);
+            err.observed(end+1:end+length (erridx)) = ...
+              strtrim (cellstr (num2str (A(erridx) (:))));
+            err.expected(end+1:end+length (erridx)) = ...
+              strtrim (cellstr (num2str (B(erridx) (:))));
+            err.reason(end+1:end+length (erridx)) = ...
+              ostrsplit (deblank (sprintf ("Abs err %.5g exceeds tol %.5g\n",...
+              [abs(A_null(erridx) - B_null(erridx))(:) mtol(erridx)(:)]')), "\n");
+          endif
+
+          k = (mtol < 0);
+          if (any (k(:)))
+            ## Test for absolute error where relative error can't be calculated.
+            erridx = find ((B_null == 0) & abs (A_null) > abs (mtol) & k);
+            if (! isempty (erridx))
+              err.index(end+1:end+length (erridx)) = ...
+                ind2tuple (size (A), erridx);
+              err.observed(end+1:end+length (erridx)) = ...
+                strtrim (cellstr (num2str (A(erridx) (:))));
+              err.expected(end+1:end+length (erridx)) = ...
+                strtrim (cellstr (num2str (B(erridx) (:))));
+              err.reason(end+1:end+length (erridx)) = ...
+                ostrsplit (deblank (sprintf ("Abs err %.5g exceeds tol %.5g\n",
+                [abs(A_null(erridx) - B_null(erridx)) -mtol(erridx)]')), "\n");
+            endif
+            ## Test for relative error
+            Bdiv = Inf (size (B_null));
+            Bdiv(k & (B_null != 0)) = B_null(k & (B_null != 0));
+            relerr = abs ((A_null - B_null) ./ abs (Bdiv));
+            erridx = find ((relerr > abs (mtol)) & k);
+            if (! isempty (erridx))
+              err.index(end+1:end+length (erridx)) = ...
+                ind2tuple (size (A), erridx);
+              err.observed(end+1:end+length (erridx)) = ...
+                strtrim (cellstr (num2str (A(erridx) (:))));
+              err.expected(end+1:end+length (erridx)) = ...
+                strtrim (cellstr (num2str (B(erridx) (:))));
+              err.reason(end+1:end+length (erridx)) = ...
+                ostrsplit (deblank (sprintf ("Rel err %.5g exceeds tol %.5g\n",
+                [relerr(erridx)(:) -mtol(erridx)(:)]')), "\n");
+            endif
           endif
         endif
+
+      endif
+
+      ## Print any errors
+      if (! isempty (err.index))
+        argin = ["(" strjoin(cellstr (argn), ",") ")"];
+        if (! isempty (errmsg))
+          errmsg = [errmsg "\n"];
+        endif
+        errmsg = [errmsg, pprint(argin, err)];
       endif
 
     endif
 
-    ## Print any errors
-    if (! isempty (err.index))
-      argin = ["(" strjoin(cellstr (argn), ",") ")"];
-      if (! isempty (errmsg))
-        errmsg = [errmsg "\n"];
-      endif
-      errmsg = [errmsg, pprint(argin, err)];
-    endif
-
-  endif
-
-  call_depth--;
+  unwind_protect_cleanup
+    call_depth--;
+  end_unwind_protect
 
   if (call_depth == -1)
     ## Last time through.  If there were any errors on any pass, raise a flag.
@@ -531,6 +547,12 @@
 %! y{1}{1}{1} = 3;
 %! fail ("assert (x,y)", "Abs err 2 exceeds tol 0");
 
+## function handles
+%!assert (@sin, @sin)
+%!error <Function handles don't match> assert (@sin, @cos)
+%!error <Expected function handle, but observed double> assert (pi, @cos)
+%!error <Class function_handle != double> assert (@sin, pi)
+
 %!test
 %! x = {[3], [1,2,3]; 100+100*eps, "dog"};
 %! y = x;