#!/usr/bin/env python

import sys

# verify system libraries
from afnipy import module_test_lib
g_testlibs = ['os', 'time', 'gc', 'numpy', 'wx', 'matplotlib', 'platform']
if module_test_lib.num_import_failures(g_testlibs,details=0):
   print("""
     -- for details, consider xmat_tool -test_libs
     -- also, many computations do not require the GUI
        (e.g. 'xmat_tool -load_xmat X.xmat.1D -show_cormat_warnings')
   """)
   sys.exit(1)

import os, gc, time, platform

# ** There are version differences to iron out, test with python version.
g_py_ver = platform.python_version().split('.')[0]

import numpy as N

import wx
import wx.grid

# must use matplotlib with wx, not pylab
import matplotlib
matplotlib.use('WX')

# set some resource font values
matplotlib.rc('axes',titlesize=11)
matplotlib.rc('axes',labelsize=9)
matplotlib.rc('xtick',labelsize=8)
matplotlib.rc('ytick',labelsize=7)

from matplotlib.backends.backend_wx import FigureCanvasWx as FigureCanvas
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
from matplotlib.figure import Figure

from matplotlib.ticker import FormatStrFormatter

from afnipy import afni_util as UTIL
from afnipy import afni_xmat as AM
from afnipy import ui_xmat as UIX

# ----------------------------------------------------------------------
# globals

# --------------------------------------------------
# global widget IDs

def set_wx_id(idstr, defval):
    """set idstr as a variable to wx.idstr
       if that fails, use the integer 'defval'"""
    if hasattr(wx, idstr): val = getattr(wx, idstr)
    else:                  val = defval
    return val

# try to get some IDs from wx
ID_EXIT = set_wx_id('ID_EXIT', 101 )
ID_ABOUT = set_wx_id('ID_ABOUT', 102 )
ID_CLOSE = set_wx_id('ID_CLOSE', 103 )
ID_SAVE = set_wx_id('ID_SAVEAS', 105 )
ID_HELP = set_wx_id('ID_HELP', 104 )
ID_HELP_CMD = set_wx_id('ID_HELP_COMMANDS', 106 )
ID_APPLY_CHOICE = set_wx_id('ID_APPLY', 111 )
ID_LOAD_XMAT = set_wx_id('ID_OPEN', 201 )

ID_HIST             = 131

ID_LOAD_1D          = 202

ID_SHOW_XMAT        = 301
ID_SHOW_XMAT_CONDS  = 302
ID_SHOW_CORMAT      = 311
ID_SHOW_CORMAT_EVIL = 312
ID_SHOW_COSMAT      = 316
ID_SHOW_COSMAT_EVIL = 317
ID_SHOW_1D          = 321
ID_SHOW_FIT_BETAS   = 331

ID_PLOT_XMAT        = 401
ID_PLOT_1D          = 402
ID_PLOT_BEST_FIT    = 403
ID_PLOT_CORMAT      = 411

ID_OPT_CORMAT_CUT   = 601
ID_OPT_COSMAT_CUT   = 602
ID_OPT_VERB         = 611

ID_PLOT_AS_ONE      = 1001      # check item in menu

# --------------------------------------------------
# values for global text display list
gSTR_BLANK              = ''

gSTR_1D_FILE            = ' 1D timeseries file: '
gSTR_XMAT_FILE          = ' X-matrix file: '
gSTR_COLS_HDR           = '   -- COLUMNS (regressors) by type-- '
gSTR_COLS_CHOSEN        = '   cols of chosen regressors: '
gSTR_COLS_ALL           = '   cols of ALL regressors: '
gSTR_COLS_MAIN          = '   cols of main regressors: '
gSTR_COLS_BASE          = '   cols of baseline regressors: '
gSTR_COLS_MOTION        = '   cols of motion regressors: '
gSTR_COND_HDR           = '   -- CONDITION NUMBERS -- '
gSTR_COND_CHOSEN        = '   cond of chosen columns: '
gSTR_COND_ALL           = '   cond of all regressors: '
gSTR_COND_MAIN          = '   cond of main regressors: '
gSTR_COND_M_BASE        = '   cond of main+baseline: '
gSTR_COND_M_MOT         = '   cond of main+motion: '
gSTR_COND_BASE_MOT      = '   cond of baseline+motion: '

g_textnames = [ 
        gSTR_1D_FILE,                                           gSTR_BLANK,
        gSTR_XMAT_FILE,                                         gSTR_BLANK,
        gSTR_COLS_HDR, gSTR_COLS_CHOSEN, gSTR_COLS_ALL,
        gSTR_COLS_MAIN,                                         gSTR_BLANK,
        gSTR_COLS_BASE, gSTR_COLS_MOTION,                       gSTR_BLANK,
        gSTR_COND_HDR, gSTR_COND_CHOSEN, gSTR_COND_ALL,
        gSTR_COND_MAIN,                                         gSTR_BLANK,
        gSTR_COND_M_BASE, gSTR_COND_M_MOT, gSTR_COND_BASE_MOT ]


# global text display list
g_textlist = [
   # leading string, flag (takes value), value string, call-back function
   [gSTR_1D_FILE,       1, '', None], [gSTR_BLANK,         1, '', None],
   [gSTR_XMAT_FILE,     1, '', None], [gSTR_BLANK,         1, '', None],
   [gSTR_COLS_HDR,      0, '', None],
   [gSTR_COLS_CHOSEN,   1, '', None],
   [gSTR_COLS_ALL,      1, '', None],
   [gSTR_COLS_MAIN,     1, '', None], [gSTR_BLANK,         1, '', None],
   [gSTR_COLS_BASE,     1, '', None],
   [gSTR_COLS_MOTION,   1, '', None], [gSTR_BLANK,         1, '', None],
   [gSTR_COND_HDR,      0, '', None],
   [gSTR_COND_CHOSEN,   1, '', None],
   [gSTR_COND_ALL,      1, '', None],
   [gSTR_COND_MAIN,     1, '', None], [gSTR_BLANK,         1, '', None],
   [gSTR_COND_M_BASE,   1, '', None],
   [gSTR_COND_M_MOT,    1, '', None],
   [gSTR_COND_BASE_MOT, 1, '', None]
]

# ======================================================================
# main graphical user interface class
# 
# Most call-back (cb_*) functions immediately call GUI (gui_*) function
# to do the work.  The GUI functions do not require an event parameter,
# and can therefore be called any time, once the main XmatGUI is running,
# and not just from user call-backs.

class XmatGUI(wx.App):
   def OnInit(self):
      return True

   def init_gui(self, XM=None, verb=1):
      """init the gui, possibly given XmatInterface and verb values
     
         Note that the top-level GUI will contain nothing but the MainFrame,
         which will store the GUI and XmatInterface data."""

      if not XM: XM = UIX.XmatInterface(verb)

      self.Gframe = MainFrame(None, -1, "xmat_tool.py", xinter=XM)
      self.Gframe.Show(True)
      self.SetTopWindow(self.Gframe)

class MainFrame(wx.Frame):
   """GUI information will be stored in this MainFrame, while user
      information will be stored in the (contained) XmatInterface, XM."""

   def __init__(self, parent, ID, title, xinter=None):
      """initialize with XmatInterface """
      wx.Frame.__init__(self, parent, ID, title, wx.DefaultPosition, (500,400))

      if not xinter:
         print('** cannot init MainFrame without XmatInterface')
         return False

      # init all local variables
      self.XM = xinter                  # XmatInterface
      self.help_frame     = None        # Help window
      self.hist_frame     = None        # History window
      self.plotx_frame    = None
      self.plot_as_one    = 0
      self.plotx_cols     = []
      self.plot1D_frame   = None
      self.plot1D_redo    = 0           # do we need to re-create the plot
      self.plotfit_frame  = None
      self.cormat_grid    = None

      self.fixed_font     = None        # facename of fixed-width font
      self.textwin_size   = None        # compute default size for text window

      self.choicectrl     = None        # column choice textctrl

      self.CreateStatusBar()

      self.add_menubar()
      self.assign_fixed_font()
      self.add_text_widgets()

      self.Bind(wx.EVT_CLOSE, self.cb_exit)

      if self.XM.verb > 2: print('-- MainFrame initialized')

   # ------------------------------------------------------------
   # fill the main frame with descriptive text widgets
   #
   # column choice line, and then an array of strings and values
   def add_text_widgets(self):

      panel1 = wx.Panel(self, -1)
      st = wx.StaticText(panel1, -1, '  Choose Matrix Columns : ')
      self.choicectrl = wx.TextCtrl(panel1, -1, size=(160,-1))
      button = wx.Button(panel1, ID_APPLY_CHOICE, 'apply')
      button.SetForegroundColour('red')
      panel1.Bind(wx.EVT_BUTTON, self.cb_apply_col_choice, id=ID_APPLY_CHOICE)
      
      box = wx.BoxSizer(wx.HORIZONTAL)
      box.Add(st, 1, wx.CENTRE)
      box.Add(self.choicectrl, 1, wx.CENTRE)
      box.Add(button, wx.CENTRE)
      panel1.SetSizer(box)
      panel1.Refresh()
      
      # make GridSizer for the rest of the table
      panel2 = wx.Panel(self, -1)
      self.tw_stlist = []
      tlen = len(g_textlist)
      if tlen != len(g_textnames): # be sure
         print('*** textlist lengths do not match')
         sys.exit(1)

      gs = wx.GridSizer(tlen, 2, 5, 5)
      for data in g_textlist:
         # add label
         st = wx.StaticText(panel2, -1, data[0])
         # need to find fixed with font
         # st.SetFont(self.fixed_font)
         gs.Add(st, flag=wx.EXPAND, border=4)

         # add and store data section
         st = wx.StaticText(panel2, -1, '')
         self.tw_stlist.append(st)
         gs.Add(st, flag=wx.EXPAND, border=4)
      panel2.SetSizer(gs)

      # and fill in sizer
      sizer = wx.BoxSizer(wx.VERTICAL)
      if g_py_ver == '2': sizer.AddSpacer((20,20))
      else:               sizer.AddSpacer(20)
      sizer.Add(panel1, 0)
      if g_py_ver == '2': sizer.AddSpacer((20,20))
      else:               sizer.AddSpacer(20)
      sizer.Add(panel2, 0)
      self.SetSizerAndFit(sizer)

      # per line
      # self.CreateStatusBar(1)
     

   # ------------------------------------------------------------
   # apply user-specified options
   def apply_options(self):
      """call this to update GUI from UIX data"""

      for opt in self.XM.gui_opts:
         if opt.name == '-gui_plot_xmat_as_one':
            self.plot_as_one = 1
         else:
            print("** unknown -gui option '%s'" % opt.name)

      # in case either has previously been set...
      self.update_textlist_from_xmat()
      self.update_textlist_from_1D()
      self.check_plot_as_one.Check(self.plot_as_one)

   # ------------------------------------------------------------
   # main callback to apply new choice of columns
   def cb_apply_col_choice(self, event):

      ret = self.XM.set_cols_from_string(self.choicectrl.GetValue())
      if ret == None:
         self.update_textlist_from_xmat()
      else:
         self.popup_warning(ret)

      return

   # ------------------------------------------------------------
   # convert system font to fixed-width, and save as self.fixed_font
   def assign_fixed_font(self):
      # rcr - ponder how to find fixed with font...
      if g_py_ver == '2':
        font = wx.SystemSettings_GetFont(wx.SYS_SYSTEM_FONT)
        self.set_fixed_width_font(font) # and set
      # else: pass

   # ------------------------------------------------------------
   # menu bar
   def add_menubar(self):
      menubar = wx.MenuBar()
      # File menu
      menu = wx.Menu()
      menu.Append(ID_LOAD_XMAT, "load X &matrix", "load X matrix from file")
      menu.Append(ID_LOAD_1D, "load &1D file", "load 1d time series")
      menu.Append(ID_EXIT, "E&xit", "terminate program")
      menubar.Append(menu, "&File")
      # Show menu
      menu = wx.Menu()
      menu.Append(ID_SHOW_XMAT, "Show X&mat", "Show info about Xmat")
      menu.Append(ID_SHOW_XMAT_CONDS,
                  "Show Xmat co&nds", "Show Xmat condition numbers")
      menu.Append(ID_SHOW_CORMAT, "Show X-&Cormat",
                                  "Show correlation matrix (of X-matrix)")
      menu.Append(ID_SHOW_CORMAT_EVIL, "Show X-Cormat &Warnings",
                         "Show questionable entries in correlation matrix")
      menu.Append(ID_SHOW_COSMAT, "Show X-Co&smat",
                                  "Show cosine matrix (of X-matrix)")
      menu.Append(ID_SHOW_COSMAT_EVIL, "Show X-Cosmat Warnings",
                         "Show questionable entries in cosine matrix")
      menu.Append(ID_SHOW_1D, "Show &1D", "Show info about time series")
      menu.Append(ID_SHOW_FIT_BETAS, "Show &Fit Betas",
                        "Show info about the Betas of the fitts")
      menubar.Append(menu, "&Show")
      # Plot menu
      menu = wx.Menu()
      menu.Append(ID_PLOT_XMAT, "Plot X&mat", "Graph selected X matrix columns")
      self.check_plot_as_one =  \
           menu.Append(ID_PLOT_AS_ONE, "   plot Xmat as &one", kind=wx.ITEM_CHECK)
                  # help='plot all regressors in one graph', kind=wx.ITEM_CHECK)
      # maintain the check box in case this is updated externally
      self.check_plot_as_one.Check(self.plot_as_one)
      menu.Append(ID_PLOT_1D, "Plot &1D", "Graph 1D time series")
      menu.Append(ID_PLOT_BEST_FIT, "Plot Best &Fit",
            "Graph best fit of selected X matrix columns against 1D array")
      menu.Append(ID_PLOT_CORMAT, "Graph &Corr Mat", "Graph Correlation Matrix")
      menubar.Append(menu, "&Plot")
      # Opt menu
      menu = wx.Menu()
      menu.Append(ID_OPT_CORMAT_CUT, "set co&rmat cutoff",
                              "set cutoff for correlation matrix warnings")
      menu.Append(ID_OPT_COSMAT_CUT, "set co&smat cutoff",
                              "set cutoff for cosine matrix warnings")
      menu.Append(ID_OPT_VERB, "set &verb level",
                              "set verbose level")
      menubar.Append(menu, "&Options")
      # Help menu
      menu = wx.Menu()
      menu.Append(ID_ABOUT, "&About", "information about this program")
      menu.Append(ID_HELP, "Help (&GUI)", "help using graphical user interface")
      menu.Append(ID_HELP_CMD, "Help (&Command-line)", "help for command-line")
      menu.Append(ID_HIST, "H&istory", "program history")
      menubar.Append(menu, "&Help")

      self.SetMenuBar(menubar)

      # ------------------------------
      # set menu call-backs
      self.set_event_handler(ID_EXIT, self.cb_exit)
      self.set_event_handler(ID_ABOUT, self.cb_about)
      self.set_event_handler(ID_HELP, self.cb_help)
      self.set_event_handler(ID_HELP_CMD, self.cb_help_cmd)
      self.set_event_handler(ID_HIST, self.cb_history)

      self.set_event_handler(ID_LOAD_XMAT, self.cb_load_mat)
      self.set_event_handler(ID_LOAD_1D, self.cb_load_mat)

      self.set_event_handler(ID_SHOW_XMAT, self.cb_show_mat)
      self.set_event_handler(ID_SHOW_XMAT_CONDS, self.cb_show_xmat_conds)
      self.set_event_handler(ID_SHOW_CORMAT, self.cb_show_xmat_cormat)
      self.set_event_handler(ID_SHOW_CORMAT_EVIL, self.cb_show_xmat_cormat_warns)
      self.set_event_handler(ID_SHOW_COSMAT, self.cb_show_xmat_cosmat)
      self.set_event_handler(ID_SHOW_COSMAT_EVIL, self.cb_show_xmat_cosmat_warns)
      self.set_event_handler(ID_SHOW_1D, self.cb_show_mat)
      self.set_event_handler(ID_SHOW_FIT_BETAS, self.cb_show_fit_betas)

      self.set_event_handler(ID_PLOT_XMAT, self.cb_plot_mat)
      self.set_event_handler(ID_PLOT_AS_ONE, self.cb_plot_as_one)
      self.set_event_handler(ID_PLOT_1D, self.cb_plot_1D)
      self.set_event_handler(ID_PLOT_BEST_FIT, self.cb_plot_fit)
      self.set_event_handler(ID_PLOT_CORMAT, self.cb_graph_corr_mat)

      self.set_event_handler(ID_OPT_CORMAT_CUT, self.cb_set_cormat_cutoff)
      self.set_event_handler(ID_OPT_COSMAT_CUT, self.cb_set_cosmat_cutoff)
      self.set_event_handler(ID_OPT_VERB, self.cb_set_verb)

   def set_event_handler(self, cbid, callback):
      if g_py_ver == '2':
         wx.EVT_MENU(self, cbid, callback)
      else:
         wx.EvtHandler.Bind(self, wx.EVT_MENU, callback, id=cbid)

   # ----------------------------------------------------------------------
   # In general, the cb_ functions are the callbacks that call the respective
   # action functions.  Those action functions, which depend only on the main
   # class (and not the event, say) are driving functions that can be called
   # from elsewhere.  For example, to close a window, plot, or possibly even
   # drive the program from the command line (not sure if I'll do that).

   def cb_show_mat(self, event):        # gui event

      id = event.GetId() # xmat or 1D
      if id == ID_SHOW_XMAT:
         self.gui_show_xmat()
      elif id == ID_SHOW_1D:
         self.gui_show_1D()

   def gui_show_xmat(self):
      """create a text window containing show() text for the X matrix"""
      if self.XM.matX:
         mstr = self.XM.matX.make_show_str()
         title = os.path.basename(self.XM.fname_mat)
      else:
         self.popup_warning("please load an X matrix, first")
         return
      self.show_text_window(mstr, title)

   def gui_show_1D(self):
      """create a text window containing show() text for the 1D time series"""
      if self.XM.mat1D:
         mstr = self.XM.mat1D.make_show_str()
         title = os.path.basename(self.XM.fname_1D)
      else:
         self.popup_warning("please load a 1D time series, first")
         return
      self.show_text_window(mstr, title)

   def cb_show_fit_betas(self, event):
      self.gui_show_fit_betas()

   def gui_show_fit_betas(self):
      """create a text window describing the fit betas"""

      clist = self.XM.col_list
      if not clist: clist = list(range(len(self.XM.col_list)))

      # ignore failure, to show error string in text window
      rv, rstr = self.XM.make_matrix_fit_betas_string(clist)
      title = 'Matrix to 1D Fit Betas'
      self.show_text_window(rstr, title)

   def cb_show_xmat_cormat(self, event):
      self.gui_show_xmat_cormat()

   def gui_show_xmat_cormat(self):

      mat = self.XM.matX

      if not mat:
         self.popup_warning("please load an X matrix, first")
         return
      elif not mat.ready:
         return

      if not mat.cormat_ready:    # then set it
         mat.set_cormat()

      # mstr = 'Correlation Matrix:\n\n%s\n' % str(mat.cormat)
      rv, mstr = self.XM.make_cormat_string()

      title = os.path.basename(self.XM.fname_mat) + ' correlation matrix'

      self.show_text_window(mstr, title, wrap=0)

   def cb_show_xmat_cosmat(self, event):

      mat = self.XM.matX

      if not mat:
         self.popup_warning("please load an X matrix, first")
         return
      elif not mat.ready:
         return

      if not mat.cormat_ready:    # then set it
         mat.set_cormat()

      rv, mstr = self.XM.make_cosmat_string()

      title = os.path.basename(self.XM.fname_mat) + ' cosine matrix'

      self.show_text_window(mstr, title, wrap=0)

   def cb_show_xmat_cormat_warns(self, event):
      self.gui_show_xmat_cormat_warns()

   def gui_show_xmat_cormat_warns(self):

      mat = self.XM.matX

      if not mat:
         self.popup_warning("please load an X matrix, first")
         return
      elif not mat.ready:
         return

      rv, mstr = self.XM.make_cormat_warnings_string()

      title = os.path.basename(self.XM.fname_mat) + ' correlation warnings'

      self.show_text_window(mstr, title, wrap=0)

   def cb_show_xmat_cosmat_warns(self, event):

      mat = self.XM.matX

      if not mat:
         self.popup_warning("please load an X matrix, first")
         return
      elif not mat.ready:
         return

      rv, mstr = self.XM.make_cosmat_warnings_string()

      title = os.path.basename(self.XM.fname_mat) + ' cosine warnings'

      self.show_text_window(mstr, title, wrap=0)

   def cb_show_xmat_conds(self, event): # gui event
      self.gui_show_xmat_conds()

   def gui_show_xmat_conds(self):

      mat = self.XM.matX

      if not mat:
         self.popup_warning("please load an X matrix, first")
         return

      mstr = mat.make_show_conds_str()

      title = os.path.basename(self.XM.fname_mat) + ' condition numbers'

      self.show_text_window(mstr, title)

   def cb_graph_corr_mat(self, event):        # gui event
      self.gui_graph_corr_mat()

   def gui_graph_corr_mat(self):

      mat = self.XM.matX

      if not mat:
         self.popup_warning("please load an X matrix, first")
         return

      if self.cormat_grid :
         self.cormat_grid.Raise()
         return

      if not mat.cormat_ready: mat.set_cormat() # initialize?

      if self.XM.verb > 1:
         print('++ showing cormat, size = %s' % str(mat.cormat.shape))

      self.cormat_grid = MatFrame(mat.cormat,'correlation matrix',
                                  rlabels=mat.labels, rind=1, corr_colors=1)
      self.cormat_grid.Bind(wx.EVT_CLOSE, self.cb_close_grid)

      self.cormat_grid.Show(True)

   def cb_close_grid(self, event):
      if self.cormat_grid:
         if self.XM.verb > 2: print('-- destroying cormat_grid')
         self.cormat_grid.Destroy()
         self.cormat_grid = None
         self.XM.cleanup_memory()

   def cb_plot_as_one(self, event):        # gui event
      self.plot_as_one = 1 - self.plot_as_one
      if self.XM.verb > 1:
         print('++ toggling plot_as_one to %d' % self.plot_as_one)

   def cb_plot_mat(self, event):        # gui event
      self.gui_plot_xmat()

   def gui_plot_xmat(self):

      if not self.XM.matX:
         self.popup_warning("please load an X matrix, first")
         return

      cols = self.XM.col_list

      if not UTIL.lists_are_same(self.plotx_cols, cols):
         self.gui_close_plot()    # destroy old one
         
      elif self.plotx_frame:      # otherwise, if already open, just raise
         self.plotx_frame.Raise()
         return

      if self.XM.verb > 1: print('++ showing xmat with cols : %s' % str(cols))

      if len(cols) < 1:
         self.popup_warning("no columns chosen for X matrix")
         return

      self.plotx_frame = CanvasFrame(title='X-matrix', as_one=self.plot_as_one,
                                     verb=self.XM.verb)
      self.plotx_cols  = cols           # keep track of what was used

      self.plotx_frame.Show(True)

      self.plotx_frame.plot_mat_by_cols(self.XM.matX, cols)

      self.plotx_frame.Bind(wx.EVT_CLOSE, self.cb_close_plot)

      if self.XM.verb > 1: print('-- xmat done')

   def cb_plot_fit(self, event):
      self.gui_plot_fit()

   def gui_plot_fit(self):

      if not self.XM.matX:
         self.popup_warning("please load an Xmatrix, first")
         return

      if not self.XM.mat1D:
         self.popup_warning("please load a 1D timeseries, first")
         return

      self.gui_close_plot_fit() # nuke any existing frame, regardless
         
      rv, mesg = self.XM.fit_xmat_to_1D(self.XM.col_list)
      if rv:
         self.popup_warning("X-matrix fitting failed\n\n(%s)" % mesg)
         return

      bothmats = self.XM.mat1D.copy()
      bothmats.nruns = self.XM.matX.nruns
      bothmats.run_len = self.XM.matX.run_len
      rv = bothmats.append([self.XM.matfit])
      if rv:
         self.popup_warning("X-matrix appending failed")
         return

      if self.XM.verb > 1: print('++ showing fit plot')

      self.plotfit_frame = CanvasFrame(title='fit time series',
                                       verb=self.XM.verb)
      self.plotfit_redo = 0

      self.plotfit_frame.Show(True)

      self.plotfit_frame.plot_matlist([bothmats], 'time series and fit',
                                      ftcolors=1)
      del(bothmats)

      self.plotfit_frame.Bind(wx.EVT_CLOSE, self.cb_close_plot_fit)

      if self.XM.verb > 1: print('-- plot fit done')

   def cb_plot_1D(self, event):
      self.gui_plot_1D()

   def gui_plot_1D(self):

      if not self.XM.mat1D:
         self.popup_warning("please load a 1D timeseries, first")
         return

      if self.plot1D_redo:          # need update, so destroy old old
         self.gui_close_plot_1D()
         
      elif self.plot1D_frame:     # otherwise, if already open, just raise
         self.plot1D_frame.Raise()
         return

      if self.XM.verb > 1: print('++ showing 1D plot')

      self.plot1D_frame = CanvasFrame(title='1D time series',verb=self.XM.verb)
      self.plot1D_redo = 0

      self.plot1D_frame.Show(True)

      self.plot1D_frame.plot_mat_by_cols(self.XM.mat1D, [0])

      self.plot1D_frame.Bind(wx.EVT_CLOSE, self.cb_close_plot_1D)

      if self.XM.verb > 1: print('-- plot 1D done')

   def cb_set_cormat_cutoff(self, event):
      dlg = wx.TextEntryDialog(self, 'correlation warning cutoff',
                                     'cormat cutoff')
      val = self.XM.cormat_cut
      if val < 0.0: val = UIX.g_cormat_cut
      dlg.SetValue('%.3f' % val)

      if dlg.ShowModal() == wx.ID_OK:
         try: val = float(dlg.GetValue())
         except: pass
         else:
            self.XM.cormat_cut = val
            if self.XM.verb > 1: print('++ applying new cormat_cut = %s' % val)
      dlg.Destroy()

   def cb_set_cosmat_cutoff(self, event):
      dlg = wx.TextEntryDialog(self, 'cosine warning cutoff',
                                     'cosmat cutoff')
      val = self.XM.cosmat_cut
      if val < 0.0: val = UIX.g_cosmat_cut
      dlg.SetValue('%.3f' % val)

      if dlg.ShowModal() == wx.ID_OK:
         try: val = float(dlg.GetValue())
         except: pass
         else: self.XM.cosmat_cut = val
         if self.XM.verb > 1: print('++ applying new cosmat_cut = %s' % val)
      dlg.Destroy()

   def cb_set_verb(self, event):
      dlg = wx.TextEntryDialog(self, 'set verbose level',
                                     'verbose level')
      val = self.XM.verb
      dlg.SetValue('%d' % val)

      if dlg.ShowModal() == wx.ID_OK:
         try: val = int(dlg.GetValue())
         except: pass
         else:
            self.XM.verb = val
            if self.XM.verb > 1: print('++ applying new verb = %s' % val)
      dlg.Destroy()

   def cb_load_mat(self, event):        # gui event
      if g_py_ver == '2':
         fname = wx.FileSelector(default_path=os.getcwd(),
                                 default_extension='1D', wildcard='*.1D')
      else:
         fname = wx.FileSelector('select X-matrix', default_path=os.getcwd(),
                                 default_extension='1D', wildcard='*.1D')
      if fname == '': return

      id = event.GetId()
      if id == ID_LOAD_XMAT:
         self.gui_load_xmat(fname)
      else:
         self.gui_load_1D(fname)

   def gui_load_xmat(self, fname):
      if self.XM.verb > 2:
         print('-- gui_load_xmat, file = %s' % fname)

      rv = self.XM.set_xmat(fname)

      if rv or not self.XM.matX:  # if failure
         self.popup_warning("failed to load X matrix from '%s'" % fname)
         return

      # fill screen display data for this matrix
      self.update_textlist_from_xmat()

      badlist = self.XM.matX.list_cormat_warnings(cutoff=1.0)
      if badlist:
         self.popup_warning("duplicate regressors found in matrix\n\n"  \
                         "'Show: X-Cormat Warnings' for complete list")
         del(badlist)

   def gui_load_1D(self, fname):
      if self.XM.verb > 2:
         print('-- gui_load_xmat, file = %s' % fname)

      rv = self.XM.set_1D(fname)

      if rv or not self.XM.mat1D:
         self.popup_warning("failed to load 1D file '%s'" % fname)
         return

      self.plot1D_redo = 1      # might need to redo the plot window

      # fill screen display data for the choice
      self.update_textlist_from_1D()

   def update_textlist_from_1D(self):
      """given the 1D file, fill screen data list"""
      if not self.XM.mat1D: return

      mat = self.XM.mat1D

      ind = self.textlist_index(gSTR_1D_FILE)
      if ind >= 0: self.tw_stlist[ind].SetLabel(os.path.basename(mat.fname))


   def update_textlist_from_xmat(self):
      """given the X matrix and columns, fill screen data list"""
      if not self.XM.matX: return

      matx = self.XM.matX

      ccols = self.XM.col_list
      if self.XM.verb > 1: print('++ updating text for columns: %s' % ccols)

      ind = self.textlist_index(gSTR_XMAT_FILE)
      if ind >= 0: self.tw_stlist[ind].SetLabel(os.path.basename(matx.fname))

      # show columns

      self.set_textlist_label(self.textlist_index(gSTR_COLS_CHOSEN),
                              str=UTIL.encode_1D_ints(ccols))

      self.set_textlist_label(self.textlist_index(gSTR_COLS_ALL),
                              str=UTIL.encode_1D_ints(list(range(matx.ncols))))

      self.set_textlist_label(self.textlist_index(gSTR_COLS_MAIN),
                  str=UTIL.encode_1D_ints(matx.cols_by_group_list([],allroi=1)))

      self.set_textlist_label(self.textlist_index(gSTR_COLS_BASE),
                  str=UTIL.encode_1D_ints(matx.cols_by_group_list([-1])))

      self.set_textlist_label(self.textlist_index(gSTR_COLS_MOTION),
                  str=UTIL.encode_1D_ints(matx.cols_by_group_list([0])))

      # show select condition numbers

      cond = matx.cond_num_by_cols(ccols)
      self.set_textlist_label(self.textlist_index(gSTR_COND_CHOSEN), val=cond)

      cond = matx.cond_num_by_cols(list(range(matx.ncols)))
      self.set_textlist_label(self.textlist_index(gSTR_COND_ALL), val=cond)

      cond = matx.cond_num_by_cols(matx.cols_by_group_list([],allroi=1))
      self.set_textlist_label(self.textlist_index(gSTR_COND_MAIN), val=cond)

      cond = matx.cond_num_by_cols(matx.cols_by_group_list([-1],allroi=1))
      self.set_textlist_label(self.textlist_index(gSTR_COND_M_BASE), val=cond)

      cond = matx.cond_num_by_cols(matx.cols_by_group_list([0],allroi=1))
      self.set_textlist_label(self.textlist_index(gSTR_COND_M_MOT), val=cond)

      cond = matx.cond_num_by_cols(matx.cols_by_group_list([-1,0]))
      self.set_textlist_label(self.textlist_index(gSTR_COND_BASE_MOT), val=cond)

   def set_textlist_label(self, ind, str='', val=-666.6, vlimit=666):
      """set the Label for the given index to the string or condition number
            ind         : index int tw_stlist
            str         : string to set
            val         : float value to fill with
            vlimit      : if val > vlimit, color in red
         If str='' and val=-666.6, then clear the label."""

      if ind < 0 or ind > len(self.tw_stlist):
         if self.XM.verb > 1: print('** STL: index %d out of range' % ind)
         return

      if val != -666.6:
         if val > 10e6: self.tw_stlist[ind].SetLabel('infinite?')
         else:          self.tw_stlist[ind].SetLabel('%.1f' % val)
         if val > vlimit: self.tw_stlist[ind].SetForegroundColour('red')
         else:            self.tw_stlist[ind].SetForegroundColour('black')
      else: self.tw_stlist[ind].SetLabel(str)

   def textlist_index(self, name):
      """return the index of g_textlist for the given name (-1 on failure)"""
      for ind in range(len(g_textlist)):
         if g_textlist[ind][0] == name: return ind
      return -1

   def cb_close_plot(self, event):
      self.gui_close_plot()

   def gui_close_plot(self):
      if self.plotx_frame:
         if self.XM.verb > 2: print('-- destroying plotx')
         self.plotx_frame.Destroy()
         self.plotx_frame = None
         del(self.plotx_cols)
         self.plotx_cols = []
         self.XM.cleanup_memory()

   def cb_close_plot_1D(self, event):
      self.gui_close_plot_1D()

   def gui_close_plot_1D(self):
      if self.plot1D_frame:
         if self.XM.verb > 2: print('-- destroying plot1D')
         self.plot1D_frame.Destroy()
         self.plot1D_frame = None
         self.plot1D_redo = 0

   def cb_close_plot_fit(self, event):
      self.gui_close_plot_fit()

   def gui_close_plot_fit(self):
      if self.plotfit_frame:
         if self.XM.verb > 2: print('-- destroying plotfit')
         self.plotfit_frame.Destroy()
         self.plotfit_frame = None

   def cb_exit(self, event):
      if self.XM.verb > 1: print('exiting GUI...')
      if self.plotx_frame:
         self.cb_close_plot(1)
      self.Destroy()
      sys.exit(0)

   def cb_about(self, event):           # gui event
      dlg = wx.MessageDialog(self,
                "%s\n\n"
                "This program is for inspecting\n"
                "and evaluating X matrices." % UIX.g_version,
                "About Program", wx.OK | wx.ICON_INFORMATION)
      dlg.ShowModal()
      dlg.Destroy()

   def cb_help(self, event):
      self.gui_show_help()

   def gui_show_help(self):
      self.show_text_window(UIX.gui_help_string, 'Program Help (for GUI)')

   def cb_help_cmd(self, event):
      self.gui_show_help_cmd()

   def gui_show_help_cmd(self):
      self.show_text_window(UIX.g_help_string,'Program Help (for command line)')

   def cb_history(self, event):
      self.gui_show_history()

   def gui_show_history(self):
      self.show_text_window(UIX.g_history, 'Program History')

   def popup_warning(self, mesg):
      dlg = wx.MessageDialog(self, mesg, "warning: ", wx.OK|wx.ICON_EXCLAMATION)
      dlg.ShowModal()
      dlg.Destroy()

   def show_text_window(self, text, title='', wrap=1, wrapstr='\n'):
      if not text: return
      if len(text) < 1: return

      if wrap: text = UTIL.add_line_wrappers(text, wrapstr)

      text_frame = wx.Frame(self, -1, title)

      # ----------------------------------------
      # create TextCtrl portion
      ctrl = wx.TextCtrl(text_frame, -1, style = wx.TE_MULTILINE)
      ctrl.SetValue(text)

      # adjust the font style to be fixed-width
      if g_py_ver == '2':
         ctrl.SetFont(self.fixed_font)

      # note the size for 80 character lines
      size = self.get_textwin_size(ctrl)

      # move to top
      ctrl.SetInsertionPoint(0)

      text_frame.ctrl = ctrl    # save it
      # ----------------------------------------

      # add a horizontal line
      # hline=wx.StaticLine(text_frame, -1, (-1,-1), (-1,-1), wx.LI_HORIZONTAL)

      # fill a sizer with buttons
      
      hsizer = wx.BoxSizer(wx.HORIZONTAL)  # was wx.NewId()
      b1 = wx.Button(text_frame, ID_SAVE, "&Save Text", (-1,-1), wx.DefaultSize)
      b2 = wx.Button(text_frame, ID_CLOSE, "&Close", (-1,-1), wx.DefaultSize)
      hsizer.Add(b1, 0)
      hsizer.Add(b2, 0, wx.LEFT, 10)

      # and set button callbacks

      text_frame.Bind(wx.EVT_BUTTON, self.cb_save_text, id=ID_SAVE)
      text_frame.Bind(wx.EVT_BUTTON, self.cb_close_win, id=ID_CLOSE)

      # ----------------------------------------
      #  put it all in a vertical sizer

      vspace = 5
      vsizer = wx.BoxSizer(wx.VERTICAL)
      vsizer.Add(hsizer, 0, wx.ALIGN_LEFT | wx.ALL, vspace)
      # vsizer.Add(hline, 0, wx.GROW | wx.ALL, vspace)
      vsizer.Add(ctrl, 1, wx.GROW | wx.ALL, vspace)

      text_frame.SetSizerAndFit(vsizer)

      text_frame.SetSize(self.textwin_size)

      text_frame.Show(True)

   def cb_tw_keydown(self, event):      # rcr - unused for now
      keycode = event.GetKeyCode()
      # maybe close the window
      if keycode == wx.WXK_ESCAPE or keycode == 'q' or keycode == 'Q':
         win = event.GetEventObject()
         win = win.GetTopLevelParent()
         win.Close()

   def get_textwin_size(self, ctrl):
      """given a TextCtrl window, set and return self.textwin_size for an
         80 column window (multiply default height be 1.5)"""

      if self.textwin_size: return self.textwin_size

      # else, compute and set it
      str80 = '0123456789012345678901234567890123456789'        \
              '0123456789012345678901234567890123456789'
      width,height,d,l = ctrl.GetFullTextExtent(str80)

      width  *= 1.05    # add 4 chars for scrollbar
      height *= 25      # 25 lines of text

      if self.XM.verb > 2:
         print("-- setting default textwin size to (%d,%d)" % (width,height))

      self.textwin_size = (width,height)

      ctrl.SetSize((width,height))

   def cb_save_text(self, event):
      win = event.GetEventObject()
      win = win.GetTopLevelParent()
      if self.XM.verb > 3:
         print("-- cb: save text from window '%s' ..." % win.GetTitle())
      try:
         ctrl = win.ctrl
         text = ctrl.GetValue()
      except:
         print('** no ctrl/text for save_text window')
         return

      fname = wx.SaveFileSelector('text', '')
      if fname == '': return

      if self.XM.verb > 1:
         print("-- saving text from window '%s' to file '%s'" %   \
               (win.GetTitle(), fname))
      UTIL.write_text_to_file(fname, text)

   def cb_close_win(self, event):       # no gui_ for generic close
      win = event.GetEventObject()
      win = win.GetTopLevelParent()
      if self.XM.verb > 2:
         print("-- closing window '%s' ..." % win.GetTitle())
      win.Destroy()

   def set_fixed_width_font(self, font):
      """find some fixed-width font, and store it as self.fixed_font"""

      if self.fixed_font: return self.fixed_font    # been here, did this

      if self.XM.verb > 1: print('-- testing for fixed-width font...')

      if font.IsFixedWidth():   # nothing to do but save for later
         return self.set_fixed_font(font)

      if self.XM.verb > 1: print('-- trying specific families ...')

      for family in [wx.NORMAL, wx.TELETYPE]:   # try different families
         font.SetFamily(family)
         if font.IsFixedWidth():
            return self.set_fixed_font(font)

      if self.XM.verb > 1: print('-- trying specific facenames ...')

      for name in ['Courier', 'Courier New', 'MiscFixed', 'Fixed',
                   'Monospace', 'LucidaTypewriter']:
         if not font.SetFaceName(name):         # doesn't exist, continue
            continue
         if font.IsFixedWidth():                # verify
            return self.set_fixed_font(font)

      if self.XM.verb > 1: print('-- searching all facenames for first...')

      # no luck, search through faces and take the first one
      # (but don't further corrupt passed font)
      fontenum = wx.FontEnumerator()
      fontenum.EnumerateFacenames()
      facenames = fontenum.GetFacenames()
      for name in facenames:
         tfont = wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL,
                         underline=False, face=name)
         if tfont.IsFixedWidth():
            return self.set_fixed_font(tfont)

      # failure, use what we have
      print('** cannot find fixed-width font, sorry...')
      return self.set_fixed_font(font)

   def set_fixed_font(self, font):
      if self.XM.verb > 1:
         print('++ setting font to face=%s, family=%s, style=%s, fixed=%s' % \
               (font.GetFaceName(), font.GetFamily(), font.GetStyle(),
                font.IsFixedWidth()))
      self.fixed_font = font
      return font

# ======================================================================
# text window combo class
class TextCombo(wx.Panel):
   def __init__(self, parent, id):
      wx.Panel.__init__(self, parent, id)

      # ----------------------------------------
      # create TextCtrl portion
      self.ctrl = wx.TextCtrl(self, -1, style = wx.TE_MULTILINE)

      # ----------------------------------------

      # add a horizontal line
      hline = wx.StaticLine(self, -1, (-1,-1), (-1,-1), wx.LI_HORIZONTAL)

      # fill a sizer with buttons
      
      hsizer = wx.BoxSizer(wx.HORIZONTAL)
      b1 = wx.Button(self, wx.NewId(), "&OK", (-1, -1), wx.DefaultSize)
      b2 = wx.Button(self, wx.NewId(), "&Cancel", (-1, -1), wx.DefaultSize)
      hsizer.Add(b1, 0)
      hsizer.Add(b2, 0, wx.LEFT, 10)

      # ----------------------------------------
      #  put it all in a vertical sizer

      vspace = 5
      vsizer = wx.BoxSizer(wx.VERTICAL)
      vsizer.Add(self.ctrl, 0, wx.GROW | wx.ALL, vspace)
      vsizer.Add(hline, 0, wx.GROW | wx.ALL, vspace)
      vsizer.Add(hsizer, 0, wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM, vspace)

      self.SetSizerAndFit(vsizer)

# ======================================================================
# matrix grid (show matrix in a spreadsheet format)
#

class MatFrame(wx.Frame):
   def __init__(self, mat, title='', rlabels=[], rind=0, clabels=[],
                corr_colors=0):
      wx.Frame.__init__(self, None, -1, title, size=(700,500))
      self.grid = MatGrid(self, mat, rlabels, rind, clabels, corr_colors)

class MatGrid(wx.grid.Grid):
   def __init__(self, parent, mat, rlabels=[], rind=0,
                clabels=[], corr_colors=0):
      """display a Numpy matrix in a spreadsheet
            required parameters:
                parent      : parent widget
                mat         : Numpy matrix
            optional parameters:
                title       : window title
                rlabels     : row labels
                rind        : add indices to row labels
                clabels     : column labels
                corr_colors : display correlation matrix colors
      """

      wx.grid.Grid.__init__(self, parent, wx.ID_ANY)
      self.parent = parent

      # set rows and cols
      self.nrows, self.ncols = mat.shape
      self.CreateGrid(self.nrows, self.ncols)

      # general attributes
      self.attr = wx.grid.GridCellAttr()
      self.attr.SetTextColour('black')

      if rlabels:
         self.SetRowLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE)
         if rind:
            for r in range(len(rlabels)):
               rlabels[r] = '%3d : %s' % (r, rlabels[r])
         lmax = max([len(name) for name in rlabels])
         self.SetRowLabelSize(10*lmax)
         for r in range(len(rlabels)):
            self.SetRowLabelValue(r, rlabels[r])

      if clabels:
         lmax = max([len(name) for name in clabels])
         if lmax < 6: lmax = 6
         for c in range(self.ncols):
            self.SetColSize(c, 10*lmax)
         for c in range(len(clabels)):
           self.SetColLabelValue(c, clabels[c])
      else:
         for c in range(self.ncols):
            self.SetColSize(c, 6*10)
            self.SetColLabelValue(c, str(c))

      self.fill_vals(mat, corr_colors)

   def fill_vals(self, mat, corr_colors=0):
      """fill values from Numpy matrix, color if corr_colors is set"""
      for row in range(self.nrows):
         self.SetRowAttr(row, self.attr)
         for col in range(self.ncols):
            val = mat[row,col]
            if corr_colors:  # show on lower triangle
               if row == col:
                  self.SetCellTextColour(row, col, 'blue')
               elif row > col:
                  self.SetCellTextColour(row, col, self.corr_color(val))
            self.SetCellValue(row, col, '%.3f' % val)
            self.SetReadOnly(row, col, True)
            self.SetCellAlignment(row, col, wx.ALIGN_RIGHT, wx.ALIGN_CENTRE)

   def corr_color(self, value):
      aval = abs(value)
      if aval < 0.4: return 'black'
      if aval < 0.6: return 'green'
      return 'red'
         

# ======================================================================
# plotting canvas class
#
# This canvas is only used for plotting graphs of matrices.

class CanvasFrame(wx.Frame):
   def __init__(self, title='', as_one=0, verb=1):
      wx.Frame.__init__(self, None, -1, title, size=(400,300))
      self.verb   = verb
      self.figure = Figure()
      self.canvas = FigureCanvas(self, -1, self.figure)
      self.sizer = wx.BoxSizer(wx.VERTICAL)
      self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
      self.SetSizer(self.sizer)
      self.Fit()

      self.canvas.mpl_connect('key_press_event', self.cb_keypress)
      self.as_one  = as_one

      self.toolbar = NavigationToolbar2Wx(self.canvas)
      self.toolbar.Realize()
      self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
      self.toolbar.update()

   def cb_keypress(self, event):
      if event.key == 'q':
         self.Close()

   def plot_matlist(self, matlist, title='', ylabels=[], ftcolors=0, verb=1):
      """plot AfniMatrix list, one graph per AfniMatrix
              matlist   - list of AfniMatrix elements
              title     - optional window title
              ylabels   - optional list of ylabel per mat
              ftcolors  - flag: use fit/timeseries colors (black blue)
              verb      - optional verbose level
      """

      if not matlist: return
      nmats = len(matlist)
      if nmats < 1: return

      yformat = FormatStrFormatter('%5.1f')
      matplotlib.rcParams['lines.linewidth'] = 2

      # make a label list, and get max length (among any label[0])
      if ylabels: labels = ylabels
      else:       labels = []
      rlen = 0
      nruns = 0
      maxlen = 0
      for ind in range(nmats):
         if ylabels:                lab = ylabels[ind]
         else:
            if matlist[ind].labels: lab = matlist[ind].labels[0]
            else:                   lab = ''
            labels.append(lab)
         if len(lab) > maxlen: maxlen = len(lab)

         # note run info
         if nruns == 0 and matlist[ind].nruns > 1:
            nruns = matlist[ind].nruns
            rlen = matlist[ind].run_len

      for ind in range(nmats):
         amat = matlist[ind]

         if ind == 0: tstr = title
         else:        tstr = ''

         ax = self.figure.add_subplot(nmats,1,ind+1,title=tstr)
         #if ftcolors and amat.ncols == 2:   # use blue over black?
         #   ax.plot(amat.mat[:,0], 'k')
         #   ax.plot(amat.mat[:,1], 'b')
         ax.plot(amat.mat)

         # apply y tickmarks
         ymin, ymax = ax.get_ylim()
         width = ymax - ymin
         if ymin*ymax < 0.0: ymean = 0.0
         else: ymean = round((ymin+ymax)/2.0,2)

         ax.grid(True)

         ax.yaxis.set_major_formatter(yformat)

         # if there are many graphs, reduce the number of yticks
         if nmats > 10:  ax.set_yticks(N.array([ymin,ymax]))
         elif nmats > 2: ax.set_yticks(N.array([ymin,ymean,ymax]))

         if rlen > 0: ax.set_xticks(N.array([r*rlen for r in range(nruns+1)]))
         if ind < nmats - 1: ax.set_xticklabels([])
         else:               ax.set_xlabel('TRs')

         if labels:
            if nmats > 1:
               ax.set_ylabel('%-*s ' % (maxlen,labels[ind]),
                             rotation='horizontal')
               rv, plist = self.axis_posn(ax)
               if rv == 0: ax.set_position((0.15, plist[1], 0.7, 0.7/nmats))
            else:
               ax.set_ylabel('%s' % labels[ind])
               ax.set_position((0.15, 0.2, 0.7, 0.7))

      self.canvas.draw()

      matplotlib.rcParams['lines.linewidth'] = 1   # reset

   def axis_posn(self, ax):
      """return error code, posn array"""

      # first attempt is to look for simple array return
      try: posn = ax.get_position()
      except:
         if self.verb > 1: print('** failed ax.get_position()')
         return 1, None

      # have list, ready for some return
      if type(posn) == type([]):
         if len(posn) < 4:
            if self.verb > 1:
               print('** get_position returns len %d list' % len(posn))
            return 1, None
         if self.verb > 2: print('-- get_position returns list %s' % \
                         UTIL.float_list_string(posn))
         return 0, posn

      # no list, assume Bbox and expect get_points() to return 2x2 numpy array
      try: plist = posn.get_points()
      except:
         if self.verb > 1: print('** failed posn.get_points()')
         return 1, None

      if type(plist) != type(N.array([])):
         if self.verb > 1: print('** posn.get_points() does not return N.array?')
         return 1, None

      try: pnlist = [plist[0][0], plist[0][1], plist[1][0], plist[1][1]]
      except:
         if self.verb > 1: print('** bad plist shape %s' % plist.shape)
         return 1, None

      if self.verb > 2: print('-- get_position returns Bbox list: %s' % \
                        UTIL.float_list_string(pnlist))

      return 0, pnlist

   def plot_mat_by_cols(self, amat, cols):

      if not cols: return
      ncols = len(cols)
      if ncols < 1: return

      # create reduced matrix and column labels
      mat = amat.mat[:,cols]

      yformat = FormatStrFormatter('%5.1f')
      if amat.labels:
         labels = N.array(amat.labels)
         labels = labels[cols]
         labels = labels.tolist()

         # create axis formatter string

         striplabs = 1
         for label in labels: # strip trailing '#' if indices are all 0
            ind = regress_index(label)
            if ind > 0: striplabs = 0

         maxlen = 0
         for l in range(len(labels)):
            label = labels[l]
            if striplabs:
               labels[l] = labels[l][0:labels[l].rfind('#')]
            if len(labels[l]) > maxlen: maxlen = len(labels[l])

      for i in range(ncols):
         if i == 0: title = '%s [%s]' % (os.path.basename(amat.fname),
                                         UTIL.encode_1D_ints(cols))
         else     : title = ''

         if self.as_one:
            # then only create one axis
            if i == 0: ax = self.figure.add_subplot(1,1,1,title=title)
         else:
            ax = self.figure.add_subplot(ncols,1,i+1,title=title)
         data = mat[:,i]
         ax.plot(data)

         # apply y tickmarks
         ymin, ymax = ax.get_ylim()
         width = ymax - ymin
         if ymin*ymax < 0.0: ymean = 0.0
         else: ymean = round((ymin+ymax)/2.0,2)

         ax.grid(True)

         ax.yaxis.set_major_formatter(yformat)
         amax = round(data.max(),1)

         if self.as_one:                   pass # use default yticks
         elif amax == 1.0 and ymin == 0.0: ax.set_yticks(N.array([ymin,amax]))
         elif ncols > 10:                  ax.set_yticks(N.array([ymin,ymax]))
         else:                       ax.set_yticks(N.array([ymin,ymean,ymax]))

         # now that yticks are set, prevent tight limits
         if not self.as_one:
            if ymin == data.min():
               ymin -= 0.15 * width
               ax.set_ylim((ymin, ymax))

            if ymax == data.max():
               ymax += 0.15 * width
               ax.set_ylim((ymin, ymax))

            if amat.run_len > 10 and amat.nruns > 1:
               ax.set_xticks(N.array([r*amat.run_len
                                      for r in range(int(amat.nruns)+1)]))

         if i < ncols - 1: ax.set_xticklabels([])
         else:             ax.set_xlabel('TRs')

         if self.as_one: ax.set_ylabel('')
         elif amat.labels:
            ax.set_ylabel('%-*s ' % (maxlen,labels[i]), rotation='horizontal')
            rv, plist = self.axis_posn(ax)
            # rcr - fix
            #if rv == 0: ax.set_position((0.2, plist[1], plist[2], plist[3]))
            if rv == 0: ax.set_position((0.15, plist[1], 0.7, 0.7/ncols))

      self.Fit()
      self.canvas.draw()

def regress_index(label):
   """return integer after trailing '#' if found, else -1"""

   ind = label.rfind('#')
   if ind < 0: return -1

   try:    val = int(label[ind+1:-1])
   except: val = -1

   return val

