source: sasview/sansguiframe/src/sans/guiframe/gui_manager.py @ ebbc421

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since ebbc421 was b00086f, checked in by Jae Cho <jhjcho@…>, 13 years ago

apply confirm dialog only on x icon

  • Property mode set to 100644
File size: 128.8 KB
Line 
1
2################################################################################
3#This software was developed by the University of Tennessee as part of the
4#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
5#project funded by the US National Science Foundation.
6#
7#See the license text in license.txt
8#
9#copyright 2008, University of Tennessee
10################################################################################
11
12
13import wx
14import wx.aui
15import os
16import sys
17import xml
18import time
19import py_compile
20# Try to find a local config
21import imp
22import warnings
23warnings.simplefilter("ignore")
24import logging
25
26
27from sans.guiframe.events import EVT_STATUS
28from sans.guiframe.events import EVT_APPEND_BOOKMARK
29from sans.guiframe.events import EVT_PANEL_ON_FOCUS
30from sans.guiframe.events import EVT_NEW_LOAD_DATA
31from sans.guiframe.events import EVT_NEW_COLOR
32from sans.guiframe.events import StatusEvent
33from sans.guiframe.events import NewPlotEvent
34from sans.guiframe.gui_style import GUIFRAME
35from sans.guiframe.gui_style import GUIFRAME_ID
36from sans.guiframe.data_panel import DataPanel
37from sans.guiframe.panel_base import PanelBase
38from sans.guiframe.gui_toolbar import GUIToolBar
39from sans.guiframe.data_processor import GridFrame
40from sans.guiframe.data_processor import BatchOutputFrame
41from sans.guiframe.events import EVT_NEW_BATCH
42from sans.dataloader.loader import Loader
43
44def get_app_dir():
45    """
46        The application directory is the one where the default custom_config.py
47        file resides.
48    """
49    # First, try the directory of the executable we are running
50    app_path = sys.path[0]
51    if os.path.isfile(app_path):
52        app_path = os.path.dirname(app_path)
53    if os.path.isfile(os.path.join(app_path, "custom_config.py")):
54        app_path = os.path.abspath(app_path)
55        logging.info("Using application path: %s", app_path)
56        return app_path
57   
58    # Next, try the current working directory
59    if os.path.isfile(os.path.join(os.getcwd(), "custom_config.py")):
60        logging.info("Using application path: %s", os.getcwd())
61        return os.path.abspath(os.getcwd())
62   
63    # Finally, try the directory of the sansview module
64    #TODO: gui_manager will have to know about sansview until we
65    # clean all these module variables and put them into a config class
66    # that can be passed by sansview.py.
67    logging.info(sys.executable)
68    logging.info(str(sys.argv))
69    from sans import sansview
70    app_path = os.path.dirname(sansview.__file__)
71    logging.info("Using application path: %s", app_path)
72    return app_path
73
74def get_user_directory():
75    USERDIR = os.path.join(os.path.expanduser("~"),".sansview")
76    if not os.path.isdir(USERDIR):
77        os.makedirs(USERDIR)
78    return USERDIR
79   
80def _find_local_config(file, path):
81    """
82        Find configuration file for the current application
83    """   
84    config_module = None
85    fObj = None
86    try:
87        fObj, path_config, descr = imp.find_module(file, [path])
88        config_module = imp.load_module(file, fObj, path_config, descr) 
89    except:
90        pass
91    finally:
92        if fObj is not None:
93            fObj.close()
94    return config_module
95
96# Get APP folder
97PATH_APP = get_app_dir() 
98DATAPATH = PATH_APP
99
100# GUI always starts from the App folder
101#os.chdir(PATH_APP)
102# Read in the local config, which can either be with the main
103# application or in the installation directory
104config = _find_local_config('local_config', PATH_APP)
105if config is None:
106    config = _find_local_config('local_config', os.getcwd())
107    if config is None:
108        # Didn't find local config, load the default
109        import sans.guiframe.config as config
110        logging.info("using default local_config")       
111    else:
112        logging.info("found local_config in %s" % os.getcwd()) 
113else:
114    logging.info("found local_config in %s" % PATH_APP)     
115           
116from sans.guiframe.customdir  import SetupCustom
117c_conf_dir = SetupCustom().setup_dir(PATH_APP)
118custom_config = _find_local_config('custom_config', c_conf_dir)
119if custom_config is None:
120    custom_config = _find_local_config('custom_config', os.getcwd())
121    if custom_config is None:
122        msg = "Custom_config file was not imported"
123        logging.info(msg)
124    else:
125        logging.info("using custom_config in %s" % os.getcwd())
126else:
127    logging.info("using custom_config from %s" % c_conf_dir)
128
129#read some constants from config
130APPLICATION_STATE_EXTENSION = config.APPLICATION_STATE_EXTENSION
131APPLICATION_NAME = config.__appname__
132SPLASH_SCREEN_PATH = config.SPLASH_SCREEN_PATH
133WELCOME_PANEL_ON = config.WELCOME_PANEL_ON
134SPLASH_SCREEN_WIDTH = config.SPLASH_SCREEN_WIDTH
135SPLASH_SCREEN_HEIGHT = config.SPLASH_SCREEN_HEIGHT
136SS_MAX_DISPLAY_TIME = config.SS_MAX_DISPLAY_TIME
137if not WELCOME_PANEL_ON:
138        WELCOME_PANEL_SHOW = False
139else:
140    WELCOME_PANEL_SHOW = True
141try:
142    DATALOADER_SHOW = custom_config.DATALOADER_SHOW
143    TOOLBAR_SHOW = custom_config.TOOLBAR_SHOW
144    FIXED_PANEL = custom_config.FIXED_PANEL
145    if WELCOME_PANEL_ON:
146        WELCOME_PANEL_SHOW = custom_config.WELCOME_PANEL_SHOW
147    PLOPANEL_WIDTH = custom_config.PLOPANEL_WIDTH
148    DATAPANEL_WIDTH = custom_config.DATAPANEL_WIDTH
149    GUIFRAME_WIDTH = custom_config.GUIFRAME_WIDTH
150    GUIFRAME_HEIGHT = custom_config.GUIFRAME_HEIGHT
151    DEFAULT_PERSPECTIVE = custom_config.DEFAULT_PERSPECTIVE
152    CLEANUP_PLOT = custom_config.CLEANUP_PLOT
153    # custom open_path
154    open_folder = custom_config.DEFAULT_OPEN_FOLDER
155    if open_folder != None and os.path.isdir(open_folder):
156        DEFAULT_OPEN_FOLDER = os.path.abspath(open_folder)
157    else:
158        DEFAULT_OPEN_FOLDER = PATH_APP
159except:
160    DATALOADER_SHOW = True
161    TOOLBAR_SHOW = True
162    FIXED_PANEL = True
163    WELCOME_PANEL_SHOW = False
164    PLOPANEL_WIDTH = config.PLOPANEL_WIDTH
165    DATAPANEL_WIDTH = config.DATAPANEL_WIDTH
166    GUIFRAME_WIDTH = config.GUIFRAME_WIDTH
167    GUIFRAME_HEIGHT = config.GUIFRAME_HEIGHT
168    DEFAULT_PERSPECTIVE = None
169    CLEANUP_PLOT = False
170    DEFAULT_OPEN_FOLDER = PATH_APP
171
172DEFAULT_STYLE = config.DEFAULT_STYLE
173
174
175PLOPANEL_HEIGTH = config.PLOPANEL_HEIGTH
176DATAPANEL_HEIGHT = config.DATAPANEL_HEIGHT
177PLUGIN_STATE_EXTENSIONS =  config.PLUGIN_STATE_EXTENSIONS
178OPEN_SAVE_MENU = config.OPEN_SAVE_PROJECT_MENU
179VIEW_MENU = config.VIEW_MENU
180EDIT_MENU = config.EDIT_MENU
181extension_list = []
182if APPLICATION_STATE_EXTENSION is not None:
183    extension_list.append(APPLICATION_STATE_EXTENSION)
184EXTENSIONS = PLUGIN_STATE_EXTENSIONS + extension_list
185try:
186    PLUGINS_WLIST = '|'.join(config.PLUGINS_WLIST)
187except:
188    PLUGINS_WLIST = ''
189APPLICATION_WLIST = config.APPLICATION_WLIST
190IS_WIN = True
191CLOSE_SHOW = True
192TIME_FACTOR = 1
193NOT_SO_GRAPH_LIST = ["BoxSum"]
194if sys.platform.count("win32")==0:
195    IS_WIN = False
196    TIME_FACTOR = 2
197    if int(wx.__version__.split('.')[0]) == 2:
198        if int(wx.__version__.split('.')[1]) < 9:
199            CLOSE_SHOW = False
200   
201class ViewerFrame(wx.Frame):
202    """
203    Main application frame
204    """
205   
206    def __init__(self, parent, title, 
207                 size=(GUIFRAME_WIDTH, GUIFRAME_HEIGHT),
208                 gui_style=DEFAULT_STYLE, 
209                 style=wx.DEFAULT_FRAME_STYLE,
210                 pos=wx.DefaultPosition):
211        """
212        Initialize the Frame object
213        """
214       
215        wx.Frame.__init__(self, parent=parent, title=title, pos=pos,size=size)
216        # title
217        self.title = title
218        # Preferred window size
219        self._window_width, self._window_height = size
220        self.__gui_style = gui_style       
221        path = os.path.dirname(__file__)
222        temp_path = os.path.join(path,'images')
223        ico_file = os.path.join(temp_path,'ball.ico')
224        if os.path.isfile(ico_file):
225            self.SetIcon(wx.Icon(ico_file, wx.BITMAP_TYPE_ICO))
226        else:
227            temp_path = os.path.join(os.getcwd(),'images')
228            ico_file = os.path.join(temp_path,'ball.ico')
229            if os.path.isfile(ico_file):
230                self.SetIcon(wx.Icon(ico_file, wx.BITMAP_TYPE_ICO))
231            else:
232                ico_file = os.path.join(os.path.dirname(os.path.sys.path[0]),
233                             'images', 'ball.ico')
234                if os.path.isfile(ico_file):
235                    self.SetIcon(wx.Icon(ico_file, wx.BITMAP_TYPE_ICO))
236        self.path = PATH_APP
237        self.application_name = APPLICATION_NAME
238        ## Application manager
239        self._input_file = None
240        self.app_manager = None
241        self._mgr = None
242        #add current perpsective
243        self._current_perspective = None
244        self._plotting_plugin = None
245        self._data_plugin = None
246        #Menu bar and item
247        self._menubar = None
248        self._file_menu = None
249        self._data_menu = None
250        self._view_menu = None
251        self._window_menu = None
252        self._data_panel_menu = None
253        self._help_menu = None
254        self._tool_menu = None
255        self._applications_menu_pos = -1
256        self._applications_menu_name = None
257        self._applications_menu = None
258        self._edit_menu = None
259        self._toolbar_menu = None
260        self._save_appl_menu = None
261        #tool bar
262        self._toolbar = None
263        # number of plugins
264        self._num_perspectives = 0
265        # plot duck cleanup option
266        self.cleanup_plots = CLEANUP_PLOT
267        # (un)-focus color
268        #self.color = '#b3b3b3'
269        ## Find plug-ins
270        # Modify this so that we can specify the directory to look into
271        self.plugins = []
272        #add local plugin
273        self.plugins += self._get_local_plugins()
274        self.plugins += self._find_plugins()
275        ## List of panels
276        self.panels = {}
277        # List of plot panels
278        self.plot_panels = {}
279        # default Graph number fot the plotpanel caption
280        self.graph_num = 0
281
282        # Default locations
283        self._default_save_location = DEFAULT_OPEN_FOLDER       
284        # Welcome panel
285        self.defaultPanel = None
286        #panel on focus
287        self.panel_on_focus = None
288        #control_panel on focus
289        self.cpanel_on_focus = None
290        self.loader = Loader()   
291        #data manager
292        self.batch_on = False
293        from data_manager import DataManager
294        self._data_manager = DataManager()
295        self._data_panel = DataPanel(parent=self)
296        if self.panel_on_focus is not None:
297            self._data_panel.set_panel_on_focus(self.panel_on_focus.window_caption)
298        # list of plot panels in schedule to full redraw
299        self.schedule = False
300        #self.callback = True
301        self._idle_count = 0
302        self.schedule_full_draw_list = []
303        self.idletimer = wx.CallLater(TIME_FACTOR, self._onDrawIdle)
304       
305        self.batch_frame = GridFrame(parent=self)
306        self.batch_frame.Hide()
307        self.on_batch_selection(event=None)
308        self.add_icon()
309        # Check for update
310        #self._check_update(None)
311        # Register the close event so it calls our own method
312        wx.EVT_CLOSE(self, self.WindowClose)
313        # Register to status events
314        self.Bind(EVT_STATUS, self._on_status_event)
315        #Register add extra data on the same panel event on load
316        self.Bind(EVT_PANEL_ON_FOCUS, self.set_panel_on_focus)
317        self.Bind(EVT_APPEND_BOOKMARK, self.append_bookmark)
318        self.Bind(EVT_NEW_LOAD_DATA, self.on_load_data)
319        self.Bind(EVT_NEW_BATCH, self.on_batch_selection)
320        self.Bind(EVT_NEW_COLOR, self.on_color_selection)
321        self.setup_custom_conf()
322       
323    def add_icon(self):
324        """
325        get list of child and attempt to add the default icon
326        """
327       
328        list_children = self.GetChildren() 
329        for frame in list_children:
330            if hasattr(frame, "IsIconized"):
331                if not frame.IsIconized():
332                    try:
333                        icon = self.GetIcon()
334                        frame.SetIcon(icon)
335                    except:
336                        pass
337       
338    def on_set_batch_result(self, data_outputs, data_inputs=None,
339                             plugin_name=""):
340        """
341        Display data into a grid in batch mode and show the grid
342        """
343        t = time.localtime(time.time())
344        time_str = time.strftime("%b %d %H;%M of %Y", t)
345        name = "Batch"  +  time_str
346        details = "File Generated by %s : %s" % (APPLICATION_NAME,
347                                                     str(plugin_name))
348        details += "on %s.\n" % time_str
349        ext = ".csv"
350        file_name = "Batch_" + str(plugin_name)+ "_" + time_str + ext
351        file_name = self._default_save_location + str(file_name)
352       
353        self.open_with_localapp(file_name=file_name,
354                                details=details,
355                                data_inputs=data_inputs,
356                                    data_outputs=data_outputs)
357     
358   
359    def open_with_localapp(self, data_inputs=None, details="", file_name=None,
360                           data_outputs=None):
361        """
362        Display value of data into the application grid
363        :param data: dictionary of string and list of items
364        """
365        self.batch_frame.set_data(data_inputs=data_inputs, 
366                                  data_outputs=data_outputs,
367                                  details=details,
368                                  file_name=file_name)
369        self.batch_frame.Show(True)
370       
371    def on_read_batch_tofile(self, event):
372        """
373        Open a file dialog , extract the file to read and display values
374        into a grid
375        """
376        path = None
377        if self._default_save_location == None:
378            self._default_save_location = os.getcwd()
379       
380        dlg = wx.FileDialog(self, 
381                            "Choose a file", 
382                            self._default_save_location, "",
383                             "(*.csv)|*.csv| Text Files (*.txt)|*.txt")
384        if dlg.ShowModal() == wx.ID_OK:
385            path = dlg.GetPath()
386            if path is not None:
387                self._default_save_location = os.path.dirname(path)
388        dlg.Destroy()
389        self.read_batch_tofile(file_name=path)
390       
391    def read_batch_tofile(self, file_name):
392        """
393        Extract value from file name and Display them into a grid
394        """
395        if file_name is None or file_name.strip() == "":
396            return
397        data = {}
398        fd = open(file_name, 'r')
399        _, ext = os.path.splitext(file_name)
400        separator = "\t"
401        if ext.lower() == ".csv":
402            separator = ","
403        buffer = fd.read()
404        lines = buffer.split('\n')
405        fd.close()
406        column_names_line  = ""
407        index = None
408        details = ""
409        for index in range(len(lines)):
410            line = lines[index]
411            count = 0
412            if line.find(separator) != -1:
413                if line.count(separator) >= 2:
414                    #found the first line containing the label
415                    col_name_toks = line.split(separator)
416                    for item in col_name_toks:
417                        if item.strip() != "":
418                            count += 1
419            else:
420                details += line
421            if count >= 2:
422                column_names_line = line
423                first_data_index = index
424                break 
425           
426        if column_names_line.strip() == "" or index is None:
427            return 
428        col_name_toks = column_names_line.split(separator)
429        c_index = 0
430        for col_index in range(len(col_name_toks)):
431            c_name = col_name_toks[col_index]
432            if c_name.strip() != "":
433                data[c_name] = [ lines[row].split(separator)[c_index]
434                                for row in range(index + 1, len(lines)-1)]
435                c_index += 1
436               
437     
438        self.open_with_localapp(data_outputs=data, data_inputs=None,
439                                file_name=file_name, details=details)
440       
441    def write_batch_tofile(self, data, file_name, details=""):
442        """
443        Helper to write result from batch into cvs file
444        """
445        self._default_save_location = os.path.dirname(file_name)
446        file_name = os.path.basename(file_name)
447        if data is None or file_name is None or file_name.strip() == "":
448            return
449        _, ext = os.path.splitext(file_name)
450       
451        fd = open(file_name, 'w')
452        separator = "\t"
453        if ext.lower() == ".csv":
454            separator = ","
455        fd.write(str(details))
456        for col_name  in data.keys():
457             fd.write(str(col_name))
458             fd.write(separator)
459        fd.write('\n')
460        max_list = [len(value) for value in data.values()]
461        if len(max_list) == 0:
462            return
463        max_index = max(max_list)
464        index = 0
465        while(index < max_index):
466            for value_list in data.values():
467                if index < len(value_list):
468                    fd.write(str(value_list[index]))
469                    fd.write(separator)
470                else:
471                    fd.write('')
472                    fd.write(separator)
473            fd.write('\n')
474            index += 1
475        fd.close()
476           
477    def open_with_externalapp(self, data, file_name, details=""):
478        """
479        Display data in the another application , by default Excel
480        """
481        if not os.path.exists(file_name):
482            self.write_batch_tofile(data=data, file_name=file_name,
483                                               details=details)
484        try:
485            from win32com.client import Dispatch
486            excel_app = Dispatch('Excel.Application')     
487            wb = excel_app.Workbooks.Open(file_name) 
488            excel_app.Visible = 1
489        except:
490            msg = "Error occured when calling Excel\n"
491            msg += "Check that Excel in installed in this machine or \n"
492            msg += "Check that %s really exists.\n" % str(file_name)
493            wx.PostEvent(self, StatusEvent(status=msg,
494                                             info="error"))
495           
496         
497    def on_batch_selection(self, event=None):
498        """
499        :param event: contains parameter enable . when enable is set to True
500        the application is in Batch mode
501        else the application is default mode(single mode)
502        """
503        if event is not None:
504            self.batch_on = event.enable
505        for plug in self.plugins:
506            plug.set_batch_selection(self.batch_on)
507           
508    def on_color_selection(self, event):
509        """
510        :param event: contains parameters for id and color
511        """ 
512        color, id = event.color, event.id
513        for plug in self.plugins:
514            plug.add_color(color, id)
515       
516       
517    def setup_custom_conf(self):
518        """
519        Set up custom configuration if exists
520        """
521        if custom_config == None:
522            return
523       
524        if not FIXED_PANEL:
525            self.__gui_style &= (~GUIFRAME.FIXED_PANEL)
526            self.__gui_style |= GUIFRAME.FLOATING_PANEL
527
528        if not DATALOADER_SHOW:
529            self.__gui_style &= (~GUIFRAME.MANAGER_ON)
530
531        if not TOOLBAR_SHOW:
532            self.__gui_style &= (~GUIFRAME.TOOLBAR_ON)
533
534        if WELCOME_PANEL_SHOW:
535            self.__gui_style |= GUIFRAME.WELCOME_PANEL_ON   
536             
537    def set_custom_default_perspective(self):
538        """
539        Set default starting perspective
540        """
541        if custom_config == None:
542            return
543        for plugin in self.plugins:
544            try:
545                if plugin.sub_menu == DEFAULT_PERSPECTIVE:
546                   
547                    plugin.on_perspective(event=None)
548                    #self._check_applications_menu()
549                    break
550            except:
551                pass 
552        return         
553               
554    def on_load_data(self, event):
555        """
556        received an event to trigger load from data plugin
557        """
558        if self._data_plugin is not None:
559            self._data_plugin.load_data(event)
560           
561    def get_current_perspective(self):
562        """
563        return the current perspective
564        """
565        return self._current_perspective
566   
567    def set_input_file(self, input_file):
568        """
569        :param input_file: file to read
570        """
571        self._input_file = input_file
572       
573    def get_data_manager(self):
574        """
575        """
576        return self._data_manager
577   
578    def get_toolbar(self):
579        """
580        """
581        return self._toolbar
582   
583    def set_panel_on_focus(self, event):
584        """
585        Store reference to the last panel on focus
586        update the toolbar if available
587        update edit menu if available
588        """
589        if event != None:
590            self.panel_on_focus = event.panel
591        panel_name = 'No panel on focus'
592        application_name = 'No Selected Analysis'
593        if self.panel_on_focus is not None:
594            #Disable save application if the current panel is in batch mode
595            flag = self.panel_on_focus.get_save_flag()
596            self._save_appl_menu.Enable(flag)
597
598            if self.panel_on_focus not in self.plot_panels.values():
599                for ID in self.panels.keys():
600                    if self.panel_on_focus != self.panels[ID]:
601                        self.panels[ID].on_kill_focus(None)
602
603            if self._data_panel is not None and \
604                            self.panel_on_focus is not None:
605                self.set_panel_on_focus_helper()
606                #update toolbar
607                self._update_toolbar_helper()
608                #update edit menu
609                self.enable_edit_menu()
610   
611    def set_panel_on_focus_helper(self):
612        """
613        Helper for panel on focus with data_panel
614        """
615        panel_name = self.panel_on_focus.window_caption
616        ID = self.panel_on_focus.uid
617        self._data_panel.set_panel_on_focus(ID)
618        #update combo
619        if self.panel_on_focus in self.plot_panels.values():
620            combo = self._data_panel.cb_plotpanel
621            combo_title = str(self.panel_on_focus.window_caption)
622            combo.SetStringSelection(combo_title)
623            combo.SetToolTip( wx.ToolTip(combo_title )) 
624        elif self.panel_on_focus != self._data_panel:
625            cpanel = self.panel_on_focus
626            if self.cpanel_on_focus != cpanel:
627                cpanel.on_tap_focus()
628                self.cpanel_on_focus = self.panel_on_focus
629               
630    def reset_bookmark_menu(self, panel):
631        """
632        Reset Bookmark menu list
633       
634        : param panel: a control panel or tap where the bookmark is
635        """
636        cpanel = panel
637        if self._toolbar != None and cpanel._bookmark_flag:
638            for item in  self._toolbar.get_bookmark_items():
639                self._toolbar.remove_bookmark_item(item)
640            self._toolbar.add_bookmark_default()
641            pos = 0
642            for bitem in cpanel.popUpMenu.GetMenuItems():
643                pos += 1
644                if pos < 3:
645                    continue
646                id =  bitem.GetId()
647                label = bitem.GetLabel()
648                self._toolbar.append_bookmark_item(id, label)
649                wx.EVT_MENU(self, id, cpanel._back_to_bookmark)
650            self._toolbar.Realize()
651             
652
653    def build_gui(self):
654        """
655        """
656        # set tool bar
657        self._setup_tool_bar()
658        # Set up the layout
659        self._setup_layout()
660       
661        # Set up the menu
662        self._setup_menus()
663       
664        try:
665            self.load_from_cmd(self._input_file)
666        except:
667            msg = "%s Cannot load file %s\n" %(str(APPLICATION_NAME), 
668                                             str(self._input_file))
669            msg += str(sys.exc_value) + '\n'
670            print msg
671        if self._data_panel is not None and len(self.plugins) > 0:
672            self._data_panel.fill_cbox_analysis(self.plugins)
673        self.post_init()
674        # Set Custom default
675        self.set_custom_default_perspective()
676        # Set up extra custom tool menu
677        self._setup_extra_custom()
678        #self.Show(True)
679        #self._check_update(None)
680   
681    def _setup_extra_custom(self): 
682        """
683        Set up toolbar and welcome view if needed
684        """
685        style = self.__gui_style & GUIFRAME.TOOLBAR_ON
686        if (style == GUIFRAME.TOOLBAR_ON) & (not self._toolbar.IsShown()):
687            self._on_toggle_toolbar() 
688       
689        # Set Custom deafult start page
690        welcome_style = self.__gui_style & GUIFRAME.WELCOME_PANEL_ON
691        if welcome_style == GUIFRAME.WELCOME_PANEL_ON:
692            self.show_welcome_panel(None)
693     
694    def _setup_layout(self):
695        """
696        Set up the layout
697        """
698        # Status bar
699        from gui_statusbar import StatusBar
700        self.sb = StatusBar(self, wx.ID_ANY)
701        self.SetStatusBar(self.sb)
702        # Add panel
703        default_flag = wx.aui.AUI_MGR_DEFAULT#| wx.aui.AUI_MGR_ALLOW_ACTIVE_PANE
704        self._mgr = wx.aui.AuiManager(self, flags=default_flag)
705        self._mgr.SetDockSizeConstraint(0.5, 0.5)
706        # border color
707        #self.b_color = wx.aui.AUI_DOCKART_BORDER_COLOUR 
708        #self._mgr.GetArtProvider().SetColor(self.b_color, self.color)
709        #self._mgr.SetArtProvider(wx.aui.AuiDockArt(wx.AuiDefaultDockArt))
710        #print "set", self._dockart.GetColour(13)
711        # Load panels
712        self._load_panels()
713        self.set_default_perspective()
714        self._mgr.Update()
715       
716    def SetStatusText(self, *args, **kwds):
717        """
718        """
719        number = self.sb.get_msg_position()
720        wx.Frame.SetStatusText(self, number=number, *args, **kwds)
721       
722    def PopStatusText(self, *args, **kwds):
723        """
724        """
725        field = self.sb.get_msg_position()
726        wx.Frame.PopStatusText(self, field=field)
727       
728    def PushStatusText(self, *args, **kwds):
729        """
730        """
731        field = self.sb.get_msg_position()
732        wx.Frame.PushStatusText(self, field=field, string=string)
733
734    def add_perspective(self, plugin):
735        """
736        Add a perspective if it doesn't already
737        exist.
738        """
739        self._num_perspectives += 1
740        is_loaded = False
741        for item in self.plugins:
742            item.set_batch_selection(self.batch_on)
743            if plugin.__class__ == item.__class__:
744                msg = "Plugin %s already loaded" % plugin.sub_menu
745                logging.info(msg)
746                is_loaded = True 
747        if not is_loaded:
748            self.plugins.append(plugin) 
749     
750    def _get_local_plugins(self):
751        """
752        get plugins local to guiframe and others
753        """
754        plugins = []
755        #import guiframe local plugins
756        #check if the style contain guiframe.dataloader
757        style1 = self.__gui_style & GUIFRAME.DATALOADER_ON
758        style2 = self.__gui_style & GUIFRAME.PLOTTING_ON
759        if style1 == GUIFRAME.DATALOADER_ON:
760            try:
761                from sans.guiframe.local_perspectives.data_loader import data_loader
762                self._data_plugin = data_loader.Plugin()
763                plugins.append(self._data_plugin)
764            except:
765                msg = "ViewerFrame._get_local_plugins:"
766                msg += "cannot import dataloader plugin.\n %s" % sys.exc_value
767                logging.error(msg)
768        if style2 == GUIFRAME.PLOTTING_ON:
769            try:
770                from sans.guiframe.local_perspectives.plotting import plotting
771                self._plotting_plugin = plotting.Plugin()
772                plugins.append(self._plotting_plugin)
773            except:
774                msg = "ViewerFrame._get_local_plugins:"
775                msg += "cannot import plotting plugin.\n %s" % sys.exc_value
776                logging.error(msg)
777     
778        return plugins
779   
780    def _find_plugins(self, dir="perspectives"):
781        """
782        Find available perspective plug-ins
783       
784        :param dir: directory in which to look for plug-ins
785       
786        :return: list of plug-ins
787       
788        """
789        import imp
790        plugins = []
791        # Go through files in panels directory
792        try:
793            list = os.listdir(dir)
794            ## the default panel is the panel is the last plugin added
795            for item in list:
796                toks = os.path.splitext(os.path.basename(item))
797                name = ''
798                if not toks[0] == '__init__':
799                    if toks[1] == '.py' or toks[1] == '':
800                        name = toks[0]
801                    #check the validity of the module name parsed
802                    #before trying to import it
803                    if name is None or name.strip() == '':
804                        continue
805                    path = [os.path.abspath(dir)]
806                    file = ''
807                    try:
808                        if toks[1] == '':
809                            mod_path = '.'.join([dir, name])
810                            module = __import__(mod_path, globals(),
811                                                locals(), [name])
812                        else:
813                            (file, path, info) = imp.find_module(name, path)
814                            module = imp.load_module( name, file, item, info)
815                        if hasattr(module, "PLUGIN_ID"):
816                            try: 
817                                plug = module.Plugin()
818                                if plug.set_default_perspective():
819                                    self._current_perspective = plug
820                                plugins.append(plug)
821                               
822                                msg = "Found plug-in: %s" % module.PLUGIN_ID
823                                logging.info(msg)
824                            except:
825                                msg = "Error accessing PluginPanel"
826                                msg += " in %s\n  %s" % (name, sys.exc_value)
827                                config.printEVT(msg)
828                    except:
829                        msg = "ViewerFrame._find_plugins: %s" % sys.exc_value
830                        #print msg
831                        logging.error(msg)
832                    finally:
833                        if not file == None:
834                            file.close()
835        except:
836            # Should raise and catch at a higher level and
837            # display error on status bar
838            pass 
839
840        return plugins
841   
842    def set_welcome_panel(self, panel_class):
843        """
844        Sets the default panel as the given welcome panel
845       
846        :param panel_class: class of the welcome panel to be instantiated
847       
848        """
849        self.defaultPanel = panel_class(self, -1, style=wx.RAISED_BORDER)
850       
851    def _get_panels_size(self, p):
852        """
853        find the proper size of the current panel
854        get the proper panel width and height
855        """
856        panel_height_min = self._window_height
857        panel_width_min = self._window_width
858        style = self.__gui_style & (GUIFRAME.MANAGER_ON)
859        if self._data_panel is not None  and (p == self._data_panel):
860            panel_width_min = DATAPANEL_WIDTH
861            panel_height_min = self._window_height * 0.8
862            return panel_width_min, panel_height_min
863        if hasattr(p, "CENTER_PANE") and p.CENTER_PANE:
864            style = self.__gui_style & (GUIFRAME.PLOTTING_ON|GUIFRAME.MANAGER_ON)
865            if style == (GUIFRAME.PLOTTING_ON|GUIFRAME.MANAGER_ON):
866                panel_width_min = self._window_width -\
867                            (DATAPANEL_WIDTH +config.PLOPANEL_WIDTH)
868            return panel_width_min, panel_height_min
869        return panel_width_min, panel_height_min
870   
871    def _load_panels(self):
872        """
873        Load all panels in the panels directory
874        """
875       
876        # Look for plug-in panels
877        panels = []   
878        for item in self.plugins:
879            if hasattr(item, "get_panels"):
880                ps = item.get_panels(self)
881                panels.extend(ps)
882       
883        # Show a default panel with some help information
884        # It also sets the size of the application windows
885        #TODO: Use this for slpash screen
886        if self.defaultPanel is None:
887            self.defaultPanel = DefaultPanel(self, -1, style=wx.RAISED_BORDER)
888        # add a blank default panel always present
889        self.panels["default"] = self.defaultPanel
890        self._mgr.AddPane(self.defaultPanel, wx.aui.AuiPaneInfo().
891                              Name("default").
892                              CenterPane().
893                              #CloseButton(False).
894                              #MinimizeButton(False).
895                              # This is where we set the size of
896                              # the application window
897                              BestSize(wx.Size(self._window_width, 
898                                               self._window_height)).
899                              Show())
900
901        #add data panel
902        self.panels["data_panel"] = self._data_panel
903        w, h = self._get_panels_size(self._data_panel)
904        self._mgr.AddPane(self._data_panel, wx.aui.AuiPaneInfo().
905                              Name(self._data_panel.window_name).
906                              Caption(self._data_panel.window_caption).
907                              Left().
908                              MinimizeButton().
909                              CloseButton(CLOSE_SHOW).
910                              TopDockable(False).
911                              BottomDockable(False).
912                              LeftDockable(True).
913                              RightDockable(False).
914                              BestSize(wx.Size(w, h)).
915                              Hide())
916
917        style = self.__gui_style & GUIFRAME.MANAGER_ON
918        data_pane = self._mgr.GetPane(self.panels["data_panel"].window_name)
919        if style != GUIFRAME.MANAGER_ON:
920            self._mgr.GetPane(self.panels["data_panel"].window_name).Hide()
921        else:
922            self._mgr.GetPane(self.panels["data_panel"].window_name).Show()
923           
924        # Add the panels to the AUI manager
925        for panel_class in panels:
926            p = panel_class
927            id = wx.NewId()
928            #w, h = self._get_panels_size(p)
929            # Check whether we need to put this panel
930            # in the center pane
931            if hasattr(p, "CENTER_PANE") and p.CENTER_PANE:
932                w, h = self._get_panels_size(p)
933                if p.CENTER_PANE:
934                    self.panels[str(id)] = p
935                    self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
936                                          Name(p.window_name).
937                                          CenterPane().
938                                          Center().
939                                          CloseButton(False).
940                                          Hide())
941            else:
942                self.panels[str(id)] = p
943                self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
944                                  Name(p.window_name).Caption(p.window_caption).
945                                  Right().
946                                  Dock().
947                                  TopDockable().
948                                  BottomDockable().
949                                  LeftDockable().
950                                  RightDockable().
951                                  MinimizeButton().
952                                  Hide())       
953     
954    def update_data(self, prev_data, new_data):
955        """
956        """
957        prev_id, data_state = self._data_manager.update_data(prev_data=prev_data, 
958                                       new_data=new_data)
959       
960        self._data_panel.remove_by_id(prev_id)
961        self._data_panel.load_data_list(data_state)
962       
963    def update_theory(self, data_id, theory, state=None):
964        """
965        """ 
966        data_state = self._data_manager.update_theory(data_id=data_id, 
967                                         theory=theory,
968                                         state=state) 
969        wx.CallAfter(self._data_panel.load_data_list, data_state)
970       
971    def onfreeze(self, theory_id):
972        """
973        """
974        data_state_list = self._data_manager.freeze(theory_id)
975        self._data_panel.load_data_list(list=data_state_list)
976        for data_state in data_state_list.values():
977            new_plot = data_state.get_data()
978           
979            wx.PostEvent(self, NewPlotEvent(plot=new_plot,
980                                             title=new_plot.title))
981       
982    def freeze(self, data_id, theory_id):
983        """
984        """
985        data_state_list = self._data_manager.freeze_theory(data_id=data_id, 
986                                                theory_id=theory_id)
987        self._data_panel.load_data_list(list=data_state_list)
988        for data_state in data_state_list.values():
989            new_plot = data_state.get_data()
990            wx.PostEvent(self, NewPlotEvent(plot=new_plot,
991                                             title=new_plot.title))
992       
993    def delete_data(self, data):
994        """
995        """
996        self._current_perspective.delete_data(data)
997       
998   
999    def get_context_menu(self, plotpanel=None):
1000        """
1001        Get the context menu items made available
1002        by the different plug-ins.
1003        This function is used by the plotting module
1004        """
1005        if plotpanel is None:
1006            return
1007        menu_list = []
1008        for item in self.plugins:
1009            menu_list.extend(item.get_context_menu(plotpanel=plotpanel))
1010        return menu_list
1011       
1012    def popup_panel(self, p):
1013        """
1014        Add a panel object to the AUI manager
1015       
1016        :param p: panel object to add to the AUI manager
1017       
1018        :return: ID of the event associated with the new panel [int]
1019       
1020        """
1021        ID = wx.NewId()
1022        self.panels[str(ID)] = p
1023        self.graph_num += 1
1024        if p.window_caption.split()[0] in NOT_SO_GRAPH_LIST:
1025            windowcaption = p.window_caption
1026        else:
1027            windowcaption = 'Graph'#p.window_caption
1028        windowname = p.window_name
1029        """
1030        count = 0
1031        for item in self.panels:
1032            if self.panels[item].window_name.startswith(p.window_name)\
1033               or self._mgr.GetPane(p.window_name).IsOk():
1034                count += 1
1035       
1036        if count > 0:
1037            windowname += str(count+1)
1038        """
1039        # Append nummber
1040        captions = self._get_plotpanel_captions()
1041        while (1):
1042            caption = windowcaption + '%s'% str(self.graph_num)
1043            if caption not in captions:
1044                break
1045            self.graph_num += 1
1046            # protection from forever-loop: max num = 1000
1047            if self.graph_num > 1000:
1048                break
1049        if p.window_caption.split()[0] not in NOT_SO_GRAPH_LIST:
1050            p.window_caption = caption
1051        #p.window_caption = windowcaption+ str(self.graph_num)
1052        p.window_name = windowname + str(self.graph_num)
1053       
1054        style1 = self.__gui_style & GUIFRAME.FIXED_PANEL
1055        style2 = self.__gui_style & GUIFRAME.FLOATING_PANEL
1056        if style1 == GUIFRAME.FIXED_PANEL:
1057            self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
1058                              Name(p.window_name).
1059                              Caption(p.window_caption).
1060                              Position(10).
1061                              Floatable().
1062                              Right().
1063                              Dock().
1064                              MinimizeButton().
1065                              Resizable(True).
1066                              # Use a large best size to make sure the AUI
1067                              # manager takes all the available space
1068                              BestSize(wx.Size(PLOPANEL_WIDTH, 
1069                                               PLOPANEL_HEIGTH)))
1070       
1071            self._popup_fixed_panel(p)
1072   
1073        elif style2 == GUIFRAME.FLOATING_PANEL:
1074            self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
1075                              Name(p.window_name).Caption(p.window_caption).
1076                              MinimizeButton().
1077                              Resizable(True).
1078                              # Use a large best size to make sure the AUI
1079                              #  manager takes all the available space
1080                              BestSize(wx.Size(PLOPANEL_WIDTH, 
1081                                               PLOPANEL_HEIGTH)))
1082
1083            self._popup_floating_panel(p)
1084 
1085        # Register for showing/hiding the panel
1086        wx.EVT_MENU(self, ID, self.on_view)
1087        if p not in self.plot_panels.values() and p.group_id != None:
1088            self.plot_panels[ID] = p
1089            if len(self.plot_panels) == 1:
1090                self.panel_on_focus = p
1091                self.set_panel_on_focus(None)
1092            if self._data_panel is not None and \
1093                self._plotting_plugin is not None:
1094                ind = self._data_panel.cb_plotpanel.FindString('None')
1095                if ind != wx.NOT_FOUND:
1096                    self._data_panel.cb_plotpanel.Delete(ind)
1097                if caption not in self._data_panel.cb_plotpanel.GetItems():
1098                    self._data_panel.cb_plotpanel.Append(str(caption), p)
1099        return ID
1100   
1101    def _get_plotpanel_captions(self):
1102        """
1103        Get all the plotpanel cations
1104       
1105        : return: list of captions
1106        """
1107        captions = []
1108        for Id in self.plot_panels.keys():
1109            captions.append(self.plot_panels[Id].window_caption)
1110       
1111        return captions
1112         
1113    def _setup_menus(self):
1114        """
1115        Set up the application menus
1116        """
1117        # Menu
1118        self._menubar = wx.MenuBar()
1119        self._add_menu_file()
1120        self._add_menu_edit()
1121        self._add_menu_view()
1122        #self._add_menu_data()
1123        self._add_menu_application()
1124        self._add_menu_tool()
1125        self._add_current_plugin_menu()
1126        self._add_menu_window()
1127        self._add_help_menu()
1128        self.SetMenuBar(self._menubar)
1129       
1130    def _setup_tool_bar(self):
1131        """
1132        add toolbar to the frame
1133        """
1134        #set toolbar
1135        self._toolbar = GUIToolBar(self, -1)
1136        self.SetToolBar(self._toolbar)
1137        self._update_toolbar_helper()
1138        self._on_toggle_toolbar(event=None)
1139   
1140    def _update_toolbar_helper(self):
1141        """
1142        """
1143        application_name = 'No Selected Analysis'
1144        panel_name = 'No Panel on Focus'
1145        if self._toolbar is  None:
1146            return
1147        if self.cpanel_on_focus is not None:
1148            self.reset_bookmark_menu(self.cpanel_on_focus)
1149        self._toolbar.update_toolbar(self.cpanel_on_focus)
1150        if self._current_perspective is not None:
1151            application_name = self._current_perspective.sub_menu
1152        if self.cpanel_on_focus is not None:
1153            panel_name = self.cpanel_on_focus.window_caption
1154           
1155        self._toolbar.update_button(application_name=application_name, 
1156                                        panel_name=panel_name)
1157       
1158        self._toolbar.Realize()
1159       
1160    def _add_menu_tool(self):
1161        """
1162        Tools menu
1163        Go through plug-ins and find tools to populate the tools menu
1164        """
1165        style = self.__gui_style & GUIFRAME.CALCULATOR_ON
1166        if style == GUIFRAME.CALCULATOR_ON:
1167            self._tool_menu = None
1168            for item in self.plugins:
1169                if hasattr(item, "get_tools"):
1170                    for tool in item.get_tools():
1171                        # Only create a menu if we have at least one tool
1172                        if self._tool_menu is None:
1173                            self._tool_menu = wx.Menu()
1174                        if tool[0].lower().count('python') > 0:
1175                            self._tool_menu.AppendSeparator()
1176                        id = wx.NewId()
1177                        self._tool_menu.Append(id, tool[0], tool[1])
1178                        wx.EVT_MENU(self, id, tool[2])
1179            if self._tool_menu is not None:
1180                self._menubar.Append(self._tool_menu, '&Tool')
1181               
1182    def _add_current_plugin_menu(self):
1183        """
1184        add current plugin menu
1185        Look for plug-in menus
1186        Add available plug-in sub-menus.
1187        """
1188        if (self._menubar is None) or (self._current_perspective is None):
1189            return
1190        #replace or add a new menu for the current plugin
1191       
1192        pos = self._menubar.FindMenu(str(self._applications_menu_name))
1193        if pos != -1:
1194            menu_list = self._current_perspective.populate_menu(self)
1195            if menu_list:
1196                for (menu, name) in menu_list:
1197                    hidden_menu = self._menubar.Replace(pos, menu, name) 
1198                    self._applications_menu_name = name
1199                #self._applications_menu_pos = pos
1200            else:
1201                hidden_menu = self._menubar.Remove(pos)
1202                self._applications_menu_name = None
1203            #get the position of the menu when it first added
1204            self._applications_menu_pos = pos
1205           
1206        else:
1207            menu_list = self._current_perspective.populate_menu(self)
1208            if menu_list:
1209                for (menu,name) in menu_list:
1210                    if self._applications_menu_pos == -1:
1211                        self._menubar.Append(menu, name)
1212                    else:
1213                        self._menubar.Insert(self._applications_menu_pos, menu, name)
1214                    self._applications_menu_name = name
1215                 
1216    def _add_help_menu(self):
1217        """
1218        add help menu
1219        """
1220        # Help menu
1221        self._help_menu = wx.Menu()
1222        style = self.__gui_style & GUIFRAME.WELCOME_PANEL_ON
1223        if style == GUIFRAME.WELCOME_PANEL_ON or custom_config != None:
1224            # add the welcome panel menu item
1225            if config.WELCOME_PANEL_ON and self.defaultPanel is not None:
1226                id = wx.NewId()
1227                self._help_menu.Append(id, '&Welcome', '')
1228                self._help_menu.AppendSeparator()
1229                wx.EVT_MENU(self, id, self.show_welcome_panel)
1230        # Look for help item in plug-ins
1231        for item in self.plugins:
1232            if hasattr(item, "help"):
1233                id = wx.NewId()
1234                self._help_menu.Append(id,'&%s Help' % item.sub_menu, '')
1235                wx.EVT_MENU(self, id, item.help)
1236        if config._do_tutorial:
1237            self._help_menu.AppendSeparator()
1238            id = wx.NewId()
1239            self._help_menu.Append(id,'&Tutorial', 'Software tutorial')
1240            wx.EVT_MENU(self, id, self._onTutorial)
1241           
1242        if config._do_aboutbox:
1243            self._help_menu.AppendSeparator()
1244            id = wx.NewId()
1245            self._help_menu.Append(id,'&About', 'Software information')
1246            wx.EVT_MENU(self, id, self._onAbout)
1247       
1248        # Checking for updates needs major refactoring to work with py2exe
1249        # We need to make sure it doesn't hang the application if the server
1250        # is not up. We also need to make sure there's a proper executable to
1251        # run if we spawn a new background process.
1252        #id = wx.NewId()
1253        #self._help_menu.Append(id,'&Check for update',
1254        # 'Check for the latest version of %s' % config.__appname__)
1255        #wx.EVT_MENU(self, id, self._check_update)
1256        self._menubar.Append(self._help_menu, '&Help')
1257           
1258    def _add_menu_view(self):
1259        """
1260        add menu items under view menu
1261        """
1262        if not VIEW_MENU:
1263            return
1264        self._view_menu = wx.Menu()
1265        style = self.__gui_style & GUIFRAME.MANAGER_ON
1266        id = wx.NewId()
1267        self._data_panel_menu = self._view_menu.Append(id,
1268                                                '&Show Data Explorer', '')
1269        wx.EVT_MENU(self, id, self.show_data_panel)
1270        if style == GUIFRAME.MANAGER_ON:
1271            self._data_panel_menu.SetText('Hide Data Explorer')
1272        else:
1273            self._data_panel_menu.SetText('Show Data Explorer')
1274 
1275        self._view_menu.AppendSeparator()
1276        id = wx.NewId()
1277        style1 = self.__gui_style & GUIFRAME.TOOLBAR_ON
1278        if style1 == GUIFRAME.TOOLBAR_ON:
1279            self._toolbar_menu = self._view_menu.Append(id,'&Hide Toolbar', '')
1280        else:
1281            self._toolbar_menu = self._view_menu.Append(id,'&Show Toolbar', '')
1282        wx.EVT_MENU(self, id, self._on_toggle_toolbar)
1283                   
1284        self._view_menu.AppendSeparator()
1285        id = wx.NewId()
1286        hint = "Display batch results into a grid"
1287        self._view_menu.Append(id, '&Show Batch Results', hint) 
1288        wx.EVT_MENU(self, id, self.show_batch_frame)
1289
1290        if custom_config != None:
1291            self._view_menu.AppendSeparator()
1292            id = wx.NewId()
1293            preference_menu = self._view_menu.Append(id,'Startup Setting', '')
1294            wx.EVT_MENU(self, id, self._on_preference_menu)
1295           
1296        self._menubar.Append(self._view_menu, '&View')   
1297         
1298    def show_batch_frame(self, event=None):
1299        """
1300        show the grid of result
1301        """
1302        # Show(False) before Show(True) in order to bring it to the front
1303        self.batch_frame.Show(False)
1304        self.batch_frame.Show(True)
1305       
1306    def _on_preference_menu(self, event):     
1307        """
1308        Build a panel to allow to edit Mask
1309        """
1310       
1311        from sans.guiframe.startup_configuration \
1312        import StartupConfiguration as ConfDialog
1313       
1314        self.panel = ConfDialog(parent=self, gui=self.__gui_style)
1315        self.panel.ShowModal()
1316        #wx.PostEvent(self.parent, event)
1317       
1318
1319    def _add_menu_window(self):
1320        """
1321        add a menu window to the menu bar
1322        Window menu
1323        Attach a menu item for each panel in our
1324        panel list that also appears in a plug-in.
1325       
1326        Only add the panel menu if there is only one perspective and
1327        it has more than two panels.
1328        Note: the first plug-in is always the plotting plug-in.
1329        The first application
1330        #plug-in is always the second one in the list.
1331        """
1332        self._window_menu = wx.Menu()
1333        if self._plotting_plugin is not None:
1334            for (menu, name) in self._plotting_plugin.populate_menu(self):
1335                self._window_menu.AppendSubMenu(menu, name)
1336        self._menubar.Append(self._window_menu, '&Graph')
1337
1338        style = self.__gui_style & GUIFRAME.PLOTTING_ON
1339        if style == GUIFRAME.PLOTTING_ON:
1340           
1341            self._window_menu.AppendSeparator()
1342            id = wx.NewId()
1343            hint = "Hide all the graph panels"
1344            self._window_menu.Append(id, '&Hide  All', hint)
1345            wx.EVT_MENU(self, id, self.hide_all_plotpanels)
1346            id = wx.NewId()
1347            hint = "Show all the graph panels"
1348            self._window_menu.Append(id, '&Show All', hint)
1349            wx.EVT_MENU(self, id, self.show_all_plotpanels)
1350           
1351            self._window_menu.AppendSeparator()
1352            id = wx.NewId()
1353            preferences_menu = wx.Menu()
1354            hint = "All plot panels will floating"
1355            preferences_menu.AppendRadioItem(id, '&Float All', hint)
1356            wx.EVT_MENU(self, id, self.set_plotpanel_floating)
1357            style = self.__gui_style & GUIFRAME.FLOATING_PANEL
1358            f_menu = preferences_menu.FindItemById(id)
1359            if style == GUIFRAME.FLOATING_PANEL: 
1360                f_checked = True
1361            else:
1362                f_checked = False
1363            f_menu.Check(f_checked)
1364
1365            id = wx.NewId()
1366            hint = "All plot panels will displayed within the frame"
1367            preferences_menu.AppendRadioItem(id, '&Dock All', hint)
1368            wx.EVT_MENU(self, id, self.set_plotpanel_fixed) 
1369            if not f_checked:
1370                d_menu = preferences_menu.FindItemById(id)
1371                d_menu.Check(True)
1372            preferences_menu.AppendSeparator()
1373            id = wx.NewId()
1374            hint = "Clean up the dock area for plots on new-plot"
1375            preferences_menu.AppendCheckItem(id, '&CleanUp Dock on NewPlot', hint)
1376            wx.EVT_MENU(self, id, self.on_cleanup_dock)
1377            flag = self.cleanup_plots
1378            if self.cleanup_plots:
1379                c_menu = preferences_menu.FindItemById(id)
1380                c_menu.Check(True) 
1381            self._window_menu.AppendSubMenu(preferences_menu,'&Preferences')
1382        if self._window_menu.GetMenuItemCount() == 0:
1383            pos = self._menubar.FindMenu('Graph')
1384            self._menubar.Remove(pos)
1385        #wx.EVT_MENU(self, id, self.show_preferences_panel)   
1386        """
1387        if len(self.plugins) == 2:
1388            plug = self.plugins[1]
1389            pers = plug.get_perspective()
1390       
1391            if len(pers) > 1:
1392                self._window_menu = wx.Menu()
1393                for item in self.panels:
1394                    if item == 'default':
1395                        continue
1396                    panel = self.panels[item]
1397                    if panel.window_name in pers:
1398                        self._window_menu.Append(int(item),
1399                                                  panel.window_caption,
1400                                        "Show %s window" % panel.window_caption)
1401                        wx.EVT_MENU(self, int(item), self.on_view)
1402                self._menubar.Append(self._window_menu, '&Window')
1403                """
1404
1405               
1406    def _add_menu_application(self):
1407        """
1408       
1409        # Attach a menu item for each defined perspective or application.
1410        # Only add the perspective menu if there are more than one perspectives
1411        add menu application
1412        """
1413        #style = self.__gui_style & GUIFRAME.MULTIPLE_APPLICATIONS
1414        #if style == GUIFRAME.MULTIPLE_APPLICATIONS:
1415        if self._num_perspectives  > 1:
1416            plug_data_count = False
1417            plug_no_data_count = False
1418            self._applications_menu = wx.Menu()
1419            pos = 0
1420            separator = self._applications_menu.AppendSeparator()
1421            for plug in self.plugins:
1422                if len(plug.get_perspective()) > 0:
1423                    id = wx.NewId()
1424                    if plug.use_data():
1425                       
1426                        self._applications_menu.InsertCheckItem(pos, id, plug.sub_menu,
1427                                      "Switch to analysis: %s" % plug.sub_menu)
1428                        plug_data_count = True
1429                        pos += 1
1430                    else:
1431                        plug_no_data_count = True
1432                        self._applications_menu.AppendCheckItem(id, plug.sub_menu,
1433                                      "Switch to analysis: %s" % plug.sub_menu)
1434                    wx.EVT_MENU(self, id, plug.on_perspective)
1435            #self._applications_menu.
1436            if (not plug_data_count or not plug_no_data_count):
1437                self._applications_menu.RemoveItem(separator)
1438            self._menubar.Append(self._applications_menu, '&Analysis')
1439            self._check_applications_menu()
1440           
1441    def _populate_file_menu(self):
1442        """
1443        Insert menu item under file menu
1444        """
1445        for plugin in self.plugins:
1446            if len(plugin.populate_file_menu()) > 0:
1447                for item in plugin.populate_file_menu():
1448                    m_name, m_hint, m_handler = item
1449                    id = wx.NewId()
1450                    self._file_menu.Append(id, m_name, m_hint)
1451                    wx.EVT_MENU(self, id, m_handler)
1452                self._file_menu.AppendSeparator()
1453               
1454    def _add_menu_file(self):
1455        """
1456        add menu file
1457        """
1458       
1459         # File menu
1460        self._file_menu = wx.Menu()
1461        #append item from plugin under menu file if necessary
1462        self._populate_file_menu()
1463        style = self.__gui_style & GUIFRAME.DATALOADER_ON
1464        style1 = self.__gui_style & GUIFRAME.MULTIPLE_APPLICATIONS
1465        if OPEN_SAVE_MENU:
1466            id = wx.NewId()
1467            hint_load_file = "read all analysis states saved previously"
1468            self._save_appl_menu = self._file_menu.Append(id, 
1469                                    '&Open Project', hint_load_file)
1470            wx.EVT_MENU(self, id, self._on_open_state_project)
1471           
1472        if style1 == GUIFRAME.MULTIPLE_APPLICATIONS:
1473            # some menu of plugin to be seen under file menu
1474            hint_load_file = "Read a status files and load"
1475            hint_load_file += " them into the analysis"
1476            id = wx.NewId()
1477            self._save_appl_menu = self._file_menu.Append(id, 
1478                                    '&Open Analysis', hint_load_file)
1479            wx.EVT_MENU(self, id, self._on_open_state_application)
1480        if OPEN_SAVE_MENU:       
1481            self._file_menu.AppendSeparator()
1482            id = wx.NewId()
1483            self._file_menu.Append(id, '&Save Project',
1484                                 'Save the state of the whole analysis')
1485            wx.EVT_MENU(self, id, self._on_save_project)
1486        if style1 == GUIFRAME.MULTIPLE_APPLICATIONS:
1487            #self._file_menu.AppendSeparator()
1488            id = wx.NewId()
1489            self._save_appl_menu = self._file_menu.Append(id, 
1490                                                      '&Save Analysis',
1491                        'Save state of the current active analysis panel')
1492            wx.EVT_MENU(self, id, self._on_save_application)
1493            self._file_menu.AppendSeparator()
1494       
1495        id = wx.NewId()
1496        self._file_menu.Append(id, '&Quit', 'Exit') 
1497        wx.EVT_MENU(self, id, self.Close)
1498        # Add sub menus
1499        self._menubar.Append(self._file_menu, '&File')
1500       
1501    def _add_menu_edit(self):
1502        """
1503        add menu edit
1504        """
1505        if not EDIT_MENU:
1506            return
1507        # Edit Menu
1508        self._edit_menu = wx.Menu()
1509        self._edit_menu.Append(GUIFRAME_ID.UNDO_ID, '&Undo', 
1510                               'Undo the previous action')
1511        wx.EVT_MENU(self, GUIFRAME_ID.UNDO_ID, self.on_undo_panel)
1512        self._edit_menu.Append(GUIFRAME_ID.REDO_ID, '&Redo', 
1513                               'Redo the previous action')
1514        wx.EVT_MENU(self, GUIFRAME_ID.REDO_ID, self.on_redo_panel)
1515        self._edit_menu.AppendSeparator()
1516        self._edit_menu.Append(GUIFRAME_ID.COPY_ID, '&Copy Params', 
1517                               'Copy parameter values')
1518        wx.EVT_MENU(self, GUIFRAME_ID.COPY_ID, self.on_copy_panel)
1519        self._edit_menu.Append(GUIFRAME_ID.PASTE_ID, '&Paste Params', 
1520                               'Paste parameter values')
1521        wx.EVT_MENU(self, GUIFRAME_ID.PASTE_ID, self.on_paste_panel)
1522        self._edit_menu.AppendSeparator()
1523       
1524        self._edit_menu.Append(GUIFRAME_ID.PREVIEW_ID, '&Report Results',
1525                               'Preview current panel')
1526        wx.EVT_MENU(self, GUIFRAME_ID.PREVIEW_ID, self.on_preview_panel)
1527        #self._edit_menu.Append(GUIFRAME_ID.PRINT_ID, '&Print',
1528        #                       'Print current panel')
1529        #wx.EVT_MENU(self, GUIFRAME_ID.PRINT_ID, self.on_print_panel)
1530        self._edit_menu.Append(GUIFRAME_ID.RESET_ID, '&Reset Page', 
1531                               'Reset current panel')
1532        wx.EVT_MENU(self, GUIFRAME_ID.RESET_ID, self.on_reset_panel)
1533   
1534        self._menubar.Append(self._edit_menu,  '&Edit')
1535        self.enable_edit_menu()
1536       
1537    def get_style(self):
1538        """
1539        """
1540        return  self.__gui_style
1541   
1542    def _add_menu_data(self):
1543        """
1544        Add menu item item data to menu bar
1545        """
1546        if self._data_plugin is not None:
1547            menu_list = self._data_plugin.populate_menu(self)
1548            if menu_list:
1549                for (menu, name) in menu_list:
1550                    self._menubar.Append(menu, name)
1551       
1552                       
1553    def _on_toggle_toolbar(self, event=None):
1554        """
1555        hide or show toolbar
1556        """
1557        if self._toolbar is None:
1558            return
1559        if self._toolbar.IsShown():
1560            if self._toolbar_menu is not None:
1561                self._toolbar_menu.SetItemLabel('Show Toolbar')
1562            self._toolbar.Hide()
1563        else:
1564            if self._toolbar_menu is not None:
1565                self._toolbar_menu.SetItemLabel('Hide Toolbar')
1566            self._toolbar.Show()
1567        self._toolbar.Realize()
1568       
1569    def _on_status_event(self, evt):
1570        """
1571        Display status message
1572        """
1573        # This CallAfter fixes many crashes on MAC.
1574        wx.CallAfter(self.sb.set_status, evt)
1575       
1576    def on_view(self, evt):
1577        """
1578        A panel was selected to be shown. If it's not already
1579        shown, display it.
1580       
1581        :param evt: menu event
1582       
1583        """
1584        panel_id = str(evt.GetId())
1585        self.on_set_plot_focus(self.panels[panel_id])
1586        self.show_panel(evt.GetId(), 'on')     
1587        wx.CallLater(5*TIME_FACTOR, self.set_schedule(True))
1588        self.set_plot_unfocus()
1589       
1590    def on_close_welcome_panel(self):
1591        """
1592        Close the welcome panel
1593        """
1594        if self.defaultPanel is None:
1595            return 
1596        default_panel = self._mgr.GetPane(self.panels["default"].window_name)
1597        if default_panel.IsShown():
1598            default_panel.Hide()
1599            # Recover current perspective
1600            perspective = self._current_perspective
1601            perspective.on_perspective(event=None)
1602            self._mgr.Update()
1603            # Show toolbar
1604            #style = self.__gui_style & GUIFRAME.TOOLBAR_ON
1605            #if (style == GUIFRAME.TOOLBAR_ON) & (not self._toolbar.IsShown()):
1606            #    self._on_toggle_toolbar()
1607           
1608    def show_welcome_panel(self, event):
1609        """   
1610        Display the welcome panel
1611        """
1612        if self.defaultPanel is None:
1613            return 
1614        for id, panel in self.panels.iteritems():
1615            if id  ==  'default':
1616                # Show default panel
1617                if not self._mgr.GetPane(self.panels["default"].window_name).IsShown():
1618                    self._mgr.GetPane(self.panels["default"].window_name).Show(True)
1619            elif id == "data_panel":
1620                flag = self._mgr.GetPane(self.panels["data_panel"].window_name).IsShown()
1621                self._mgr.GetPane(self.panels["data_panel"].window_name).Show(flag)
1622            elif panel not in self.plot_panels.values() :
1623                self._mgr.GetPane(self.panels[id].window_name).IsShown()
1624                self._mgr.GetPane(self.panels[id].window_name).Hide()
1625        #style = self.__gui_style & GUIFRAME.TOOLBAR_ON
1626        #if (style == GUIFRAME.TOOLBAR_ON) & (self._toolbar.IsShown()):
1627        #    #    self._toolbar.Show(True)
1628        #    self._on_toggle_toolbar()
1629
1630        self._mgr.Update()
1631       
1632    def show_panel(self, uid, show=None):
1633        """
1634        Shows the panel with the given id
1635       
1636        :param uid: unique ID number of the panel to show
1637       
1638        """
1639        ID = str(uid)
1640        config.printEVT("show_panel: %s" % ID)
1641        if ID in self.panels.keys():
1642            if not self._mgr.GetPane(self.panels[ID].window_name).IsShown(): 
1643                if show == 'on':
1644                    self._mgr.GetPane(self.panels[ID].window_name).Show()   
1645                elif self.panels[ID].window_caption.split(" ")[0] == \
1646                                                            "Residuals":
1647                    self._mgr.GetPane(self.panels[ID].window_name).Hide()
1648                else:
1649                    self._mgr.GetPane(self.panels[ID].window_name).Show()
1650                # Hide default panel
1651                self._mgr.GetPane(self.panels["default"].window_name).Hide()
1652        self._mgr.Update()     
1653        self._redraw_idle()
1654
1655    def show_all_plotpanels(self, event):
1656        """
1657        Show all plotpanels shown
1658       
1659        :param event: menu event
1660        """
1661        #event.Skip()
1662        any_hidden = False
1663        for id in self.plot_panels.keys():
1664            if self._mgr.GetPane(self.plot_panels[id].window_name).IsShown():
1665                continue
1666            else:
1667                any_hidden = True
1668                self.show_panel(id)
1669        if not any_hidden:
1670            msg = "No hidden graph panel exists."
1671        else:
1672            # Make sure the Checkmenuitem checked: Need this for-loop \
1673            # because the check menu is not responding on floating panel
1674            if IS_WIN:
1675                try:
1676                    for item in self._plotting_plugin.menu.GetMenuItems():
1677                        item.Check(True)
1678                except:
1679                    # MAC is not using checkmenuitem
1680                    pass
1681            msg = "All graph panels are shown."
1682        wx.PostEvent(self, StatusEvent(status=msg))
1683           
1684    def hide_all_plotpanels(self, event):
1685        """
1686        Hide all plotpanels shown
1687       
1688        :param event: menu event
1689        """
1690        #event.Skip()
1691        any_shown = False
1692        for ID in self.plot_panels.keys():
1693            if self._mgr.GetPane(self.plot_panels[ID].window_name).IsShown():
1694                any_shown = True
1695                try:
1696                    self.hide_panel(ID)
1697                except:
1698                    print "hide_panel: No such plot id %s" % ID
1699            else:
1700                continue
1701        if not any_shown:
1702            msg = "No shown graph panel exists."
1703        else:
1704            # Make sure the Checkmenuitem unchecked: Need this for-loop
1705            # because the check menu is not responding on floating panel
1706            if IS_WIN:
1707                try:
1708                    for item in self._plotting_plugin.menu.GetMenuItems():
1709                        item.Check(False)
1710                except:
1711                    # MAC is not using checkmenuitem
1712                    pass
1713            msg = "All graph panels are hidden."
1714        wx.PostEvent(self, StatusEvent(status=msg))
1715                   
1716    def hide_panel(self, uid):
1717        """
1718        hide panel except default panel
1719        """
1720        ID = str(uid)
1721        caption = self.panels[ID].window_caption
1722        config.printEVT("hide_panel: %s" % ID)
1723        if ID in self.panels.keys():
1724            if self._mgr.GetPane(self.panels[ID].window_name).IsShown():
1725                self._mgr.GetPane(self.panels[ID].window_name).Hide()
1726                if self._data_panel is not None and \
1727                            ID in self.plot_panels.keys():
1728                    self._data_panel.cb_plotpanel.Append(str(caption), p)
1729                # Do not Hide default panel here...
1730                #self._mgr.GetPane(self.panels["default"].window_name).Hide()
1731            self._mgr.Update()
1732               
1733    def delete_panel(self, uid):
1734        """
1735        delete panel given uid
1736        """
1737        ID = str(uid)
1738        config.printEVT("delete_panel: %s" % ID)
1739        try:
1740            caption = self.panels[ID].window_caption
1741        except:
1742            print "delete_panel: No such plot id as %s" % ID
1743            return
1744        if ID in self.panels.keys():
1745            self.panel_on_focus = None
1746            panel = self.panels[ID]
1747            self._mgr.DetachPane(panel)
1748            self._plotting_plugin.delete_panel(panel.group_id)
1749            panel.Hide()
1750            panel.clear()
1751            panel.Close()
1752            if panel in self.schedule_full_draw_list:
1753               self.schedule_full_draw_list.remove(panel) 
1754           
1755            #delete uid number not str(uid)
1756            if ID in self.plot_panels.keys():
1757                del self.plot_panels[ID]
1758            if ID in self.panels.keys():
1759                del self.panels[ID]
1760            #CallAfter: make sure panel is clear before updating mgr
1761            wx.CallAfter(self._mgr.Update)
1762            return 
1763     
1764    def clear_panel(self):
1765        """
1766        """
1767        for item in self.panels:
1768            try:
1769                self.panels[item].clear_panel()
1770            except:
1771                pass
1772           
1773    def create_gui_data(self, data, path=None):
1774        """
1775        """
1776        return self._data_manager.create_gui_data(data, path)
1777   
1778    def get_data(self, path):
1779        """
1780        """
1781        message = ""
1782        log_msg = ''
1783        output = []
1784        error_message = ""
1785        basename  = os.path.basename(path)
1786        root, extension = os.path.splitext(basename)
1787        if extension.lower() not in EXTENSIONS:
1788            log_msg = "File Loader cannot "
1789            log_msg += "load: %s\n" % str(basename)
1790            log_msg += "Try Data opening...."
1791            logging.info(log_msg)
1792            self.load_complete(output=output, error_message=error_message,
1793                   message=log_msg, path=path)   
1794            return
1795       
1796        #reading a state file
1797        for plug in self.plugins:
1798            reader, ext = plug.get_extensions()
1799            if reader is not None:
1800                #read the state of the single plugin
1801                if extension == ext:
1802                    reader.read(path)
1803                    return
1804                elif extension == APPLICATION_STATE_EXTENSION:
1805                    reader.read(path)
1806       
1807        style = self.__gui_style & GUIFRAME.MANAGER_ON
1808        if style == GUIFRAME.MANAGER_ON:
1809            if self._data_panel is not None:
1810                #data_state = self._data_manager.get_selected_data()
1811                #self._data_panel.load_data_list(data_state)
1812                self._mgr.GetPane(self._data_panel.window_name).Show(True)
1813     
1814    def load_from_cmd(self,  path):   
1815        """
1816        load data from cmd or application
1817        """ 
1818        if path is None:
1819            return
1820        else:
1821            path = os.path.abspath(path)
1822            if not os.path.isfile(path) and not os.path.isdir(path):
1823               return
1824           
1825            if os.path.isdir(path):
1826                self.load_folder(path)
1827                return
1828
1829        basename  = os.path.basename(path)
1830        root, extension = os.path.splitext(basename)
1831        if extension.lower() not in EXTENSIONS:
1832            self.load_data(path)
1833        else:
1834            self.load_state(path)
1835
1836        self._default_save_location = os.path.dirname(path)
1837
1838    def load_state(self, path):   
1839        """
1840        load data from command line or application
1841        """
1842        if path and (path is not None) and os.path.isfile(path):
1843            basename  = os.path.basename(path)
1844            if APPLICATION_STATE_EXTENSION is not None \
1845                and basename.endswith(APPLICATION_STATE_EXTENSION):
1846                #Hide current plot_panels i
1847                for ID in self.plot_panels.keys():
1848                    panel = self._mgr.GetPane(self.plot_panels[ID].window_name)
1849                    if panel.IsShown():
1850                        panel.Hide()
1851            self.get_data(path)
1852        if self.defaultPanel is not None and \
1853            self._mgr.GetPane(self.panels["default"].window_name).IsShown():
1854            self.on_close_welcome_panel()
1855           
1856    def load_data(self, path):
1857        """
1858        load data from command line
1859        """
1860        if not os.path.isfile(path):
1861            return
1862        basename  = os.path.basename(path)
1863        root, extension = os.path.splitext(basename)
1864        if extension.lower() in EXTENSIONS:
1865            log_msg = "Data Loader cannot "
1866            log_msg += "load: %s\n" % str(path)
1867            log_msg += "Try File opening ...."
1868            print log_msg
1869            return
1870        message = ""
1871        log_msg = ''
1872        output = {}
1873        error_message = ""
1874        try:
1875            print "Loading Data...:\n" + str(path) + "\n"
1876            temp =  self.loader.load(path)
1877            if temp.__class__.__name__ == "list":
1878                for item in temp:
1879                    data = self.create_gui_data(item, path)
1880                    output[data.id] = data
1881            else:
1882                data = self.create_gui_data(temp, path)
1883                output[data.id] = data
1884           
1885            self.add_data(data_list=output)
1886        except:
1887            error_message = "Error while loading"
1888            error_message += " Data from cmd:\n %s\n" % str(path)
1889            error_message += str(sys.exc_value) + "\n"
1890            print error_message
1891 
1892    def load_folder(self, path):
1893        """
1894        Load entire folder
1895        """   
1896        if not os.path.isdir(path):
1897            return
1898        if self._data_plugin is None:
1899            return
1900        try:
1901            if path is not None:
1902                self._default_save_location = os.path.dirname(path)
1903                file_list = self._data_plugin.get_file_path(path)
1904                self._data_plugin.get_data(file_list)
1905            else:
1906                return 
1907        except:
1908            error_message = "Error while loading"
1909            error_message += " Data folder from cmd:\n %s\n" % str(path)
1910            error_message += str(sys.exc_value) + "\n"
1911            print error_message
1912           
1913    def _on_open_state_application(self, event):
1914        """
1915        """
1916        path = None
1917        if self._default_save_location == None:
1918            self._default_save_location = os.getcwd()
1919       
1920        plug_wlist = self._on_open_state_app_helper()
1921        dlg = wx.FileDialog(self, 
1922                            "Choose a file", 
1923                            self._default_save_location, "",
1924                            plug_wlist)
1925        if dlg.ShowModal() == wx.ID_OK:
1926            path = dlg.GetPath()
1927            if path is not None:
1928                self._default_save_location = os.path.dirname(path)
1929        dlg.Destroy()
1930        self.load_state(path=path) 
1931   
1932    def _on_open_state_app_helper(self):
1933        """
1934        Helps '_on_open_state_application()' to find the extension of
1935        the current perspective/application
1936        """
1937        # No current perspective or no extension attr
1938        if self._current_perspective is None:
1939            return PLUGINS_WLIST
1940        try:
1941            # Find the extension of the perspective and get that as 1st item in list
1942            ind = None
1943            app_ext = self._current_perspective._extensions
1944            plug_wlist = config.PLUGINS_WLIST
1945            for ext in set(plug_wlist):
1946                if ext.count(app_ext) > 0:
1947                    ind = ext
1948                    break
1949            # Found the extension
1950            if ind != None:
1951                plug_wlist.remove(ind)
1952                plug_wlist.insert(0, ind)
1953                try:
1954                    plug_wlist ='|'.join(plug_wlist)
1955                except:
1956                    plug_wlist = ''
1957
1958        except:
1959            plug_wlist = PLUGINS_WLIST
1960           
1961        return plug_wlist
1962           
1963    def _on_open_state_project(self, event):
1964        """
1965        """
1966        path = None
1967        if self._default_save_location == None:
1968            self._default_save_location = os.getcwd()
1969       
1970        dlg = wx.FileDialog(self, 
1971                            "Choose a file", 
1972                            self._default_save_location, "",
1973                             APPLICATION_WLIST)
1974        if dlg.ShowModal() == wx.ID_OK:
1975            path = dlg.GetPath()
1976            if path is not None:
1977                self._default_save_location = os.path.dirname(path)
1978        dlg.Destroy()
1979       
1980        #try:   
1981        #    os.popen(path)
1982        #    #self.Close()
1983        #except:
1984        self.load_state(path=path)
1985       
1986    def _on_save_application(self, event):
1987        """
1988        save the state of the current active application
1989        """
1990        if self.cpanel_on_focus is not None:
1991            try:
1992                self.cpanel_on_focus.on_save(event)
1993            except:
1994                msg = "Error occurred while saving: "
1995                msg += "To save, the application panel should have a data set.."
1996                wx.PostEvent(self, StatusEvent(status=msg)) 
1997           
1998    def _on_save_project(self, event):
1999        """
2000        save the state of the SansView as *.svs
2001        """
2002        if self._current_perspective is  None:
2003            return
2004        reader, ext = self._current_perspective.get_extensions()
2005        path = None
2006        extension = '*' + APPLICATION_STATE_EXTENSION
2007        dlg = wx.FileDialog(self, "Save Project file",
2008                            self._default_save_location, "sansview_proj",
2009                             extension, 
2010                             wx.SAVE)
2011        if dlg.ShowModal() == wx.ID_OK:
2012            path = dlg.GetPath()
2013            self._default_save_location = os.path.dirname(path)
2014        else:
2015            return None
2016        dlg.Destroy()
2017        try:
2018            if path is None:
2019                return
2020            # default cansas xml doc
2021            doc = None
2022            for panel in self.panels.values():
2023                temp = panel.save_project(doc)
2024                if temp is not None:
2025                    doc = temp
2026             
2027            # Write the XML document
2028            extens = APPLICATION_STATE_EXTENSION
2029            fName = os.path.splitext(path)[0] + extens
2030            if doc != None:
2031                fd = open(fName, 'w')
2032                fd.write(doc.toprettyxml())
2033                fd.close()
2034            else:
2035                msg = "%s cannot read %s\n" % (str(APPLICATION_NAME), str(path))
2036                logging.error(msg)
2037        except:
2038           msg = "Error occurred while saving: "
2039           msg += "To save, at leat one application panel "
2040           msg += "should have a data set.."
2041           wx.PostEvent(self, StatusEvent(status=msg))   
2042                   
2043    def on_save_helper(self, doc, reader, panel, path):
2044        """
2045        Save state into a file
2046        """
2047        try:
2048            if reader is not None:
2049                # case of a panel with multi-pages
2050                if hasattr(panel, "opened_pages"):
2051                    for uid, page in panel.opened_pages.iteritems():
2052                        data = page.get_data()
2053                        # state must be cloned
2054                        state = page.get_state().clone()
2055                        if data is not None:
2056                            new_doc = reader.write_toXML(data, state)
2057                            if doc != None and hasattr(doc, "firstChild"):
2058                                child = new_doc.firstChild.firstChild
2059                                doc.firstChild.appendChild(child) 
2060                            else:
2061                                doc = new_doc
2062                # case of only a panel
2063                else:
2064                    data = panel.get_data()
2065                    state = panel.get_state()
2066                    if data is not None:
2067                        new_doc = reader.write_toXML(data, state)
2068                        if doc != None and hasattr(doc, "firstChild"):
2069                            child = new_doc.firstChild.firstChild
2070                            doc.firstChild.appendChild(child) 
2071                        else:
2072                            doc = new_doc
2073        except: 
2074            raise
2075            #pass
2076
2077        return doc
2078
2079    def quit_guiframe(self):
2080        """
2081        Pop up message to make sure the user wants to quit the application
2082        """
2083        message = "\nDo you really want to exit this application?        \n\n"
2084        dial = wx.MessageDialog(self, message, 'Confirm Exit',
2085                           wx.YES_NO|wx.YES_DEFAULT|wx.ICON_QUESTION)
2086        if dial.ShowModal() == wx.ID_YES:
2087            return True
2088        else:
2089            return False   
2090       
2091    def WindowClose(self, event=None):
2092        """
2093        Quit the application from x icon
2094        """
2095        flag = self.quit_guiframe()
2096        if flag:
2097            self.Close()
2098           
2099    def Close(self, event=None):
2100        """
2101        Quit the application
2102        """
2103        wx.Exit()
2104        sys.exit()
2105           
2106    def _check_update(self, event=None): 
2107        """
2108        Check with the deployment server whether a new version
2109        of the application is available.
2110        A thread is started for the connecting with the server. The thread calls
2111        a call-back method when the current version number has been obtained.
2112        """
2113       
2114        if hasattr(config, "__update_URL__"):
2115            import version
2116            checker = version.VersionThread2(config.__update_URL__,
2117                                            self._process_version,
2118                                            baggage=event==None)
2119            checker.start() 
2120   
2121    def _process_version(self, version, standalone=True):
2122        """
2123        Call-back method for the process of checking for updates.
2124        This methods is called by a VersionThread object once the current
2125        version number has been obtained. If the check is being done in the
2126        background, the user will not be notified unless there's an update.
2127       
2128        :param version: version string
2129        :param standalone: True of the update is being checked in
2130           the background, False otherwise.
2131           
2132        """
2133        try:
2134            if cmp(version, config.__version__) > 0:
2135                msg = "Version %s is available! See the Help " % str(version)
2136                msg += "menu to download it." 
2137                self.SetStatusText(msg)
2138                if not standalone:
2139                    import webbrowser
2140                    webbrowser.open(config.__download_page__)
2141            else:
2142                if not standalone:
2143                    msg = "You have the latest version"
2144                    msg += " of %s" % str(config.__appname__)
2145                    self.SetStatusText(msg)
2146        except:
2147            msg = "guiframe: could not get latest application"
2148            msg += " version number\n  %s" % sys.exc_value
2149            logging.error(msg)
2150            if not standalone:
2151                msg = "Could not connect to the application server."
2152                msg += " Please try again later."
2153                self.SetStatusText(msg)
2154                   
2155    def _onAbout(self, evt):
2156        """
2157        Pop up the about dialog
2158       
2159        :param evt: menu event
2160       
2161        """
2162        if config._do_aboutbox:
2163            import aboutbox 
2164            dialog = aboutbox.DialogAbout(None, -1, "")
2165            dialog.ShowModal()   
2166                     
2167    def _onTutorial(self, evt):
2168        """
2169        Pop up the tutorial dialog
2170       
2171        :param evt: menu event
2172       
2173        """
2174        if config._do_tutorial:   
2175            path = config.TUTORIAL_PATH
2176            if IS_WIN:
2177                try:
2178                    from sans.guiframe.pdfview import PDFFrame
2179                   
2180                    dialog = PDFFrame(None, -1, "Tutorial", path)
2181                    if hasattr(dialog, "IsIconized"):
2182                        if not dialog.IsIconized():
2183                            try:
2184                                icon = self.GetIcon()
2185                                dialog.SetIcon(icon)
2186                            except:
2187                                pass   
2188                    #self.SetTopWindow(dialog)
2189                    dialog.Show(True) 
2190                except:
2191                    msg = "This feature requires 'Adobe pdf Reader'\n"
2192                    msg += "Please install it first (Free)..."
2193                    wx.MessageBox(msg, 'Error')
2194            else:
2195                try:
2196                    command = "open "
2197                    command += path
2198                    os.system(command)
2199                except:
2200                    msg = "This feature requires 'Preview' Application\n"
2201                    msg += "Please install it first..."
2202                    wx.MessageBox(msg, 'Error')
2203
2204                     
2205    def set_manager(self, manager):
2206        """
2207        Sets the application manager for this frame
2208       
2209        :param manager: frame manager
2210        """
2211        self.app_manager = manager
2212       
2213    def post_init(self):
2214        """
2215        This initialization method is called after the GUI
2216        has been created and all plug-ins loaded. It calls
2217        the post_init() method of each plug-in (if it exists)
2218        so that final initialization can be done.
2219        """
2220        for item in self.plugins:
2221            if hasattr(item, "post_init"):
2222                item.post_init()
2223       
2224    def set_default_perspective(self):
2225        """
2226        Choose among the plugin the first plug-in that has
2227        "set_default_perspective" method and its return value is True will be
2228        as a default perspective when the welcome page is closed
2229        """
2230        for item in self.plugins:
2231            if hasattr(item, "set_default_perspective"):
2232                if item.set_default_perspective():
2233                    item.on_perspective(event=None)
2234                    return 
2235       
2236    def set_perspective(self, panels):
2237        """
2238        Sets the perspective of the GUI.
2239        Opens all the panels in the list, and closes
2240        all the others.
2241       
2242        :param panels: list of panels
2243        """
2244        #style = self.__gui_style & GUIFRAME.TOOLBAR_ON
2245        #if (style == GUIFRAME.TOOLBAR_ON) & (not self._toolbar.IsShown()):
2246        #    self._on_toggle_toolbar()
2247        for item in self.panels:
2248            # Check whether this is a sticky panel
2249            if hasattr(self.panels[item], "ALWAYS_ON"):
2250                if self.panels[item].ALWAYS_ON:
2251                    continue 
2252           
2253            if self.panels[item].window_name in panels:
2254                if not self._mgr.GetPane(self.panels[item].window_name).IsShown():
2255                    self._mgr.GetPane(self.panels[item].window_name).Show()
2256            else:
2257                # always show the data panel if enable
2258                style = self.__gui_style & GUIFRAME.MANAGER_ON
2259                if (style == GUIFRAME.MANAGER_ON) and self.panels[item] == self._data_panel:
2260                    if 'data_panel' in self.panels.keys():
2261                        flag = self._mgr.GetPane(self.panels['data_panel'].window_name).IsShown()
2262                        self._mgr.GetPane(self.panels['data_panel'].window_name).Show(flag)
2263                else:
2264                    if self._mgr.GetPane(self.panels[item].window_name).IsShown():
2265                        self._mgr.GetPane(self.panels[item].window_name).Hide()
2266               
2267        self._mgr.Update()
2268       
2269    def show_data_panel(self, event=None, action=True):
2270        """
2271        show the data panel
2272        """
2273        if self._data_panel_menu == None:
2274            return
2275        label = self._data_panel_menu.GetText()
2276        if label == 'Show Data Explorer':
2277            pane = self._mgr.GetPane(self.panels["data_panel"].window_name)
2278            #if not pane.IsShown():
2279            if action: 
2280                pane.Show(True)
2281                self._mgr.Update()
2282            self.__gui_style = self.__gui_style | GUIFRAME.MANAGER_ON
2283           
2284            self._data_panel_menu.SetText('Hide Data Explorer')
2285        else:
2286            pane = self._mgr.GetPane(self.panels["data_panel"].window_name)
2287            #if not pane.IsShown():
2288            if action:
2289                pane.Show(False)
2290                self._mgr.Update()
2291            self.__gui_style = self.__gui_style & (~GUIFRAME.MANAGER_ON)
2292            self._data_panel_menu.SetText('Show Data Explorer')
2293   
2294    def add_data_helper(self, data_list):
2295        """
2296        """
2297        if self._data_manager is not None:
2298            self._data_manager.add_data(data_list)
2299       
2300    def add_data(self, data_list):
2301        """
2302        receive a dictionary of data from loader
2303        store them its data manager if possible
2304        send to data the current active perspective if the data panel
2305        is not active.
2306        :param data_list: dictionary of data's ID and value Data
2307        """
2308        #Store data into manager
2309        self.add_data_helper(data_list)
2310        # set data in the data panel
2311        if self._data_panel is not None:
2312            data_state = self._data_manager.get_data_state(data_list.keys())
2313            self._data_panel.load_data_list(data_state)
2314        #if the data panel is shown wait for the user to press a button
2315        #to send data to the current perspective. if the panel is not
2316        #show  automatically send the data to the current perspective
2317        style = self.__gui_style & GUIFRAME.MANAGER_ON
2318        if style == GUIFRAME.MANAGER_ON:
2319            #wait for button press from the data panel to set_data
2320            if self._data_panel is not None:
2321                self._mgr.GetPane(self._data_panel.window_name).Show(True)
2322                self._mgr.Update() 
2323        else:
2324            #automatically send that to the current perspective
2325            self.set_data(data_id=data_list.keys())
2326            self.on_close_welcome_panel()
2327       
2328    def set_data(self, data_id, theory_id=None): 
2329        """
2330        set data to current perspective
2331        """
2332        list_data, _ = self._data_manager.get_by_id(data_id)
2333        if self._current_perspective is not None:
2334            if self.cleanup_plots:
2335                for uid, panel in self.plot_panels.iteritems():
2336                    #panel = self.plot_panels[uid]
2337                    window = self._mgr.GetPane(panel.window_name)
2338                    # To hide all docked plot panels when set the data
2339                    if not window.IsFloating():
2340                        self.hide_panel(uid)
2341            self._current_perspective.set_data(list_data.values())
2342            self.on_close_welcome_panel()
2343        else:
2344            msg = "Guiframe does not have a current perspective"
2345            logging.info(msg)
2346           
2347    def set_theory(self, state_id, theory_id=None):
2348        """
2349        """
2350        _, list_theory = self._data_manager.get_by_id(theory_id)
2351        if self._current_perspective is not None:
2352            try:
2353                self._current_perspective.set_theory(list_theory.values())
2354            except:
2355                msg = "Guiframe set_theory: \n" + str(sys.exc_value)
2356                logging.info(msg)
2357                wx.PostEvent(self, StatusEvent(status=msg, info="error"))
2358        else:
2359            msg = "Guiframe does not have a current perspective"
2360            logging.info(msg)
2361           
2362    def plot_data(self,  state_id, data_id=None,
2363                  theory_id=None, append=False):
2364        """
2365        send a list of data to plot
2366        """
2367        total_plot_list = []
2368        data_list, _ = self._data_manager.get_by_id(data_id)
2369        _, temp_list_theory = self._data_manager.get_by_id(theory_id)
2370        total_plot_list = data_list.values()
2371        for item in temp_list_theory.values():
2372            theory_data, theory_state = item
2373            total_plot_list.append(theory_data)
2374        GROUP_ID = wx.NewId()
2375        for new_plot in total_plot_list:
2376            if append:
2377                if self.panel_on_focus is None:
2378                    message = "cannot append plot. No plot panel on focus!"
2379                    message += "please click on any available plot to set focus"
2380                    wx.PostEvent(self, StatusEvent(status=message, 
2381                                                   info='warning'))
2382                    return 
2383                else:
2384                    if self.enable_add_data(new_plot):
2385                        new_plot.group_id = self.panel_on_focus.group_id
2386                    else:
2387                        message = "Only 1D Data can be append to"
2388                        message += " plot panel containing 1D data.\n"
2389                        message += "%s not be appended.\n" %str(new_plot.name)
2390                        message += "try new plot option.\n"
2391                        wx.PostEvent(self, StatusEvent(status=message, 
2392                                                   info='warning'))
2393            else:
2394                if self.cleanup_plots:
2395                    for id, panel in self.plot_panels.iteritems():
2396                        window = self._mgr.GetPane(panel.window_name)
2397                        # To hide all docked plot panels when set the data
2398                        if not window.IsFloating():
2399                            self.hide_panel(id)
2400                #if not append then new plot
2401                from sans.guiframe.dataFitting import Data2D
2402                if issubclass(Data2D, new_plot.__class__):
2403                    #for 2 D always plot in a separated new plot
2404                    new_plot.group_id = wx.NewId()
2405                else:
2406                    # plot all 1D in a new plot
2407                    new_plot.group_id = GROUP_ID
2408            title = "PLOT " + str(new_plot.title)
2409            wx.PostEvent(self, NewPlotEvent(plot=new_plot,
2410                                                  title=title,
2411                                                  group_id = new_plot.group_id))
2412           
2413    def remove_data(self, data_id, theory_id=None):
2414        """
2415        Delete data state if data_id is provide
2416        delete theory created with data of id data_id if theory_id is provide
2417        if delete all true: delete the all state
2418        else delete theory
2419        """
2420        temp = data_id + theory_id
2421        """
2422        value = [plug.is_in_use(temp) for plug in self.plugins]
2423        if len(value) > 0:
2424            print "value"
2425            return
2426            from data_panel import DataDialog
2427            dlg = DataDialog(data_list=data_list, nb_data=MAX_NBR_DATA)
2428            if dlg.ShowModal() == wx.ID_OK:
2429                selected_data_list = dlg.get_data()
2430            dlg.Destroy()
2431        """
2432        for plug in self.plugins:
2433            plug.delete_data(temp)
2434        total_plot_list = []
2435        data_list, _ = self._data_manager.get_by_id(data_id)
2436        _, temp_list_theory = self._data_manager.get_by_id(theory_id)
2437        total_plot_list = data_list.values()
2438        for item in temp_list_theory.values():
2439            theory_data, theory_state = item
2440            total_plot_list.append(theory_data)
2441        for new_plot in total_plot_list:
2442            id = new_plot.id
2443            for group_id in new_plot.list_group_id:
2444                wx.PostEvent(self, NewPlotEvent(id=id,
2445                                                   group_id=group_id,
2446                                                   action='remove'))
2447                #remove res plot: Todo: improve
2448                wx.CallAfter(self._remove_res_plot, id)
2449        self._data_manager.delete_data(data_id=data_id, 
2450                                       theory_id=theory_id)
2451       
2452    def _remove_res_plot(self, id):
2453        """
2454        Try to remove corresponding res plot
2455       
2456        : param id: id of the data
2457        """
2458        try:
2459            wx.PostEvent(self, NewPlotEvent(id=("res"+str(id)),
2460                                           group_id=("res"+str(id)),
2461                                           action='remove'))
2462        except:
2463            pass
2464   
2465    def save_data1d(self, data, fname):
2466        """
2467        Save data dialog
2468        """
2469        default_name = fname
2470        wildcard = "Text files (*.txt)|*.txt|"\
2471                    "CanSAS 1D files(*.xml)|*.xml" 
2472        path = None
2473        dlg = wx.FileDialog(self, "Choose a file",
2474                            self._default_save_location,
2475                            default_name, wildcard , wx.SAVE)
2476       
2477        if dlg.ShowModal() == wx.ID_OK:
2478            path = dlg.GetPath()
2479            # ext_num = 0 for .txt, ext_num = 1 for .xml
2480            # This is MAC Fix
2481            ext_num = dlg.GetFilterIndex()
2482            if ext_num == 0:
2483                format = '.txt'
2484            else:
2485                format = '.xml'
2486            path = os.path.splitext(path)[0] + format
2487            mypath = os.path.basename(path)
2488           
2489            #TODO: This is bad design. The DataLoader is designed
2490            #to recognize extensions.
2491            # It should be a simple matter of calling the .
2492            #save(file, data, '.xml') method
2493            # of the sans.dataloader.loader.Loader class.
2494            from sans.dataloader.loader import  Loader
2495            #Instantiate a loader
2496            loader = Loader() 
2497            format = ".txt"
2498            if os.path.splitext(mypath)[1].lower() == format:
2499                # Make sure the ext included in the file name
2500                # especially on MAC
2501                fName = os.path.splitext(path)[0] + format
2502                self._onsaveTXT(data, fName)
2503            format = ".xml"
2504            if os.path.splitext(mypath)[1].lower() == format:
2505                # Make sure the ext included in the file name
2506                # especially on MAC
2507                fName = os.path.splitext(path)[0] + format
2508                loader.save(fName, data, format)
2509            try:
2510                self._default_save_location = os.path.dirname(path)
2511            except:
2512                pass   
2513        dlg.Destroy()
2514       
2515       
2516    def _onsaveTXT(self, data, path):
2517        """
2518        Save file as txt 
2519        :TODO: Refactor and remove this method. See TODO in _onSave.
2520        """
2521        if not path == None:
2522            out = open(path, 'w')
2523            has_errors = True
2524            if data.dy == None or data.dy == []:
2525                has_errors = False
2526            # Sanity check
2527            if has_errors:
2528                try:
2529                    if len(data.y) != len(data.dy):
2530                        has_errors = False
2531                except:
2532                    has_errors = False
2533            if has_errors:
2534                if data.dx != None and data.dx != []:
2535                    out.write("<X>   <Y>   <dY>   <dX>\n")
2536                else:
2537                    out.write("<X>   <Y>   <dY>\n")
2538            else:
2539                out.write("<X>   <Y>\n")
2540               
2541            for i in range(len(data.x)):
2542                if has_errors:
2543                    if data.dx != None and data.dx != []:
2544                        out.write("%g  %g  %g  %g\n" % (data.x[i], 
2545                                                    data.y[i],
2546                                                    data.dy[i],
2547                                                    data.dx[i]))
2548                    else:
2549                        out.write("%g  %g  %g\n" % (data.x[i], 
2550                                                    data.y[i],
2551                                                    data.dy[i]))
2552                else:
2553                    out.write("%g  %g\n" % (data.x[i], 
2554                                            data.y[i]))
2555            out.close()                   
2556           
2557    def save_data2d(self, data, fname):   
2558        """
2559        Save data2d dialog
2560        """
2561        default_name = fname
2562        wildcard = "IGOR/DAT 2D file in Q_map (*.dat)|*.DAT"
2563        dlg = wx.FileDialog(self, "Choose a file",
2564                            self._default_save_location,
2565                            default_name, wildcard , wx.SAVE)
2566       
2567        if dlg.ShowModal() == wx.ID_OK:
2568            path = dlg.GetPath()
2569            # ext_num = 0 for .txt, ext_num = 1 for .xml
2570            # This is MAC Fix
2571            ext_num = dlg.GetFilterIndex()
2572            if ext_num == 0:
2573                format = '.dat'
2574            else:
2575                format = ''
2576            path = os.path.splitext(path)[0] + format
2577            mypath = os.path.basename(path)
2578           
2579            #TODO: This is bad design. The DataLoader is designed
2580            #to recognize extensions.
2581            # It should be a simple matter of calling the .
2582            #save(file, data, '.xml') method
2583            # of the DataLoader.loader.Loader class.
2584            from sans.dataloader.loader import  Loader
2585            #Instantiate a loader
2586            loader = Loader() 
2587
2588            format = ".dat"
2589            if os.path.splitext(mypath)[1].lower() == format:
2590                # Make sure the ext included in the file name
2591                # especially on MAC
2592                fileName = os.path.splitext(path)[0] + format
2593                loader.save(fileName, data, format)
2594            try:
2595                self._default_save_location = os.path.dirname(path)
2596            except:
2597                pass   
2598        dlg.Destroy() 
2599                     
2600    def set_current_perspective(self, perspective):
2601        """
2602        set the current active perspective
2603        """
2604        self._current_perspective = perspective
2605        name = "No current analysis selected"
2606        if self._current_perspective is not None:
2607            self._add_current_plugin_menu()
2608            for panel in self.panels.values():
2609                if hasattr(panel, 'CENTER_PANE') and panel.CENTER_PANE:
2610                    for name in self._current_perspective.get_perspective():
2611                        if name == panel.window_name:
2612                            panel.on_set_focus(event=None)
2613                            break               
2614            name = self._current_perspective.sub_menu
2615            if self._data_panel is not None:
2616                self._data_panel.set_active_perspective(name)
2617                self._check_applications_menu()
2618            #Set the SansView title
2619            self._set_title_name(name)
2620         
2621           
2622    def _set_title_name(self, name):
2623        """
2624        Set the SansView title w/ the current application name
2625       
2626        : param name: application name [string]
2627        """
2628        # Set SanView Window title w/ application anme
2629        title = self.title + "  - " + name + " -"
2630        self.SetTitle(title)
2631           
2632    def _check_applications_menu(self):
2633        """
2634        check the menu of the current application
2635        """
2636        if self._applications_menu is not None:
2637            for menu in self._applications_menu.GetMenuItems():
2638                if self._current_perspective is not None:
2639                    name = self._current_perspective.sub_menu
2640                    if menu.IsCheckable():
2641                        if menu.GetLabel() == name:
2642                            menu.Check(True)
2643                        else:
2644                             menu.Check(False) 
2645           
2646    def set_plotpanel_floating(self, event=None):
2647        """
2648        make the plot panel floatable
2649        """
2650       
2651        self.__gui_style &= (~GUIFRAME.FIXED_PANEL)
2652        self.__gui_style |= GUIFRAME.FLOATING_PANEL
2653        plot_panel = []
2654        id = event.GetId()
2655        menu = self._window_menu.FindItemById(id)
2656        if self._plotting_plugin is not None:
2657            plot_panel = self.plot_panels.values()
2658            for p in plot_panel:
2659                self._popup_floating_panel(p)
2660            menu.Check(True)
2661           
2662    def set_plotpanel_fixed(self, event=None):
2663        """
2664        make the plot panel fixed
2665        """
2666        self.__gui_style &= (~GUIFRAME.FLOATING_PANEL)
2667        self.__gui_style |= GUIFRAME.FIXED_PANEL
2668        plot_panel = []
2669        id = event.GetId()
2670        menu = self._window_menu.FindItemById(id)
2671        if self._plotting_plugin is not None:
2672            plot_panel = self.plot_panels.values()
2673            for p in plot_panel:
2674                self._popup_fixed_panel(p)
2675            menu.Check(True)
2676           
2677    def on_cleanup_dock(self, event=None):     
2678        """
2679        Set Cleanup Dock option
2680        """
2681        if event == None:
2682            return
2683        id = event.GetId()
2684        menu = self._window_menu.FindItemById(id)
2685        Flag = self.cleanup_plots
2686        if not Flag:
2687            menu.Check(True)
2688            self.cleanup_plots = True
2689            msg = "Cleanup-Dock option set to 'ON'."
2690        else:
2691            menu.Check(False)
2692            self.cleanup_plots = False
2693            msg = "Cleanup-Dock option set to 'OFF'."
2694
2695        wx.PostEvent(self, StatusEvent(status= msg))
2696         
2697    def _popup_fixed_panel(self, p):
2698        """
2699        """
2700        style = self.__gui_style & GUIFRAME.FIXED_PANEL
2701        if style == GUIFRAME.FIXED_PANEL:
2702            self._mgr.GetPane(p.window_name).Dock()
2703            self._mgr.GetPane(p.window_name).Floatable()
2704            self._mgr.GetPane(p.window_name).Right()
2705            self._mgr.GetPane(p.window_name).TopDockable(False)
2706            self._mgr.GetPane(p.window_name).BottomDockable(False)
2707            self._mgr.GetPane(p.window_name).LeftDockable(False)
2708            self._mgr.GetPane(p.window_name).RightDockable(True)
2709            self._mgr.Update()
2710           
2711    def _popup_floating_panel(self, p):
2712        """
2713        """
2714        style = self.__gui_style &  GUIFRAME.FLOATING_PANEL
2715        if style == GUIFRAME.FLOATING_PANEL: 
2716            self._mgr.GetPane(p.window_name).Floatable(True)
2717            self._mgr.GetPane(p.window_name).Float()
2718            self._mgr.GetPane(p.window_name).Dockable(False)
2719            self._mgr.Update()
2720           
2721    def enable_add_data(self, new_plot):
2722        """
2723        Enable append data on a plot panel
2724        """
2725
2726        if self.panel_on_focus not in self._plotting_plugin.plot_panels.values():
2727            return
2728        is_theory = len(self.panel_on_focus.plots) <= 1 and \
2729            self.panel_on_focus.plots.values()[0].__class__.__name__ == "Theory1D"
2730           
2731        is_data2d = hasattr(new_plot, 'data')
2732       
2733        is_data1d = self.panel_on_focus.__class__.__name__ == "ModelPanel1D"\
2734            and self.panel_on_focus.group_id is not None
2735        has_meta_data = hasattr(new_plot, 'meta_data')
2736       
2737        #disable_add_data if the data is being recovered from  a saved state file.
2738        is_state_data = False
2739        if has_meta_data:
2740            if 'invstate' in new_plot.meta_data: is_state_data = True
2741            if  'prstate' in new_plot.meta_data: is_state_data = True
2742            if  'fitstate' in new_plot.meta_data: is_state_data = True
2743   
2744        return is_data1d and not is_data2d and not is_theory and not is_state_data
2745   
2746    def check_multimode(self, perspective=None):
2747        """
2748        Check the perspective have batch mode capablitity
2749        """
2750        if perspective == None or self._data_panel == None:
2751            return
2752        flag = perspective.get_batch_capable()
2753        flag_on = perspective.batch_on
2754        if flag:
2755            self._data_panel.rb_single_mode.SetValue(not flag_on)
2756            self._data_panel.rb_batch_mode.SetValue(flag_on)
2757        else:
2758            self._data_panel.rb_single_mode.SetValue(True)
2759            self._data_panel.rb_batch_mode.SetValue(False)
2760        self._data_panel.rb_single_mode.Enable(flag)
2761        self._data_panel.rb_batch_mode.Enable(flag)
2762               
2763
2764   
2765    def enable_edit_menu(self):
2766        """
2767        enable menu item under edit menu depending on the panel on focus
2768        """
2769        if self.cpanel_on_focus is not None and self._edit_menu is not None:
2770            flag = self.cpanel_on_focus.get_undo_flag()
2771            self._edit_menu.Enable(GUIFRAME_ID.UNDO_ID, flag)
2772            flag = self.cpanel_on_focus.get_redo_flag()
2773            self._edit_menu.Enable(GUIFRAME_ID.REDO_ID, flag)
2774            flag = self.cpanel_on_focus.get_copy_flag()
2775            self._edit_menu.Enable(GUIFRAME_ID.COPY_ID, flag)
2776            flag = self.cpanel_on_focus.get_paste_flag()
2777            self._edit_menu.Enable(GUIFRAME_ID.PASTE_ID, flag)
2778            #flag = self.cpanel_on_focus.get_print_flag()
2779            #self._edit_menu.Enable(GUIFRAME_ID.PRINT_ID, flag)
2780            flag = self.cpanel_on_focus.get_preview_flag()
2781            self._edit_menu.Enable(GUIFRAME_ID.PREVIEW_ID, flag)
2782            flag = self.cpanel_on_focus.get_reset_flag()
2783            self._edit_menu.Enable(GUIFRAME_ID.RESET_ID, flag)
2784        else:
2785            flag = False
2786            self._edit_menu.Enable(GUIFRAME_ID.UNDO_ID, flag)
2787            self._edit_menu.Enable(GUIFRAME_ID.REDO_ID, flag)
2788            self._edit_menu.Enable(GUIFRAME_ID.COPY_ID, flag)
2789            self._edit_menu.Enable(GUIFRAME_ID.PASTE_ID, flag)
2790            #self._edit_menu.Enable(GUIFRAME_ID.PRINT_ID, flag)
2791            self._edit_menu.Enable(GUIFRAME_ID.PREVIEW_ID, flag)
2792            self._edit_menu.Enable(GUIFRAME_ID.RESET_ID, flag)
2793           
2794    def on_undo_panel(self, event=None):
2795        """
2796        undo previous action of the last panel on focus if possible
2797        """
2798        if self.cpanel_on_focus is not None:
2799            self.cpanel_on_focus.on_undo(event)
2800           
2801    def on_redo_panel(self, event=None):
2802        """
2803        redo the last cancel action done on the last panel on focus
2804        """
2805        if self.cpanel_on_focus is not None:
2806            self.cpanel_on_focus.on_redo(event)
2807           
2808    def on_copy_panel(self, event=None):
2809        """
2810        copy the last panel on focus if possible
2811        """
2812        if self.cpanel_on_focus is not None:
2813            self.cpanel_on_focus.on_copy(event)
2814           
2815    def on_paste_panel(self, event=None):
2816        """
2817        paste clipboard to the last panel on focus
2818        """
2819        if self.cpanel_on_focus is not None:
2820            self.cpanel_on_focus.on_paste(event)
2821                   
2822    def on_bookmark_panel(self, event=None):
2823        """
2824        bookmark panel
2825        """
2826        if self.cpanel_on_focus is not None:
2827            self.cpanel_on_focus.on_bookmark(event)
2828           
2829    def append_bookmark(self, event=None):
2830        """
2831        Bookmark available information of the panel on focus
2832        """
2833        self._toolbar.append_bookmark(event)
2834           
2835    def on_save_panel(self, event=None):
2836        """
2837        save possible information on the current panel
2838        """
2839        if self.cpanel_on_focus is not None:
2840            self.cpanel_on_focus.on_save(event)
2841           
2842    def on_preview_panel(self, event=None):
2843        """
2844        preview information on the panel on focus
2845        """
2846        if self.cpanel_on_focus is not None:
2847            self.cpanel_on_focus.on_preview(event)
2848           
2849    def on_print_panel(self, event=None):
2850        """
2851        print available information on the last panel on focus
2852        """
2853        if self.cpanel_on_focus is not None:
2854            self.cpanel_on_focus.on_print(event)
2855           
2856    def on_zoom_panel(self, event=None):
2857        """
2858        zoom on the current panel if possible
2859        """
2860        if self.cpanel_on_focus is not None:
2861            self.cpanel_on_focus.on_zoom(event)
2862           
2863    def on_zoom_in_panel(self, event=None):
2864        """
2865        zoom in of the panel on focus
2866        """
2867        if self.cpanel_on_focus is not None:
2868            self.cpanel_on_focus.on_zoom_in(event)
2869           
2870    def on_zoom_out_panel(self, event=None):
2871        """
2872        zoom out on the panel on focus
2873        """
2874        if self.cpanel_on_focus is not None:
2875            self.cpanel_on_focus.on_zoom_out(event)
2876           
2877    def on_drag_panel(self, event=None):
2878        """
2879        drag apply to the panel on focus
2880        """
2881        if self.cpanel_on_focus is not None:
2882            self.cpanel_on_focus.on_drag(event)
2883           
2884    def on_reset_panel(self, event=None):
2885        """
2886        reset the current panel
2887        """
2888        if self.cpanel_on_focus is not None:
2889            self.cpanel_on_focus.on_reset(event)
2890           
2891    def on_change_caption(self, name, old_caption, new_caption):     
2892        """
2893        Change the panel caption
2894       
2895        :param name: window_name of the pane
2896        :param old_caption: current caption [string]
2897        :param new_caption: new caption [string]
2898        """
2899        # wx.aui.AuiPaneInfo
2900        pane_info = self.get_paneinfo(name) 
2901        # update the data_panel.cb_plotpanel
2902        if 'data_panel' in self.panels.keys():
2903            # remove from data_panel combobox
2904            data_panel = self.panels["data_panel"]
2905            if data_panel.cb_plotpanel is not None:
2906                # Check if any panel has the same caption
2907                has_newstring = data_panel.cb_plotpanel.FindString\
2908                                                            (str(new_caption)) 
2909                caption = new_caption
2910                if has_newstring != wx.NOT_FOUND:
2911                    captions = self._get_plotpanel_captions()
2912                    # Append nummber
2913                    inc = 1
2914                    while (1):
2915                        caption = new_caption + '_%s'% str(inc)
2916                        if caption not in captions:
2917                            break
2918                        inc += 1
2919                    # notify to users
2920                    msg = "Found Same Title: Added '_%s'"% str(inc)
2921                    wx.PostEvent(self, StatusEvent(status=msg))
2922                # update data_panel cb
2923                pos = data_panel.cb_plotpanel.FindString(str(old_caption)) 
2924                if pos != wx.NOT_FOUND:
2925                    data_panel.cb_plotpanel.SetString(pos, caption)
2926                    data_panel.cb_plotpanel.SetStringSelection(caption)
2927        # update window Show menu
2928        if self._window_menu != None:
2929            for item in self._window_menu.GetMenuItems():
2930                pos = self._window_menu.FindItem(old_caption)
2931                if self._window_menu.GetLabel(pos) == str(old_caption):
2932                    self._window_menu.SetLabel(pos, caption)
2933                break
2934        # New Caption
2935        pane_info.Caption(caption)
2936        # update aui manager
2937        self._mgr.Update()
2938        return caption
2939       
2940    def get_paneinfo(self, name):
2941        """
2942        Get pane Caption from window_name
2943       
2944        :param name: window_name in AuiPaneInfo
2945        : return: AuiPaneInfo of the name
2946        """
2947        return self._mgr.GetPane(name) 
2948   
2949    def enable_undo(self):
2950        """
2951        enable undo related control
2952        """
2953        if self.cpanel_on_focus is not None:
2954            self._toolbar.enable_undo(self.cpanel_on_focus)
2955           
2956    def enable_redo(self):
2957        """
2958        enable redo
2959        """
2960        if self.cpanel_on_focus is not None:
2961            self._toolbar.enable_redo(self.cpanel_on_focus)
2962           
2963    def enable_copy(self):
2964        """
2965        enable copy related control
2966        """
2967        if self.cpanel_on_focus is not None:
2968            self._toolbar.enable_copy(self.cpanel_on_focus)
2969           
2970    def enable_paste(self):
2971        """
2972        enable paste
2973        """
2974        if self.cpanel_on_focus is not None:
2975            self._toolbar.enable_paste(self.cpanel_on_focus)
2976                       
2977    def enable_bookmark(self):
2978        """
2979        Bookmark
2980        """
2981        if self.cpanel_on_focus is not None:
2982            self._toolbar.enable_bookmark(self.cpanel_on_focus)
2983           
2984    def enable_save(self):
2985        """
2986        save
2987        """
2988        if self.cpanel_on_focus is not None:
2989            self._toolbar.enable_save(self.cpanel_on_focus)
2990           
2991    def enable_preview(self):
2992        """
2993        preview
2994        """
2995        if self.cpanel_on_focus is not None:
2996            self._toolbar.enable_preview(self.cpanel_on_focus)
2997           
2998    def enable_print(self):
2999        """
3000        print
3001        """
3002        if self.cpanel_on_focus is not None:
3003            self._toolbar.enable_print(self.cpanel_on_focus)
3004           
3005    def enable_zoom(self):
3006        """
3007        zoom
3008        """
3009        if self.cpanel_on_focus is not None:
3010            self._toolbar.enable_zoom(self.panel_on_focus)
3011           
3012    def enable_zoom_in(self):
3013        """
3014        zoom in
3015        """
3016        if self.cpanel_on_focus is not None:
3017            self._toolbar.enable_zoom_in(self.panel_on_focus)
3018           
3019    def enable_zoom_out(self):
3020        """
3021        zoom out
3022        """
3023        if self.cpanel_on_focus is not None:
3024            self._toolbar.enable_zoom_out(self.panel_on_focus)
3025           
3026    def enable_drag(self, event=None):
3027        """
3028        drag
3029        """
3030        if self.cpanel_on_focus is not None:
3031            self._toolbar.enable_drag(self.panel_on_focus)
3032           
3033    def enable_reset(self):
3034        """
3035        reset the current panel
3036        """
3037        if self.cpanel_on_focus is not None:
3038            self._toolbar.enable_reset(self.panel_on_focus)
3039
3040    def set_schedule_full_draw(self, panel=None, func='del'):
3041        """
3042        Add/subtract the schedule full draw list with the panel given
3043       
3044        :param panel: plot panel
3045        :param func: append or del [string]
3046        """
3047
3048        # append this panel in the schedule list if not in yet
3049        if func == 'append':
3050            if not panel in self.schedule_full_draw_list:
3051                self.schedule_full_draw_list.append(panel) 
3052        # remove this panel from schedule list
3053        elif func == 'del':
3054            if len(self.schedule_full_draw_list) > 0:
3055                if panel in self.schedule_full_draw_list:
3056                    self.schedule_full_draw_list.remove(panel)
3057
3058        # reset the schdule
3059        if len(self.schedule_full_draw_list) == 0:
3060            self.schedule = False
3061        else:
3062            self.schedule = True   
3063       
3064    def full_draw(self):
3065        """
3066        Draw the panels with axes in the schedule to full dwar list
3067        """
3068        count = len(self.schedule_full_draw_list)
3069        #if not self.schedule:
3070        if count < 1:
3071            self.set_schedule(False)
3072            return
3073        else:
3074            ind = 0
3075            # if any of the panel is shown do full_draw
3076            for panel in self.schedule_full_draw_list:
3077                ind += 1
3078                if self._mgr.GetPane(panel.window_name).IsShown():
3079                    break
3080                # otherwise, return
3081                if ind == count:
3082                    return
3083
3084        #Simple redraw only for a panel shown
3085        def f_draw(panel):
3086            """
3087            Draw A panel in the full dwar list
3088            """
3089            try:
3090                # This checking of GetCapture is to stop redrawing
3091                # while any panel is capture.
3092                if self.GetCapture() == None:
3093                    # draw if possible
3094                    panel.set_resizing(False)
3095                    panel.Show(False)
3096                    panel.draw_plot()
3097                   
3098                    # Check if the panel is not shown
3099                    if not self._mgr.GetPane(panel.window_name).IsShown():
3100                        self._mgr.GetPane(panel.window_name).Hide()
3101                    else:
3102                        panel.Show(True)
3103            except:
3104                pass
3105       
3106        # Draw all panels       
3107        map(f_draw, self.schedule_full_draw_list)
3108       
3109        # Reset the attr 
3110        if len(self.schedule_full_draw_list) == 0:
3111            self.set_schedule(False)
3112        else:
3113            self.set_schedule(True)
3114        # do not update mgr
3115        #self._mgr.Update()
3116       
3117    def set_schedule(self, schedule=False): 
3118        """
3119        Set schedule
3120        """
3121        self.schedule = schedule
3122               
3123    def get_schedule(self): 
3124        """
3125        Get schedule
3126        """
3127        return self.schedule
3128   
3129    def on_set_plot_focus(self, panel):
3130        """
3131        Set focus on a plot panel
3132        """
3133        self.set_plot_unfocus()
3134        panel.on_set_focus(None) 
3135        # set focusing panel
3136        self.panel_on_focus = panel 
3137        self.set_panel_on_focus(None)
3138
3139    def set_plot_unfocus(self): 
3140        """
3141        Un focus all plot panels
3142        """
3143        for plot in self.plot_panels.values():
3144            plot.on_kill_focus(None)
3145
3146    def _onDrawIdle(self, *args, **kwargs):
3147        """
3148        ReDraw with axes
3149        """
3150        try:
3151            # check if it is time to redraw
3152            if self.GetCapture() == None:
3153                # Draw plot, changes resizing too
3154                self.full_draw()
3155        except:
3156            pass
3157           
3158        # restart idle       
3159        self._redraw_idle(*args, **kwargs)
3160
3161           
3162    def _redraw_idle(self, *args, **kwargs):
3163        """
3164        Restart Idle
3165        """
3166        # restart idle   
3167        self.idletimer.Restart(55*TIME_FACTOR, *args, **kwargs)
3168
3169       
3170class DefaultPanel(wx.Panel, PanelBase):
3171    """
3172    Defines the API for a panels to work with
3173    the GUI manager
3174    """
3175    ## Internal nickname for the window, used by the AUI manager
3176    window_name = "default"
3177    ## Name to appear on the window title bar
3178    window_caption = "Welcome panel"
3179    ## Flag to tell the AUI manager to put this panel in the center pane
3180    CENTER_PANE = True
3181    def __init__(self, parent, *args, **kwds):
3182        wx.Panel.__init__(self, parent, *args, **kwds)
3183        PanelBase.__init__(self, parent)
3184   
3185
3186
3187# Toy application to test this Frame
3188class ViewApp(wx.App):
3189    """
3190    """
3191    def OnInit(self):
3192        """
3193        """
3194        pos, size = self.window_placement((GUIFRAME_WIDTH, GUIFRAME_HEIGHT))
3195        self.frame = ViewerFrame(parent=None, 
3196                                 title=APPLICATION_NAME, 
3197                                 pos=pos, 
3198                                 gui_style = DEFAULT_STYLE,
3199                                 size=size) 
3200        self.frame.Hide()
3201        self.s_screen = None
3202
3203        try:
3204            self.open_file()
3205        except:
3206            msg = "%s Could not load " % str(APPLICATION_NAME)
3207            msg += "input file from command line.\n"
3208            logging.error(msg)
3209        # Display a splash screen on top of the frame.
3210        if len(sys.argv) > 1 and '--time' in sys.argv[1:]:
3211            log_time("Starting to display the splash screen")
3212        try:
3213            if os.path.isfile(SPLASH_SCREEN_PATH):
3214                self.s_screen = self.display_splash_screen(parent=self.frame, 
3215                                        path=SPLASH_SCREEN_PATH)
3216            else:
3217                self.frame.Show()   
3218        except:
3219            if self.s_screen is not None:
3220                self.s_screen.Close()
3221            msg = "Cannot display splash screen\n"
3222            msg += str (sys.exc_value)
3223            logging.error(msg)
3224            self.frame.Show()
3225 
3226        if hasattr(self.frame, 'special'):
3227            self.frame.special.SetCurrent()
3228        self.SetTopWindow(self.frame)
3229 
3230        return True
3231
3232    def open_file(self):
3233        """
3234        open a state file at the start of the application
3235        """
3236        input_file = None
3237        if len(sys.argv) >= 2:
3238            cmd = sys.argv[0].lower()
3239            basename  = os.path.basename(cmd)
3240            app_base = str(APPLICATION_NAME).lower()
3241            if os.path.isfile(cmd) or basename.lower() == app_base:
3242                app_py = app_base + '.py'
3243                app_exe = app_base + '.exe'
3244                app_app = app_base + '.app'
3245                if basename.lower() in [app_py, app_exe, app_app, app_base]:
3246                    data_base = sys.argv[1]
3247                    input_file = os.path.normpath(os.path.join(DATAPATH, 
3248                                                               data_base))
3249        if input_file is None:
3250            return
3251        if self.frame is not None:
3252            self.frame.set_input_file(input_file=input_file)
3253         
3254           
3255    def set_manager(self, manager):
3256        """
3257        Sets a reference to the application manager
3258        of the GUI manager (Frame)
3259        """
3260        self.frame.set_manager(manager)
3261       
3262    def build_gui(self):
3263        """
3264        Build the GUI
3265        """
3266        #try to load file at the start
3267        try:
3268            self.open_file()
3269        except:
3270            raise
3271        self.frame.build_gui()
3272        #if self.s_screen is not None and self.s_screen.IsShown():
3273        #    self.s_screen.Close()
3274       
3275    def set_welcome_panel(self, panel_class):
3276        """
3277        Set the welcome panel
3278       
3279        :param panel_class: class of the welcome panel to be instantiated
3280       
3281        """
3282        self.frame.set_welcome_panel(panel_class)
3283       
3284    def add_perspective(self, perspective):
3285        """
3286        Manually add a perspective to the application GUI
3287        """
3288        self.frame.add_perspective(perspective)
3289   
3290    def window_placement(self, size):
3291        """
3292        Determines the position and size of the application frame such that it
3293        fits on the user's screen without obstructing (or being obstructed by)
3294        the Windows task bar.  The maximum initial size in pixels is bounded by
3295        WIDTH x HEIGHT.  For most monitors, the application
3296        will be centered on the screen; for very large monitors it will be
3297        placed on the left side of the screen.
3298        """
3299        window_width, window_height = size
3300        screen_size = wx.GetDisplaySize()
3301        window_height = window_height if screen_size[1]>window_height else screen_size[1]-10
3302        window_width  = window_width if screen_size[0]> window_width else screen_size[0]-10
3303        xpos = ypos = 0
3304
3305        # Note that when running Linux and using an Xming (X11) server on a PC
3306        # with a dual  monitor configuration, the reported display size may be
3307        # that of both monitors combined with an incorrect display count of 1.
3308        # To avoid displaying this app across both monitors, we check for
3309        # screen 'too big'.  If so, we assume a smaller width which means the
3310        # application will be placed towards the left hand side of the screen.
3311
3312        _, _, x, y = wx.Display().GetClientArea() # size excludes task bar
3313        if len(sys.argv) > 1 and '--platform' in sys.argv[1:]:
3314            w, h = wx.DisplaySize()  # size includes task bar area
3315        # display on left side, not centered on screen
3316        if x > 1920 and x > (2*y): x = x / 2 
3317        if x > window_width:  xpos = (x - window_width)/2
3318        if y > window_height: ypos = (y - window_height)/2
3319
3320        # Return the suggested position and size for the application frame.
3321        return (xpos, ypos), (min(x, window_width), min(y, window_height))
3322   
3323    def display_splash_screen(self, parent, 
3324                              path=SPLASH_SCREEN_PATH):
3325        """Displays the splash screen.  It will exactly cover the main frame."""
3326       
3327        # Prepare the picture.  On a 2GHz intel cpu, this takes about a second.
3328        x, y = parent.GetSizeTuple()
3329        image = wx.Image(path, wx.BITMAP_TYPE_PNG)
3330        image.Rescale(SPLASH_SCREEN_WIDTH, 
3331                      SPLASH_SCREEN_HEIGHT, wx.IMAGE_QUALITY_HIGH)
3332        bm = image.ConvertToBitmap()
3333
3334        # Create and show the splash screen.  It will disappear only when the
3335        # program has entered the event loop AND either the timeout has expired
3336        # or the user has left clicked on the screen.  Thus any processing
3337        # performed in this routine (including sleeping) or processing in the
3338        # calling routine (including doing imports) will prevent the splash
3339        # screen from disappearing.
3340        #
3341        # Note that on Linux, the timeout appears to occur immediately in which
3342        # case the splash screen disappears upon entering the event loop.
3343        s_screen = wx.SplashScreen(bitmap=bm,
3344                         splashStyle=(wx.SPLASH_TIMEOUT|
3345                                              wx.SPLASH_CENTRE_ON_SCREEN),
3346                                 style=(wx.SIMPLE_BORDER|
3347                                        wx.FRAME_NO_TASKBAR|
3348                                        wx.STAY_ON_TOP),
3349                                       
3350                        milliseconds=SS_MAX_DISPLAY_TIME,
3351                        parent=parent,
3352                        id=wx.ID_ANY)
3353        from gui_statusbar import SPageStatusbar
3354        statusBar = SPageStatusbar(s_screen)
3355        s_screen.SetStatusBar(statusBar)
3356        s_screen.Bind(wx.EVT_CLOSE, self.on_close_splash_screen)
3357        s_screen.Show()
3358        return s_screen
3359       
3360       
3361    def on_close_splash_screen(self, event):
3362        """
3363        """
3364        self.frame.Show(True)
3365        event.Skip()
3366
3367if __name__ == "__main__": 
3368    app = ViewApp(0)
3369    app.MainLoop()
3370
3371             
Note: See TracBrowser for help on using the repository browser.