diff --git a/fsl/data/strings.py b/fsl/data/strings.py
index ffbadc580a64bca2065dabe89e15e187631bf2c7..ece2469269a8ec182ed4ba263d1d23a2f1744340 100644
--- a/fsl/data/strings.py
+++ b/fsl/data/strings.py
@@ -69,6 +69,11 @@ messages = TypeDict({
                                           'calling render directly with '
                                           'this command: \n{}',
 
+    'CanvasPanel.showCommandLineArgs.title'   : 'Scene parameters',
+    'CanvasPanel.showCommandLineArgs.message' : 'Use these parameters on the '
+                                                'command line to recreate '
+                                                'the current scene',
+
     'PlotPanel.screenshot'              : 'Save screenshot',
 
     'PlotPanel.screenshot.error'       : 'An error occurred while saving the '
@@ -155,6 +160,7 @@ actions = TypeDict({
     'LoadColourMapAction' : 'Load custom colour map',
 
     'CanvasPanel.screenshot'              : 'Take screenshot',
+    'CanvasPanel.showCommandLineArgs'     : 'Show command line for scene',
     'CanvasPanel.toggleColourBar'         : 'Colour bar',
     'CanvasPanel.toggleOverlayList'       : 'Overlay list',
     'CanvasPanel.toggleDisplayProperties' : 'Overlay display properties',
diff --git a/fsl/fslview/controls/atlasoverlaypanel.py b/fsl/fslview/controls/atlasoverlaypanel.py
index ddb44e79663a185c9c23e978d5df40a54e289de5..20f2b03e4126f1279267680fc7b5a7bed1f60a7b 100644
--- a/fsl/fslview/controls/atlasoverlaypanel.py
+++ b/fsl/fslview/controls/atlasoverlaypanel.py
@@ -211,6 +211,7 @@ class AtlasOverlayPanel(fslpanel.FSLViewPanel):
                 self.__updateAtlasState(atlasIdx)
 
             msgdlg.ProcessingDialog(
+                self,
                 strings.messages[self, 'loadRegions'].format(atlasDesc.name),
                 buildRegionList).Run()
             
diff --git a/fsl/fslview/overlay.py b/fsl/fslview/overlay.py
index 0989a12427b27c48fdafebf8da4df9b52669ab18..cc26e907337936f5b8b25d65b223ad3a6123a327 100644
--- a/fsl/fslview/overlay.py
+++ b/fsl/fslview/overlay.py
@@ -22,6 +22,7 @@ import fsl.data.featresults as featresults
 import fsl.data.featimage   as fslfeatimage
 import fsl.data.strings     as strings
 import fsl.data.model       as fslmodel
+import fsl.utils.dialog     as fsldlg
 import fsl.fslview.settings as fslsettings
 
 
@@ -226,27 +227,13 @@ def loadOverlays(paths, loadFunc='default', errorFunc='default', saveDir=True):
     # to show the currently loading image
     if defaultLoad:
         import wx
-        loadDlg       = wx.Frame(wx.GetApp().GetTopWindow(), style=0)
-        loadDlgStatus = wx.StaticText(loadDlg, style=wx.ST_ELLIPSIZE_MIDDLE)
-        
-        sizer = wx.BoxSizer(wx.HORIZONTAL)
-        sizer.Add(loadDlgStatus,
-                  border=25,
-                  proportion=1,
-                  flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTRE)
-        loadDlg.SetSizer(sizer)
-        
-        loadDlg.SetSize((400, 100))
-        loadDlg.Layout()
+        loadDlg = fsldlg.SimpleMessageDialog(wx.GetApp().GetTopWindow())
 
     # The default load function updates
     # the dialog window created above
     def defaultLoadFunc(s):
         msg = strings.messages['overlay.loadOverlays.loading'].format(s)
-        loadDlgStatus.SetLabel(msg)
-        loadDlg.Layout()
-        loadDlg.Refresh()
-        loadDlg.Update()
+        loadDlg.SetMessage(msg)
 
     # The default error function
     # shows an error dialog
@@ -274,8 +261,7 @@ def loadOverlays(paths, loadFunc='default', errorFunc='default', saveDir=True):
     # function, show the dialog
     if defaultLoad:
         loadDlg.CentreOnParent()
-        loadDlg.Show(True)
-        loadDlg.Update()
+        loadDlg.Show()
 
     # Load the images
     for path in paths:
diff --git a/fsl/fslview/views/canvaspanel.py b/fsl/fslview/views/canvaspanel.py
index d275473600c47c61ead4332997c0d820e8336fbd..b76e0ee83c5ed0f8559f7c9ea9e1ec91d14c4ce3 100644
--- a/fsl/fslview/views/canvaspanel.py
+++ b/fsl/fslview/views/canvaspanel.py
@@ -19,6 +19,7 @@ import props
 
 import fsl
 import fsl.tools.fslview_parseargs as fslview_parseargs
+import fsl.utils.dialog            as fsldlg
 import fsl.data.image              as fslimage
 import fsl.data.strings            as strings
 import fsl.fslview.overlay         as fsloverlay
@@ -31,134 +32,6 @@ import                                viewpanel
 log = logging.getLogger(__name__)
 
 
-def _takeScreenShot(overlayList, displayCtx, canvas):
-
-    overlays = displayCtx.getOrderedOverlays()
-    ovlCopy  = list(overlays)
-
-    # Check to make sure that all overlays are saved
-    # on disk, and ask the user what they want to
-    # do about the ones that aren't.
-    for overlay in overlays:
-
-        # Skip disabled overlays
-        display = displayCtx.getDisplay(overlay)
-        
-        if not display.enabled:
-            ovlCopy.remove(overlay)
-            continue
-
-        # If the image is not saved, popup a dialog
-        # telling the user they must save the image
-        # before the screenshot can proceed
-        if isinstance(overlay, fslimage.Image) and not overlay.saved:
-            title = strings.titles[  'CanvasPanel.screenshot.notSaved']
-            msg   = strings.messages['CanvasPanel.screenshot.notSaved']
-            msg   = msg.format(overlay.name)
-
-            dlg = wx.MessageDialog(canvas,
-                                   message=msg,
-                                   caption=title,
-                                   style=(wx.CENTRE |
-                                          wx.YES_NO |
-                                          wx.CANCEL |
-                                          wx.ICON_QUESTION))
-            dlg.SetYesNoCancelLabels(
-                strings.labels['CanvasPanel.screenshot.notSaved.save'],
-                strings.labels['CanvasPanel.screenshot.notSaved.skip'],
-                strings.labels['CanvasPanel.screenshot.notSaved.cancel'])
-
-            result = dlg.ShowModal()
-
-            # The user chose to save the image
-            if result == wx.ID_YES:
-                fsloverlay.saveOverlay(overlay)
-
-            # The user chose to skip the image
-            elif result == wx.ID_NO:
-                ovlCopy.remove(overlay)
-                continue
-
-            # the user clicked cancel, or closed the dialog
-            else:
-                return
-
-    overlays = ovlCopy
-
-    # Ask the user where they want 
-    # the screenshot to be saved
-    dlg = wx.FileDialog(canvas,
-                        message=strings.messages['CanvasPanel.screenshot'],
-                        style=wx.FD_SAVE)
-
-    if dlg.ShowModal() != wx.ID_OK:
-        return
-
-    filename = dlg.GetPath()
-
-    # Make the dialog go away before
-    # the screenshot gets taken
-    dlg.Destroy()
-    wx.Yield()
-
-    # Screnshot size and scene options
-    sceneOpts     = canvas.getSceneOptions()
-    width, height = canvas.getCanvasPanel().GetClientSize().Get()
-
-    # Generate command line arguments for
-    # a callout to render.py - start with
-    # the render.py specific options
-    argv  = []
-    argv += ['--outfile', filename]
-    argv += ['--size', '{}'.format(width), '{}'.format(height)]
-    argv += ['--background', '0', '0', '0', '255']
-
-    # Add scene options
-    argv += fslview_parseargs.generateSceneArgs(
-        overlayList, displayCtx, sceneOpts)
-
-    # Add ortho specific options, if it's 
-    # an orthopanel we're dealing with
-    if isinstance(sceneOpts, displayctx.OrthoOpts):
-
-        xcanvas = canvas.getXCanvas()
-        ycanvas = canvas.getYCanvas()
-        zcanvas = canvas.getZCanvas()
-        
-        argv += ['--{}'.format(fslview_parseargs.ARGUMENTS[sceneOpts,
-                                                           'xcentre'][1])]
-        argv += ['{}'.format(c) for c in xcanvas.pos.xy]
-        argv += ['--{}'.format(fslview_parseargs.ARGUMENTS[sceneOpts,
-                                                           'ycentre'][1])]
-        argv += ['{}'.format(c) for c in ycanvas.pos.xy]
-        argv += ['--{}'.format(fslview_parseargs.ARGUMENTS[sceneOpts,
-                                                           'zcentre'][1])]
-        argv += ['{}'.format(c) for c in zcanvas.pos.xy]
-
-    # Add display options for each overlay
-    for overlay in overlays:
-
-        display = displayCtx.getDisplay(overlay)
-        fname   = overlay.dataSource
-        ovlArgv = fslview_parseargs.generateOverlayArgs(overlay, displayCtx)
-        argv   += [fname] + ovlArgv
-
-    log.debug('Generating screenshot with call '
-              'to render: {}'.format(' '.join(argv)))
-
-    # Run render.py to generate the screenshot
-    msg     = strings.messages['CanvasPanel.screenshot.pleaseWait']
-    busyDlg = wx.BusyInfo(msg, canvas)
-    result  = fsl.runTool('render', argv)
-    
-    busyDlg.Destroy()
-
-    if result != 0:
-        title = strings.titles[  'CanvasPanel.screenshot.error']
-        msg   = strings.messages['CanvasPanel.screenshot.error']
-        msg   = msg.format(' '.join(['render'] + argv))
-        wx.MessageBox(msg, title, wx.ICON_ERROR | wx.OK) 
-
 
 class CanvasPanel(viewpanel.ViewPanel):
     """
@@ -186,6 +59,7 @@ class CanvasPanel(viewpanel.ViewPanel):
 
         actionz = dict({
             'screenshot'              : self.screenshot,
+            'showCommandLineArgs'     : self.showCommandLineArgs,
             'toggleOverlayList'         : lambda *a: self.togglePanel(
                 fslcontrols.OverlayListPanel),
             'toggleAtlasPanel'        : lambda *a: self.togglePanel(
@@ -265,14 +139,18 @@ class CanvasPanel(viewpanel.ViewPanel):
             
         viewpanel.ViewPanel.destroy(self)
 
-        
-    def getSceneOptions(self):
-        return self.__opts
-        
     
     def screenshot(self, *a):
-        _takeScreenShot(self._overlayList, self._displayCtx, self)
+        _screenshot(self._overlayList, self._displayCtx, self)
+
+
+    def showCommandLineArgs(self, *a):
+        _showCommandLineArgs(self._overlayList, self._displayCtx, self)
 
+
+    def getSceneOptions(self):
+        return self.__opts
+                
         
     def getCanvasPanel(self):
         return self.__canvasPanel
@@ -375,3 +253,159 @@ class CanvasPanel(viewpanel.ViewPanel):
 
         if opts.volume == limit - 1: opts.volume  = 0
         else:                        opts.volume += 1
+
+
+
+def _genCommandLineArgs(overlayList, displayCtx, canvas):
+
+    argv = []
+
+    # Add scene options
+    sceneOpts = canvas.getSceneOptions()
+    argv += fslview_parseargs.generateSceneArgs(
+        overlayList, displayCtx, sceneOpts)
+
+    # Add ortho specific options, if it's 
+    # an orthopanel we're dealing with
+    if isinstance(sceneOpts, displayctx.OrthoOpts):
+
+        xcanvas = canvas.getXCanvas()
+        ycanvas = canvas.getYCanvas()
+        zcanvas = canvas.getZCanvas()
+        
+        argv += ['--{}'.format(fslview_parseargs.ARGUMENTS[sceneOpts,
+                                                           'xcentre'][1])]
+        argv += ['{}'.format(c) for c in xcanvas.pos.xy]
+        argv += ['--{}'.format(fslview_parseargs.ARGUMENTS[sceneOpts,
+                                                           'ycentre'][1])]
+        argv += ['{}'.format(c) for c in ycanvas.pos.xy]
+        argv += ['--{}'.format(fslview_parseargs.ARGUMENTS[sceneOpts,
+                                                           'zcentre'][1])]
+        argv += ['{}'.format(c) for c in zcanvas.pos.xy]
+
+    # Add display options for each overlay
+    for overlay in overlayList:
+
+        fname   = overlay.dataSource
+        ovlArgv = fslview_parseargs.generateOverlayArgs(overlay, displayCtx)
+        argv   += [fname] + ovlArgv
+
+    return argv
+
+
+def _showCommandLineArgs(overlayList, displayCtx, canvas):
+
+    args = _genCommandLineArgs(overlayList, displayCtx, canvas)
+    dlg  = fsldlg.TextEditDialog(
+        canvas,
+        title=strings.messages[  canvas, 'showCommandLineArgs', 'title'],
+        message=strings.messages[canvas, 'showCommandLineArgs', 'message'],
+        text=' '.join(args),
+        icon=wx.ICON_INFORMATION,
+        style=(fsldlg.TED_OK        |
+               fsldlg.TED_READONLY  |
+               fsldlg.TED_MULTILINE |
+               fsldlg.TED_COPY))
+
+    dlg.CentreOnParent()
+
+    dlg.ShowModal()
+
+
+def _screenshot(overlayList, displayCtx, canvas):
+
+    overlays = displayCtx.getOrderedOverlays()
+    ovlCopy  = list(overlays)
+
+    # Check to make sure that all overlays are saved
+    # on disk, and ask the user what they want to
+    # do about the ones that aren't.
+    for overlay in overlays:
+
+        # Skip disabled overlays
+        display = displayCtx.getDisplay(overlay)
+        
+        if not display.enabled:
+            ovlCopy.remove(overlay)
+            continue
+
+        # If the image is not saved, popup a dialog
+        # telling the user they must save the image
+        # before the screenshot can proceed
+        if isinstance(overlay, fslimage.Image) and not overlay.saved:
+            title = strings.titles[  'CanvasPanel.screenshot.notSaved']
+            msg   = strings.messages['CanvasPanel.screenshot.notSaved']
+            msg   = msg.format(overlay.name)
+
+            dlg = wx.MessageDialog(canvas,
+                                   message=msg,
+                                   caption=title,
+                                   style=(wx.CENTRE |
+                                          wx.YES_NO |
+                                          wx.CANCEL |
+                                          wx.ICON_QUESTION))
+            dlg.SetYesNoCancelLabels(
+                strings.labels['CanvasPanel.screenshot.notSaved.save'],
+                strings.labels['CanvasPanel.screenshot.notSaved.skip'],
+                strings.labels['CanvasPanel.screenshot.notSaved.cancel'])
+
+            result = dlg.ShowModal()
+
+            # The user chose to save the image
+            if result == wx.ID_YES:
+                fsloverlay.saveOverlay(overlay)
+
+            # The user chose to skip the image
+            elif result == wx.ID_NO:
+                ovlCopy.remove(overlay)
+                continue
+
+            # the user clicked cancel, or closed the dialog
+            else:
+                return
+
+    overlays = ovlCopy
+
+    # Ask the user where they want 
+    # the screenshot to be saved
+    dlg = wx.FileDialog(canvas,
+                        message=strings.messages['CanvasPanel.screenshot'],
+                        style=wx.FD_SAVE)
+
+    if dlg.ShowModal() != wx.ID_OK:
+        return
+
+    filename = dlg.GetPath()
+
+    # Make the dialog go away before
+    # the screenshot gets taken
+    dlg.Destroy()
+    wx.Yield()
+
+    width, height = canvas.getCanvasPanel().GetClientSize().Get()
+
+    # generate command line arguments for
+    # a callout to render.py - start with
+    # the render.py specific options
+    argv  = []
+    argv += ['--outfile', filename]
+    argv += ['--size', '{}'.format(width), '{}'.format(height)]
+    argv += ['--background', '0', '0', '0', '255']
+
+    argv += _genCommandLineArgs(overlayList, displayCtx, canvas)
+
+    log.debug('Generating screenshot with call '
+              'to render: {}'.format(' '.join(argv)))
+
+    # Run render.py to generate the screenshot
+    msg     = strings.messages['CanvasPanel.screenshot.pleaseWait']
+    busyDlg = wx.BusyInfo(msg, canvas)
+    result  = fsl.runTool('render', argv)
+    
+    busyDlg.Destroy()
+
+    if result != 0:
+        title = strings.titles[  'CanvasPanel.screenshot.error']
+        msg   = strings.messages['CanvasPanel.screenshot.error']
+        msg   = msg.format(' '.join(['render'] + argv))
+        wx.MessageBox(msg, title, wx.ICON_ERROR | wx.OK) 
diff --git a/fsl/fslview/views/histogrampanel.py b/fsl/fslview/views/histogrampanel.py
index b2e8aee8dfa191afbee02078ecada5de43be3020..d28dbe7d3074bd5b8559c2eebd98df94dfa7a1a9 100644
--- a/fsl/fslview/views/histogrampanel.py
+++ b/fsl/fslview/views/histogrampanel.py
@@ -15,7 +15,7 @@ import props
 
 import fsl.data.image         as fslimage
 import fsl.data.strings       as strings
-import fsl.utils.messagedlg   as messagedlg
+import fsl.utils.dialog       as fsldlg
 import fsl.fslview.controls   as fslcontrols
 import                           plotpanel
 
@@ -515,7 +515,8 @@ class HistogramPanel(plotpanel.PlotPanel):
         # calculations. Show a message while this is
         # happening.
         if baseHs is None:
-            hs = messagedlg.ProcessingDialog(
+            hs = fsldlg.ProcessingDialog(
+                self,
                 strings.messages[self, 'calcHist'].format(overlay.name),
                 loadHs).Run()
 
diff --git a/fsl/tools/fslview_parseargs.py b/fsl/tools/fslview_parseargs.py
index 699dd6a14809d0ae21abe7ae8794c7f8058b1aa0..3919c467869786cf5c8a15bef68107110839f9b7 100644
--- a/fsl/tools/fslview_parseargs.py
+++ b/fsl/tools/fslview_parseargs.py
@@ -216,11 +216,12 @@ ARGUMENTS = td.TypeDict({
     'ImageOpts.transform'     : ('tf', 'transform'),
     'ImageOpts.volume'        : ('vl', 'volume'),
 
-    'VolumeOpts.displayRange'  : ('dr', 'displayRange'),
-    'VolumeOpts.interpolation' : ('in', 'interp'),
-    'VolumeOpts.clippingRange' : ('cr', 'clippingRange'),
-    'VolumeOpts.cmap'          : ('cm', 'cmap'),
-    'VolumeOpts.invert'        : ('ci', 'cmapInvert'),
+    'VolumeOpts.displayRange'   : ('dr', 'displayRange'),
+    'VolumeOpts.interpolation'  : ('in', 'interp'),
+    'VolumeOpts.invertClipping' : ('ic', 'invertClipping'),
+    'VolumeOpts.clippingRange'  : ('cr', 'clippingRange'),
+    'VolumeOpts.cmap'           : ('cm', 'cmap'),
+    'VolumeOpts.invert'         : ('ci', 'cmapInvert'),
 
     'MaskOpts.colour'    : ('co', 'maskColour'),
     'MaskOpts.invert'    : ('mi', 'maskInvert'),
@@ -301,11 +302,12 @@ HELP = td.TypeDict({
     'ImageOpts.transform'  : 'Transformation',
     'ImageOpts.volume'     : 'Volume',
 
-    'VolumeOpts.displayRange'  : 'Display range',
-    'VolumeOpts.clippingRange' : 'Clipping range',
-    'VolumeOpts.cmap'          : 'Colour map',
-    'VolumeOpts.interpolation' : 'Interpolation',
-    'VolumeOpts.invert'        : 'Invert colour map',
+    'VolumeOpts.displayRange'   : 'Display range',
+    'VolumeOpts.clippingRange'  : 'Clipping range',
+    'VolumeOpts.invertClipping' : 'Invert clipping',
+    'VolumeOpts.cmap'           : 'Colour map',
+    'VolumeOpts.interpolation'  : 'Interpolation',
+    'VolumeOpts.invert'         : 'Invert colour map',
 
     'MaskOpts.colour'    : 'Colour',
     'MaskOpts.invert'    : 'Invert',
diff --git a/fsl/utils/dialog.py b/fsl/utils/dialog.py
new file mode 100644
index 0000000000000000000000000000000000000000..0910e00a1bebf854f4b8ffe70173fedacf9775cc
--- /dev/null
+++ b/fsl/utils/dialog.py
@@ -0,0 +1,292 @@
+#!/usr/bin/env python
+#
+# dialog.py - Miscellaneous dialogs.
+#
+# Author: Paul McCarthy <pauldmccarthy@gmail.com>
+#
+
+import wx
+
+import fsl.data.strings as strings
+
+
+class SimpleMessageDialog(wx.Dialog):
+
+    
+    def __init__(self, parent=None, message=''):
+
+        wx.Dialog.__init__(self, parent, style=wx.STAY_ON_TOP)
+        
+        self.__message = wx.StaticText(
+            self,
+            style=(wx.ST_ELLIPSIZE_MIDDLE     |
+                   wx.ALIGN_CENTRE_HORIZONTAL |
+                   wx.ALIGN_CENTRE_VERTICAL))
+        
+        self.__sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.__sizer.Add(self.__message,
+                         border=25,
+                         proportion=1,
+                         flag=wx.CENTRE | wx.ALL)
+
+        self.SetBackgroundColour((250, 250, 225))
+        
+        self.SetSizer(self.__sizer)
+        self.SetMessage(message)
+
+        
+    def SetMessage(self, msg):
+
+        msg = str(msg)
+        
+        self.__message.SetLabel(msg)
+
+        # Figure out the dialog size
+        # required to fit the message
+        dc = wx.ClientDC(self.__message)
+        
+        defWidth, defHeight = 25, 25
+        msgWidth, msgHeight = dc.GetTextExtent(msg)
+
+        if msgWidth  > defWidth:  width  = msgWidth  + 25
+        else:                     width  = defWidth
+        
+        if msgHeight > defHeight: height = msgHeight + 25
+        else:                     height = defHeight
+
+        self.__message.SetMinSize((width, height))
+
+        self.Fit()
+        self.Refresh()
+        self.Update()
+
+
+class TimeoutDialog(SimpleMessageDialog):
+
+
+    def __init__(self, parent, message, timeout=1000):
+
+        SimpleMessageDialog.__init__(self, parent, message)
+        self.__timeout = timeout
+
+
+    def __close(self):
+        self.Close()
+        self.Destroy()
+
+        
+    def Show(self):
+        wx.CallLater(self.__timeout, self.__close)
+        SimpleMessageDialog.Show(self)
+
+
+    def ShowModal(self):
+        wx.CallLater(self.__timeout, self.__close)
+        SimpleMessageDialog.ShowModal(self)
+
+        
+class ProcessingDialog(SimpleMessageDialog):
+
+    def __init__(self, parent, message, task, *args, **kwargs):
+        """
+        
+        :arg message:
+        
+        :arg task:
+
+        :arg passFuncs:
+
+        :arg messageFunc:
+
+        :arg errorFunc:
+        """
+
+        passFuncs = kwargs.get('passFuncs', False)
+        
+        if not passFuncs:
+            kwargs.pop('messageFunc', None)
+            kwargs.pop('errorFunc',   None)
+        else:
+            kwargs['messageFunc'] = kwargs.get('messageFunc',
+                                               self.__defaultMessageFunc)
+            kwargs['errortFunc']  = kwargs.get('errorFunc',
+                                               self.__defaultErrorFunc)
+
+        self.task   = task
+        self.args   = args
+        self.kwargs = kwargs
+        
+        SimpleMessageDialog.__init__(self, parent, message)
+
+
+    def Run(self):
+
+        disable = wx.WindowDisabler()
+
+        self.CentreOnParent()
+        self.Show()
+        self.SetFocus()
+        self.Update()
+
+        result  = self.task(*self.args, **self.kwargs)
+        
+        self.Close()
+        self.Destroy()
+
+        del disable
+        
+        return result
+
+        
+    def __defaultMessageFunc(self, msg):
+        self.SetMessage(msg)
+
+    
+    def __defaultErrorFunc(self, msg, err):
+        err   = str(err)
+        msg   = strings.messages[self, 'error'].format(msg, err)
+        title = strings.titles[  self, 'error']
+        wx.MessageBox(msg, title, wx.ICON_ERROR | wx.OK) 
+
+
+
+TED_READONLY  = 1
+TED_MULTILINE = 2
+TED_OK        = 4
+TED_CANCEL    = 8
+TED_OK_CANCEL = 12
+TED_COPY      = 16
+
+
+class TextEditDialog(wx.Dialog):
+    """A dialog which shows an editable/selectable text field."""
+
+    def __init__(self,
+                 parent,
+                 title='',
+                 message='',
+                 text='',
+                 icon=None,
+                 style=TED_OK):
+
+        wx.Dialog.__init__(self,
+                           parent,
+                           title=title,
+                           style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
+
+        textStyle = 0
+        if style & TED_READONLY:  textStyle |= wx.TE_READONLY
+        if style & TED_MULTILINE: textStyle |= wx.TE_MULTILINE
+
+        self.__message  = wx.StaticText(self)
+        self.__textEdit = wx.TextCtrl(  self, style=textStyle)
+
+        self.__message .SetLabel(message)
+        self.__textEdit.SetValue(text)
+
+        # set the min size of the text 
+        # ctrl so it can fit a few lines
+        self.__textEdit.SetMinSize((-1, 120))
+
+        self.__ok     = (-1, -1)
+        self.__copy   = (-1, -1)
+        self.__cancel = (-1, -1)
+        self.__icon   = (-1, -1)
+
+        if icon is not None:
+            
+            icon = wx.ArtProvider.GetMessageBoxIcon(icon)
+            bmp  = wx.EmptyBitmap(icon.GetWidth(), icon.GetHeight())
+            bmp.CopyFromIcon(icon)
+            self.__icon = wx.StaticBitmap(self)
+            self.__icon.SetBitmap(bmp)
+
+        if style & TED_OK:
+            self.__ok = wx.Button(self, id=wx.ID_OK)
+            self.__ok.Bind(wx.EVT_BUTTON, self.__onOk)
+            
+        if style & TED_CANCEL:
+            self.__cancel = wx.Button(self, id=wx.ID_CANCEL)
+            self.__cancel.Bind(wx.EVT_BUTTON, self.__onCancel)
+
+        if style & TED_COPY:
+            self.__copy = wx.Button(self, label='Copy to clipboard')
+            self.__copy.Bind(wx.EVT_BUTTON, self.__onCopy) 
+
+        textSizer = wx.BoxSizer(wx.VERTICAL)
+        iconSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer  = wx.BoxSizer(wx.HORIZONTAL)
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+
+        textSizer.Add(self.__message,
+                      flag=wx.ALL | wx.CENTRE,
+                      border=20)
+        textSizer.Add(self.__textEdit,
+                      flag=wx.ALL | wx.EXPAND,
+                      border=20,
+                      proportion=1)
+
+        iconSizer.Add(self.__icon, flag=wx.ALL | wx.CENTRE, border=20)
+        iconSizer.Add(textSizer, flag=wx.EXPAND, proportion=1)
+
+        btnSizer.AddStretchSpacer()
+        btnSizer.Add(self.__ok,
+                     flag=wx.ALL | wx.CENTRE,
+                     border=10)
+        btnSizer.Add(self.__copy,
+                     flag=wx.ALL | wx.CENTRE,
+                     border=10) 
+        btnSizer.Add(self.__cancel,
+                     flag=wx.ALL | wx.CENTRE,
+                     border=10)
+        btnSizer.Add((-1, 20))
+
+        mainSizer.Add(iconSizer, flag=wx.EXPAND, proportion=1)
+        mainSizer.Add(btnSizer,  flag=wx.EXPAND)
+
+        self.SetSizer(mainSizer)
+        self.Fit()
+
+        
+    def __onOk(self, ev):
+        self.EndModal(wx.ID_OK)
+
+        
+    def __onCancel(self, ev):
+        self.EndModal(wx.ID_CANCEL)
+
+        
+    def __onCopy(self, ev):
+        text = self.__textEdit.GetValue()
+
+        cb = wx.TheClipboard
+
+        if cb.Open():
+            cb.SetData(wx.TextDataObject(text))
+            cb.Close()
+            td = TimeoutDialog(self, 'Copied!')
+            td.CentreOnParent()
+            td.Show()
+
+            
+    def SetMessage(self, message):
+        self.__message.SetLabel(message)
+
+        
+    def SetOkLabel(self, label):
+        self.__ok.SetLabel(label)
+
+    def SetCopyLabel(self, label):
+        self.__copy.SetLabel(label)
+
+        
+    def SetCancelLabel(self, label):
+        self.__cancel.SetLabel(label)
+
+
+    def SetText(self, text):
+        self.__textEdit.SetValue(text)
+
+
+    def GetText(self):
+        return self.__textEdit.GetValue()
diff --git a/fsl/utils/messagedlg.py b/fsl/utils/messagedlg.py
deleted file mode 100644
index e79537da69424016a9a958991f37a646828ef4f1..0000000000000000000000000000000000000000
--- a/fsl/utils/messagedlg.py
+++ /dev/null
@@ -1,121 +0,0 @@
-#!/usr/bin/env python
-#
-# messagedlg.py -
-#
-# Author: Paul McCarthy <pauldmccarthy@gmail.com>
-#
-
-import wx
-
-import fsl.data.strings as strings
-
-
-class ProcessingDialog(wx.Dialog):
-
-    def __init__(self, message, task, *args, **kwargs):
-        """
-        
-        :arg message:
-        
-        :arg task:
-
-        :arg passFuncs:
-
-        :arg messageFunc:
-
-        :arg errorFunc:
-        """
-
-        passFuncs = kwargs.get('passFuncs', False)
-        
-        if not passFuncs:
-            kwargs.pop('messageFunc', None)
-            kwargs.pop('errorFunc',   None)
-        else:
-            kwargs['messageFunc'] = kwargs.get('messageFunc',
-                                               self.__defaultMessageFunc)
-            kwargs['errortFunc']  = kwargs.get('errorFunc',
-                                               self.__defaultErrorFunc)
-
-        self.task   = task
-        self.args   = args
-        self.kwargs = kwargs
-        
-        wx.Dialog.__init__(
-            self, wx.GetApp().GetTopWindow(),
-            style=wx.STAY_ON_TOP)
-        
-        self.message = wx.StaticText(self, style=wx.ST_ELLIPSIZE_MIDDLE)
-        
-        self.sizer = wx.BoxSizer(wx.HORIZONTAL)
-        self.sizer.Add(self.message,
-                       border=25,
-                       proportion=1,
-                       flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTRE)
-        
-        self.SetSizer(self.sizer)
-        self.SetMessage(message)
-
-
-    def Run(self):
-
-        disable = wx.WindowDisabler()
-
-        self.CentreOnParent()
-        self.Show()
-        self.SetFocus()
-        self.Update()
-
-        result  = self.task(*self.args, **self.kwargs)
-        
-        self.Close()
-        self.Destroy()
-
-        del disable
-        
-        return result
-
-
-    def SetMessage(self, msg):
-
-        msg = str(msg)
-        
-        self.message.SetLabel(msg)
-
-        # Figure out the dialog size
-        # required to fit the message
-        dc = wx.ClientDC(self.message)
-        
-        defWidth, defHeight = 400, 100
-        msgWidth, msgHeight = dc.GetTextExtent(msg)
-
-        # Account for box sizer
-        # border (see __init__)
-        msgWidth  += 50
-        msgHeight += 50
-
-        if msgWidth  > defWidth:  width  = msgWidth  + 50
-        else:                     width  = defWidth
-        
-        if msgHeight > defHeight: height = msgHeight + 50
-        else:                     height = defHeight
-
-        # TODO Should I impose a
-        # maximum dialog width?
-
-        self.SetSize((width, height))
-
-        self.Layout()
-        self.Refresh()
-        self.Update()
-
-        
-    def __defaultMessageFunc(self, msg):
-        self.SetMessage(msg)
-
-    
-    def __defaultErrorFunc(self, msg, err):
-        err   = str(err)
-        msg   = strings.messages[self, 'error'].format(msg, err)
-        title = strings.titles[  self, 'error']
-        wx.MessageBox(msg, title, wx.ICON_ERROR | wx.OK)