'''
 ====================================================================
 Copyright (c) 2003-2006 Barry A Scott.  All rights reserved.

 This software is licensed as described in the file LICENSE.txt,
 which you should have received as part of this distribution.

 ====================================================================

    wb_frame.py

'''
import sys
import wx
import wx.stc
import logging

import wb_tree_panel
import wb_list_panel
import wb_list_panel_common
import wb_ids
import wb_exceptions
import wb_version
import wb_images
import wb_preferences_dialog
import wb_source_control_providers
import wb_platform_specific
import wb_bookmarks_dialogs

import wb_config

# point size and face need to choosen for platform
if wx.Platform == '__WXMSW__':
    face = 'Courier New'
    point_size = 8
elif wx.Platform == '__WXMAC__':
    face = 'Monaco'
    point_size = 12
else:
    face = 'Courier'
    point_size = 12

class WbFrame(wx.Frame):
    status_general = 0
    status_search = 0    # use the general area
    status_progress = 1
    status_action = 2
    status_num_fields = 3
    status_widths = [-1, 100, -2]

    def __init__( self, app ):
        self.app = app
        title = 'Work Bench'

        win_prefs = self.app.prefs.getWindow()

        extra_style = 0
        if win_prefs.maximized:
            extra_style = wx.MAXIMIZE
        wx.Frame.__init__(self, None, -1, title,
                win_prefs.frame_position,
                win_prefs.frame_size,
                wx.DEFAULT_FRAME_STYLE|extra_style )

        self.menu_edit = wx.Menu()
        self.menu_edit.Append( wb_ids.id_SP_EditCopy, "&Copy", "Copy Files" )
        self.menu_edit.Append( wb_ids.id_SP_EditCut, "&Cut", "Cut Files" )
        self.menu_edit.Append( wb_ids.id_SP_EditPaste, "&Paste", "Paste Files" )
        self.menu_edit.AppendSeparator()
        self.menu_edit.Append( wb_ids.id_ClearLog, "&Clear log", "Clear the log window" )

        if wx.Platform != '__WXMAC__':
            self.menu_file = wx.Menu()
        else:
            self.menu_file = self.menu_edit

        self.menu_file.Append( wx.ID_PREFERENCES, "&Preferences...", "Preferences" )
        self.menu_file.Append( wx.ID_EXIT, "E&xit", "Exit the application" )

        self.menu_actions = wx.Menu()
        self.menu_actions.Append(  wb_ids.id_Command_Shell, '&Command Shell', 'Command Shell' )
        self.menu_actions.Append(  wb_ids.id_File_Browser, '&File Browser', 'File Browser' )
        self.menu_actions.AppendSeparator()
        self.menu_actions.Append(  wb_ids.id_File_Edit, 'Edit', 'Edit' )
        self.menu_actions.Append(  wb_ids.id_Shell_Open, 'Open', 'Open' )
        self.menu_actions.AppendSeparator()
        self.menu_actions.Append(  wb_ids.id_SP_DiffWorkBase, 'Diff WC vs. BASE...', 'Diff WC vs. BASE...' )
        self.menu_actions.Append(  wb_ids.id_SP_DiffWorkHead, 'Diff WC vs. HEAD...', 'Diff WC vs. HEAD...' )
        self.menu_actions.Append(  wb_ids.id_SP_DiffWorkBranchOriginBase, 'Diff WC vs. branch origin BASE...', 'Diff WC vs. branch origin BASE...' )
        self.menu_actions.Append(  wb_ids.id_SP_DiffWorkBranchOriginHead, 'Diff WC vs. branch origin HEAD...', 'Diff WC vs. branch origin HEAD...' )

        self.menu_actions_conflict = wx.Menu()
        self.menu_actions.AppendMenu( wb_ids.id_SP_ConflictMenu, 'Conflict', self.menu_actions_conflict )
        self.menu_actions_conflict.Append( wb_ids.id_SP_DiffOldMine, 'Diff Conflict Old vs. Mine...', 'Diff Conflict Old vs. Mine...' )
        self.menu_actions_conflict.Append( wb_ids.id_SP_DiffMineNew, 'Diff Conflict Mine vs. New...', 'Diff Conflict Mine vs. New...' )
        self.menu_actions_conflict.Append( wb_ids.id_SP_DiffOldNew, 'Diff Conflict Old vs. New...', 'Diff Conflict Old vs. New...' )
        self.menu_actions_conflict.AppendSeparator()
        self.menu_actions_conflict.Append( wb_ids.id_SP_Resolved, 'Resolved Conflict', 'Resolved Conflict' )

        self.menu_actions.AppendSeparator()
        self.menu_actions.Append( wb_ids.id_SP_Annotate, 'Annotate...', 'Annotate...' )
        self.menu_actions.Append( wb_ids.id_SP_History, 'Log history...', 'Log history...' )
        self.menu_actions.Append( wb_ids.id_SP_Info, 'Information...', 'Information...' )
        self.menu_actions.Append( wb_ids.id_SP_Properties, 'Properties...', 'Properties...' )
        self.menu_actions.AppendSeparator()
        self.menu_actions.Append( wb_ids.id_SP_Update, 'Update', 'Update' )
        self.menu_actions.Append( wb_ids.id_SP_Checkout, 'Checkout', 'Checkout' )
        self.menu_actions.AppendSeparator()
        self.menu_actions.Append( wb_ids.id_SP_Checkin, 'Checkin...', 'Checkin...' )
        self.menu_actions.AppendSeparator()
        self.menu_actions.Append( wb_ids.id_SP_Lock, 'Lock...', 'Lock...' )
        self.menu_actions.Append( wb_ids.id_SP_Unlock, 'Unlock...', 'Unlock...' )
        self.menu_actions.AppendSeparator()
        self.menu_actions.Append( wb_ids.id_SP_NewFile, 'New File...', 'New File...' )
        self.menu_actions.Append( wb_ids.id_SP_Mkdir, 'Make directory...', 'Make directory...' )
        self.menu_actions.Append( wb_ids.id_SP_Add, 'Add', 'Add' )
        self.menu_actions.Append( wb_ids.id_SP_Rename, 'Rename...', 'Rename' )
        self.menu_actions.AppendSeparator()
        self.menu_actions.Append( wb_ids.id_SP_Delete, 'Delete...', 'Delete' )
        self.menu_actions.Append( wb_ids.id_SP_Revert, 'Revert...', 'Revert' )
        self.menu_actions.AppendSeparator()
        self.menu_actions.Append( wb_ids.id_SP_Cleanup, 'Clean up', 'Clean up working copy' )

        self.menu_reports = wx.Menu()
        self.menu_reports.Append( wb_ids.id_SP_Report_LocksWc, 'Working copy Locks...', 'Locks held in Working Copy' )
        self.menu_reports.Append( wb_ids.id_SP_Report_LocksRepos, 'Repository Locks...', 'Locks held in Repository' )
        self.menu_reports.AppendSeparator()
        self.menu_reports.Append( wb_ids.id_SP_Checkin, 'Changes...', 'Changes available for checkin' )
        self.menu_reports.Append( wb_ids.id_SP_Report_Updates, 'Updates...', 'Updates available in the Repository' )
        self.menu_reports.Append( wb_ids.id_SP_Report_BranchChanges, 'Branch changes...', 'Files changed in this branch' )

        self.menu_view = wx.Menu()
        self.menu_view.AppendCheckItem( wb_ids.id_View_ControlledFiles, "Show &Controlled files", "Show Controlled files" )
        self.menu_view.AppendCheckItem( wb_ids.id_View_UncontrolledFiles, "Show &Uncontrolled files", "Show Uncontrolled files" )
        self.menu_view.AppendCheckItem( wb_ids.id_View_IgnoredFiles, "Show &Ignored files", "Show ignored files" )
        self.menu_view.AppendSeparator()
        self.menu_view.AppendCheckItem( wb_ids.id_View_Recursive, "Show &Recursive files", "Show recursive files" )
        self.menu_view.AppendSeparator()
        self.menu_view.Append( wb_ids.id_View_Refresh, "&Refresh\tF5", "Refresh display" )
        self.menu_view.AppendCheckItem( wb_ids.id_View_AutoRefresh, "&Automatic Refresh", "Automatic refresh" )

        self.all_bookmark_ids = {}
        self.all_bookmark_top_level_menu_ids = []
        self.all_bookmark_folders = {}

        self.menu_bookmarks = wx.Menu()
        self.menu_bookmarks.Append( wb_ids.id_Bookmark_Add, 'Add', 'Add Bookmark' )
        self.menu_bookmarks.Append( wb_ids.id_Bookmark_Manage, 'Manage...', 'Manage Bookmarks' )
        self.menu_bookmarks.AppendSeparator()

        self.__bookmarkMenuReorder()

        self.menu_project = wx.Menu()
        self.menu_project.Append( wb_ids.id_Project_Add, 'Add...', 'Project Add' )
        self.menu_project.Append( wb_ids.id_Project_Update, 'Update...', 'Update Project' )
        self.menu_project.AppendSeparator()
        self.menu_project.Append( wb_ids.id_Project_Delete, 'Delete...', 'Delete Project' )

        self.menu_help = wx.Menu()
        self.menu_help.Append( wx.ID_ABOUT, "&About...", "About the application" )

        self.menu_bar = wx.MenuBar()
        if wx.Platform != '__WXMAC__':
            self.menu_bar.Append( self.menu_file, "&File" )
        self.menu_bar.Append( self.menu_edit, "&Edit" )
        self.menu_bar.Append( self.menu_view, "&View" )
        self.menu_bar.Append( self.menu_actions, "&Actions" )
        self.menu_bar.Append( self.menu_reports, "&Reports" )
        self.menu_bar.Append( self.menu_bookmarks, "&Bookmarks" )
        self.menu_bar.Append( self.menu_project, "&Project" )
        self.menu_bar.Append( self.menu_help, "&Help" )

        self.SetMenuBar( self.menu_bar )

        # Set the application icon
        self.SetIcon( wb_images.getIcon( 'wb.png') )

        # Add tool bar
        t = self.CreateToolBar( name="main",
                                style=wx.TB_HORIZONTAL ) # | wx.NO_BORDER | wx.TB_TEXT )

        bitmap_size = (32, 32)
        t.SetToolBitmapSize( bitmap_size )
        t.AddSimpleTool( wb_ids.id_SP_EditCut,
            wb_images.getBitmap( 'toolbar_images/editcut.png', bitmap_size ),
            'Cut Files and Folders', 'Cut Files and Folders' )
        t.AddSimpleTool( wb_ids.id_SP_EditCopy,
            wb_images.getBitmap( 'toolbar_images/editcopy.png', bitmap_size ),
            'Copy Files and Folders', 'Copy Files and Folders' )
        t.AddSimpleTool( wb_ids.id_SP_EditPaste,
            wb_images.getBitmap( 'toolbar_images/editpaste.png', bitmap_size ),
            'Paste Files and Folders', 'Paste Files and Folders' )
        t.AddSeparator()
        t.AddSimpleTool( wb_ids.id_Command_Shell,
            wb_images.getBitmap( 'toolbar_images/terminal.png', bitmap_size ),
            'Command Shell', 'Start new command shell' )
        t.AddSimpleTool( wb_ids.id_File_Browser,
            wb_images.getBitmap( 'toolbar_images/file_browser.png', bitmap_size ),
            'File Browser', 'File Browser' )
        t.AddSeparator()
        t.AddSimpleTool( wb_ids.id_File_Edit,
            wb_images.getBitmap( 'toolbar_images/edit.png', bitmap_size ),
            'Edit File', 'Edit File' )
        t.AddSimpleTool( wb_ids.id_Shell_Open,
            wb_images.getBitmap( 'toolbar_images/open.png', bitmap_size ),
            'Open File', 'Open File' )
        t.AddSeparator()
        t.AddSimpleTool( wb_ids.id_SP_DiffWorkBase,
            wb_images.getBitmap( 'toolbar_images/diff.png', bitmap_size ),
            'Diff changes against base', 'Diff changes against base' )
        t.AddSimpleTool( wb_ids.id_SP_History,
            wb_images.getBitmap( 'toolbar_images/history.png', bitmap_size ),
            'Show History log', 'Show History log' )
        t.AddSimpleTool( wb_ids.id_SP_Info,
            wb_images.getBitmap( 'toolbar_images/info.png', bitmap_size ),
            'File Information', 'File Information' )
        t.AddSimpleTool( wb_ids.id_SP_Properties,
            wb_images.getBitmap( 'toolbar_images/property.png', bitmap_size ),
            'File Properties', 'File Properties' )
        t.AddSeparator()
        t.AddSimpleTool( wb_ids.id_SP_Add,
            wb_images.getBitmap( 'toolbar_images/add.png', bitmap_size ),
            'Add Files and Folders', 'Add Files and Folders' )
        t.AddSimpleTool( wb_ids.id_SP_Delete,
            wb_images.getBitmap( 'toolbar_images/delete.png', bitmap_size ),
            'Delete selected Files and Folders', 'Delete selected Files and Folders' )
        t.AddSimpleTool( wb_ids.id_SP_Revert,
            wb_images.getBitmap( 'toolbar_images/revert.png', bitmap_size ),
            'Revert selected Files and Folders', 'Revert selected Files and Folders' )
        t.AddSeparator()
        t.AddSimpleTool( wb_ids.id_SP_Lock,
            wb_images.getBitmap( 'toolbar_images/lock.png', bitmap_size ),
            'Lock File', 'Lock File' )
        t.AddSimpleTool( wb_ids.id_SP_Unlock,
            wb_images.getBitmap( 'toolbar_images/unlock.png', bitmap_size ),
            'Unlock File', 'Unlock File' )
        t.AddSeparator()
        t.AddSimpleTool( wb_ids.id_SP_Checkin,
            wb_images.getBitmap( 'toolbar_images/checkin.png', bitmap_size ),
            'Checkin changes', 'Checkin changes' )
        t.AddSimpleTool( wb_ids.id_SP_Update,
            wb_images.getBitmap( 'toolbar_images/update.png', bitmap_size ),
            'Update working copy', 'Update working copy' )

        t.Realize()

        # Add the status bar
        s = self.CreateStatusBar()
        s.SetFieldsCount( WbFrame.status_num_fields )
        s.SetStatusWidths( WbFrame.status_widths )
        s.SetStatusText("Work Bench", WbFrame.status_general)
        s.SetStatusText("", WbFrame.status_progress)
        s.SetStatusText("Ready", WbFrame.status_action)
        if WbFrame.status_search != WbFrame.status_general:
            s.SetStatusText("", WbFrame.status_search)

        # Create the splitter windows
        self.h_split = wx.SplitterWindow( self, -1 )
        self.v_split = wx.SplitterWindow( self.h_split, -1 )

        # Make sure the splitters can't be removed by setting a minimum size
        self.v_split.SetMinimumPaneSize( 100 )
        self.h_split.SetMinimumPaneSize( 100 )

        # Create the main panels
        self.log_panel = LogCtrlPanel( self.app, self.h_split )
        self.list_panel = wb_list_panel.WbListPanel( self.app, self, self.v_split )
        self.tree_panel = wb_tree_panel.WbTreePanel( self.app, self, self.v_split )

        try_wrapper = wb_exceptions.TryWrapperFactory( self.app.log )

        size = self.GetClientSize()

        h_sash_pos = max( 200, int( size.height * win_prefs.h_sash_ratio) )
        v_sash_pos = max( 200, int( size.width  * win_prefs.v_sash_ratio) )

        # Arrange the panels with the splitter windows
        self.v_split.SplitVertically( self.tree_panel, self.list_panel, v_sash_pos )
        self.h_split.SplitHorizontally( self.v_split, self.log_panel, h_sash_pos )

        # for some unknown reason MENU events get blocked by tree and list controls
        for event_source in [self, self.tree_panel.tree_ctrl, self.list_panel.list_ctrl]:
            # Set up the event handlers
            wx.EVT_MENU( event_source, wx.ID_ABOUT, try_wrapper( self.OnCmdAbout ) )
            wx.EVT_MENU( event_source, wx.ID_PREFERENCES, try_wrapper( self.OnCmdPreferences ) )
            wx.EVT_MENU( event_source, wx.ID_EXIT, try_wrapper( self.OnCmdExit ) )
            wx.EVT_MENU( event_source, wb_ids.id_ClearLog, try_wrapper( self.OnCmdClearLog ) )

            wx.EVT_MENU( event_source, wb_ids.id_View_Refresh, try_wrapper( self.OnRefresh ) )

            wx.EVT_MENU( event_source, wb_ids.id_View_AutoRefresh, try_wrapper( self.OnToggleAutoRefresh ) )
            wx.EVT_UPDATE_UI( event_source, wb_ids.id_View_AutoRefresh, try_wrapper( self.OnUpdateAutoRefresh ) )

            wx.EVT_MENU( event_source, wb_ids.id_View_ControlledFiles, try_wrapper( self.OnToggleViewControlled ) )
            wx.EVT_UPDATE_UI( event_source, wb_ids.id_View_ControlledFiles, try_wrapper( self.OnUpdateViewControlled ) )

            wx.EVT_MENU( event_source, wb_ids.id_View_UncontrolledFiles, try_wrapper( self.OnToggleViewUncontrolled ) )
            wx.EVT_UPDATE_UI( event_source, wb_ids.id_View_UncontrolledFiles, try_wrapper( self.OnUpdateViewUncontrolled ) )

            wx.EVT_MENU( event_source, wb_ids.id_View_IgnoredFiles, try_wrapper( self.OnToggleViewIgnored ) )
            wx.EVT_UPDATE_UI( event_source, wb_ids.id_View_IgnoredFiles, try_wrapper( self.OnUpdateViewIgnored ) )

            wx.EVT_MENU( event_source, wb_ids.id_View_Recursive, try_wrapper( self.OnToggleViewRecursive ) )
            wx.EVT_UPDATE_UI( event_source, wb_ids.id_View_Recursive, try_wrapper( self.OnUpdateViewRecursive ) )

            wx.EVT_MENU( event_source, wb_ids.id_SP_EditCopy, self.app.eventWrapper( self.OnSpEditCopy ) )
            wx.EVT_UPDATE_UI( event_source, wb_ids.id_SP_EditCopy, self.app.eventWrapper( self.OnUpdateUiSpEditCopy ) )
            wx.EVT_MENU( event_source, wb_ids.id_SP_EditCut, self.app.eventWrapper( self.OnSpEditCut ) )
            wx.EVT_UPDATE_UI( event_source, wb_ids.id_SP_EditCut, self.app.eventWrapper( self.OnUpdateUiSpEditCut ) )
            wx.EVT_MENU( event_source, wb_ids.id_SP_EditPaste, self.app.eventWrapper( self.OnSpEditPaste ) )
            wx.EVT_UPDATE_UI( event_source, wb_ids.id_SP_EditPaste, self.app.eventWrapper( self.OnUpdateUiSpEditPaste ) )


        wx.EVT_MENU( self, wb_ids.id_File_Edit, try_wrapper( self.OnFileEdit ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_File_Edit, try_wrapper( self.OnUpdateUiFileEdit ) )
        wx.EVT_MENU( self, wb_ids.id_Shell_Open, try_wrapper( self.OnShellOpen ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_Shell_Open, try_wrapper( self.OnUpdateUiShellOpen ) )

        wx.EVT_MENU( self, wb_ids.id_Command_Shell, try_wrapper( self.OnCommandShell ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_Command_Shell, try_wrapper( self.OnUpdateUiCommandShell ) )
        wx.EVT_MENU( self, wb_ids.id_File_Browser, try_wrapper( self.OnFileBrowser ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_File_Browser, try_wrapper( self.OnUpdateUiFileBrowser ) )

        wx.EVT_MENU( self, wb_ids.id_SP_Add, self.app.eventWrapper( self.OnSpAdd ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Add, self.app.eventWrapper( self.OnUpdateUiSpAdd ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Annotate, self.app.eventWrapper( self.OnSpAnnotate ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Annotate, self.app.eventWrapper( self.OnUpdateUiSpAnnotate ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Checkin, self.app.eventWrapper( self.OnSpCheckin ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Checkin, self.app.eventWrapper( self.OnUpdateUiSpCheckin ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Checkout, self.app.eventWrapper( self.OnSpCheckout ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Checkout, self.app.eventWrapper( self.OnUpdateUiSpCheckout ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Cleanup, self.app.eventWrapper( self.OnSpCleanup ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Cleanup, self.app.eventWrapper( self.OnUpdateUiSpCleanup ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Delete, self.app.eventWrapper( self.OnSpDelete ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Delete, self.app.eventWrapper( self.OnUpdateUiSpDelete ) )
        wx.EVT_MENU( self, wb_ids.id_SP_DiffMineNew, self.app.eventWrapper( self.OnSpDiffMineNew ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_DiffMineNew, self.app.eventWrapper( self.OnUpdateUiSpDiffMineNew ) )
        wx.EVT_MENU( self, wb_ids.id_SP_DiffOldMine, self.app.eventWrapper( self.OnSpDiffOldMine ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_DiffOldMine, self.app.eventWrapper( self.OnUpdateUiSpDiffOldMine ) )
        wx.EVT_MENU( self, wb_ids.id_SP_DiffOldNew, self.app.eventWrapper( self.OnSpDiffOldNew ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_DiffOldNew, self.app.eventWrapper( self.OnUpdateUiSpDiffOldNew ) )
        wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkBase, self.app.eventWrapper( self.OnSpDiffWorkBase ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_DiffWorkBase, self.app.eventWrapper( self.OnUpdateUiSpDiffWorkBase ) )
        wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkHead, self.app.eventWrapper( self.OnSpDiffWorkHead ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_DiffWorkHead, self.app.eventWrapper( self.OnUpdateUiSpDiffWorkHead ) )
        wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkBranchOriginBase, self.app.eventWrapper( self.OnSpDiffWorkBranchOriginBase ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_DiffWorkBranchOriginBase, self.app.eventWrapper( self.OnUpdateUiSpDiffWorkBranchOriginBase ) )
        wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkBranchOriginHead, self.app.eventWrapper( self.OnSpDiffWorkBranchOriginHead ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_DiffWorkBranchOriginHead, self.app.eventWrapper( self.OnUpdateUiSpDiffWorkBranchOriginHead ) )
        wx.EVT_MENU( self, wb_ids.id_SP_History, self.app.eventWrapper( self.OnSpHistory ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_History, self.app.eventWrapper( self.OnUpdateUiSpHistory ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Info, self.app.eventWrapper( self.OnSpInfo ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Info, self.app.eventWrapper( self.OnUpdateUiSpInfo ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Lock, self.app.eventWrapper( self.OnSpLock ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Lock, self.app.eventWrapper( self.OnUpdateUiSpLock ) )
        wx.EVT_MENU( self, wb_ids.id_SP_NewFile, self.app.eventWrapper( self.OnSpNewFile ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_NewFile, self.app.eventWrapper( self.OnUpdateUiSpNewFile ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Mkdir, self.app.eventWrapper( self.OnSpMkdir ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Mkdir, self.app.eventWrapper( self.OnUpdateUiSpMkdir ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Properties, self.app.eventWrapper( self.OnSpProperties ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Properties, self.app.eventWrapper( self.OnUpdateUiSpProperties ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Rename, self.app.eventWrapper( self.OnSpRename ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Rename, self.app.eventWrapper( self.OnUpdateUiSpRename ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Resolved, self.app.eventWrapper( self.OnSpResolved ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Resolved, self.app.eventWrapper( self.OnUpdateUiSpResolved ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Revert, self.app.eventWrapper( self.OnSpRevert ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Revert, self.app.eventWrapper( self.OnUpdateUiSpRevert ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Unlock, self.app.eventWrapper( self.OnSpUnlock ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Unlock, self.app.eventWrapper( self.OnUpdateUiSpUnlock ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Update, self.app.eventWrapper( self.OnSpUpdate ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Update, self.app.eventWrapper( self.OnUpdateUiSpUpdate ) )

        wx.EVT_MENU( self, wb_ids.id_SP_Report_Updates, self.app.eventWrapper( self.OnSpReportUpdates ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Report_Updates, self.app.eventWrapper( self.OnUpdateUiSpReportUpdates ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Report_LocksWc, self.app.eventWrapper( self.OnSpReportLocksWc ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Report_LocksWc, self.app.eventWrapper( self.OnUpdateUiSpReportLocksWc ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Report_LocksRepos, self.app.eventWrapper( self.OnSpReportLocksRepos ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Report_LocksRepos, self.app.eventWrapper( self.OnUpdateUiSpReportLocksRepos ) )
        wx.EVT_MENU( self, wb_ids.id_SP_Report_BranchChanges, self.app.eventWrapper( self.OnSpReportBranchChanges ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Report_BranchChanges, self.app.eventWrapper( self.OnUpdateUiSpReportBranchChanges ) )

        wx.EVT_MENU( self, wb_ids.id_Project_Add, try_wrapper( self.app.eventWrapper( self.tree_panel.OnProjectAdd ) ) )
        wx.EVT_MENU( self, wb_ids.id_Project_Update, try_wrapper( self.app.eventWrapper( self.tree_panel.OnProjectUpdate ) ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_Project_Update, try_wrapper( self.app.eventWrapper( self.OnUpdateUiProjectUpdateOrDelete ) ) )
        wx.EVT_MENU( self, wb_ids.id_Project_Delete, try_wrapper( self.app.eventWrapper( self.tree_panel.OnProjectDelete ) ) )
        wx.EVT_UPDATE_UI( self, wb_ids.id_Project_Delete, try_wrapper( self.app.eventWrapper( self.OnUpdateUiProjectUpdateOrDelete ) ) )

        wx.EVT_MENU( self, wb_ids.id_Bookmark_Add, try_wrapper( self.OnBookmarkAdd ) )
        wx.EVT_MENU( self, wb_ids.id_Bookmark_Manage, try_wrapper( self.OnBookmarkManage ) )

        wx.EVT_SIZE( self, self.OnSize )
        wx.EVT_MOVE( self, self.OnMove )

        wx.EVT_SIZE( self.v_split, self.OnVertSize )
        wx.EVT_SIZE( self.h_split, self.OnHorizSize )
        wx.EVT_SPLITTER_SASH_POS_CHANGED( self.v_split, -1, self.OnVertSashPositionChanged )
        wx.EVT_SPLITTER_SASH_POS_CHANGED( self.h_split, -1, self.OnHorizSashPositionChanged )

        wx.EVT_CLOSE(self, try_wrapper( self.OnCloseWindow ))

        # default to the tree panel as the first set_focus can go missing
        self.event_handler = None

        self.ui_state_tree = None
        self.ui_state_list = None
        self.ui_state_focus = None

        self.setEventHandler( self.tree_panel )

        # need to set focus away from the filter controls
        # should not have to reach through these levels
        self.list_panel.list_ctrl.SetFocus()

    def setEventHandler( self, handler ):
        self.app.log.debug( 'setEventHandler from %r' % self.event_handler )
        self.app.log.debug( 'setEventHandler   to %r' % handler )
        if self.event_handler is not handler:
            self.event_handler = handler
            self.clearUpdateUiState()

    def clearEventHandler( self ):
        self.app.log.debug( 'clearEventHandler from %r to None' % self.event_handler )
        self.event_handler = None
        self.clearUpdateUiState()

    # Status bar settings
    def setStatus( self, text ):
        self.GetStatusBar().SetStatusText( text, WbFrame.status_general )

    def setProgress( self, text ):
        self.GetStatusBar().SetStatusText( text, WbFrame.status_progress )

    def setAction( self, text ):
        self.GetStatusBar().SetStatusText( text, WbFrame.status_action )

    def setSearch( self, text ):
        self.GetStatusBar().SetStatusText( text, WbFrame.status_search )

    def savePreferences( self ):
        win_prefs = self.app.prefs.getWindow()
        # Size and Position are already saved
        win_prefs.maximized = self.IsMaximized()

        self.tree_panel.savePreferences()
        self.list_panel.savePreferences()

    # Handler for the Exit menu command
    def OnCmdExit(self, event):
        self.Close()

    # Handler for the About menu command
    def OnCmdAbout(self, event):
        str_message =    ('Work Bench version: %d.%d.%d-%d\n' %
                    (wb_version.major, wb_version.minor,
                     wb_version.patch, wb_version.build) +
                '\n' + wb_source_control_providers.getProviderAboutStrings() +
                '\nWxPython %d.%d.%d.%d %s' % wx.VERSION +
                '\nPython %d.%d.%d %s %d\n' % sys.version_info +
                '\nCopyright Barry Scott (c) 2003-2006. All rights reserved'
                )
        wx.LogMessage( str_message )

    def OnCmdPreferences( self, event ):
        pref_dialog = wb_preferences_dialog.PreferencesDialog( self, self.app )
        rc = pref_dialog.ShowModal()
        if rc == wx.ID_OK:
            self.app.savePreferences()

        self.list_panel.updateHandler()
        self.refreshFrame()

    def OnUnlockedUi( self ):
        self.setAction( 'Ready' )
        self.tree_panel.updateTree()

    def OnSize( self, event ):
        if not self.IsMaximized():
            self.app.prefs.getWindow().frame_size = event.GetSize()
        event.Skip()

    def OnMove( self, event ):
        if not self.IsMaximized() and not self.IsIconized():
            # don't use the event.GetPosition() as it
            # is off by the window frame thinkness
            pt = self.GetPosition()
            self.app.prefs.getWindow().frame_position = pt
        self.app.prefs.getWindow().is_maximized = self.IsMaximized()
        event.Skip()

    def OnHorizSashPositionChanged( self, event ):
        _, h = self.h_split.GetClientSizeTuple()
        win_prefs = self.app.prefs.getWindow()
        win_prefs.h_sash_ratio = float( event.GetSashPosition() ) / float( h )
        event.Skip()

    def OnVertSashPositionChanged( self, event ):
        w, _ = self.v_split.GetClientSizeTuple()
        win_prefs = self.app.prefs.getWindow()
        win_prefs.v_sash_ratio = float( event.GetSashPosition() ) / float( w )
        event.Skip()

    def OnHorizSize( self, event ):
        win_prefs = self.app.prefs.getWindow()
        _, h = self.h_split.GetClientSizeTuple()
        self.h_split.SetSashPosition( max( 200, int( h * win_prefs.h_sash_ratio ) ) )
        event.Skip()

    def OnVertSize( self, event ):
        win_prefs = self.app.prefs.getWindow()
        w, _ = self.v_split.GetClientSizeTuple()
        self.v_split.SetSashPosition( max( 200, int( w * win_prefs.v_sash_ratio ) ) )
        event.Skip()

    #------------------------------------------------------------------------
    def OnActivateApp( self, is_active ):
        if is_active and self.app.prefs.getView().auto_refresh:
            self.refreshFrame()

    def OnToggleAutoRefresh( self, event ):
        view_prefs = self.app.prefs.getView()
        view_prefs.auto_refresh = not view_prefs.auto_refresh
        if view_prefs.auto_refresh:
            self.refreshFrame()

    def OnUpdateAutoRefresh( self, event ):
        view_prefs = self.app.prefs.getView()
        event.Check( view_prefs.auto_refresh )

    def OnToggleViewControlled( self, event ):
        view_prefs = self.app.prefs.getView()
        view_prefs.view_controlled = not view_prefs.view_controlled
        self.refreshFrame()

    def OnUpdateViewControlled( self, event ):
        view_prefs = self.app.prefs.getView()
        event.Check( view_prefs.view_controlled )

    def OnToggleViewUncontrolled( self, event ):
        view_prefs = self.app.prefs.getView()
        view_prefs.view_uncontrolled = not view_prefs.view_uncontrolled
        self.refreshFrame()

    def OnUpdateViewUncontrolled( self, event ):
        view_prefs = self.app.prefs.getView()
        event.Check( view_prefs.view_uncontrolled )

    def OnToggleViewIgnored( self, event ):
        view_prefs = self.app.prefs.getView()
        view_prefs.view_ignored = not view_prefs.view_ignored
        self.refreshFrame()

    def OnUpdateViewIgnored( self, event ):
        view_prefs = self.app.prefs.getView()
        event.Check( view_prefs.view_ignored )

    def OnToggleViewRecursive( self, event ):
        view_prefs = self.app.prefs.getView()
        view_prefs.view_recursive = not view_prefs.view_recursive
        self.refreshFrame()

    def OnUpdateViewRecursive( self, event ):
        view_prefs = self.app.prefs.getView()
        event.Check( view_prefs.view_recursive )

    def OnRefresh( self, event ):
        self.refreshFrame()

    def refreshFrame( self ):
        # tell the tree to refresh it will tell the list
        self.tree_panel.refreshTree()

    def expandSelectedTreeNode( self ):
        self.tree_panel.expandSelectedTreeNode()

    def selectTreeNodeInParent( self, filename ):
        self.tree_panel.selectTreeNodeInParent( filename )

    def selectTreeNode( self, filename ):
        self.tree_panel.selectTreeNode( filename )

    #------------------------------------------------------------------------
    def __addBookmarkMenuItem( self, pi ):
        bm_id = wx.NewId()
        self.all_bookmark_ids[ bm_id ] = pi

        if pi.menu_folder == '':
            menu = self.menu_bookmarks
            self.all_bookmark_top_level_menu_ids.append( bm_id )
        else:
            if pi.menu_folder not in self.all_bookmark_folders:
                menu_id = wx.NewId()
                self.all_bookmark_top_level_menu_ids.append( menu_id )

                menu = wx.Menu()

                self.all_bookmark_folders[ pi.menu_folder ] = menu
                self.menu_bookmarks.AppendMenu( menu_id, pi.menu_folder, menu )
            else:
                menu = self.all_bookmark_folders[ pi.menu_folder ]

        menu.Append( bm_id, pi.menu_name, pi.wc_path )
        try_wrapper = wb_exceptions.TryWrapperFactory( self.app.log )
        wx.EVT_MENU( self, bm_id, try_wrapper( self.OnBookmarkGoto ) )

    def __bookmarkMenuReorder( self ):
        for menu_id in self.all_bookmark_top_level_menu_ids:
            self.menu_bookmarks.Delete( menu_id )

        self.all_bookmark_ids = {}
        self.all_bookmark_folders = {}
        self.all_bookmark_top_level_menu_ids = []

        bm_prefs = self.app.prefs.getBookmarks()
        bookmark_list = []
        for bm_name in bm_prefs.getBookmarkNames():
            if bm_name == 'last position':
                continue

            bookmark_list.append( bm_prefs.getBookmark( bm_name ) )

        def sortFunction( a, b ):
            #
            #   Sort only by name - do not sort folder then name
            #
            a_name = a.menu_folder
            if a_name == '':
                a_name = a.menu_name

            b_name = b.menu_folder
            if b_name == '':
                b_name = b.menu_name

            rc = cmp( a_name.lower(), b_name.lower() )
            return rc

        bookmark_list.sort( sortFunction )

        for pi in bookmark_list:
            self.__addBookmarkMenuItem( pi )

    def OnBookmarkAdd( self, event ):
        pi = self.tree_panel.getSelectionProjectInfo()
        if pi is None:
            return
        bm_prefs = self.app.prefs.getBookmarks()
 
        if not bm_prefs.hasBookmark( pi.url ):
            print 'Adding bookmark to %s' % pi.wc_path
            bm_prefs.addBookmark( pi )

            self.__addBookmarkMenuItem( pi )
            self.__bookmarkMenuReorder()
            self.app.savePreferences()

    def OnBookmarkManage( self, event ):
        bookmarks = self.app.prefs.getBookmarks()
        dialog = wb_bookmarks_dialogs.BookmarkManageDialog( self, self.app, bookmarks )
        rc = dialog.ShowModal()
        if rc != wx.ID_OK:
            return

        dialog.setPreferences()
        self.app.savePreferences()
        self.__bookmarkMenuReorder()

    def OnBookmarkGoto( self, event ):
        self.tree_panel.gotoBookmark( self.all_bookmark_ids[event.GetId()].wc_path )

    #------------------------------------------------------------------------
    def OnCmdClearLog( self, event ):
        self.log_panel.ClearLog()

    def OnCloseWindow( self, event ):
        if self.app.exitAppNow():
            self.Destroy()

    #------------------------------------------------------------------------
    def OnFileEdit( self, event ):
        return self.list_panel.OnFileEdit()

    def OnUpdateUiFileEdit( self, event ):
        self.getUpdateUiState()
        #self.ui_state_list.printState('OnUpdateUiFileEdit')
        #print 'isListHandler() => %r' % self.event_handler.isListHandler()
        event.Enable( self.ui_state_list.file_exists and self.event_handler.isListHandler())

    def OnShellOpen( self, event ):
        return self.list_panel.OnShellOpen()

    def OnUpdateUiShellOpen( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_list.file_exists and self.event_handler.isListHandler() )

    def OnCommandShell( self, event ):
        return self.tree_panel.OnCommandShell()

    def OnUpdateUiCommandShell( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_tree.file_exists )

    def OnFileBrowser( self, event ):
        return self.tree_panel.OnFileBrowser()

    def OnUpdateUiFileBrowser( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_tree.file_exists )


    def OnSpEditCopy( self, event ):
        return self.Sp_Dispatch( 'OnSpEditCopy' )

    def OnUpdateUiSpEditCopy( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_focus.file_exists )

    def OnSpEditCut( self, event ):
        return self.Sp_Dispatch( 'OnSpEditCut' )

    def OnUpdateUiSpEditCut( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_focus.file_exists )

    def OnSpEditPaste( self, event ):
        return self.Sp_Dispatch( 'OnSpEditPaste' )

    def OnUpdateUiSpEditPaste( self, event ):
        self.getUpdateUiState()
        event.Enable( self.app.hasPasteData() )

    #----------------------------------------
    def OnSpAdd( self, event ):
        return self.Sp_Dispatch( 'OnSpAdd' )

    def OnUpdateUiSpAdd( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_focus.unversioned and self.ui_state_focus.file_exists )

    def OnSpAnnotate( self, event ):
        return self.Sp_Dispatch( 'OnSpAnnotate' )

    def OnUpdateUiSpAnnotate( self, event ):
        self.getUpdateUiState()
        if self.ui_state_focus.need_checkout:
            event.Enable( False )
        else:
            event.Enable( self.ui_state_tree.versioned )

    def OnSpCheckin( self, event ):
        return self.Sp_Dispatch( 'OnSpCheckin' )

    def OnUpdateUiSpCheckin( self, event ):
        self.getUpdateUiState()
        if self.ui_state_focus.need_checkout:
            event.Enable( False )
        else:
            event.Enable( self.ui_state_focus.need_checkin
                or (self.ui_state_focus.versioned
                    and self.event_handler is not None
                    and self.event_handler.isTreeHandler()) )

    def OnSpCheckout( self, event ):
        self.clearUpdateUiState()
        return self.tree_panel.OnSpCheckout()

    def OnUpdateUiSpCheckout( self, event ):
        self.getUpdateUiState()

        # this is a tree only command
        event.Enable( self.ui_state_tree.is_project_parent
                        and not self.ui_state_tree.file_exists
                        and self.ui_state_tree.versioned )

    def OnSpCleanup( self, event ):
        return self.Sp_Dispatch( 'OnSpCleanup' )

    def OnUpdateUiSpCleanup( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_focus.file_exists and self.ui_state_focus.versioned )

    def OnSpDelete( self, event ):
        return self.Sp_Dispatch( 'OnSpDelete' )

    def OnUpdateUiSpDelete( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_focus.file_exists )

    def OnSpDiffMineNew( self, event ):
        return self.Sp_Dispatch( 'OnSpDiffMineNew' )

    def OnUpdateUiSpDiffMineNew( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_list.conflict )

    def OnSpDiffOldMine( self, event ):
        return self.Sp_Dispatch( 'OnSpDiffOldMine' )

    def OnUpdateUiSpDiffOldMine( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_list.conflict )

    def OnSpDiffOldNew( self, event ):
        return self.Sp_Dispatch( 'OnSpDiffOldNew' )

    def OnUpdateUiSpDiffOldNew( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_list.conflict )

    def OnSpDiffWorkBase( self, event ):
        return self.Sp_Dispatch( 'OnSpDiffWorkBase' )

    def OnUpdateUiSpDiffWorkBase( self, event ):
        self.getUpdateUiState()
        if self.ui_state_list is self.ui_state_focus:
            event.Enable( self.ui_state_list.modified )
        else:
            event.Enable( True )

    def OnSpDiffWorkHead( self, event ):
        return self.Sp_Dispatch( 'OnSpDiffWorkHead' )

    def OnUpdateUiSpDiffWorkHead( self, event ):
        self.getUpdateUiState()
        if self.ui_state_focus.need_checkout:
            event.Enable( False )
        else:
            if self.ui_state_list is self.ui_state_focus:
                event.Enable( self.ui_state_list.versioned and not self.ui_state_list.new_versioned )
            else:
                event.Enable( self.ui_state_tree.versioned )

    def OnSpDiffWorkBranchOriginBase( self, event ):
        return self.Sp_Dispatch( 'OnSpDiffWorkBranchOriginBase' )

    def OnSpDiffWorkBranchOriginHead( self, event ):
        return self.Sp_Dispatch( 'OnSpDiffWorkBranchOriginHead' )

    def OnUpdateUiSpDiffWorkBranchOriginBase( self, event ):
        self.getUpdateUiState()
        if self.ui_state_focus.need_checkout:
            event.Enable( False )
        else:
            event.Enable( self.ui_state_list.versioned and not self.ui_state_list.new_versioned )

    def OnUpdateUiSpDiffWorkBranchOriginHead( self, event ):
        self.getUpdateUiState()
        if self.ui_state_focus.need_checkout:
            event.Enable( False )
        else:
            event.Enable( self.ui_state_list.versioned and not self.ui_state_list.new_versioned )

    def OnSpHistory( self, event ):
        return self.Sp_Dispatch( 'OnSpHistory' )

    def OnUpdateUiSpHistory( self, event ):
        self.getUpdateUiState()
        if self.ui_state_focus.need_checkout:
            event.Enable( False )
        else:
            event.Enable( self.ui_state_focus.versioned and not self.ui_state_focus.new_versioned )

    def OnSpInfo( self, event ):
        return self.Sp_Dispatch( 'OnSpInfo' )

    def OnUpdateUiSpInfo( self, event ):
        self.getUpdateUiState()
        if self.ui_state_focus.need_checkout:
            event.Enable( False )
        else:
            event.Enable( self.ui_state_focus.versioned )

    def OnSpLock( self, event ):
        return self.Sp_Dispatch( 'OnSpLock' )

    def OnUpdateUiSpLock( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_tree.file_exists )

    def OnSpMkdir( self, event ):
        return self.Sp_Dispatch( 'OnSpMkdir' )

    def OnUpdateUiSpMkdir( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_tree.file_exists )

    def OnSpNewFile( self, event ):
        return self.Sp_Dispatch( 'OnSpNewFile' )

    def OnUpdateUiSpNewFile( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_tree.file_exists )

    def OnSpProperties( self, event ):
        return self.Sp_Dispatch( 'OnSpProperties' )

    def OnUpdateUiSpProperties( self, event ):
        self.getUpdateUiState()
        if self.ui_state_focus.need_checkout:
            event.Enable( False )
        else:
            event.Enable( self.ui_state_focus.versioned )

    def OnSpRename( self, event ):
        return self.Sp_Dispatch( 'OnSpRename' )

    def OnUpdateUiSpRename( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_focus.file_exists )

    def OnSpReportUpdates( self, event ):
        return self.Sp_Dispatch( 'OnReportUpdates' )

    def OnUpdateUiSpReportUpdates( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_focus.versioned )

    def OnSpReportLocksWc( self, event ):
        return self.Sp_Dispatch( 'OnReportLocksWc' )

    def OnUpdateUiSpReportLocksWc( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_focus.versioned )

    def OnSpReportBranchChanges( self, event ):
        return self.Sp_Dispatch( 'OnReportBranchChanges' )

    def OnUpdateUiSpReportBranchChanges( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_focus.versioned )

    def OnSpReportLocksRepos( self, event ):
        return self.Sp_Dispatch( 'OnReportLocksRepos' )

    def OnUpdateUiSpReportLocksRepos( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_focus.versioned )

    def OnSpResolved( self, event ):
        return self.Sp_Dispatch( 'OnSpResolved' )

    def OnUpdateUiSpResolved( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_list.conflict )

    def OnSpRevert( self, event ):
        return self.Sp_Dispatch( 'OnSpRevert' )

    def OnUpdateUiSpRevert( self, event ):
        self.getUpdateUiState()
        if self.ui_state_focus.need_checkout:
            event.Enable( False )
        else:
            event.Enable( self.ui_state_focus.need_checkin
                or self.ui_state_focus.conflict
                or (not self.ui_state_focus.file_exists
                    and self.ui_state_focus.versioned) )

    def OnSpUnlock( self, event ):
        return self.Sp_Dispatch( 'OnSpUnlock' )

    def OnUpdateUiSpUnlock( self, event ):
        self.getUpdateUiState()
        event.Enable( self.ui_state_tree.file_exists )

    def OnSpUpdate( self, event ):
        return self.Sp_Dispatch( 'OnSpUpdate' )

    def OnUpdateUiSpUpdate( self, event ):
        self.getUpdateUiState()

        if self.ui_state_focus.need_checkout:
            event.Enable( False )

        elif self.ui_state_focus.is_project_parent:
            event.Enable( self.ui_state_focus.versioned and self.ui_state_focus.file_exists )

        else:
            event.Enable( self.ui_state_focus.versioned )

    def OnUpdateUiProjectUpdateOrDelete( self, event ):
        handler = self.tree_panel.getSelectionProjectHandler()
        if handler and handler.isProjectParent():
            event.Enable( True )
        else:
            event.Enable( False )

    #----------------------------------------
    def Sp_Dispatch( self, sp_func_name ):
        self.clearUpdateUiState()

        if self.event_handler is None:
            print 'No event_handler, cannot call', sp_func_name
            return None

        fn = getattr( self.event_handler, sp_func_name, None )
        if fn is None:
            print 'Not implemented: %r in %r' % (sp_func_name, self.event_handler)
            return None
        else:
            return fn()

    def getUpdateUiState( self ):
        if self.ui_state_tree is None:
            if wb_config.debug_selection: print 'Z: getUpdateUiState() tree'
            self.ui_state_tree = self.tree_panel.getUpdateUiState()
            if self.ui_state_tree is None:
                self.ui_state_tree = wb_tree_panel.TreeState()
            if wb_config.debug_selection: print 'Z: getUpdateUiState() tree need_checkout', self.ui_state_tree.need_checkout

        if self.ui_state_list is None:
            if wb_config.debug_selection: print 'Z: getUpdateUiState() list'
            self.ui_state_list = self.list_panel.getUpdateUiState()
            if self.ui_state_list is None:
                self.ui_state_list = wb_list_panel_common.ListItemState()
            if wb_config.debug_selection: print 'Z: getUpdateUiState() list need_checkout', self.ui_state_list.need_checkout

        if self.ui_state_focus is None:
            if self.event_handler is None:
                if wb_config.debug_selection: print 'Z: getUpdateUiState() event_handler is None set tree'
                self.ui_state_focus = self.ui_state_tree
            elif self.event_handler.isTreeHandler():
                if wb_config.debug_selection: print 'Z: getUpdateUiState() event_handler is Tree set tree'
                self.ui_state_focus = self.ui_state_tree
            else:
                if wb_config.debug_selection: print 'Z: getUpdateUiState() event_handler is List set list'
                self.ui_state_focus = self.ui_state_list
            if wb_config.debug_selection: print 'Z: getUpdateUiState() focus need_checkout', self.ui_state_focus.need_checkout

    def clearUpdateUiState( self ):
        if wb_config.debug_selection: print 'Z: clearUpdateUiState()'
        self.ui_state_tree = None
        self.ui_state_list = None
        self.ui_state_focus = None


#--------------------------------------------------------------------------------
class LogCtrlPanel(wx.Panel):
    ''' LogCtrlPanel '''
    def __init__( self, app, parent ):
        wx.Panel.__init__(self, parent, -1)

        self.app = app
        self.text_ctrl = StyledLogCtrl( self.app, self )

        # Redirect the console IO to this panel
        sys.stdin = file( wb_platform_specific.getNullDevice(), 'r' )
        sys.stdout = self
        sys.stderr = self

        # Redirect log to the Log panel
        log_handler = LogHandler( self.text_ctrl )
        self.app.log.addHandler( log_handler )

        wx.EVT_SIZE( self, wb_exceptions.TryWrapper( self.app.log, self.OnSize ) )

    #---------- Event handlers ------------------------------------------------------------

    def OnSize( self, event ):
        self.text_ctrl.SetWindowSize( self.GetSize() )

    #---------- Public methods ------------------------------------------------------------

    def write( self, string ):
        # only allowed to use GUI objects on the foreground thread
        if not self.app.isMainThread():
            self.app.foregroundProcess( self.write, (string,) )
            return

        if string[:6] == 'Error:':
            self.text_ctrl.WriteError(string)
        elif string[:5] == 'Info:':
            self.text_ctrl.WriteInfo(string)
        elif string[:8] == 'Warning:':
            self.text_ctrl.WriteWarning(string)
        elif string[:5] == 'Crit:':
            self.text_ctrl.WriteCritical(string)
        else:
            self.text_ctrl.WriteNormal(string)

        if not self.app.isStdIoRedirect():
            sys.__stdout__.write( string )

    def close( self ):
        pass

    def ClearLog( self ):
        self.text_ctrl.ClearText()

#--------------------------------------------------------------------------------
class LogHandler(logging.Handler):
    def __init__( self, log_ctrl ):
        self.log_ctrl = log_ctrl
        logging.Handler.__init__( self )

    def emit( self, record ):
        try:
            msg = self.format(record) + '\n'
            level = record.levelno
            if level >= logging.CRITICAL:
                self.log_ctrl.WriteCritical( msg )
            elif level >= logging.ERROR:
                self.log_ctrl.WriteError( msg )
            elif level >= logging.WARNING:
                self.log_ctrl.WriteWarning( msg )
            elif level >= logging.INFO:
                self.log_ctrl.WriteInfo( msg )
            elif level >= logging.DEBUG:
                self.log_ctrl.WriteDebug( msg )
            else:
                self.log_ctrl.WriteError( msg )
        except Exception:
            self.handleError(record)

#--------------------------------------------------------------------------------
class StyledLogCtrl(wx.stc.StyledTextCtrl):
    'StyledLogCtrl'
    def __init__(self, app, parent):
        self.app = app

        wx.stc.StyledTextCtrl.__init__(self, parent, -1,
                wx.DefaultPosition, wx.DefaultSize, wx.NO_BORDER)

        self.SetReadOnly( True )

        self.style_normal = 0
        self.style_error = 1
        self.style_info = 2
        self.style_warning = 3
        self.style_critical = 4
        self.style_debug = 4

        self.SetMarginWidth(0, 0)
        self.SetMarginWidth(1, 0)
        self.SetMarginWidth(2, 0)

        self.StyleSetSpec( wx.stc.STC_STYLE_DEFAULT, 
                "size:%d,face:%s,fore:#000000" % (point_size, face) )

        self.StyleSetSpec( self.style_normal,   "fore:#000000" )
        self.StyleSetSpec( self.style_error,    "fore:#DC143C" )    # Crimson
        self.StyleSetSpec( self.style_info,     "fore:#191970" )    # Midnight Blue
        self.StyleSetSpec( self.style_warning,  "fore:#008000" )    # Green
        self.StyleSetSpec( self.style_critical, "fore:#BA55D3" )    # Medium Orchid
        self.StyleSetSpec( self.style_debug,    "fore:#DC143C" )    # Crimson

    def SetWindowSize( self, size ):
        wx.stc.StyledTextCtrl.SetSize( self, size )
        self.EnsureCaretVisible()

    def WriteStyledText( self, text, style ):
        # only allowed to use GUI objects on the foreground thread
        if not self.app.isMainThread():
            self.app.foregroundProcess( self.WriteStyledText, (text, style) )
            return

        self.SetReadOnly(False)
        carot_pos = self.GetCurrentPos()
        insert_pos = self.GetLength()
        self.InsertText( insert_pos, text )
        self.StartStyling( insert_pos, 0xff )
        self.SetStyling( len(text), style )
        if carot_pos == insert_pos:
            new_carot_pos = self.GetLength()
            self.SetCurrentPos( new_carot_pos )
            self.SetSelectionStart( new_carot_pos )
            self.SetSelectionEnd( new_carot_pos )
            self.EnsureCaretVisible()
        self.SetReadOnly(True)

    def WriteNormal( self, text ):
        self.WriteStyledText( text, self.style_normal )

    def WriteError( self, text ):
        self.WriteStyledText( text, self.style_error )

    def WriteInfo( self, text ):
        self.WriteStyledText( text, self.style_info )

    def WriteWarning( self, text ):
        self.WriteStyledText( text, self.style_warning )

    def WriteCritical( self, text ):
        self.WriteStyledText( text, self.style_critical )

    def WriteDebug( self, text ):
        self.WriteStyledText( text, self.style_debug )

    def ClearText( self ):
        self.SetReadOnly(False)
        self.ClearAll()
        self.SetReadOnly(True)

