source: sasview/guiframe/gui_manager.py @ cc31608

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 cc31608 was 80ddbd0, checked in by Gervaise Alina <gervyh@…>, 13 years ago

add switch to batch mode on guiframe

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