Index: src/sas/sasgui/perspectives/calculator/data_operator.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/data_operator.py	(revision d85c194ea2f78cb2b9b3de9c969e1b8ebea6b6d2)
+++ src/sas/sasgui/perspectives/calculator/data_operator.py	(revision d85c194ea2f78cb2b9b3de9c969e1b8ebea6b6d2)
@@ -0,0 +1,986 @@
+"""
+GUI for the data operations panel (sum and multiply)
+"""
+import wx
+import sys
+import time
+import numpy
+from sas.sascalc.dataloader.data_info import Data1D
+from sas.sasgui.plottools.PlotPanel import PlotPanel
+from sas.sasgui.plottools.plottables import Graph
+from sas.sasgui.plottools import transform
+from matplotlib.font_manager import FontProperties
+from sas.sasgui.guiframe.events import StatusEvent
+from sas.sasgui.perspectives.calculator import calculator_widgets as widget
+from sas.sasgui.guiframe.documentation_window import DocumentationWindow
+
+#Control panel width 
+if sys.platform.count("win32") > 0:
+    PANEL_TOP = 0
+    PANEL_WIDTH = 790
+    PANEL_HEIGTH = 370
+    FONT_VARIANT = 0
+    _BOX_WIDTH = 200
+    ON_MAC = False
+else:
+    PANEL_TOP = 60
+    _BOX_WIDTH = 230
+    PANEL_WIDTH = 900
+    PANEL_HEIGTH = 430
+    FONT_VARIANT = 1
+    ON_MAC = True
+
+class DataOperPanel(wx.ScrolledWindow):
+    """
+    """
+    def __init__(self, parent, *args, **kwds):
+        kwds['name'] = "Data Operation"
+        kwds["size"] = (PANEL_WIDTH, PANEL_HEIGTH)
+        wx.ScrolledWindow.__init__(self, parent, *args, **kwds)
+        self.parent = parent
+        #sizers etc.
+        self.main_sizer = None
+        self.name_sizer = None
+        self.button_sizer = None
+        self.data_namectr = None
+        self.numberctr = None
+        self.data1_cbox = None
+        self.operator_cbox = None
+        self.data2_cbox = None
+        self.data_title_tcl = None
+        self.out_pic = None
+        self.equal_pic = None
+        self.data1_pic = None
+        self.operator_pic = None
+        self.data2_pic = None
+        self.output = None
+        self._notes = None
+        #text grayed color
+        self.color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
+        #data
+        self._data = self.get_datalist()
+        self._do_layout()
+        self.fill_data_combox()
+        self.fill_oprator_combox()
+        self.Bind(wx.EVT_SET_FOCUS, self.set_panel_on_focus)
+
+    def _define_structure(self):
+        """
+        define initial sizer
+        """
+        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
+        title = "Data Operation "
+        title += "[ + (add); - (subtract); "
+        title += "* (multiply); / (divide); "
+        title += "| (append) ]"
+        name_box = wx.StaticBox(self, -1, title)
+        self.name_sizer = wx.StaticBoxSizer(name_box, wx.HORIZONTAL)
+        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
+
+    def _layout_name(self):
+        """
+        Do the layout for data name related widgets
+        """
+        new_data_sizer = wx.BoxSizer(wx.VERTICAL)
+        equal_sizer = wx.BoxSizer(wx.VERTICAL)
+        old_data1_sizer = wx.BoxSizer(wx.VERTICAL)
+        operator_sizer = wx.BoxSizer(wx.VERTICAL)
+        old_data2_sizer = wx.BoxSizer(wx.VERTICAL)
+        data2_hori_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        data_name = wx.StaticText(self, -1, 'Output Data Name')
+        equal_name = wx.StaticText(self, -1, ' =', size=(50, 25))
+        data1_name = wx.StaticText(self, -1, 'Data1')
+        operator_name = wx.StaticText(self, -1, 'Operator')
+        data2_name = wx.StaticText(self, -1, 'Data2 (or Number)')
+        self.data_namectr = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 25), style=wx.TE_PROCESS_ENTER)
+        self.data_namectr.SetToolTipString("Hit 'Enter' key after typing.")
+        self.data_namectr.SetValue(str('MyNewDataName'))
+        self.numberctr = wx.TextCtrl(self, -1, size=(_BOX_WIDTH / 3, 25), style=wx.TE_PROCESS_ENTER)
+        self.numberctr.SetToolTipString("Hit 'Enter' key after typing.")
+        self.numberctr.SetValue(str(1.0))
+        self.data1_cbox = wx.ComboBox(self, -1, size=(_BOX_WIDTH, 25),
+                                      style=wx.CB_READONLY)
+        self.operator_cbox = wx.ComboBox(self, -1, size=(70, 25),
+                                         style=wx.CB_READONLY)
+        operation_tip = "Add: +, Subtract: -, "
+        operation_tip += "Multiply: *, Divide: /, "
+        operation_tip += "Append(Combine): | "
+        self.operator_cbox.SetToolTipString(operation_tip)
+        self.data2_cbox = wx.ComboBox(self, -1, size=(_BOX_WIDTH * 2 / 3, 25),
+                                       style=wx.CB_READONLY)
+
+        self.out_pic = SmallPanel(self, -1, True,
+                                    size=(_BOX_WIDTH, _BOX_WIDTH),
+                                    style=wx.NO_BORDER)
+        self.equal_pic = SmallPanel(self, -1, True, '=',
+                                    size=(50, _BOX_WIDTH),
+                                    style=wx.NO_BORDER)
+        self.data1_pic = SmallPanel(self, -1, True,
+                                    size=(_BOX_WIDTH, _BOX_WIDTH),
+                                    style=wx.NO_BORDER)
+        self.operator_pic = SmallPanel(self, -1, True, '+',
+                                    size=(70, _BOX_WIDTH),
+                                    style=wx.NO_BORDER)
+        self.data2_pic = SmallPanel(self, -1, True,
+                                    size=(_BOX_WIDTH, _BOX_WIDTH),
+                                    style=wx.NO_BORDER)
+        for ax in self.equal_pic.axes:
+            ax.set_frame_on(False)
+        for ax in self.operator_pic.axes:
+            ax.set_frame_on(False)
+
+        new_data_sizer.AddMany([(data_name, 0, wx.LEFT, 3),
+                                       (self.data_namectr, 0, wx.LEFT, 3),
+                                       (self.out_pic, 0, wx.LEFT, 3)])
+        equal_sizer.AddMany([(13, 13), (equal_name, 0, wx.LEFT, 3),
+                                       (self.equal_pic, 0, wx.LEFT, 3)])
+        old_data1_sizer.AddMany([(data1_name, 0, wx.LEFT, 3),
+                                       (self.data1_cbox, 0, wx.LEFT, 3),
+                                       (self.data1_pic, 0, wx.LEFT, 3)])
+        operator_sizer.AddMany([(operator_name, 0, wx.LEFT, 3),
+                                 (self.operator_cbox, 0, wx.LEFT, 3),
+                                 (self.operator_pic, 0, wx.LEFT, 3)])
+        data2_hori_sizer.AddMany([(self.data2_cbox, 0, wx.LEFT, 0),
+                                       (self.numberctr, 0, wx.RIGHT, 0)])
+        old_data2_sizer.AddMany([(data2_name, 0, wx.LEFT, 3),
+                                       (data2_hori_sizer, 0, wx.LEFT, 3),
+                                       (self.data2_pic, 0, wx.LEFT, 3)])
+        self.name_sizer.AddMany([(new_data_sizer, 0, wx.LEFT | wx.TOP, 5),
+                                       (equal_sizer, 0, wx.TOP, 5),
+                                       (old_data1_sizer, 0, wx.TOP, 5),
+                                       (operator_sizer, 0, wx.TOP, 5),
+                                       (old_data2_sizer, 0, wx.TOP, 5)])
+        self.data2_cbox.Show(True)
+
+        self._show_numctrl(self.numberctr, False)
+
+        wx.EVT_TEXT_ENTER(self.data_namectr, -1, self.on_name)
+        wx.EVT_TEXT_ENTER(self.numberctr, -1, self.on_number)
+        wx.EVT_COMBOBOX(self.data1_cbox, -1, self.on_select_data1)
+        wx.EVT_COMBOBOX(self.operator_cbox, -1, self.on_select_operator)
+        wx.EVT_COMBOBOX(self.data2_cbox, -1, self.on_select_data2)
+
+    def _show_numctrl(self, ctrl, enable=True):
+        """
+        Show/Hide on Win
+        Enable/Disable on MAC
+        """
+        if ON_MAC:
+            ctrl.Enable(enable)
+            children = ctrl.GetChildren()
+            if len(children) > 0:
+                ctrl.GetChildren()[0].SetBackGroundColour(self.color)
+            if enable:
+                wx.EVT_TEXT_ENTER(self.numberctr, -1, self.on_number)
+        else:
+            if not ctrl.IsEnabled():
+                ctrl.Enable(True)
+            ctrl.Show(enable)
+
+    def on_name(self, event=None):
+        """
+        On data name typing
+        """
+        if event != None:
+            event.Skip()
+        item = event.GetEventObject()
+        if item.IsEnabled():
+            self._set_textctrl_color(item, 'white')
+        else:
+            self._set_textctrl_color(item, self.color)
+        text = item.GetValue().strip()
+        self._check_newname(text)
+
+    def _check_newname(self, name=None):
+        """
+        Check name ctr strings
+        """
+        self.send_warnings('')
+        msg = ''
+        if name == None:
+            text = self.data_namectr.GetValue().strip()
+        else:
+            text = name
+        state_list = self.get_datalist().values()
+        name_list = []
+        for state in state_list:
+            if state.data == None:
+                theory_list = state.get_theory()
+                theory, _ = theory_list.values()[0]
+                d_name = str(theory.name)
+            else:
+                d_name = str(state.data.name)
+            name_list.append(d_name)
+        if text in name_list:
+            self._set_textctrl_color(self.data_namectr, 'pink')
+            msg = "DataOperation: The name already exists."
+        if len(text) == 0:
+            self._set_textctrl_color(self.data_namectr, 'pink')
+            msg = "DataOperation: Type the data name first."
+        if self._notes:
+            self.send_warnings(msg, 'error')
+        self.name_sizer.Layout()
+        self.Refresh()
+
+    def _set_textctrl_color(self, ctrl, color):
+        """
+        Set TextCtrl color
+        """
+        if ON_MAC:
+            children = ctrl.GetChildren()
+            if len(children) > 0:
+                children[0].SetBackgroundColour(color)
+        else:
+            ctrl.SetBackgroundColour(color)
+        self.name_sizer.Layout()
+
+    def on_number(self, event=None):
+        """
+        On selecting Number for Data2
+        """
+        self.send_warnings('')
+        item = event.GetEventObject()
+        text = item.GetValue().strip()
+        if self.numberctr.IsShown():
+            if self.numberctr.IsEnabled():
+                self._set_textctrl_color(self.numberctr, 'white')
+                try:
+                    val = float(text)
+                    pos = self.data2_cbox.GetCurrentSelection()
+                    self.data2_cbox.SetClientData(pos, val)
+                except:
+                    self._set_textctrl_color(self.numberctr, 'pink')
+                    msg = "DataOperation: Number requires a float number."
+                    self.send_warnings(msg, 'error')
+                    return
+            else:
+                self._set_textctrl_color(self.numberctr, self.color)
+
+        self.put_text_pic(self.data2_pic, content=str(val))
+        self.check_data_inputs()
+        if self.output != None:
+            self.output.name = str(self.data_namectr.GetValue())
+        self.draw_output(self.output)
+        self.Refresh()
+
+    def on_select_data1(self, event=None):
+        """
+        On select data1
+        """
+        self.send_warnings('')
+        item = event.GetEventObject()
+        pos = item.GetCurrentSelection()
+        data = item.GetClientData(pos)
+        if data == None:
+            content = "?"
+            self.put_text_pic(self.data1_pic, content)
+        else:
+            self.data1_pic.add_image(data)
+        self.check_data_inputs()
+        if self.output != None:
+            self.output.name = str(self.data_namectr.GetValue())
+        self.draw_output(self.output)
+
+    def on_select_operator(self, event=None):
+        """
+        On Select an Operator
+        """
+        self.send_warnings('')
+        item = event.GetEventObject()
+        text = item.GetValue().strip()
+        self.put_text_pic(self.operator_pic, content=text)
+        self.check_data_inputs()
+        if self.output != None:
+            self.output.name = str(self.data_namectr.GetValue())
+        self.draw_output(self.output)
+
+    def on_select_data2(self, event=None):
+        """
+        On Selecting Data2
+        """
+        self.send_warnings('')
+        item = event.GetEventObject()
+        text = item.GetValue().strip().lower()
+        self._show_numctrl(self.numberctr, text == 'number')
+        pos = item.GetCurrentSelection()
+        data = item.GetClientData(pos)
+        content = "?"
+        if not (self.numberctr.IsShown() and self.numberctr.IsEnabled()):
+            if data == None:
+                content = "?"
+                self.put_text_pic(self.data2_pic, content)
+            else:
+                self.data2_pic.add_image(data)
+            self.check_data_inputs()
+        else:
+            content = str(self.numberctr.GetValue().strip())
+            try:
+                content = float(content)
+                data = content
+            except:
+                self._set_textctrl_color(self.numberctr, 'pink')
+                content = "?"
+                data = None
+            item.SetClientData(pos, data)
+            if data != None:
+                self.check_data_inputs()
+
+            self.put_text_pic(self.data2_pic, content)
+
+        if self.output != None:
+            self.output.name = str(self.data_namectr.GetValue())
+        self.draw_output(self.output)
+
+    def put_text_pic(self, pic=None, content=''):
+        """
+        Put text to the pic
+        """
+        pic.set_content(content)
+        pic.add_text()
+        pic.draw()
+
+    def check_data_inputs(self):
+        """
+        Check data1 and data2 whether or not they are ready for operation
+        """
+        self._set_textctrl_color(self.data1_cbox, 'white')
+        self._set_textctrl_color(self.data2_cbox, 'white')
+        flag = False
+        pos1 = self.data1_cbox.GetCurrentSelection()
+        data1 = self.data1_cbox.GetClientData(pos1)
+        if data1 == None:
+            self.output = None
+            return flag
+        pos2 = self.data2_cbox.GetCurrentSelection()
+        data2 = self.data2_cbox.GetClientData(pos2)
+
+        if data2 == None:
+            self.output = None
+            return flag
+        if self.numberctr.IsShown():
+            if self.numberctr.IsEnabled():
+                self._set_textctrl_color(self.numberctr, 'white')
+                try:
+                    float(data2)
+                    if self.operator_cbox.GetValue().strip() == '|':
+                        msg = "DataOperation: This operation can not accept "
+                        msg += "a float number."
+                        self.send_warnings(msg, 'error')
+                        self._set_textctrl_color(self.numberctr, 'pink')
+                        self.output = None
+                        return flag
+                except:
+                    msg = "DataOperation: Number requires a float number."
+                    self.send_warnings(msg, 'error')
+                    self._set_textctrl_color(self.numberctr, 'pink')
+                    self.output = None
+                    return flag
+            else:
+                self._set_textctrl_color(self.numberctr, self.color)
+        elif data1.__class__.__name__ != data2.__class__.__name__:
+            self._set_textctrl_color(self.data1_cbox, 'pink')
+            self._set_textctrl_color(self.data2_cbox, 'pink')
+            msg = "DataOperation: Data types must be same."
+            self.send_warnings(msg, 'error')
+            self.output = None
+            return flag
+        try:
+            self.output = self.make_data_out(data1, data2)
+        except:
+            self._check_newname()
+            self._set_textctrl_color(self.data1_cbox, 'pink')
+            self._set_textctrl_color(self.data2_cbox, 'pink')
+            msg = "DataOperation: Data types must be same."
+            self.send_warnings(msg, 'error')
+            self.output = None
+            return flag
+        return True
+
+    def make_data_out(self, data1, data2):
+        """
+        Make a temp. data output set
+        """
+        output = None
+        pos = self.operator_cbox.GetCurrentSelection()
+        operator = self.operator_cbox.GetClientData(pos)
+        try:
+            exec "output = data1 %s data2" % operator
+        except:
+            raise
+        return output
+
+
+    def draw_output(self, output):
+        """
+        Draw output data(temp)
+        """
+        out = self.out_pic
+        if output == None:
+            content = "?"
+            self.put_text_pic(out, content)
+        else:
+            out.add_image(output)
+        wx.CallAfter(self.name_sizer.Layout)
+        self.Layout()
+        self.Refresh()
+
+    def _layout_button(self):
+        """
+            Do the layout for the button widgets
+        """
+        self.bt_apply = wx.Button(self, -1, "Apply", size=(_BOX_WIDTH / 2, -1))
+        app_tip = "Generate the Data and send to Data Explorer."
+        self.bt_apply.SetToolTipString(app_tip)
+        self.bt_apply.Bind(wx.EVT_BUTTON, self.on_click_apply)
+
+        self.bt_help = wx.Button(self, -1, "HELP")
+        app_tip = "Get help on Data Operations."
+        self.bt_help.SetToolTipString(app_tip)
+        self.bt_help.Bind(wx.EVT_BUTTON, self.on_help)
+
+        self.bt_close = wx.Button(self, -1, 'Close', size=(_BOX_WIDTH / 2, -1))
+        self.bt_close.Bind(wx.EVT_BUTTON, self.on_close)
+        self.bt_close.SetToolTipString("Close this panel.")
+
+        self.button_sizer.AddMany([(PANEL_WIDTH / 2, 25),
+                                   (self.bt_apply, 0, wx.RIGHT, 10),
+                                   (self.bt_help, 0, wx.RIGHT, 10),
+                                   (self.bt_close, 0, wx.RIGHT, 10)])
+
+    def _do_layout(self):
+        """
+        Draw the current panel
+        """
+        self._define_structure()
+        self._layout_name()
+        self._layout_button()
+        self.main_sizer.AddMany([(self.name_sizer, 0, wx.EXPAND | wx.ALL, 10),
+                                (self.button_sizer, 0,
+                                          wx.EXPAND | wx.TOP | wx.BOTTOM, 5)])
+        self.SetSizer(self.main_sizer)
+        self.SetScrollbars(20, 20, 25, 65)
+        self.SetAutoLayout(True)
+
+    def set_panel_on_focus(self, event):
+        """
+        On Focus at this window
+        """
+        if event != None:
+            event.Skip()
+        self._data = self.get_datalist()
+        if ON_MAC:
+            self.fill_data_combox()
+        else:
+            children = self.GetChildren()
+            # update the list only when it is on the top
+            if self.FindFocus() in children:
+                self.fill_data_combox()
+
+    def fill_oprator_combox(self):
+        """
+        fill the current combobox with the operator
+        """
+        operator_list = [' +', ' -', ' *', " /", " |"]
+        for oper in operator_list:
+            pos = self.operator_cbox.Append(str(oper))
+            self.operator_cbox.SetClientData(pos, str(oper.strip()))
+        self.operator_cbox.SetSelection(0)
+
+
+    def fill_data_combox(self):
+        """
+        fill the current combobox with the available data
+        """
+        pos_pre1 = self.data1_cbox.GetCurrentSelection()
+        pos_pre2 = self.data2_cbox.GetCurrentSelection()
+        current1 = self.data1_cbox.GetLabel()
+        current2 = self.data2_cbox.GetLabel()
+        if pos_pre1 < 0:
+            pos_pre1 = 0
+        if pos_pre2 < 0:
+            pos_pre2 = 0
+        self.data1_cbox.Clear()
+        self.data2_cbox.Clear()
+
+        if not self._data:
+            pos = self.data1_cbox.Append('No Data Available')
+            self.data1_cbox.SetSelection(pos)
+            self.data1_cbox.SetClientData(pos, None)
+            pos2 = self.data2_cbox.Append('No Data Available')
+            self.data2_cbox.SetSelection(pos2)
+            self.data2_cbox.SetClientData(pos2, None)
+            return
+        pos1 = self.data1_cbox.Append('Select Data')
+        self.data1_cbox.SetSelection(pos1)
+        self.data1_cbox.SetClientData(pos1, None)
+        pos2 = self.data2_cbox.Append('Select Data')
+        self.data2_cbox.SetSelection(pos2)
+        self.data2_cbox.SetClientData(pos2, None)
+        pos3 = self.data2_cbox.Append('Number')
+        val = None
+        if (self.numberctr.IsShown() and self.numberctr.IsEnabled()):
+            try:
+                val = float(self.numberctr.GetValue())
+            except:
+                val = None
+        self.data2_cbox.SetClientData(pos3, val)
+        dnames = []
+        ids = self._data.keys()
+        for id in ids:
+            if id != None:
+                if self._data[id].data != None:
+                    dnames.append(self._data[id].data.name)
+                else:
+                    theory_list = self._data[id].get_theory()
+                    theory, _ = theory_list.values()[0]
+                    dnames.append(theory.name)
+        ind = numpy.argsort(dnames)
+        if len(ind) > 0:
+            val_list = numpy.array(self._data.values())[ind]
+            for datastate in val_list:
+                data = datastate.data
+                if data != None:
+                    name = data.name
+                    pos1 = self.data1_cbox.Append(str(name))
+                    self.data1_cbox.SetClientData(pos1, data)
+                    pos2 = self.data2_cbox.Append(str(name))
+                    self.data2_cbox.SetClientData(pos2, data)
+                    if str(current1) == str(name):
+                      pos_pre1 = pos1
+                    if str(current2) == str(name):
+                      pos_pre2 = pos2
+                try:
+                    theory_list = datastate.get_theory()
+                    for theory, _ in theory_list.values():
+                        th_name = theory.name
+                        posth1 = self.data1_cbox.Append(str(th_name))
+                        self.data1_cbox.SetClientData(posth1, theory)
+                        posth2 = self.data2_cbox.Append(str(th_name))
+                        self.data2_cbox.SetClientData(posth2, theory)
+                        if str(current1) == str(th_name):
+                            pos_pre1 = posth1
+                        if str(current2) == str(th_name):
+                            pos_pre2 = posth2
+                except:
+                    continue
+        self.data1_cbox.SetSelection(pos_pre1)
+        self.data2_cbox.SetSelection(pos_pre2)
+
+    def get_datalist(self):
+        """
+        """
+        data_manager = self.parent.parent.get_data_manager()
+        if data_manager != None:
+            return  data_manager.get_all_data()
+        else:
+            return {}
+
+    def on_click_apply(self, event):
+        """
+        changes are saved in data object imported to edit
+        """
+        self.send_warnings('')
+        self.data_namectr.SetBackgroundColour('white')
+        state_list = self.get_datalist().values()
+        name = self.data_namectr.GetValue().strip()
+        name_list = []
+        for state in state_list:
+            if state.data == None:
+                theory_list = state.get_theory()
+                theory, _ = theory_list.values()[0]
+                d_name = str(theory.name)
+            else:
+                d_name = str(state.data.name)
+            name_list.append(d_name)
+        if name in name_list:
+            self._set_textctrl_color(self.data_namectr, 'pink')
+            msg = "The Output Data Name already exists...   "
+            wx.MessageBox(msg, 'Error')
+            return
+        if name == '':
+            self._set_textctrl_color(self.data_namectr, 'pink')
+            msg = "Please type the output data name first...   "
+            wx.MessageBox(msg, 'Error')
+            return
+        if self.output == None:
+            msg = "No Output Data has been generated...   "
+            wx.MessageBox(msg, 'Error')
+            return
+        # send data to data manager
+        self.output.name = name
+        self.output.run = "Data Operation"
+        self.output.instrument = "SasView"
+        self.output.id = str(name) + str(time.time())
+        data = {self.output.id :self.output}
+        self.parent.parent.add_data(data)
+        self.name_sizer.Layout()
+        self.Refresh()
+        #must post event here
+        event.Skip()
+
+    def on_help(self, event):
+        """
+        Bring up the Data Operations Panel Documentation whenever
+        the HELP button is clicked.
+
+        Calls DocumentationWindow with the path of the location within the
+        documentation tree (after /doc/ ....".  Note that when using old
+        versions of Wx (before 2.9) and thus not the release version of
+        installers, the help comes up at the top level of the file as
+        webbrowser does not pass anything past the # to the browser when it is
+        running "file:///...."
+
+    :param evt: Triggers on clicking the help button
+    """
+
+        _TreeLocation = "user/perspectives/calculator/data_operator_help.html"
+        _doc_viewer = DocumentationWindow(self, -1, _TreeLocation, "",
+                                          "Data Operation Help")
+
+    def disconnect_panels(self):
+        """
+        """
+        self.out_pic.connect.disconnect()
+        self.equal_pic.connect.disconnect()
+        self.data1_pic.connect.disconnect()
+        self.operator_pic.connect.disconnect()
+        self.data2_pic.connect.disconnect()
+
+    def on_close(self, event):
+        """
+        leave data as it is and close
+        """
+        self.parent.OnClose()
+
+    def set_plot_unfocus(self):
+        """
+        Unfocus on right click
+        """
+
+    def send_warnings(self, msg='', info='info'):
+        """
+        Send warning to status bar
+        """
+        wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info=info))
+
+class SmallPanel(PlotPanel):
+    """
+    PlotPanel for Quick plot and masking plot
+    """
+    def __init__(self, parent, id= -1, is_number=False, content='?', **kwargs):
+        """
+        """
+        PlotPanel.__init__(self, parent, id=id, **kwargs)
+        self.is_number = is_number
+        self.content = content
+        self.point = None
+        self.position = (0.4, 0.5)
+        self.scale = 'linear'
+        self.prevXtrans = "x"
+        self.prevYtrans = "y"
+        self.viewModel = "--"
+        self.subplot.set_xticks([])
+        self.subplot.set_yticks([])
+        self.add_text()
+        self.figure.subplots_adjust(left=0.1, bottom=0.1)
+
+    def set_content(self, content=''):
+        """
+        Set text content
+        """
+        self.content = str(content)
+
+    def add_toolbar(self):
+        """
+        Add toolbar
+        """
+        # Not implemented
+        pass
+
+    def on_set_focus(self, event):
+        """
+        send to the parenet the current panel on focus
+        """
+        pass
+
+    def add_image(self, plot):
+        """
+        Add Image
+        """
+        self.content = ''
+        self.textList = []
+        self.plots = {}
+        self.clear()
+        self.point = plot
+        try:
+            self.figure.delaxes(self.figure.axes[0])
+            self.subplot = self.figure.add_subplot(111)
+            #self.figure.delaxes(self.figure.axes[1])
+        except:
+            pass
+        try:
+            name = plot.name
+        except:
+            name = plot.filename
+        self.plots[name] = plot
+
+        #init graph
+        self.graph = Graph()
+
+        #add plot
+        self.graph.add(plot)
+        #draw        
+        self.graph.render(self)
+
+        try:
+            self.figure.delaxes(self.figure.axes[1])
+        except:
+            pass
+        self.subplot.figure.canvas.resizing = False
+        self.subplot.tick_params(axis='both', labelsize=9)
+        # Draw zero axis lines
+        self.subplot.axhline(linewidth=1, color='r')
+        self.subplot.axvline(linewidth=1, color='r')
+
+        self.erase_legend()
+        try:
+            # mpl >= 1.1.0
+            self.figure.tight_layout()
+        except:
+            self.figure.subplots_adjust(left=0.1, bottom=0.1)
+        self.subplot.figure.canvas.draw()
+
+    def add_text(self):
+        """
+        Text in the plot
+        """
+        if not self.is_number:
+            return
+
+        self.clear()
+        try:
+            self.figure.delaxes(self.figure.axes[0])
+            self.subplot = self.figure.add_subplot(111)
+            self.figure.delaxes(self.figure.axes[1])
+        except:
+            pass
+        self.subplot.set_xticks([])
+        self.subplot.set_yticks([])
+        label = self.content
+        FONT = FontProperties()
+        xpos, ypos = (0.4, 0.5)
+        font = FONT.copy()
+        font.set_size(14)
+
+        self.textList = []
+        self.subplot.set_xlim((0, 1))
+        self.subplot.set_ylim((0, 1))
+
+        try:
+            if self.content != '?':
+                float(label)
+        except:
+            self.subplot.set_frame_on(False)
+        try:
+            # mpl >= 1.1.0
+            self.figure.tight_layout()
+        except:
+            self.figure.subplots_adjust(left=0.1, bottom=0.1)
+        if len(label) > 0 and xpos > 0 and ypos > 0:
+            new_text = self.subplot.text(str(xpos), str(ypos), str(label),
+                                           fontproperties=font)
+            self.textList.append(new_text)
+
+    def erase_legend(self):
+        """
+        Remove Legend
+        """
+        #for ax in self.axes:
+        self.remove_legend(self.subplot)
+
+    def onMouseMotion(self, event):
+        """
+        Disable dragging 2D image
+        """
+
+    def onWheel(self, event):
+        """
+        """
+
+    def onLeftDown(self, event):
+        """
+        Disables LeftDown
+        """
+
+    def onPick(self, event):
+        """
+        Remove Legend
+        """
+        for ax in self.axes:
+            self.remove_legend(ax)
+
+
+    def draw(self):
+        """
+        Draw
+        """
+        if self.dimension == 3:
+            pass
+        else:
+            self.subplot.figure.canvas.resizing = False
+            self.subplot.tick_params(axis='both', labelsize=9)
+            self.erase_legend()
+            self.subplot.figure.canvas.draw_idle()
+            try:
+                self.figure.delaxes(self.figure.axes[1])
+            except:
+                pass
+
+
+    def onContextMenu(self, event):
+        """
+        Default context menu for a plot panel
+        """
+        id = wx.NewId()
+        slicerpop = wx.Menu()
+        data = self.point
+        if issubclass(data.__class__, Data1D):
+            slicerpop.Append(id, '&Change Scale')
+            wx.EVT_MENU(self, id, self._onProperties)
+        else:
+            slicerpop.Append(id, '&Toggle Linear/Log Scale')
+            wx.EVT_MENU(self, id, self.ontogglescale)
+        try:
+            # mouse event
+            pos_evt = event.GetPosition()
+            pos = self.ScreenToClient(pos_evt)
+        except:
+            # toolbar event
+            pos_x, pos_y = self.toolbar.GetPositionTuple()
+            pos = (pos_x, pos_y + 5)
+        self.PopupMenu(slicerpop, pos)
+
+    def ontogglescale(self, event):
+        """
+        On toggle 2d scale
+        """
+        self._onToggleScale(event)
+        try:
+            # mpl >= 1.1.0
+            self.figure.tight_layout()
+        except:
+            self.figure.subplots_adjust(left=0.1, bottom=0.1)
+        try:
+            self.figure.delaxes(self.figure.axes[1])
+        except:
+            pass
+
+    def _onProperties(self, event):
+        """
+        when clicking on Properties on context menu ,
+        The Property dialog is displayed
+        The user selects a transformation for x or y value and
+        a new plot is displayed
+        """
+        list = []
+        list = self.graph.returnPlottable()
+        if len(list.keys()) > 0:
+            first_item = list.keys()[0]
+            if first_item.x != []:
+                from sas.sasgui.plottools.PropertyDialog import Properties
+                dial = Properties(self, -1, 'Change Scale')
+                # type of view or model used
+                dial.xvalue.Clear()
+                dial.yvalue.Clear()
+                dial.view.Clear()
+                dial.xvalue.Insert("x", 0)
+                dial.xvalue.Insert("log10(x)", 1)
+                dial.yvalue.Insert("y", 0)
+                dial.yvalue.Insert("log10(y)", 1)
+                dial.view.Insert("--", 0)
+                dial.view.Insert("Linear y vs x", 1)
+                dial.setValues(self.prevXtrans, self.prevYtrans, self.viewModel)
+                dial.Update()
+                if dial.ShowModal() == wx.ID_OK:
+                    self.xLabel, self.yLabel, self.viewModel = dial.getValues()
+                    if self.viewModel == "Linear y vs x":
+                        self.xLabel = "x"
+                        self.yLabel = "y"
+                        self.viewModel = "--"
+                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
+                    self._onEVT_FUNC_PROPERTY()
+                dial.Destroy()
+
+    def _onEVT_FUNC_PROPERTY(self, remove_fit=True):
+        """
+        Receive the x and y transformation from myDialog,
+        Transforms x and y in View
+        and set the scale
+        """
+        list = []
+        list = self.graph.returnPlottable()
+        # Changing the scale might be incompatible with
+        # currently displayed data (for instance, going
+        # from ln to log when all plotted values have
+        # negative natural logs).
+        # Go linear and only change the scale at the end.
+        self.set_xscale("linear")
+        self.set_yscale("linear")
+        _xscale = 'linear'
+        _yscale = 'linear'
+        for item in list:
+            item.setLabel(self.xLabel, self.yLabel)
+            # control axis labels from the panel itself
+            yname, yunits = item.get_yaxis()
+            xname, xunits = item.get_xaxis()
+            # Goes through all possible scales
+            # Goes through all possible scales
+            if(self.xLabel == "x"):
+                item.transformX(transform.toX, transform.errToX)
+                self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
+            if(self.xLabel == "log10(x)"):
+                item.transformX(transform.toX_pos, transform.errToX_pos)
+                _xscale = 'log'
+                self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
+            if(self.yLabel == "y"):
+                item.transformY(transform.toX, transform.errToX)
+                self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
+            if(self.yLabel == "log10(y)"):
+                item.transformY(transform.toX_pos, transform.errToX_pos)
+                _yscale = 'log'
+                self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
+            item.transformView()
+        self.prevXtrans = self.xLabel
+        self.prevYtrans = self.yLabel
+        self.set_xscale(_xscale)
+        self.set_yscale(_yscale)
+        self.draw()
+
+class DataOperatorWindow(widget.CHILD_FRAME):
+    def __init__(self, parent, manager, *args, **kwds):
+        kwds["size"] = (PANEL_WIDTH, PANEL_HEIGTH)
+        widget.CHILD_FRAME.__init__(self, parent, *args, **kwds)
+        self.parent = parent
+        self.manager = manager
+        self.panel = DataOperPanel(parent=self)
+        wx.EVT_CLOSE(self, self.OnClose)
+        self.SetPosition((wx.LEFT, PANEL_TOP))
+        self.Show()
+
+    def OnClose(self, event=None):
+        """
+        On close event
+        """
+        if self.manager != None:
+            self.manager.data_operator_frame = None
+        self.panel.disconnect_panels()
+        self.Destroy()
+
+
+if __name__ == "__main__":
+
+    app = wx.App()
+    widget.CHILD_FRAME = wx.Frame
+    window = DataOperatorWindow(parent=None, data=[], title="Data Editor")
+    app.MainLoop()
+
