diff -urN --exclude=debian svn-workbench-1.5.0/Builder/brand_version.py svn-workbench-1.5.1/Builder/brand_version.py --- svn-workbench-1.5.0/Builder/brand_version.py 2007-02-22 00:07:40.000000000 +0100 +++ svn-workbench-1.5.1/Builder/brand_version.py 2004-10-21 15:05:42.000000000 +0200 @@ -19,18 +19,18 @@ key, value = [s.strip() for s in line.split('=',1)] branding_info[ key ] = value -#build_revision = os.popen( 'svnversion -c "%s" 2>&1' % os.environ.get( 'PYSVN_EXPORTED_FROM', '..' ), 'r' ).read().strip() +build_revision = os.popen( 'svnversion -c "%s" 2>&1' % os.environ.get( 'PYSVN_EXPORTED_FROM', '..' ), 'r' ).read().strip() # build_revision is either a range nnn:mmm or mmm # we only want the mmm -#build_revision = build_revision.split(':')[-1] -#print 'Info: revision %s' % build_revision +build_revision = build_revision.split(':')[-1] +print 'Info: revision %s' % build_revision -#revision, modifiers = re.compile( '(\d+)(.*)' ).search( build_revision ).groups() +revision, modifiers = re.compile( '(\d+)(.*)' ).search( build_revision ).groups() -#if modifiers: -# branding_info['BUILD'] = '0' -#else: -# branding_info['BUILD'] = revision +if modifiers: + branding_info['BUILD'] = '0' +else: + branding_info['BUILD'] = revision # read all the input text diff -urN --exclude=debian svn-workbench-1.5.0/Builder/builder_custom_init.cmd svn-workbench-1.5.1/Builder/builder_custom_init.cmd --- svn-workbench-1.5.0/Builder/builder_custom_init.cmd 2006-05-20 02:20:20.000000000 +0200 +++ svn-workbench-1.5.1/Builder/builder_custom_init.cmd 2007-02-03 21:11:55.000000000 +0100 @@ -3,7 +3,7 @@ set PY_MAJ=2 if not "%1" == "" set PY_MAJ=%1 -set PY_MIN=4 +set PY_MIN=5 if not "%2" == "" set PY_MIN=%2 set BUILD_TYPE=Release if not "%3" == "" set BUILD_TYPE=%3 @@ -13,8 +13,8 @@ rem in development the version info can be found rem otherwise the builder will have run it already -set COMPILER=msvc60 -if "%PY_MIN%" == "4" set COMPILER=msvc71 +set COMPILER=msvc71 +if "%PY_MIN%" == "3" set COMPILER=msvc60 if exist ..\..\ReleaseEngineering\win32-%COMPILER%\software-versions.cmd call ..\..\ReleaseEngineering\win32-%COMPILER%\software-versions.cmd diff -urN --exclude=debian svn-workbench-1.5.0/Builder/builder_custom_init.sh svn-workbench-1.5.1/Builder/builder_custom_init.sh --- svn-workbench-1.5.0/Builder/builder_custom_init.sh 2006-10-12 15:56:22.000000000 +0200 +++ svn-workbench-1.5.1/Builder/builder_custom_init.sh 2006-12-03 20:34:17.000000000 +0100 @@ -9,7 +9,7 @@ if [ "$(uname)" = "Darwin" ] then # default to 2.3 on Mac OS X - PREF_VER=2.3 + PREF_VER=2.5 else PREF_VER= fi diff -urN --exclude=debian svn-workbench-1.5.0/Builder/version.info svn-workbench-1.5.1/Builder/version.info --- svn-workbench-1.5.0/Builder/version.info 2006-09-23 14:17:49.000000000 +0200 +++ svn-workbench-1.5.1/Builder/version.info 2006-10-28 11:36:22.000000000 +0200 @@ -1,4 +1,4 @@ MAJOR=1 MINOR=5 -PATCH=0 +PATCH=1 BUILD=0 diff -urN --exclude=debian svn-workbench-1.5.0/Kit/Linux/make_rpm.py svn-workbench-1.5.1/Kit/Linux/make_rpm.py --- svn-workbench-1.5.0/Kit/Linux/make_rpm.py 2006-05-22 15:13:21.000000000 +0200 +++ svn-workbench-1.5.1/Kit/Linux/make_rpm.py 2007-01-28 18:06:09.000000000 +0100 @@ -58,7 +58,9 @@ print 'Info: cp %s' % cp_src os.system( 'cp -f %s tmp/%s' % (cp_src, cp_dst_dir_fmt % locals()) ) -os.system( 'gzip tmp/ROOT/usr/local/workbench/support/libdb-4.3.so' ) +has_libdb = os.path.exists( 'tmp/ROOT/usr/local/workbench/support/libdb-4.3.so' ) +if has_libdb: + os.system( 'gzip tmp/ROOT/usr/local/workbench/support/libdb-4.3.so' ) print 'Info: Create tmp/SPECS/workbench.spec' f = file('tmp/SPECS/workbench.spec','w') @@ -68,14 +70,14 @@ Group: Development/Libraries Release: %(wb_version_package_release_string)s Summary: pysvn WorkBench %(wb_version_string)s for Subversion %(svn_version_string)s -License: Apache Software License, Version 1.1 - Copyright Barry A. Scott (c) 2003-2006 +License: Apache Software License, Version 1.1 - Copyright Barry A. Scott (c) 2003-2007 Packager: Barry A. Scott AutoReqProv: no Requires: libsvn_client-1.so.0 %%description PySVN WorkBench %(wb_version_string)s for Subversion %(svn_version_string)s -Copyright Barry A. Scott (c) 2003-2006 +Copyright Barry A. Scott (c) 2003-2007 mailto:barry@barrys-emacs.org http://pysvn.tigris.org @@ -89,8 +91,10 @@ %%post rm -f /usr/local/bin/workbench ln -f -s /usr/local/workbench/wb /usr/local/bin/workbench -gzip -d -c /usr/local/workbench/support/libdb-4.3.so - +if [ -e /usr/local/workbench/support/libdb-4.3.so.gz ] +then + gzip -d -c /usr/local/workbench/support/libdb-4.3.so +fi mkdir -p /usr/local/share/workbench ln -f -s /usr/local/workbench/WorkBench.html /usr/local/share/workbench %%postun diff -urN --exclude=debian svn-workbench-1.5.0/Kit/MacOSX/build.sh svn-workbench-1.5.1/Kit/MacOSX/build.sh --- svn-workbench-1.5.0/Kit/MacOSX/build.sh 2005-09-25 16:45:53.000000000 +0200 +++ svn-workbench-1.5.1/Kit/MacOSX/build.sh 2006-12-13 22:36:38.000000000 +0100 @@ -5,4 +5,4 @@ make -f macosx.mak clean all cd $STARTDIR -/usr/bin/python make_pkg.py +${PYTHON} make_pkg.py diff -urN --exclude=debian svn-workbench-1.5.0/Kit/MacOSX/make_pkg.py svn-workbench-1.5.1/Kit/MacOSX/make_pkg.py --- svn-workbench-1.5.0/Kit/MacOSX/make_pkg.py 2006-05-20 22:56:52.000000000 +0200 +++ svn-workbench-1.5.1/Kit/MacOSX/make_pkg.py 2007-01-28 18:06:09.000000000 +0100 @@ -74,7 +74,7 @@

pysvn WorkBench %(wb_version_string)s for Mac OS X and Subversion %(svn_version_string)s

-

Copyright Barry A. Scott (c) 2003-2006

+

Copyright Barry A. Scott (c) 2003-2007

Mail barry@barrys-emacs.org

@@ -87,7 +87,7 @@ f.close() print 'Info: python bundle' -os_system( '/usr/bin/python make_wb_bundle.py tmp/%s' % pkg_filename) +os_system( '${PYTHON} make_wb_bundle.py tmp/%s' % pkg_filename) print 'Info: Make Disk Image' os_system( 'hdiutil create -srcfolder tmp/%s tmp/tmp.dmg' % pkg_filename ) diff -urN --exclude=debian svn-workbench-1.5.0/Kit/MacOSX/make_wb_bundle.py svn-workbench-1.5.1/Kit/MacOSX/make_wb_bundle.py --- svn-workbench-1.5.0/Kit/MacOSX/make_wb_bundle.py 2006-01-16 22:38:33.000000000 +0100 +++ svn-workbench-1.5.1/Kit/MacOSX/make_wb_bundle.py 2007-01-04 00:31:55.000000000 +0100 @@ -3,6 +3,8 @@ # import bundlebuilder import sys +import pysvn +import shutil # make sure that we get 2.6 and not an earlier version if not hasattr(sys, 'frozen'): @@ -13,6 +15,25 @@ import os import traceback + +def findDylibs( image, dylib_list, depth=0 ): + cmd = 'otool -L "%s" >/tmp/pysvn_otool.tmp' % image + #print 'Debug: cmd %r' % cmd + os.system( cmd ) + # always skip the first line that lists the image being dumped + for line in file( '/tmp/pysvn_otool.tmp' ).readlines()[1:]: + line = line.strip() + #print 'Debug: line %r' % line + if( line.startswith( '/' ) + and not line.startswith( '/usr/lib' ) + and not line.startswith( '/System' ) ): + libpath = line.split()[0] + if libpath not in dylib_list: + print 'Info: ',depth,' Need lib',libpath,'for',image + dylib_list.append( libpath ) + findDylibs( libpath, dylib_list, depth+1 ) + + try: # workbench sources sys.path.append( '../../Source' ) @@ -60,26 +81,19 @@ if not lib_found: raise ValueError( 'Cannot find lib %s' % libname ) - # add in libs from Fink - if os.path.exists( '/sw' ): - for libname in [ - "/sw/lib/libintl.3.dylib", - "/sw/lib/libiconv.2.dylib", - ]: - print 'Info: Manually adding lib %s' % libname - myapp.libs.append( libname ) - if os.path.exists( '/opt/local' ): - for libname in [ - "/opt/local/lib/libapr-1.0.dylib", - "/opt/local/lib/libz.1.dylib", - ]: - print 'Info: Manually adding lib %s' % libname - myapp.libs.append( libname ) + print 'Info: Finding dylibs used by pysvn' + findDylibs( pysvn._pysvn.__file__, myapp.libs ) # Here we build the app! myapp.setup() myapp.build() + # remove unnecessary files + os.system( 'pwd' ) + doc_path = os.path.join( sys.argv[1], + 'WorkBench.app/Contents/Frameworks/Python.framework/Versions/2.5/Resources/English.lproj/Documentation' ) + print doc_path + shutil.rmtree( doc_path ) except: traceback.print_exc( file=sys.stderr ) sys.exit( 1 ) diff -urN --exclude=debian svn-workbench-1.5.0/Kit/Win32/workbench.iss svn-workbench-1.5.1/Kit/Win32/workbench.iss --- svn-workbench-1.5.0/Kit/Win32/workbench.iss 2006-05-19 23:54:16.000000000 +0200 +++ svn-workbench-1.5.1/Kit/Win32/workbench.iss 2007-01-28 18:06:09.000000000 +0100 @@ -5,7 +5,7 @@ [Setup] AppName=WorkBench AppVerName=WorkBench UNCONTROLLED -AppCopyright=Copyright (C) 2003-2006 Barry A. Scott +AppCopyright=Copyright (C) 2003-2007 Barry A. Scott DefaultDirName={pf}\PySVN\WorkBench DefaultGroupName=WorkBench for Subversion UninstallDisplayIcon={app}\WorkBench.exe diff -urN --exclude=debian svn-workbench-1.5.0/LICENSE.txt svn-workbench-1.5.1/LICENSE.txt --- svn-workbench-1.5.0/LICENSE.txt 2006-01-02 17:06:03.000000000 +0100 +++ svn-workbench-1.5.1/LICENSE.txt 2007-01-28 18:06:09.000000000 +0100 @@ -1,5 +1,5 @@ ================================================================= -Copyright (C) 2003-2006 Barry A. Scott. All rights reserved. +Copyright (C) 2003-2007 Barry A. Scott. All rights reserved. ================================================================= The Apache Software License, Version 1.1 diff -urN --exclude=debian svn-workbench-1.5.0/Source/run_wb.cmd svn-workbench-1.5.1/Source/run_wb.cmd --- svn-workbench-1.5.0/Source/run_wb.cmd 2006-01-25 23:11:19.000000000 +0100 +++ svn-workbench-1.5.1/Source/run_wb.cmd 2007-02-03 21:11:55.000000000 +0100 @@ -1,4 +1,4 @@ setlocal -set PYTHONPATH=L:\BuildRoot\Win32-MSVC71-1.3.0\pysvn\py42\Source +set PYTHONPATH=L:\BuildRoot\Win32-MSVC71-1.4.2\pysvn\py25\Source python L:\wc\pysvn\trunk\pysvn\WorkBench\Source\wb_main.py %* endlocal diff -urN --exclude=debian svn-workbench-1.5.0/Source/run_wb.sh svn-workbench-1.5.1/Source/run_wb.sh --- svn-workbench-1.5.0/Source/run_wb.sh 2006-10-15 00:48:11.000000000 +0200 +++ svn-workbench-1.5.1/Source/run_wb.sh 1970-01-01 01:00:00.000000000 +0100 @@ -1,12 +0,0 @@ -#!/bin/sh -export PYSVN_WORKBENCH_STDOUT_LOG=$(tty) -if [ "$PYTHONPATH" = "" ] -then - export PYTHONPATH=${WORKDIR}/Source -else - export PYTHONPATH=${WORKDIR}/Source:$PYTHONPATH -fi - -export LD_LIBRARY_PATH=${SVNCPP_LIB} -export DYLD_LIBRARY_PATH=${SVNCPP_LIB} -pythonw$1 wb_main.py $* diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_app.py svn-workbench-1.5.1/Source/wb_app.py --- svn-workbench-1.5.0/Source/wb_app.py 2006-09-22 01:57:18.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_app.py 2006-12-16 22:43:49.000000000 +0100 @@ -16,6 +16,7 @@ import logging import tempfile import threading +import inspect import wx import wx.lib @@ -68,7 +69,10 @@ self.setupLogging() - self.prefs = wb_preferences.Preferences( self ) + self.prefs = wb_preferences.Preferences( + self, + wb_platform_specific.getPreferencesFilename(), + wb_platform_specific.getOldPreferencesFilename() ) self.lock_ui = 0 self.need_activate_app_action = False @@ -209,6 +213,18 @@ def getPasteData( self ): return self.__paste_data + def diffFiles( self, file_left, title_left, file_right, title_right ): + diff_frame = wb_diff_frame.DiffFrame( + self, self.frame, + file_left, title_left, + file_right, title_right ) + # only show if the files could be read + if diff_frame.isOk(): + diff_frame.showAllFolds( False ) + diff_frame.Show( True ) + + self.all_diff_frames.append( diff_frame ) + def DiffDone( self, diff_frame ): self.all_diff_frames.remove( diff_frame ) @@ -335,6 +351,22 @@ self.log.exception( 'OnAppCallBack<%s.%s>\n' % (event.callback.__module__, event.callback.__name__ ) ) + def debugShowCallers( self, depth ): + if not self.__debug: + return + + stack = inspect.stack() + for index in range( 1, depth+1 ): + if index >= len(stack): + break + + caller = stack[ index ] + filename = os.path.basename( caller[1] ) + self.log.debug( 'File: %s:%d, Function: %s' % (filename, caller[2], caller[3]) ) + del caller + + del stack + class AppBackgroundFunction: def __init__( self, app, function, args ): self.app = app diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_common.mak svn-workbench-1.5.1/Source/wb_common.mak --- svn-workbench-1.5.0/Source/wb_common.mak 2006-09-23 13:37:24.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_common.mak 2006-12-13 22:36:38.000000000 +0100 @@ -28,7 +28,7 @@ $(PYTHON) make_wb_images.py wb_images.py $(IMAGES) check: - python -c "import wb_pychecker;import wb_main;wb_pychecker.report()" + $(PYTHON) -c "import wb_pychecker;import wb_main;wb_pychecker.report()" clean:: rm -f wb_version.py diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_config.py svn-workbench-1.5.1/Source/wb_config.py --- svn-workbench-1.5.0/Source/wb_config.py 2006-03-11 12:32:58.000000000 +0100 +++ svn-workbench-1.5.1/Source/wb_config.py 2007-01-28 18:06:09.000000000 +0100 @@ -1,6 +1,6 @@ ''' ==================================================================== - Copyright (c) 2006 Barry A Scott. All rights reserved. + Copyright (c) 2006-2007 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. @@ -10,9 +10,38 @@ wb_config.py ''' +import wx # -# Controls debug message that help debug problems with +# Controls debug messages that help debug problems with # selection in tree and list controls # debug_selection = False +debug_selection_update = False + +# 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 + +# control the experimental focus ring code +# that is not working yet +focus_ring = False + +# default colours +colour_status_normal = wx.BLACK +colour_status_disabled = wx.Colour( 128, 128, 128 ) +colour_status_unversioned = wx.Colour( 0, 112, 0 ) +colour_status_locked = wx.RED +colour_status_need_checkout = wx.RED +colour_status_modified = wx.BLUE +colour_status_qqq = wx.BLUE + +colour_log_normal = wx.BLACK +colour_log_tag = wx.BLUE diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_debug.py svn-workbench-1.5.1/Source/wb_debug.py --- svn-workbench-1.5.0/Source/wb_debug.py 1970-01-01 01:00:00.000000000 +0100 +++ svn-workbench-1.5.1/Source/wb_debug.py 2006-12-29 23:43:25.000000000 +0100 @@ -0,0 +1,21 @@ +''' + ==================================================================== + Copyright (c) 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_debug.py + +''' +import inspect + +def printStack( prefix='', depth=5 ): + stack = inspect.stack() + + for caller in stack[1:depth]: + print '%sFile: %s:%d, Function: %s' % (prefix, caller[1], caller[2], caller[3]) + del caller + del stack diff -urN --exclude=debian svn-workbench-1.5.0/Source/wbdev.sh svn-workbench-1.5.1/Source/wbdev.sh --- svn-workbench-1.5.0/Source/wbdev.sh 2004-05-31 16:04:18.000000000 +0200 +++ svn-workbench-1.5.1/Source/wbdev.sh 1970-01-01 01:00:00.000000000 +0100 @@ -1,6 +0,0 @@ -#!/bin/sh -set -e -set -x -export LD_LIBRARY_PATH=${SVNCPP_LIB} -export PYTHONPATH=${WORKDIR}/../Extension/Source -${PYTHON:-python} wb_main.py $* diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_diff_main.py svn-workbench-1.5.1/Source/wb_diff_main.py --- svn-workbench-1.5.0/Source/wb_diff_main.py 2006-01-02 17:06:03.000000000 +0100 +++ svn-workbench-1.5.1/Source/wb_diff_main.py 2006-11-14 21:16:27.000000000 +0100 @@ -29,7 +29,10 @@ class WbDiffApp: def __init__( self ): self.log = self - self.prefs = wb_preferences.Preferences( self ) + self.prefs = wb_preferences.Preferences( + self, + wb_platform_specific.getPreferencesFilename(), + wb_platform_specific.getOldPreferencesFilename() ) def info( self, *arg ): pass diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_frame.py svn-workbench-1.5.1/Source/wb_frame.py --- svn-workbench-1.5.0/Source/wb_frame.py 2006-09-26 21:54:32.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_frame.py 2007-01-28 18:06:09.000000000 +0100 @@ -1,6 +1,6 @@ ''' ==================================================================== - Copyright (c) 2003-2006 Barry A Scott. All rights reserved. + Copyright (c) 2003-2007 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. @@ -29,17 +29,6 @@ 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 @@ -134,11 +123,18 @@ 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_Diff_WbDiff, 'Use WorkBench Diff' ) + self.menu_view.AppendCheckItem( wb_ids.id_View_Diff_ExtGuiDiff, 'Use External GUI Diff' ) + self.menu_view.AppendCheckItem( wb_ids.id_View_Diff_ExtTextDiff, 'Use External Text Diff' ) + self.menu_view.AppendCheckItem( wb_ids.id_View_Diff_SvnDiff, 'Use SVN Diff' ) + 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 = {} @@ -310,6 +306,14 @@ 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( event_source, wb_ids.id_View_Diff_WbDiff, self.app.eventWrapper( self.OnViewDiffWbDiff ) ) + wx.EVT_UPDATE_UI( event_source, wb_ids.id_View_Diff_WbDiff, self.app.eventWrapper( self.OnUpdateUiViewDiffWbDiff ) ) + wx.EVT_MENU( event_source, wb_ids.id_View_Diff_ExtGuiDiff, self.app.eventWrapper( self.OnViewDiffExtGuiDiff ) ) + wx.EVT_UPDATE_UI( event_source, wb_ids.id_View_Diff_ExtGuiDiff, self.app.eventWrapper( self.OnUpdateUiViewDiffExtGuiDiff ) ) + wx.EVT_MENU( event_source, wb_ids.id_View_Diff_ExtTextDiff, self.app.eventWrapper( self.OnViewDiffExtTextDiff ) ) + wx.EVT_UPDATE_UI( event_source, wb_ids.id_View_Diff_ExtTextDiff, self.app.eventWrapper( self.OnUpdateUiViewDiffExtTextDiff ) ) + wx.EVT_MENU( event_source, wb_ids.id_View_Diff_SvnDiff, self.app.eventWrapper( self.OnViewDiffSvnDiff ) ) + wx.EVT_UPDATE_UI( event_source, wb_ids.id_View_Diff_SvnDiff, self.app.eventWrapper( self.OnUpdateUiViewDiffSvnDiff ) ) 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 ) ) @@ -412,17 +416,27 @@ 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 wb_config.debug_selection: + print 'ZF: setEventHandler from %r' % self.event_handler + print 'ZF: setEventHandler to %r' % handler + import wb_debug + wb_debug.printStack( ' ' ) + + self.app.debugShowCallers( 20 ) + 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 ) + if wb_config.debug_selection: print 'ZF: clearEventHandler from %r to None' % self.event_handler self.event_handler = None self.clearUpdateUiState() + def isEventHandler( self, handler ): + return self.event_handler is handler + # Status bar settings def setStatus( self, text ): self.GetStatusBar().SetStatusText( text, WbFrame.status_general ) @@ -456,7 +470,7 @@ '\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' + '\nCopyright Barry Scott (c) 2003-2007. All rights reserved' ) wx.LogMessage( str_message ) @@ -562,10 +576,42 @@ view_prefs = self.app.prefs.getView() event.Check( view_prefs.view_recursive ) + def OnViewDiffWbDiff( self, event ): + self.app.prefs.getDiffTool().diff_tool_mode = 'built-in' + self.app.prefs.writePreferences() + + def OnUpdateUiViewDiffWbDiff( self, event ): + event.Check( self.app.prefs.getDiffTool().diff_tool_mode == 'built-in' ) + + def OnViewDiffExtGuiDiff( self, event ): + self.app.prefs.getDiffTool().diff_tool_mode = 'external-gui-diff' + self.app.prefs.writePreferences() + + def OnUpdateUiViewDiffExtGuiDiff( self, event ): + event.Enable( self.app.prefs.getDiffTool().gui_diff_tool != '' ) + event.Check( self.app.prefs.getDiffTool().diff_tool_mode == 'external-gui-diff' ) + + def OnViewDiffExtTextDiff( self, event ): + self.app.prefs.getDiffTool().diff_tool_mode = 'external-shell-diff' + self.app.prefs.writePreferences() + + def OnUpdateUiViewDiffExtTextDiff( self, event ): + event.Enable( self.app.prefs.getDiffTool().shell_diff_tool != '' ) + event.Check( self.app.prefs.getDiffTool().diff_tool_mode == 'external-shell-diff' ) + + def OnViewDiffSvnDiff( self, event ): + self.app.prefs.getDiffTool().diff_tool_mode = 'svn-diff' + self.app.prefs.writePreferences() + + def OnUpdateUiViewDiffSvnDiff( self, event ): + event.Check( self.app.prefs.getDiffTool().diff_tool_mode == 'svn-diff' ) + def OnRefresh( self, event ): + self.app.log.debug( 'OnRefresh()' ) self.refreshFrame() def refreshFrame( self ): + self.app.log.debug( 'WbFrame.refreshFrame()' ) # tell the tree to refresh it will tell the list self.tree_panel.refreshTree() @@ -705,7 +751,6 @@ self.getUpdateUiState() event.Enable( self.ui_state_tree.file_exists ) - def OnSpEditCopy( self, event ): return self.Sp_Dispatch( 'OnSpEditCopy' ) @@ -743,7 +788,7 @@ if self.ui_state_focus.need_checkout: event.Enable( False ) else: - event.Enable( self.ui_state_tree.versioned ) + event.Enable( self.ui_state_focus.versioned and (not self.ui_state_focus.is_folder) ) def OnSpCheckin( self, event ): return self.Sp_Dispatch( 'OnSpCheckin' ) @@ -853,6 +898,8 @@ def OnUpdateUiSpHistory( self, event ): self.getUpdateUiState() + if wb_config.debug_selection_update: print 'ZF: OnUpdateUiSpHistory versioned %r handler %r' % ( + self.ui_state_focus.versioned, self.event_handler) if self.ui_state_focus.need_checkout: event.Enable( False ) else: @@ -873,17 +920,18 @@ def OnUpdateUiSpLock( self, event ): self.getUpdateUiState() - event.Enable( self.ui_state_tree.file_exists ) + event.Enable( (not self.ui_state_focus.is_folder) and self.ui_state_focus.file_exists ) def OnSpMkdir( self, event ): - return self.Sp_Dispatch( 'OnSpMkdir' ) + # always forward to the tree to handle + return self.tree_panel.OnSpMkdir() def OnUpdateUiSpMkdir( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_tree.file_exists ) def OnSpNewFile( self, event ): - return self.Sp_Dispatch( 'OnSpNewFile' ) + return self.tree_panel.OnSpNewFile() def OnUpdateUiSpNewFile( self, event ): self.getUpdateUiState() @@ -959,7 +1007,7 @@ def OnUpdateUiSpUnlock( self, event ): self.getUpdateUiState() - event.Enable( self.ui_state_tree.file_exists ) + event.Enable( (not self.ui_state_focus.is_folder) and self.ui_state_focus.file_exists ) def OnSpUpdate( self, event ): return self.Sp_Dispatch( 'OnSpUpdate' ) @@ -999,42 +1047,44 @@ return fn() def getUpdateUiState( self ): + all_debug_messages = [] 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 + self.ui_state_tree = wb_tree_panel.TreeState( True ) + all_debug_messages.append( 'tree place_holder %s' % self.ui_state_tree.place_holder ) 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 + self.ui_state_list = wb_list_panel_common.ListItemState( True ) + all_debug_messages.append( 'list place_holder %s' % self.ui_state_list.place_holder ) 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' + all_debug_messages.append( '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' + all_debug_messages.append( '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' + all_debug_messages.append( '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 + all_debug_messages.append( 'focus place_holder %s' % self.ui_state_focus.place_holder ) + + if wb_config.debug_selection and len(all_debug_messages)>0: + print 'ZF: getUpdateUiState() ------------------------------' + for message in all_debug_messages: + print ' %s' %message def clearUpdateUiState( self ): - if wb_config.debug_selection: print 'Z: clearUpdateUiState()' + if wb_config.debug_selection: print 'ZF: 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) @@ -1043,8 +1093,9 @@ # Redirect the console IO to this panel sys.stdin = file( wb_platform_specific.getNullDevice(), 'r' ) - sys.stdout = self - sys.stderr = self + if self.app.isStdIoRedirect(): + sys.stdout = self + sys.stderr = self # Redirect log to the Log panel log_handler = LogHandler( self.text_ctrl ) @@ -1052,8 +1103,13 @@ wx.EVT_SIZE( self, wb_exceptions.TryWrapper( self.app.log, self.OnSize ) ) + wx.EVT_MENU( self, wb_ids.id_NextControl, self.app.eventWrapper( self.OnNextControl ) ) + #---------- Event handlers ------------------------------------------------------------ + def OnNextControl( self, event ): + print 'LogCtrlPanel.OnNextControl',event + def OnSize( self, event ): self.text_ctrl.SetWindowSize( self.GetSize() ) @@ -1133,7 +1189,7 @@ self.SetMarginWidth(2, 0) self.StyleSetSpec( wx.stc.STC_STYLE_DEFAULT, - "size:%d,face:%s,fore:#000000" % (point_size, face) ) + "size:%d,face:%s,fore:#000000" % (wb_config.point_size, wb_config.face) ) self.StyleSetSpec( self.style_normal, "fore:#000000" ) self.StyleSetSpec( self.style_error, "fore:#DC143C" ) # Crimson @@ -1142,6 +1198,14 @@ self.StyleSetSpec( self.style_critical, "fore:#BA55D3" ) # Medium Orchid self.StyleSetSpec( self.style_debug, "fore:#DC143C" ) # Crimson + wx.EVT_KEY_DOWN( self, self.OnKeyDown ) + + def OnKeyDown( self, event ): + if event.GetKeyCode() == wx.WXK_TAB: + self.app.frame.tree_panel.SetFocus() + else: + event.Skip() + def SetWindowSize( self, size ): wx.stc.StyledTextCtrl.SetSize( self, size ) self.EnsureCaretVisible() diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_ids.py svn-workbench-1.5.1/Source/wb_ids.py --- svn-workbench-1.5.0/Source/wb_ids.py 2006-03-18 18:07:27.000000000 +0100 +++ svn-workbench-1.5.1/Source/wb_ids.py 2006-12-29 23:43:25.000000000 +0100 @@ -20,6 +20,11 @@ id_View_Recursive = wx.NewId() id_ClearLog = wx.NewId() +id_View_Diff_WbDiff = wx.NewId() +id_View_Diff_ExtGuiDiff = wx.NewId() +id_View_Diff_ExtTextDiff = wx.NewId() +id_View_Diff_SvnDiff = wx.NewId() + id_Project_Add = wx.NewId() id_Project_Update = wx.NewId() id_Project_Delete = wx.NewId() @@ -27,8 +32,6 @@ id_Bookmark_Add = wx.NewId() id_Bookmark_Manage = wx.NewId() -id_Return_Hotkey = wx.NewId() - id_Command_Shell = wx.NewId() id_File_Browser = wx.NewId() @@ -70,6 +73,7 @@ id_SP_Report_Updates = wx.NewId() id_SP_Report_LocksWc = wx.NewId() -id_SP_Report_LocksRepos = wx.NewId() +id_SP_Report_LocksRepos = wx.NewId()#1 id_SP_Report_BranchChanges = wx.NewId() +id_NextControl = wx.NewId() diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_list_panel_common.py svn-workbench-1.5.1/Source/wb_list_panel_common.py --- svn-workbench-1.5.0/Source/wb_list_panel_common.py 2006-09-02 20:06:43.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_list_panel_common.py 2006-12-24 14:33:52.000000000 +0100 @@ -44,7 +44,10 @@ class ListItemState: - def __init__( self ): + def __init__( self, place_holder=False ): + # used to tell if the state reflects a items state or is just a place holder + self.place_holder = place_holder + self.modified = False self.versioned = False self.new_versioned = False @@ -53,6 +56,7 @@ self.need_checkout = False self.conflict = False self.file_exists = False + self.is_folder = False self.is_project_parent = False def printState( self, title='' ): @@ -83,7 +87,7 @@ class WbListPanelCommon(wx.Panel): def __init__( self, app, frame, parent ): - wx.Panel.__init__(self, parent, -1) + wx.Panel.__init__( self, parent ) self.app = app self.frame = frame @@ -100,8 +104,21 @@ self.id_list = wx.NewId() self.list_ctrl = WbListCtrl( self, self.id_list ) + if wb_config.focus_ring: + self.focus_ring_panel = wx.Panel( self ) + + box = wx.BoxSizer( wx.VERTICAL ) + box.Add( self.list_ctrl, 1, wx.EXPAND|wx.ALL, 3) + self.focus_ring_panel.SetSizer( box ) + + self.list_ctrl.Bind( wx.EVT_PAINT, self.OnPaint ) + + list_window = self.focus_ring_panel + else: + list_window = self.list_ctrl + self.v_sizer.Add( self.header_panel, 0, wx.EXPAND|wx.ALL, 0 ) - self.v_sizer.Add( self.list_ctrl, 1, wx.EXPAND|wx.ALL, 0 ) + self.v_sizer.Add( list_window, 1, wx.EXPAND|wx.ALL, 0 ) acc_tab = wx.AcceleratorTable( self.getAcceleratorTableInit() ) self.list_ctrl.SetAcceleratorTable( acc_tab ) @@ -140,15 +157,45 @@ def __repr__( self ): return '' % self.list_handler + def OnPaint( self, event ): + event.Skip() + return + + dc = wx.PaintDC( self ) + #dc.Clear() + w, h = self.GetSize() + if self.FindFocus() == self.list_ctrl: + print 'list focus' + dc.SetPen( wx.Pen( "red", 1 ) ) + dc.DrawRectangle( 0, 0, w, h ) + else: + dc.SetPen( wx.Pen( "green", 1 ) ) + dc.DrawRectangle( 0, 0, w, h ) + print 'list unfocus' + + event.Skip() + def addToSizer( self, v_sizer ): pass + def setFocusFilter( self ): + self.header_panel.setFocusFilter() + def OnSetFocus( self, event ): - self.frame.setEventHandler( self ) + #self.frame.setEventHandler( self ) + + if wb_config.focus_ring: + self.Refresh() + + event.Skip() def OnKillFocus( self, event ): #self.frame.clearEventHandler() - pass + + if wb_config.focus_ring: + self.Refresh() + + event.Skip() def OnItemSelected( self, event ): self.frame.clearUpdateUiState() @@ -405,7 +452,6 @@ self.app = app - self.background_colour = wx.SystemSettings.GetColour( wx.SYS_COLOUR_3DFACE ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) @@ -448,11 +494,23 @@ wx.EVT_TEXT( self, self.filter_text_ctrl.GetId(), self.OnFilterTextChanged ) wx.EVT_CHOICE( self, self.filter_choice_ctrl.GetId(), self.OnFilterTypeChanged ) + wx.EVT_KEY_DOWN( self, self.OnKeyDown ) + self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() + + def OnKeyDown( self, event ): + if event.GetKeyCode() == wx.WXK_TAB: + self.app.frame.list_panel.SetFocus() + else: + event.Skip() + + def setFocusFilter( self ): + self.filter_text_ctrl.SetFocus() + def setFilterChangedHandler( self, handler ): self.filter_changed_handler = handler diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_list_panel.py svn-workbench-1.5.1/Source/wb_list_panel.py --- svn-workbench-1.5.0/Source/wb_list_panel.py 2006-03-05 18:47:23.000000000 +0100 +++ svn-workbench-1.5.1/Source/wb_list_panel.py 2006-11-15 00:12:58.000000000 +0100 @@ -19,6 +19,11 @@ def __init__( self, app, frame, parent ): wb_list_panel_common.WbListPanelCommon.__init__( self, app, frame, parent ) + wx.EVT_MENU( self, wb_ids.id_NextControl, self.app.eventWrapper( self.OnNextControl ) ) + + def OnNextControl( self, event ): + self.frame.tree_panel.SetFocus() + def __repr__( self ): return '' % self.list_handler @@ -39,7 +44,7 @@ (wx.ACCEL_ALT, ord('U'), wb_ids.id_SP_Update), (wx.ACCEL_NORMAL, wx.WXK_DELETE, wb_ids.id_SP_Delete), (wx.ACCEL_ALT, wx.WXK_BACK, wb_ids.id_SP_Delete), - (wx.ACCEL_NORMAL, wx.WXK_RETURN, wb_ids.id_Return_Hotkey), + (wx.ACCEL_NORMAL, wx.WXK_TAB, wb_ids.id_NextControl), (wx.ACCEL_ALT, ord('O'), wb_ids.id_Shell_Open), ] elif wx.Platform == '__WXMSW__': @@ -57,7 +62,7 @@ (wx.ACCEL_CTRL, ord('R'), wb_ids.id_SP_Revert), (wx.ACCEL_CTRL, ord('U'), wb_ids.id_SP_Update), (wx.ACCEL_NORMAL, wx.WXK_DELETE, wb_ids.id_SP_Delete), - (wx.ACCEL_NORMAL, wx.WXK_RETURN, wb_ids.id_Return_Hotkey), + (wx.ACCEL_NORMAL, wx.WXK_TAB, wb_ids.id_NextControl), (wx.ACCEL_CTRL, ord('O'), wb_ids.id_Shell_Open), ] else: @@ -76,6 +81,7 @@ (wx.ACCEL_CTRL, ord('R'), wb_ids.id_SP_Revert), (wx.ACCEL_CTRL, ord('U'), wb_ids.id_SP_Update), (wx.ACCEL_NORMAL, wx.WXK_DELETE, wb_ids.id_SP_Delete), - (wx.ACCEL_NORMAL, wx.WXK_RETURN, wb_ids.id_Return_Hotkey), + (wx.ACCEL_NORMAL, wx.WXK_TAB, wb_ids.id_NextControl), + (wx.ACCEL_CTRL, ord('O'), wb_ids.id_Shell_Open), ] return acc_init diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_main.py svn-workbench-1.5.1/Source/wb_main.py --- svn-workbench-1.5.0/Source/wb_main.py 2007-02-22 00:07:40.000000000 +0100 +++ svn-workbench-1.5.1/Source/wb_main.py 2006-09-23 15:11:16.000000000 +0200 @@ -34,9 +34,6 @@ except: pass -import wxversion -wxversion.select('2.6') - import wb_app import wb_subversion_provider diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_platform_specific.py svn-workbench-1.5.1/Source/wb_platform_specific.py --- svn-workbench-1.5.0/Source/wb_platform_specific.py 2006-01-02 17:06:03.000000000 +0100 +++ svn-workbench-1.5.1/Source/wb_platform_specific.py 2006-10-29 23:32:06.000000000 +0100 @@ -25,6 +25,12 @@ def getPreferencesFilename(): return os.path.join( getApplicationDir(), 'WorkBench.ini' ) +def getPreferencesFilename(): + return os.path.join( getApplicationDir(), 'WorkBench.xml' ) + +def getOldPreferencesFilename(): + return os.path.join( getApplicationDir(), 'WorkBench.ini' ) + def getLogFilename(): return os.path.join( getApplicationDir(), 'WorkBench.log' ) diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_preferences_dialog.py svn-workbench-1.5.1/Source/wb_preferences_dialog.py --- svn-workbench-1.5.0/Source/wb_preferences_dialog.py 2006-09-23 13:41:56.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_preferences_dialog.py 2006-12-29 23:43:25.000000000 +0100 @@ -184,7 +184,6 @@ return True - class DiffToolPage(PagePanel): def __init__( self, notebook, app ): self.app = app @@ -192,7 +191,6 @@ def initControls( self ): p = self.app.prefs.getDiffTool() - self.mode_values = ['built-in', 'external-gui-diff', 'external-shell-diff', 'svn-diff'] self.diff_tools = {'built-in': '', 'external-gui-diff': p.gui_diff_tool, 'external-shell-diff': p.shell_diff_tool, 'svn-diff': ''} @@ -237,23 +235,28 @@ def savePreferences( self ): self.OnModeChange( None ) + p = self.app.prefs.getDiffTool() - p.diff_tool_mode = self.mode p.gui_diff_tool = self.diff_tools['external-gui-diff'] p.shell_diff_tool = self.diff_tools['external-shell-diff'] p.gui_diff_tool_options = self.options['external-gui-diff'] p.shell_diff_tool_options = self.options['external-shell-diff'] + if p.diff_tool_mode == 'external-gui-diff' and p.gui_diff_tool == '': + p.diff_tool_mode = 'built-in' + if p.diff_tool_mode == 'external-shell-diff' and p.shell_diff_tool == '': + p.diff_tool_mode = 'built-in' + def OnModeChange( self, event ): - self.diff_tools[self.mode] = self.text_ctrl_diff_tool.GetValue() - self.options[self.mode] = self.text_ctrl_options.GetValue() - self.mode = self.mode_values[self.mode_choice.GetSelection()] - self.text_ctrl_diff_tool.SetValue( self.diff_tools[self.mode] ) - self.text_ctrl_options.SetValue( self.options[self.mode] ) - external_command = self.mode in ('external-gui-diff', 'external-shell-diff') - self.text_ctrl_diff_tool.Enable( external_command ) - self.browse_button.Enable( external_command ) - self.text_ctrl_options.Enable( external_command ) + self.diff_tools[self.mode] = self.text_ctrl_diff_tool.GetValue() + self.options[self.mode] = self.text_ctrl_options.GetValue() + self.mode = self.mode_values[self.mode_choice.GetSelection()] + self.text_ctrl_diff_tool.SetValue( self.diff_tools[self.mode] ) + self.text_ctrl_options.SetValue( self.options[self.mode] ) + external_command = self.mode in ('external-gui-diff', 'external-shell-diff') + self.text_ctrl_diff_tool.Enable( external_command ) + self.browse_button.Enable( external_command ) + self.text_ctrl_options.Enable( external_command ) def OnBrowseExe( self, event ): if wx.Platform == '__WXMSW__': diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_preferences.py svn-workbench-1.5.1/Source/wb_preferences.py --- svn-workbench-1.5.0/Source/wb_preferences.py 2006-09-23 13:41:56.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_preferences.py 2007-01-28 18:06:09.000000000 +0100 @@ -1,7 +1,7 @@ ''' ==================================================================== - Copyright (c) 2003-2006 Barry A Scott. All rights reserved. + Copyright (c) 2003-2007 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. @@ -12,19 +12,42 @@ wb_preferences.py ''' +import pprint + import os -import wx +import types import ConfigParser +import UserDict + +import xml.parsers.expat +import xml.dom.minidom + +import wx + import wb_platform_specific import wb_source_control_providers -import string + + +new_save = True + +class ParseError(Exception): + def __init__( self, value ): + self.value = value + + def __str__( self ): + return str(self.value) + + def __repr__( self ): + return repr(self.value) + class Preferences: - def __init__( self, app ): + def __init__( self, app, pref_filename, old_pref_filename ): self.app = app - self.pref_filename = wb_platform_specific.getPreferencesFilename() + self.pref_filename = pref_filename + self.old_pref_filename = old_pref_filename - self.app.log.info( 'Reading preferences from %s' % self.pref_filename ) + self.pref_data = None # all the preference section handles get created here self.pref_handlers = {} @@ -41,8 +64,11 @@ self.readPreferences() def readPreferences( self ): - self.pref_data = ConfigParser.RawConfigParser() - self.pref_data.read( self.pref_filename ) + try: + self.pref_data = PreferenceData( self.app.log, self.pref_filename, self.old_pref_filename ) + except ParseError, e: + self.app.log.error( str(e) ) + return for handler in self.pref_handlers.values(): if self.pref_data.has_section( handler.section_name ): @@ -68,12 +94,189 @@ self.pref_data.add_section( handler.section_name ) handler.writePreferences( self.pref_data ) - self.pref_data.write( open( self.pref_filename, 'w' ) ) + f = file( self.pref_filename, 'w' ) + self.pref_data.write( f ) + f.close() self.app.log.info( 'Wrote preferences to %s' % self.pref_filename ) except IOError, e: self.app.log.error( 'write preferences: %s' % e ) +class PreferenceData: + def __init__( self, log, xml_pref_filename, ini_pref_filename ): + self.all_sections = {} + + if os.path.exists( xml_pref_filename ): + log.info( 'Reading preferences from %s' % xml_pref_filename ) + self.__readXml( xml_pref_filename ) + else: + log.info( 'Reading preferences from %s' % ini_pref_filename ) + self.__readIni( ini_pref_filename ) + + def __readXml( self, xml_pref_filename ): + try: + f = file( xml_pref_filename, 'r' ) + text = f.read() + f.close() + + dom = xml.dom.minidom.parseString( text ) + + except IOError, e: + raise ParseError( str(e) ) + + except xml.parsers.expat.ExpatError, e: + raise ParseError( str(e) ) + + prefs = dom.getElementsByTagName( 'workbench-preferences' )[0] + + self.__parseXmlChildren( prefs, self.all_sections ) + + def __parseXmlChildren( self, parent, data_dict ): + for child in parent.childNodes: + if child.nodeType == xml.dom.minidom.Node.ELEMENT_NODE: + + if self.__hasChildElements( child ): + child_data_dict = {} + if child.nodeName in data_dict: + if type(data_dict[ child.nodeName ]) != types.ListType: + data_dict[ child.nodeName ] = [data_dict[ child.nodeName], child_data_dict] + else: + data_dict[ child.nodeName ].append( child_data_dict ) + else: + data_dict[ child.nodeName ] = child_data_dict + + self.__parseXmlChildren( child, child_data_dict ) + else: + data_dict[ child.nodeName ] = self.__getText( child ) + + def __hasChildElements( self, parent ): + for child in parent.childNodes: + if child.nodeType == xml.dom.minidom.Node.ELEMENT_NODE: + return True + return False + + def __getText( self, parent ): + all_text = [] + + for child in parent.childNodes: + if child.nodeType == xml.dom.minidom.Node.TEXT_NODE: + all_text.append( child.nodeValue ) + + return ''.join( all_text ) + + def __readIni( self, pref_filename ): + pref_data = ConfigParser.RawConfigParser() + pref_data.read( pref_filename ) + + for section_name in pref_data.sections(): + section_dict = {} + self.all_sections[ section_name ] = section_dict + + for option_name in pref_data.options( section_name ): + option_name_parts = option_name.split('_') + + if option_name_parts[-1][0] in '0123456789': + option_name_prefix = '_'.join( option_name_parts[:-1] ) + option_name_index = int(option_name_parts[-1]) + + section_dict.setdefault( option_name_index, {} ) + section_dict[ option_name_index ][ option_name_prefix ] = pref_data.get( section_name, option_name ) + else: + section_dict[ option_name ] = pref_data.get( section_name, option_name ) + + for section_name, num_items_name, list_name in [ + ('Bookmarks','num_bookmarks','bookmark'), + ('Projects' ,'num_projects','project')]: + if self.has_section( section_name ): + section = self.all_sections[ section_name ] + section_list = [] + num_items = int( section[ num_items_name ] ) + for index in range( 1, num_items+1 ): + section_list.append( section.pop( index ) ) + section[ list_name ] = section_list + + def __getElem( self, element_path ): + node = self._dom + for element_name in element_path: + children = node.childNodes + node = None + for child in children: + if child.nodeType == xml.dom.minidom.Node.ELEMENT_NODE and child.nodeName == element_name: + node = child + break + if node is None: + break + + return node + + def __getAttr( self, element_path, attrib_name ): + element = self.getElement( element_path ) + if element.hasAttributes() and element.attributes.has_key( attrib_name ): + return element.attributes[ attrib_name ].value + return default + + + def has_section( self, section_name ): + return section_name in self.all_sections + + def len_section( self, section_name, option_name ): + if type(self.all_sections[ section_name ][ option_name ]) == types.ListType: + length = len( self.all_sections[ section_name ][ option_name ] ) + else: + length = 1 + return length + + def has_option( self, section_name, option_name ): + return option_name in self.all_sections[ section_name ] + + def get( self, section_name, option_name ): + return self.all_sections[ section_name ][ option_name ] + + def getint( self, section_name, option_name ): + return int( self.get( section_name, option_name ).strip() ) + + def getboolean( self, section_name, option_name ): + return self.get( section_name, option_name ).strip().lower() == 'true' + + def remove_section( self, section_name ): + if section_name in self.all_sections: + del self.all_sections[ section_name ] + + def add_section( self, section_name ): + self.all_sections[ section_name ] = {} + + def append_dict( self, section_name, list_name, data ): + item_list = self.all_sections[ section_name ].setdefault( list_name, [] ) + item_list.append( data ) + + def set( self, section_name, option_name, value ): + self.all_sections[ section_name ][ option_name ] = value + + def write( self, f ): + f.write( '\n' ) + f.write( '\n' ) + self.__writeDictionary( f, self.all_sections, 4 ) + f.write( '\n' ) + + def __writeDictionary( self, f, d, indent ): + all_key_names = d.keys() + all_key_names.sort() + + for key_name in all_key_names: + value = d[ key_name ] + if type(value) == types.DictType: + if len(value) > 0: + f.write( '%*s<%s>\n' % (indent, '', key_name) ) + self.__writeDictionary( f, value, indent + 4 ) + f.write( '%*s\n' % (indent, '', key_name) ) + elif type(value) == types.ListType: + for item in value: + f.write( '%*s<%s>\n' % (indent, '', key_name) ) + self.__writeDictionary( f, item, indent + 4 ) + f.write( '%*s\n' % (indent, '', key_name) ) + else: + f.write( '%*s<%s>%s\n' % (indent, '', key_name, unicode(value).encode('utf-8'), key_name) ) + class PreferenceSection: def __init__( self, section_name ): self.section_name = section_name @@ -120,39 +323,36 @@ self.section_name = section_name def set( self, name, value, sep='' ): - if type(value) == type([]): - value = string.join( value, sep ) + if type(value) == types.ListType: + value = sep.join( value ) self.pref_data.set( self.section_name, name, value ) -class GetIndexedOption(GetOption): - def __init__( self, pref_data, section_name, index ): - GetOption.__init__( self, pref_data, section_name ) - self.index = index + 1 +class GetIndexedOption: + def __init__( self, pref_data, section_name, index, index_name ): + self.pref_list = pref_data.get( section_name, index_name ) + if type(self.pref_list) != types.ListType: + self.pref_list = [self.pref_list] + + self.index = index def has( self, name ): - return self.pref_data.has_option( self.section_name, '%s_%d' % (name, self.index) ) + return name in self.pref_list[ self.index ] + + def get( self, name ): + return self.pref_list[ self.index ][ name ] def getstr( self, name ): - return self.pref_data.get( self.section_name, '%s_%d' % (name, self.index) ).strip() + return self.get( name ).strip() def getint( self, name ): - return self.pref_data.getint( self.section_name, '%s_%d' % (name, self.index) ) + return int( self.getstr( name ) ) def getfloat( self, name ): - return self.pref_data.getfloat( self.section_name, '%s_%d' % (name, self.index) ) + return float( self.getstr( name ) ) def getbool( self, name ): - return self.pref_data.getboolean( self.section_name, '%s_%d' % (name, self.index) ) - -class SetIndexedOption: - def __init__( self, pref_data, section_name, index ): - self.pref_data = pref_data - self.section_name = section_name - self.index = index + 1 - - def set( self, name, value ): - self.pref_data.set( self.section_name, '%s_%d' % (name, self.index), value ) + return self.getstr( name ) == 'true' class ProjectsPreferences(PreferenceSection): def __init__( self, app ): @@ -165,9 +365,9 @@ if not pref_data.has_section( self.section_name ): return - num_projects = pref_data.getint( self.section_name, 'num_projects' ) + num_projects = pref_data.len_section( self.section_name, 'project' ) for index in range( num_projects ): - get_option = GetIndexedOption( pref_data, self.section_name, index ) + get_option = GetIndexedOption( pref_data, self.section_name, index, 'project' ) provider = get_option.getstr( 'provider' ) if wb_source_control_providers.hasProvider( provider ): @@ -179,12 +379,15 @@ def writePreferences( self, pref_data ): pref_data.remove_section( self.section_name ) pref_data.add_section( self.section_name ) - pref_data.set( self.section_name, 'num_projects', len( self.all_projects ) ) - for index, pi in enumerate( self.all_projects.values() ): - pi.writePreferences( SetIndexedOption( pref_data, self.section_name, index ) ) + + for pi in self.all_projects.values(): + pref_dict = {} + + pi.writePreferences( pref_dict ) + pref_data.append_dict( self.section_name, 'project', pref_dict ) def _by_project_name( self, a, b ): - return cmp( a.project_name, b.project_name ) + return cmp( a.project_name.lower(), b.project_name.lower() ) def getProjectList( self ): pl = self.all_projects.values() @@ -222,9 +425,9 @@ if get_option.has( 'leaf_names_to_ignore' ): self.leaf_names_to_ignore = get_option.getstrlist( 'leaf_names_to_ignore', ',' ) - num_bookmarks = get_option.getint( 'num_bookmarks' ) + num_bookmarks = pref_data.len_section( self.section_name, 'bookmark' ) for index in range( num_bookmarks ): - get_option = GetIndexedOption( pref_data, self.section_name, index ) + get_option = GetIndexedOption( pref_data, self.section_name, index, 'bookmark' ) bookmark_name = get_option.getstr( 'bookmark_name' ) provider = get_option.getstr( 'provider' ) @@ -248,15 +451,15 @@ pref_data.add_section( self.section_name ) set_option = SetOption( pref_data, self.section_name ) - #set_option.set( 'menu_style', self.menu_style ) - #set_option.set( 'leaf_names_to_ignore', self.leaf_names_to_ignore, ',' ) - set_option.set( 'num_bookmarks', len( self.all_bookmarks ) ) - for index, bookmark_name in enumerate( self.all_bookmarks.keys() ): - set_option = SetIndexedOption( pref_data, self.section_name, index ) - set_option.set( 'bookmark_name', bookmark_name ) + for bookmark_name in self.all_bookmarks.keys(): + pref_dict = {} + pref_dict[ 'bookmark_name' ] = bookmark_name + pi = self.all_bookmarks[ bookmark_name ] - pi.writePreferences( set_option ) + pi.writePreferences( pref_dict ) + + pref_data.append_dict( self.section_name, 'bookmark', pref_dict ) def addBookmark( self, pi, name=None ): if name is None: @@ -525,3 +728,26 @@ set_option.set( 'diff_tool_options', self.gui_diff_tool_options ) if self.shell_diff_tool_options != '': set_option.set( 'shell_diff_tool_options', self.shell_diff_tool_options ) + +if __name__ == '__main__': + class FakeApp: + def __init__( self ): + self.log = self + + def info( self, message ): + print 'Info:',message + + def error( self, message ): + print 'Error:',message + + def getCredentials( self ): + pass + + import wb_subversion_provider + + # Register all supported source control providers + wb_subversion_provider.registerProvider() + + p = Preferences( FakeApp(), '/tmp/t.xml', '/tmp/t.ini' ) + pprint.pprint( p.pref_data.all_sections ) + p.writePreferences() diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb.rc.template svn-workbench-1.5.1/Source/wb.rc.template --- svn-workbench-1.5.0/Source/wb.rc.template 2006-01-02 17:06:03.000000000 +0100 +++ svn-workbench-1.5.1/Source/wb.rc.template 2007-01-28 18:06:09.000000000 +0100 @@ -75,7 +75,7 @@ VALUE "FileDescription", "Work Bench\0" VALUE "FileVersion", "%(MAJOR)s, %(MINOR)s, %(PATCH)s, %(BUILD)s\0" VALUE "InternalName", "WorkBench\0" - VALUE "LegalCopyright", "Copyright © 2003-2006 Barry A. Scott\0" + VALUE "LegalCopyright", "Copyright © 2003-2007 Barry A. Scott\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "Workbench.exe\0" VALUE "PrivateBuild", "\0" diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_read_file.py svn-workbench-1.5.1/Source/wb_read_file.py --- svn-workbench-1.5.0/Source/wb_read_file.py 2006-05-26 14:57:38.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_read_file.py 2006-12-09 12:49:12.000000000 +0100 @@ -16,6 +16,7 @@ def readFileContentsAsUnicode( filename ): f = file( filename, 'r' ) contents = f.read() + f.close() return contentsAsUnicode( contents ) diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb.sh svn-workbench-1.5.1/Source/wb.sh --- svn-workbench-1.5.0/Source/wb.sh 2004-05-31 16:04:18.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb.sh 2006-10-24 23:51:49.000000000 +0200 @@ -1,4 +1,26 @@ #!/bin/sh -set -e -set -x -${PYTHON:-python} wb_main.py $* +export PYSVN_WORKBENCH_STDOUT_LOG=$(tty) +if [ "$PYTHONPATH" = "" ] +then + export PYTHONPATH=${WORKDIR}/Source +else + export PYTHONPATH=${WORKDIR}/Source:$PYTHONPATH +fi + +PYTHON=${PYTHON:-python} +BASENAME=$( basename ${PYTHON} ) +SUFFIX=${X#python*} +DIRNAME=$( dirname ${PYTHON} ) + +if [ "${DIRNAME}" != "" ] +then + DIRNAME=${DIRNAME}/ +fi +PYTHONW=${DIRNAME}pythonw${SUFFIX} + +if [ -e ${PYTHONW} ] +then + ${PYTHONW} wb_main.py $* +else + ${PYTHON} wb_main.py $* +fi diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_show_diff_frame.py svn-workbench-1.5.1/Source/wb_show_diff_frame.py --- svn-workbench-1.5.0/Source/wb_show_diff_frame.py 2006-10-10 23:17:24.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_show_diff_frame.py 2006-11-26 13:23:26.000000000 +0100 @@ -12,7 +12,9 @@ ''' import wx +import wx.stc +import wb_config import wb_diff_frame class ShowDiffFrame(wx.Frame): @@ -31,11 +33,14 @@ diff_prefs.frame_size, wx.DEFAULT_FRAME_STYLE|extra_style ) - text_control = wx.TextCtrl( self, -1, style=wx.TE_MULTILINE|wx.MAXIMIZE ) - text_control.SetEditable( False ) - text_control.SetFont( wx.Font(wb_diff_frame.point_size, wx.DEFAULT, - wx.NORMAL, wx.NORMAL, False, wb_diff_frame.face) ) - text_control.AppendText( text ) + text_control = wx.stc.StyledTextCtrl( self, -1, + wx.DefaultPosition, wx.DefaultSize, wx.NO_BORDER ) + text_control.StyleSetSpec( wx.stc.STC_STYLE_DEFAULT, + "size:%d,face:%s,fore:#000000" % (wb_config.point_size, wb_config.face) ) + + text_control.SetReadOnly( False ) + text_control.InsertText( 0, text ) + text_control.SetReadOnly( True ) self.CreateStatusBar() self.Show( True ) diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_source_control_providers.py svn-workbench-1.5.1/Source/wb_source_control_providers.py --- svn-workbench-1.5.0/Source/wb_source_control_providers.py 2006-03-18 17:56:14.000000000 +0100 +++ svn-workbench-1.5.1/Source/wb_source_control_providers.py 2006-10-29 23:32:06.000000000 +0100 @@ -72,10 +72,11 @@ if get_option.has( 'menu_name' ): self.menu_name = get_option.getstr( 'menu_name' ) - def writePreferences( self, set_option ): - set_option.set( 'provider', self.provider_name ) - set_option.set( 'name', self.project_name ) - set_option.set( 'new_file_template_dir', self.new_file_template_dir ) + def writePreferences( self, pref_dict ): + pref_dict[ 'provider' ] = self.provider_name + pref_dict[ 'name' ] = self.project_name + if self.new_file_template_dir not in [None,'']: + pref_dict[ 'new_file_template_dir' ] = self.new_file_template_dir if self.menu_name is not None: - set_option.set( 'menu_name', self.menu_name ) - set_option.set( 'menu_folder', self.menu_folder ) + pref_dict[ 'menu_name' ] = self.menu_name + pref_dict[ 'menu_folder' ] = self.menu_folder diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_subversion_checkin.py svn-workbench-1.5.1/Source/wb_subversion_checkin.py --- svn-workbench-1.5.0/Source/wb_subversion_checkin.py 2006-09-12 13:00:56.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_subversion_checkin.py 2006-11-06 00:13:29.000000000 +0100 @@ -179,6 +179,8 @@ wx.EVT_MENU( self, id_exclude, self.app.eventWrapper( self.OnExcludeItem ) ) wx.EVT_MENU( self, id_include, self.app.eventWrapper( self.OnIncludeItem ) ) + self.log_message_ctrl.SetFocus() + def OnExcludeItem( self, event ): self.list_handler.Cmd_Checkin_ExcludeItem( self.panel_list.getSelectedRows() ) self.panel_list.drawList() @@ -366,7 +368,6 @@ (wx.ACCEL_ALT, ord('L'), wb_ids.id_SP_History), (wx.ACCEL_ALT, ord('I'), wb_ids.id_SP_Info), (wx.ACCEL_ALT, ord('P'), wb_ids.id_SP_Properties), - (wx.ACCEL_NORMAL, wx.WXK_RETURN, wb_ids.id_Return_Hotkey), (wx.ACCEL_ALT, ord('O'), wb_ids.id_Shell_Open), ] elif wx.Platform == '__WXMSW__': @@ -376,7 +377,6 @@ (wx.ACCEL_CTRL, ord('L'), wb_ids.id_SP_History), (wx.ACCEL_CTRL, ord('I'), wb_ids.id_SP_Info), (wx.ACCEL_CTRL, ord('P'), wb_ids.id_SP_Properties), - (wx.ACCEL_NORMAL, wx.WXK_RETURN, wb_ids.id_Return_Hotkey), (wx.ACCEL_CTRL, ord('O'), wb_ids.id_Shell_Open), ] else: @@ -387,7 +387,6 @@ (wx.ACCEL_CTRL, ord('L'), wb_ids.id_SP_History), (wx.ACCEL_CTRL, ord('I'), wb_ids.id_SP_Info), (wx.ACCEL_CTRL, ord('P'), wb_ids.id_SP_Properties), - (wx.ACCEL_NORMAL, wx.WXK_RETURN, wb_ids.id_Return_Hotkey), ] return acc_init diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_subversion_diff.py svn-workbench-1.5.1/Source/wb_subversion_diff.py --- svn-workbench-1.5.0/Source/wb_subversion_diff.py 2006-10-11 15:56:20.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_subversion_diff.py 2007-01-28 18:06:09.000000000 +0100 @@ -1,6 +1,6 @@ ''' ==================================================================== - Copyright (c) 2003-2006 Barry A Scott. All rights reserved. + Copyright (c) 2003-2007 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. @@ -268,18 +268,21 @@ all_content_lines = wb_read_file.readFileContentsAsUnicode( path_info.path ).split('\n') elif path_info.revision.kind == pysvn.opt_revision_kind.base: - base_filename = os.path.join( - project_info.wc_path, - os.path.dirname( path_info.path ), - '.svn', 'text-base', - os.path.basename( path_info.path ) + '.svn-base' ) - all_content_lines = wb_read_file.readFileContentsAsUnicode( base_filename ).split('\n') + all_content_lines = project_info.client_bg.cat( + url_or_path=path_info.path, + revision=path_info.revision ).split('\n') else: - all_content_lines = project_info.client_bg.cat( - url_or_path=path_info.path, - revision=path_info.revision, - peg_revision=path_info.peg_revision ).split('\n') + if path_info.peg_revision is not None: + all_content_lines = project_info.client_bg.cat( + url_or_path=path_info.peg_path, + revision=path_info.revision, + peg_revision=path_info.peg_revision ).split('\n') + + else: + all_content_lines = project_info.client_bg.cat( + url_or_path=path_info.path, + revision=path_info.revision ).split('\n') return all_content_lines @@ -293,27 +296,30 @@ elif path_info.revision.kind == pysvn.opt_revision_kind.base: rev_description = 'BASE' - base_filename = os.path.join( - project_info.wc_path, - os.path.dirname( path_info.path ), - '.svn', 'text-base', - os.path.basename( path_info.path ) + '.svn-base' ) - all_content = wb_read_file.readFileContentsAsUnicode( base_filename ) + all_content = project_info.client_bg.cat( + url_or_path=path_info.path, + revision=path_info.revision ) else: if path_info.revision.kind == pysvn.opt_revision_kind.head: rev_description = 'HEAD' else: - rev_description = 'R%d' % path_info.revision.number + rev_description = 'R%d' % path_info.revision.number - all_content = project_info.client_bg.cat( - url_or_path=path_info.peg_path, - peg_revision=path_info.peg_revision, - revision=path_info.revision ) + if path_info.peg_revision is not None: + all_content = project_info.client_bg.cat( + url_or_path=path_info.peg_path, + peg_revision=path_info.peg_revision, + revision=path_info.revision ) + else: + all_content = project_info.client_bg.cat( + url_or_path=path_info.path, + revision=path_info.revision ) # create a temp file with a name that is based on the original filename - prefix = '%s-%s-' % (os.path.basename( path_info.path ), rev_description) - fd, filename = tempfile.mkstemp( prefix=prefix, suffix='.tmp') + prefix = 'tmp-%s-%s-' % (os.path.basename( path_info.path ), rev_description) + suffix = os.path.splitext( path_info.path )[1] + fd, filename = tempfile.mkstemp( prefix=prefix, suffix=suffix ) os.write( fd, all_content ) os.close( fd ) diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_subversion_history.py svn-workbench-1.5.1/Source/wb_subversion_history.py --- svn-workbench-1.5.0/Source/wb_subversion_history.py 2006-10-10 23:17:24.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_subversion_history.py 2007-01-28 18:06:09.000000000 +0100 @@ -1,6 +1,6 @@ ''' ==================================================================== - Copyright (c) 2003-2006 Barry A Scott. All rights reserved. + Copyright (c) 2003-2007 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. @@ -23,6 +23,7 @@ import wb_images import wb_subversion_utils import wb_subversion_diff +import wb_config id_view_cmd = wx.NewId() id_diff_cmd = wx.NewId() @@ -52,15 +53,41 @@ self.message = message self.changed_paths = changed_paths + self.changed_paths.sort( self.by_changed_path ) + + def by_changed_path( self, a, b ): + return cmp( a.path, b.path ) + + def matchFilter( self, filter_field, filter_text ): + if filter_text == '': + return True + + if self.label != '': + return True + + if filter_field == 'Author': + return filter_text.lower() in self.author.lower() + elif filter_field == 'Comment': + return filter_text.lower() in self.message.lower() + elif filter_field == 'Path': + for changed_path in self.changed_paths: + if filter_text.lower() in changed_path.path.lower(): + return True + + return False + else: + assert( False ) + return False + def getHistoryEntries( project_info, filename, limit, revision_end ): - history_entries = [] + all_history_entries = [] # need the URL and repos_root_URL # [0] first entry [0][1] the info info = project_info.client_bg.info2( filename, recurse=False )[0][1] if info.repos_root_URL is None: info = project_info.client_bg.info2( info.URL, recurse=False )[0][1] - log_entries = project_info.client_bg.log( + all_log_entries = project_info.client_bg.log( filename, strict_node_history=False, discover_changed_paths=True, @@ -68,9 +95,10 @@ revision_end=revision_end ) repos_path = info.URL[len(info.repos_root_URL):] - for log in log_entries: - history_entries.append( - LogEntry( log.revision.number, + for log in all_log_entries: + all_history_entries.append( + LogEntry( + log.revision.number, info.repos_root_URL+repos_path, log.author, log.date, @@ -85,19 +113,45 @@ repos_path = changed_path.copyfrom_path break + all_history_entries.sort( __cmpLogEntryHighToLow ) + + oldest_rev = log.revision.number + tags_url = project_info.getTagsUrl( info.URL ) if tags_url: - for ls_info in project_info.client_bg.ls( tags_url ): - history_entries.append( LogEntry(ls_info.created_rev.number, - '', - ls_info.last_author, - ls_info.time, - 'Tag ' + ls_info.name.split('/')[-1], - '', - []) ) + try: + for log in project_info.client_bg.log( tags_url, discover_changed_paths=True ): + for changed_path in log.changed_paths: + if changed_path.copyfrom_revision is not None: + # only include if it has taged an item in the history + if changed_path.copyfrom_revision.number > oldest_rev: + all_history_entries.append( + LogEntry( + changed_path.copyfrom_revision.number, + __findTaggedUrl( all_history_entries, changed_path.copyfrom_revision.number ), + log.author, + log.date, + 'Tag ' + changed_path.path.split('/')[-1], + log.message, + log.changed_paths) ) + break + + except pysvn.ClientError, e: + self.log.info( 'Cannot find tags in %s - %s' % (tags_url, str(e)) ) + + all_history_entries.sort( __cmpLogEntryHighToLow ) + return info.URL, all_history_entries + +def __cmpLogEntryHighToLow( a, b ): + return -cmp( a.rev_number, b.rev_number ) + +def __findTaggedUrl( all_history_entries, tag_revnum ): + for entry in all_history_entries: + if entry.rev_number < tag_revnum: + return entry.url - history_entries.sort() - return info.URL, history_entries + # this cannot happen + raise RuntimeError( '__findTaggedUrl failed to find tagged url' ) class HistoryDialog(wx.Dialog): def __init__( self, parent ): @@ -208,10 +262,10 @@ return pysvn.Revision( pysvn.opt_revision_kind.number, 0 ) class HistoryFileFrame(wx.Frame): - def __init__( self, app, project_info, filename, url, log_entries ): + def __init__( self, app, project_info, filename, url, all_log_entries ): wx.Frame.__init__( self, None, -1, "History of %s" % filename, size=(700,500) ) - self.panel = LogHistoryPanel( self, app, project_info, filename, url, log_entries ) + self.panel = LogHistoryPanel( self, app, project_info, filename, url, all_log_entries ) # Set the application icon self.SetIcon( wb_images.getIcon( 'wb.png' ) ) @@ -227,10 +281,10 @@ info1, info2 ) class HistoryDirFrame(wx.Frame): - def __init__( self, app, project_info, filename, url, log_entries ): + def __init__( self, app, project_info, filename, url, all_log_entries ): wx.Frame.__init__( self, None, -1, "History of %s" % filename, size=(700,500) ) - self.panel = LogHistoryPanel( self, app, project_info, filename, url, log_entries ) + self.panel = LogHistoryPanel( self, app, project_info, filename, url, all_log_entries ) # Set the application icon self.SetIcon( wb_images.getIcon( 'wb.png' ) ) @@ -245,6 +299,110 @@ app, project_info, info1, info2 ) + +class WbHistoryListCtrl(wx.ListCtrl): + def __init__( self, parent, log_history, list_id ): + wx.ListCtrl.__init__( self, parent, list_id, + style=wx.LC_REPORT|wx.NO_BORDER|wx.LC_HRULES|wx.LC_VIRTUAL ) + self.log_history = log_history + + def OnGetItemText(self, item, col): + return self.log_history.OnGetItemText( item, col ) + + def OnGetItemImage(self, item): + return -1 + + def OnGetItemAttr(self, item): + return self.log_history.OnGetItemAttr( item ) + +class PanelFilter(wx.Panel): + def __init__( self, parent, app, filter_field ): + wx.Panel.__init__(self, parent, -1) + + self.app = app + + self.background_colour = wx.SystemSettings.GetColour( wx.SYS_COLOUR_3DFACE ) + + self.v_sizer = wx.BoxSizer( wx.VERTICAL ) + self.h_sizer2 = wx.BoxSizer( wx.HORIZONTAL ) + + self.filter_changed_handler = None + + self.filter_field_choices = ['Author','Comment','Path'] + self.filter_choice_ctrl = wx.Choice( self, wx.NewId(), choices=self.filter_field_choices ) + self.filter_choice_ctrl.SetSelection( self.filter_field_choices.index( filter_field ) ) + if wx.Platform == '__WXMAC__': + self.filter_text_ctrl = wx.TextCtrl( self, wx.NewId(), '', size=(-1,-1) ) + else: + self.filter_text_ctrl = wx.TextCtrl( self, wx.NewId(), '', size=(-1,10) ) + self.filter_clear_button = wx.Button( self, wx.NewId(), 'X', style=wx.BU_EXACTFIT, size=(30, -1) ) + + border = 1 + self.h_sizer2.Add( self.filter_choice_ctrl, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, border ) + if wx.Platform == '__WXMAC__': + self.h_sizer2.Add( self.filter_text_ctrl, 1, wx.EXPAND|wx.ALL, border ) + else: + self.h_sizer2.Add( self.filter_text_ctrl, 1, wx.EXPAND|wx.ALL, border+2 ) + self.h_sizer2.Add( self.filter_clear_button, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, border ) + + border = 3 + self.v_sizer.Add( self.h_sizer2, 1, wx.EXPAND|wx.ALL, 0 ) + + wx.EVT_BUTTON( self, self.filter_clear_button.GetId(), self.OnClearFilterText ) + wx.EVT_TEXT( self, self.filter_text_ctrl.GetId(), self.OnFilterTextChanged ) + wx.EVT_CHOICE( self, self.filter_choice_ctrl.GetId(), self.OnFilterTypeChanged ) + + wx.EVT_KEY_DOWN( self, self.OnKeyDown ) + + self.SetAutoLayout( True ) + self.SetSizer( self.v_sizer ) + self.v_sizer.Fit( self ) + self.Layout() + + def OnKeyDown( self, event ): + if event.GetKeyCode() == wx.WXK_TAB: + self.app.frame.list_panel.SetFocus() + else: + event.Skip() + + def setFocusFilter( self ): + self.filter_text_ctrl.SetFocus() + + def setFilterChangedHandler( self, handler ): + self.filter_changed_handler = handler + + def __callFilterChangedHandler( self ): + self.filter_changed_handler( + self.filter_field_choices[ self.filter_choice_ctrl.GetSelection() ], + self.filter_text_ctrl.GetValue() ) + + def updateHeader(self, url_name, path_name ): + if url_name is None: + url_name = '' + if path_name is None: + path_name = '' + + self.url_text_ctrl.SetValue( url_name ) + self.path_text_ctrl.SetValue( path_name ) + + self.SetBackgroundColour( self.background_colour ) + self.Refresh() + + def clearFilterText( self ): + self.filter_text_ctrl.Clear() + self.__callFilterChangedHandler() + + def OnClearFilterText( self, event=None ): + self.filter_text_ctrl.Clear() + self.__callFilterChangedHandler() + + def OnFilterTypeChanged( self, event ): + self.filter_text_ctrl.Clear() + self.__callFilterChangedHandler() + + def OnFilterTextChanged( self, event ): + self.__callFilterChangedHandler() + class LogHistoryPanel: col_revision = 0 col_author = 1 @@ -257,13 +415,21 @@ col_copyfrom_revision = 2 col_copyfrom_path = 3 - def __init__( self, parent, app, project_info, filename, url, log_entries ): + def __init__( self, parent, app, project_info, filename, url, all_log_entries ): self.parent = parent self.app = app self.project_info = project_info self.filename = filename self.url = url - self.log_entries = log_entries + self.all_log_entries = all_log_entries + + # run from recent to old + self.all_log_entries.sort( self.by_rev ) + + + self.filter_field = 'Comment' + self.filter_text = '' + self.all_filtered_log_entries = all_log_entries # Create the splitter windows self.splitter = wx.lib.splitter.MultiSplitterWindow( parent ) @@ -283,16 +449,17 @@ self.splitter.AppendWindow( self.panel_comment, 100 ) self.splitter.AppendWindow( self.panel_changed_paths, 150 ) - # run from recent to old - self.log_entries.sort( self.cmpLogEntries ) - self.selected_revisions = {} self.v_sizer_history = wx.BoxSizer( wx.VERTICAL ) self.v_sizer_comment = wx.BoxSizer( wx.VERTICAL ) self.v_sizer_changed_paths = wx.BoxSizer( wx.VERTICAL ) - self.list_ctrl = wx.ListCtrl( self.panel_history, id_list, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT|wx.NO_BORDER) + self.panel_filter = PanelFilter( self.panel_history, app, self.filter_field ) + self.panel_filter.setFilterChangedHandler( self.OnFilterChanged ) + + self.list_ctrl = WbHistoryListCtrl( self.panel_history, self, id_list ) + self.all_item_attr = {} self.list_ctrl.InsertColumn( self.col_revision, "Revision" ) self.list_ctrl.InsertColumn( self.col_author, "Author" ) @@ -330,17 +497,18 @@ self.paths_label = wx.StaticText( self.panel_changed_paths, -1, 'Changed Paths' ) + self.v_sizer_history.Add( self.panel_filter, 0, wx.EXPAND|wx.ALL, 0 ) self.v_sizer_history.Add( self.list_ctrl, 2, wx.EXPAND|wx.ALL, 5 ) self.v_sizer_comment.Add( self.comment_label, 0, wx.ALL, 5 ) self.v_sizer_comment.Add( self.comment_ctrl, 2, wx.EXPAND|wx.ALL, 5 ) self.v_sizer_changed_paths.Add( self.paths_label, 0, wx.ALL, 5 ) self.v_sizer_changed_paths.Add( self.paths_ctrl, 2, wx.EXPAND|wx.ALL, 5 ) + wx.EVT_LIST_ITEM_SELECTED( self.panel_history, id_list, self.OnListItemSelected ) + wx.EVT_LIST_ITEM_DESELECTED( self.panel_history, id_list, self.OnListItemDeselected ) - wx.EVT_LIST_ITEM_SELECTED( self.panel_history, id_list, self.OnListItemSelected) - wx.EVT_LIST_ITEM_DESELECTED( self.panel_history, id_list, self.OnListItemDeselected) - wx.EVT_LIST_ITEM_SELECTED( self.panel_history, id_paths, self.OnPathItemSelected) - wx.EVT_LIST_ITEM_DESELECTED( self.panel_history, id_paths, self.OnPathItemDeselected) + wx.EVT_LIST_ITEM_SELECTED( self.panel_history, id_paths, self.OnPathItemSelected ) + wx.EVT_LIST_ITEM_DESELECTED( self.panel_history, id_paths, self.OnPathItemDeselected ) self.initList() @@ -358,9 +526,56 @@ sizer.Fit( panel ) panel.Layout() - def cmpLogEntries( self, a, b ): - return cmp( a.rev_number, b.rev_number ) + # ---------------------------------------- + def setFocusFilter( self ): + self.panel_filter.setFocusFilter() + + def OnFilterChanged( self, field, text ): + self.filter_field = field + self.filter_text = text + + if self.filter_text == '': + self.all_filtered_log_entries = self.all_log_entries + else: + self.all_filtered_log_entries = [ + log_entry for log_entry in self.all_log_entries + if log_entry.matchFilter( self.filter_field, self.filter_text )] + + self.initList() + + def OnGetItemText( self, index, col ): + log_entry = self.all_filtered_log_entries[ index ] + if col == self.col_revision: + return str(log_entry.rev_number) + elif col == self.col_author: + return log_entry.author + elif col == self.col_date: + return wb_subversion_utils.fmtDateTime( log_entry.date ) + elif col == self.col_label: + return log_entry.label + elif col == self.col_message: + return log_entry.message.replace( '\n', ' ' ) + else: + assert( False ) + + def OnGetItemImage( self, index ): + return -1 + + def OnGetItemAttr( self, index ): + log_entry = self.all_filtered_log_entries[ index ] + if len(log_entry.label) == 0: + colour = wb_config.colour_log_normal + else: + colour = wb_config.colour_log_tag + + if colour not in self.all_item_attr: + attr = wx.ListItemAttr() + attr.SetTextColour( colour ) + self.all_item_attr[ colour ] = attr + + return self.all_item_attr[ colour ] + # ---------------------------------------- def initButtons( self, sizer ): self.h_sizer = wx.BoxSizer( wx.HORIZONTAL ) @@ -379,20 +594,26 @@ wx.EVT_BUTTON( self.panel_history, id_view_cmd, self.OnViewCommand ) wx.EVT_BUTTON( self.panel_history, id_diff_cmd, self.OnDiffCommand ) + wx.EVT_UPDATE_UI( self.panel_history, id_diff_cmd, self.OnUpdateUiDiffCommand ) + + def getSelectedRows( self ): + all_rows = [] + item_index = -1 + while True: + item_index = self.list_ctrl.GetNextItem( item_index, wx.LIST_NEXT_ALL, wx.LIST_STATE_SELECTED ) + if item_index < 0: + break + + all_rows.append( item_index ) + + return all_rows + #---------- Event handlers ---------------------------------------------------------- def OnListItemSelected( self, event): self.updateComment( event.m_itemIndex ) - self.selected_revisions[ event.m_itemIndex ] = None - self.button_diff.Enable( len( self.selected_revisions ) in [1] ) - self.button_diff.Enable( len( self.selected_revisions ) in [1,2] ) def OnListItemDeselected( self, event): self.updateComment( -1 ) - if event.m_itemIndex in self.selected_revisions: - del self.selected_revisions[ event.m_itemIndex ] - - self.button_diff.Enable( len( self.selected_revisions ) in [1] ) - self.button_diff.Enable( len( self.selected_revisions ) in [1,2] ) def OnPathItemSelected( self, event): pass @@ -403,8 +624,8 @@ #---------- Comment handlers ------------------------------------------------------------ def updateComment( self, index ): if index >= 0: - message = self.log_entries[ index ].message - all_paths_info = self.log_entries[ index ].changed_paths + message = self.all_filtered_log_entries[ index ].message + all_paths_info = self.all_filtered_log_entries[ index ].changed_paths else: message = '' all_paths_info = [] @@ -412,10 +633,7 @@ self.comment_ctrl.SetValue( message ) self.comment_ctrl.SetInsertionPoint( 0 ) - self.log_entries.sort( self.by_rev ) - self.paths_ctrl.DeleteAllItems() - all_paths_info.sort( self.by_changed_path ) for index, info in enumerate( all_paths_info ): self.paths_ctrl.InsertStringItem( index, action_map.get( info.action, info.action ) ) @@ -442,42 +660,41 @@ w,h = self.panel_changed_paths.GetClientSizeTuple() self.v_sizer_changed_paths.SetDimension( 0, 0, w, h ) - def by_changed_path( self, a, b ): - return cmp( a.path, b.path ) - def by_rev( self, a, b ): # highest rev first return -cmp( a.rev_number, b.rev_number ) def initList( self ): - self.log_entries.sort( self.by_rev ) - - for index, log_entry in enumerate( self.log_entries ): - self.list_ctrl.InsertStringItem( index, str(log_entry.rev_number) ) - self.list_ctrl.SetStringItem( index, self.col_author, log_entry.author ) - self.list_ctrl.SetStringItem( index, self.col_date, wb_subversion_utils.fmtDateTime( log_entry.date ) ) - self.list_ctrl.SetStringItem( index, self.col_label, log_entry.label ) - self.list_ctrl.SetStringItem( index, self.col_message, log_entry.message.replace( '\n', ' ' ) ) + self.list_ctrl.DeleteAllItems() + self.list_ctrl.SetItemCount( len(self.all_filtered_log_entries) ) + if len(self.all_filtered_log_entries) > 0: + self.list_ctrl.RefreshItems( 0, len(self.all_filtered_log_entries)-1 ) #---------- Command handlers ---------------------------------------------------------- def OnViewCommand( self, event ): print 'Log history View not implemented' + def OnUpdateUiDiffCommand( self, event ): + all_rows = self.getSelectedRows() + self.button_diff.Enable( len( all_rows ) in (1,2) ) + def OnDiffCommand( self, event ): - indices = self.selected_revisions.keys() + indices = self.getSelectedRows() + indices.sort() indices.reverse() if len( indices ) not in (1,2): return - info1 = wb_subversion_diff.PathInfoForDiff() - info1.path = self.log_entries[ indices[0] ].url - info1.revision = pysvn.Revision( pysvn.opt_revision_kind.number, self.log_entries[ indices[0] ].rev_number ) - info1.peg_path = self.log_entries[ 0 ].url - info1.peg_revision = pysvn.Revision( pysvn.opt_revision_kind.number, self.log_entries[ 0 ].rev_number ) + info1.path = self.all_filtered_log_entries[ indices[0] ].url + info1.revision = pysvn.Revision( pysvn.opt_revision_kind.number, + self.all_filtered_log_entries[ indices[0] ].rev_number ) + info1.peg_path = self.all_filtered_log_entries[ 0 ].url + info1.peg_revision = pysvn.Revision( pysvn.opt_revision_kind.number, + self.all_filtered_log_entries[ 0 ].rev_number ) info1.title = '%s@%d' % (info1.path, info1.revision.number) info2 = info1.copy() @@ -489,8 +706,9 @@ info2.peg_revision = info2.revision info2.title = self.filename else: - info2.path = self.log_entries[ indices[1] ].url - info2.revision = pysvn.Revision( pysvn.opt_revision_kind.number, self.log_entries[ indices[1] ].rev_number ) + info2.path = self.all_filtered_log_entries[ indices[1] ].url + info2.revision = pysvn.Revision( pysvn.opt_revision_kind.number, + self.all_filtered_log_entries[ indices[1] ].rev_number ) info2.title = '%s@%d' % (info2.path, info2.revision.number) generator = self.parent.diffFunction( diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_subversion_info_dialog.py svn-workbench-1.5.1/Source/wb_subversion_info_dialog.py --- svn-workbench-1.5.0/Source/wb_subversion_info_dialog.py 2006-01-02 17:06:03.000000000 +0100 +++ svn-workbench-1.5.1/Source/wb_subversion_info_dialog.py 2006-12-09 12:31:51.000000000 +0100 @@ -10,6 +10,7 @@ wb_subversion_list_handler.py ''' +import types import pysvn import wx import wb_subversion_utils @@ -24,7 +25,8 @@ self.addGroup( 'Entry' ) self.addRow( 'Path:',path ) - if type(info_entry_or_info2_list) == type([]): + #print 'info_entry_or_info2_list',info_entry_or_info2_list + if type(info_entry_or_info2_list) == types.ListType: self.initForInfo2( info_entry_or_info2_list[0][1] ) else: self.initForInfo1( info_entry_or_info2_list ) @@ -145,7 +147,7 @@ elif wc_info['schedule'] == pysvn.wc_schedule.replace: self.addRow( 'Schedule:','replace' ) else: - self.addRow( 'Schedule:', str(wc_info['schedule'])) + self.addRow( 'Schedule:', unicode(wc_info['schedule'])) if wc_info['copyfrom_url']: self.addRow( 'Copied From URL:', wc_info['copyfrom_url'] ) if wc_info['copyfrom_rev'].number: @@ -169,11 +171,14 @@ def addRow( self, label, value ): label_ctrl = wx.StaticText( self, -1, label, style=wx.ALIGN_RIGHT ) - str_value = str(value) + str_value = unicode(value) + + # cannot set the controls readonly as that prevent copy of the text if '\n' in str_value: - value_ctrl = wx.TextCtrl( self, -1, str(value), size=wx.Size( -1, 100 ), style=wx.TE_READONLY|wx.TE_MULTILINE ) + value_ctrl = wx.TextCtrl( self, -1, unicode(value), size=wx.Size( -1, 100 ), + style=wx.TE_MULTILINE ) else: - value_ctrl = wx.TextCtrl( self, -1, str(value), style=wx.TE_READONLY ) + value_ctrl = wx.TextCtrl( self, -1, unicode(value) ) self.g_sizer.Add( label_ctrl, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( value_ctrl, 0, wx.EXPAND, 5 ) diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_subversion_list_handler_common.py svn-workbench-1.5.1/Source/wb_subversion_list_handler_common.py --- svn-workbench-1.5.0/Source/wb_subversion_list_handler_common.py 2006-10-11 23:29:43.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_subversion_list_handler_common.py 2007-01-28 18:06:09.000000000 +0100 @@ -1,6 +1,6 @@ ''' ==================================================================== - Copyright (c) 2003-2006 Barry A Scott. All rights reserved. + Copyright (c) 2003-2007 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. @@ -26,6 +26,7 @@ import wb_subversion_info_dialog import wb_subversion_properties_dialog import wb_subversion_diff +import wb_config col_labels = [ ('Name', 25, 10, 100, wx.LIST_FORMAT_LEFT), @@ -236,10 +237,10 @@ if len(filter_text) > 0: if filter_field == 'Name': self.all_files = [f for f in self.project_info.getFilesStatus() - if filter_text in f.path[prefix_len:]] + if filter_text.lower() in f.path[prefix_len:].lower()] elif filter_field == 'Author': self.all_files = [f for f in self.project_info.getFilesStatus() - if f.entry is not None and filter_text in f.entry.commit_author] + if f.entry is not None and filter_text.lower() in f.entry.commit_author.lower()] else: self.all_files = self.project_info.getFilesStatus() @@ -310,7 +311,6 @@ return self.column_info.getColumnOrder()[col] def OnGetItemText( self, index, col ): - prefix_len = len( self.project_info.wc_path ) + 1 column = self.column_info.getNameByColumn( col ) @@ -354,7 +354,7 @@ def OnGetItemAttr( self, index ): if self.project_info.need_checkout: - colour = wx.RED + colour = wb_config.colour_status_need_checkout else: colour = self.statusColour( self.all_files[ index ] ) @@ -453,6 +453,7 @@ state.need_checkin = True state.conflict = True state.file_exists = True + state.is_folder = True for row in all_rows: filename = self.all_files[ row ].path @@ -464,6 +465,8 @@ state.modified = False state.conflict = False state.file_exists = False + else: + state.is_folder = False text_status = self.getTextStatus( row ) if text_status in [pysvn.wc_status_kind.unversioned]: @@ -531,7 +534,8 @@ if status.entry is None: return '' else: - return os.path.join( self.project_info.wc_path, status.entry.conflict_old ) + return os.path.join( self.project_info.wc_path, + status.entry.conflict_old ) def getConflictNew( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) @@ -561,29 +565,29 @@ return wb_subversion_utils._status_format( status ) def getAllGreyFilenames( self ): - raise NotImplemented + raise NotImplementedError def statusColour( self, status ): # default colour when nothing special is know for the file - colour = wx.BLACK + colour = wb_config.colour_status_normal # show that a file is on the clipboard if status.path in self.getAllGreyFilenames(): - colour = wx.Colour( 128, 128, 128 ) + colour = wb_config.colour_status_disabled # show that a file is uncontrolled elif status.entry is None: - colour = wx.GREEN + colour = wb_config.colour_status_unversioned else: # show a file is locked if( status.is_locked ): - colour = wx.RED + colour = wb_config.colour_status_locked # show a file is modified elif( self.getTextStatus( status ) != pysvn.wc_status_kind.normal or self.getPropStatus( status ) not in [pysvn.wc_status_kind.normal,pysvn.wc_status_kind.none] or status.is_copied or status.is_switched ): - colour = wx.BLUE + colour = wb_config.colour_status_modified return colour @@ -719,15 +723,15 @@ info1, info2 ) - if type(generator) == types.GeneratorType: - while True: - try: - where_to_go_next = generator.next() - except StopIteration: - # no problem all done - break + if type(generator) == types.GeneratorType: + while True: + try: + where_to_go_next = generator.next() + except StopIteration: + # no problem all done + break - yield where_to_go_next + yield where_to_go_next self.app.setAction( 'Ready' ) @@ -949,7 +953,8 @@ __pychecker__ = '--no-returnvalues' if field == SubversionListHandlerCommon.col_name: - return status.path + return status.path.lower() + if field == SubversionListHandlerCommon.col_state: # Use positive text_status first # then positive prop_status diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_subversion_list_handler.py svn-workbench-1.5.1/Source/wb_subversion_list_handler.py --- svn-workbench-1.5.0/Source/wb_subversion_list_handler.py 2006-09-15 16:52:37.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_subversion_list_handler.py 2006-12-23 13:19:55.000000000 +0100 @@ -23,18 +23,9 @@ import wb_subversion_utils import wb_subversion_checkin import wb_clipboard +import wb_diff_frame class SubversionListHandler(wb_subversion_list_handler_common.SubversionListHandlerCommon): - col_name = 'Name' - col_state = 'State' - col_date = 'Date' - col_revision = 'Rev' - col_author = 'Author' - col_type = 'Type' - col_size = 'Size' - col_mime_type = 'Mimetype' - col_eol_style = 'EOL' - def __init__( self, app, list_panel, project_info ): wb_subversion_list_handler_common.SubversionListHandlerCommon.__init__( self, app, list_panel, project_info ) @@ -274,21 +265,21 @@ old_filename = self.getConflictOld( row ) new_filename = self.getConflictNew( row ) # qqq diff old_filename@None, new_filename@None - self.app.Diff( old_filename, old_filename, new_filename, new_filename ) + self.app.diffFiles( old_filename, old_filename, new_filename, new_filename ) def Cmd_File_DiffOldMine( self, all_rows ): for row in all_rows: old_filename = self.getConflictOld( row ) mine_filename = self.getConflictMine( row ) # qqq diff old_filename@None, mine_filename@None - self.app.Diff( old_filename, old_filename, mine_filename, mine_filename ) + self.app.diffFiles( old_filename, old_filename, mine_filename, mine_filename ) def Cmd_File_DiffMineNew( self, all_rows ): for row in all_rows: mine_filename = self.getConflictMine( row ) new_filename = self.getConflictNew( row ) # qqq diff mine_filename@None, new_filename@None - self.app.Diff( mine_filename, mine_filename, new_filename, new_filename ) + self.app.diffFiles( mine_filename, mine_filename, new_filename, new_filename ) # Cmd_File_History = from SubversionListHandlerCommon # Cmd_File_Info = from SubversionListHandlerCommon @@ -313,14 +304,19 @@ if text_status == pysvn.wc_status_kind.added: # need to save and restore the props around the rename dance - _, prop_dict = self.project_info.client_fg.proplist( old_filename, - revision=pysvn.Revision( pysvn.opt_revision_kind.working ) )[0] + all_prop_lists = self.project_info.client_fg.proplist( old_filename, + revision=pysvn.Revision( pysvn.opt_revision_kind.working ) ) self.project_info.client_fg.revert( old_filename ) print 'Rename %s %s' % (old_filename, new_full_filename) os.rename( old_filename, new_full_filename ) self.project_info.client_fg.add( new_full_filename ) - for prop_name, prop_value in prop_dict.items(): - self.project_info.client_fg.propset( prop_name, prop_value, new_full_filename ) + + # all_prop_lists is empty if there are no properties set + if len(all_prop_lists) > 0: + _, prop_dict = all_prop_lists[0] + + for prop_name, prop_value in prop_dict.items(): + self.project_info.client_fg.propset( prop_name, prop_value, new_full_filename ) elif( text_status == pysvn.wc_status_kind.modified or prop_status == pysvn.wc_status_kind.modified ): diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_subversion_project_info.py svn-workbench-1.5.1/Source/wb_subversion_project_info.py --- svn-workbench-1.5.0/Source/wb_subversion_project_info.py 2006-09-22 01:57:18.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_subversion_project_info.py 2006-12-23 13:19:55.000000000 +0100 @@ -54,7 +54,7 @@ self.tags_url = None def __repr__( self ): - return '' % self.wc_path + return '' % self.wc_path #return '' % (self.url, self.wc_path) def init( self, project_name, **kws): @@ -165,13 +165,13 @@ wc_path = os.path.expanduser( wc_path ) self.init( name, url=url, wc_path=wc_path, tags_url=tags_url ) - def writePreferences( self, set_option ): + def writePreferences( self, pref_dict ): # save state into a preference file - wb_source_control_providers.ProjectInfo.writePreferences( self, set_option ) + wb_source_control_providers.ProjectInfo.writePreferences( self, pref_dict ) - set_option.set( 'url', self.url ) - set_option.set( 'wc_path', self.wc_path ) - set_option.set( 'tags_url', self.tags_url ) + pref_dict[ 'url' ] = self.url + pref_dict[ 'wc_path' ] = self.wc_path + pref_dict[ 'tags_url' ] = self.tags_url def isEqual( self, pi ): return (self.provider_name == pi.provider_name @@ -316,15 +316,20 @@ def __proplist( self, path ): if os.path.isdir( path ): prop_file = os.path.join( path, '.svn', 'dir-props' ) + base_prop_file = os.path.join( path, '.svn', 'dir-prop-base' ) else: dirname, basename = os.path.split( path ) prop_file = os.path.join( dirname, '.svn', 'props', basename + '.svn-work' ) + base_prop_file = os.path.join( dirname, '.svn', 'prop-base', basename + '.svn-base' ) result = {} try: f = file( prop_file ) except EnvironmentError: - return result + try: + f = file( base_prop_file ) + except EnvironmentError: + return result while True: line = f.readline() diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_subversion_properties_dialog.py svn-workbench-1.5.1/Source/wb_subversion_properties_dialog.py --- svn-workbench-1.5.0/Source/wb_subversion_properties_dialog.py 2006-01-02 17:06:03.000000000 +0100 +++ svn-workbench-1.5.1/Source/wb_subversion_properties_dialog.py 2006-11-30 10:37:01.000000000 +0100 @@ -97,6 +97,27 @@ def getValue( self ): return self.value_ctrl.GetValue() +class SinglePropertyMultiLine(SingleProperty): + def __init__( self, dialog, name, present, value ): + SingleProperty.__init__( self, dialog, name, present ) + + self.setValueCtrl( wx.TextCtrl( self.dialog, self.value_id, value, + size=(600,100), style=wx.TE_MULTILINE|wx.HSCROLL ), value ) + + def isValid( self ): + if not self.isPresent(): + return True + + text = self.value_ctrl.GetValue() + if text.strip() == '': + wx.MessageBox( 'Enter a value for %s' % self.name, + 'Warning', style=wx.OK|wx.ICON_EXCLAMATION ) + return False + return True + + def getValue( self ): + return self.value_ctrl.GetValue() + class SinglePropertyChoice(SingleProperty): def __init__( self, dialog, name, present, value, choices ): SingleProperty.__init__( self, dialog, name, present ) @@ -203,7 +224,7 @@ PropertiesDialogBase.__init__( self, app, parent, path, prop_dict ) self.known_properties_names = ['svn:eol-style', 'svn:executable', 'svn:mime-type', 'svn:needs-lock', - 'svn:keywords'] + 'svn:keywords', 'svn:special'] self.initDialog() def initKnownProperties( self ): @@ -213,6 +234,11 @@ prop = 'svn:executable' self.property_ctrls[ prop ] = SinglePropertyNoValue( self, prop, self.prop_dict.has_key( prop ) ) + # special is managed by SVN only the user must not change it + prop = 'svn:special' + self.property_ctrls[ prop ] = SinglePropertyNoValue( self, prop, self.prop_dict.has_key( prop ) ) + self.property_ctrls[ prop ].checkbox.Enable( False ) + prop = 'svn:eol-style' self.property_ctrls[ prop ] = SinglePropertyChoice( self, prop, self.prop_dict.has_key( prop ), self.prop_dict.get( prop, 'native' ), ['native','CRLF','LF','CR'] ) @@ -227,10 +253,13 @@ class DirPropertiesDialog(PropertiesDialogBase): def __init__( self, app, parent, path, prop_dict ): PropertiesDialogBase.__init__( self, app, parent, path, prop_dict ) - self.known_properties_names = ['svn:ignore', 'svn:needs-lock'] + self.known_properties_names = ['svn:ignore', 'svn:externals'] self.initDialog() def initKnownProperties( self ): prop = 'svn:ignore' - self.property_ctrls[ prop ] = SinglePropertyText( self, prop, self.prop_dict.has_key( prop ), + self.property_ctrls[ prop ] = SinglePropertyMultiLine( self, prop, self.prop_dict.has_key( prop ), + self.prop_dict.get( prop, '' ) ) + prop = 'svn:externals' + self.property_ctrls[ prop ] = SinglePropertyMultiLine( self, prop, self.prop_dict.has_key( prop ), self.prop_dict.get( prop, '' ) ) diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_subversion_report_branch_changes.py svn-workbench-1.5.1/Source/wb_subversion_report_branch_changes.py --- svn-workbench-1.5.0/Source/wb_subversion_report_branch_changes.py 2006-03-20 22:14:13.000000000 +0100 +++ svn-workbench-1.5.1/Source/wb_subversion_report_branch_changes.py 2007-01-28 18:06:09.000000000 +0100 @@ -1,6 +1,6 @@ ''' ==================================================================== - Copyright (c) 2006 Barry A Scott. All rights reserved. + Copyright (c) 2006-2007 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. @@ -153,9 +153,9 @@ def statusColour( self, file ): # show that a file is on the exclude list if file.path in self.getAllGreyFilenames(): - return wx.Colour( 128, 128, 128 ) + return wb_config.colour_status_disabled else: - return wx.BLACK + return wb_config.colour_status_normal def getContextMenu( self ): menu_template = \ diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_subversion_report_lock.py svn-workbench-1.5.1/Source/wb_subversion_report_lock.py --- svn-workbench-1.5.0/Source/wb_subversion_report_lock.py 2006-03-18 18:07:27.000000000 +0100 +++ svn-workbench-1.5.1/Source/wb_subversion_report_lock.py 2007-01-28 18:06:09.000000000 +0100 @@ -1,6 +1,6 @@ ''' ==================================================================== - Copyright (c) 2006 Barry A Scott. All rights reserved. + Copyright (c) 2006-2007 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. @@ -185,9 +185,9 @@ def statusColour( self, file ): # show that a file is on the exclude list if file.path in self.getAllGreyFilenames(): - return wx.Colour( 128, 128, 128 ) + return wb_config.colour_status_disabled else: - return wx.BLACK + return wb_config.colour_status_normal def getContextMenu( self ): menu_template = \ diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_subversion_report_updates.py svn-workbench-1.5.1/Source/wb_subversion_report_updates.py --- svn-workbench-1.5.0/Source/wb_subversion_report_updates.py 2006-03-18 18:07:27.000000000 +0100 +++ svn-workbench-1.5.1/Source/wb_subversion_report_updates.py 2007-01-28 18:06:09.000000000 +0100 @@ -1,6 +1,6 @@ ''' ==================================================================== - Copyright (c) 2006 Barry A Scott. All rights reserved. + Copyright (c) 2006-2007 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. @@ -202,9 +202,9 @@ def statusColour( self, file ): # show that a file is on the exclude list if file.path in self.getAllGreyFilenames(): - return wx.Colour( 128, 128, 128 ) + return wb_config.colour_status_disabled else: - return wx.BLACK + return wb_config.colour_status_normal def getContextMenu( self ): menu_template = \ diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_subversion_tree_handler.py svn-workbench-1.5.1/Source/wb_subversion_tree_handler.py --- svn-workbench-1.5.0/Source/wb_subversion_tree_handler.py 2006-10-10 23:17:24.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_subversion_tree_handler.py 2007-01-28 18:06:09.000000000 +0100 @@ -1,6 +1,6 @@ ''' ==================================================================== - Copyright (c) 2003-2006 Barry A Scott. All rights reserved. + Copyright (c) 2003-2007 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. @@ -32,6 +32,7 @@ import wb_subversion_properties_dialog import wb_clipboard import wb_dialogs +import wb_config class SubversionProject(wb_tree_panel.TreeProjectItem): def __init__( self, app, project_info ): @@ -91,23 +92,23 @@ # no status available - make a guess if not os.path.exists( self.project_info.wc_path ): # nothing there - return wx.RED + return wb_config.colour_status_need_checkout elif not os.path.exists( os.path.join( self.project_info.wc_path, '.svn' ) ): # not versioned - return wx.GREEN + return wb_config.colour_status_unversioned else: # versioned and present - return wx.BLACK + return wb_config.colour_status_normal elif not os.path.exists( dir_status.path ): # nothing there - return wx.RED + return wb_config.colour_status_need_checkout elif dir_status.text_status in [pysvn.wc_status_kind.unversioned]: # unversioned - return wx.GREEN + return wb_config.colour_status_unversioned # versioned and present - return wx.BLACK + return wb_config.colour_status_normal def getState( self ): state = wb_tree_panel.TreeState() @@ -418,6 +419,8 @@ self.app.refreshFrame() def Cmd_Dir_Info( self ): + filename = self.project_info.wc_path + try: if hasattr( self.project_info.client_fg, 'info2' ): entry = self.project_info.client_fg.info2( filename, recurse=False ) @@ -427,7 +430,7 @@ dialog = wb_subversion_info_dialog.InfoDialog( self.app, self.app.frame.tree_panel.tree_ctrl, self.project_info.wc_path, - entry[0] ) + entry ) dialog.ShowModal() except pysvn.ClientError, e: diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_tree_panel.py svn-workbench-1.5.1/Source/wb_tree_panel.py --- svn-workbench-1.5.0/Source/wb_tree_panel.py 2006-09-26 21:54:32.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_tree_panel.py 2007-01-28 18:06:09.000000000 +0100 @@ -1,6 +1,6 @@ ''' ==================================================================== - Copyright (c) 2003-2006 Barry A Scott. All rights reserved. + Copyright (c) 2003-2007 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. @@ -25,7 +25,10 @@ import wb_config class TreeState: - def __init__( self ): + def __init__( self, place_holder=False ): + # used to tell if the state reflects a items state or is just a place holder + self.place_holder = place_holder + self.modified = False self.versioned = False self.new_versioned = False @@ -34,6 +37,7 @@ self.need_checkout = False self.conflict = False self.file_exists = False + self.is_folder = True self.is_project_parent = False def printState( self, title='' ): @@ -59,13 +63,18 @@ self.list_panel = frame.list_panel try_wrapper = wb_exceptions.TryWrapperFactory( self.app.log ) - wx.Panel.__init__( self, parent, -1 ) + wx.Panel.__init__( self, parent ) self.tree_ctrl = WbTreeCtrl( self, self.app ) - if wx.Platform == '__WXMAC__': - acc_mod = wx.ACCEL_ALT + if wb_config.focus_ring: + box = wx.BoxSizer() + box.Add( self.tree_ctrl, 1, wx.EXPAND|wx.ALL, 3) + self.SetSizer( box ) + + self.tree_ctrl.Bind( wx.EVT_PAINT, self.OnPaint ) + if wx.Platform == '__WXMAC__': acc_init = [ (wx.ACCEL_ALT, ord('C'), wb_ids.id_SP_EditCopy), (wx.ACCEL_ALT, ord('X'), wb_ids.id_SP_EditCut), @@ -78,7 +87,8 @@ (wx.ACCEL_ALT, ord('R'), wb_ids.id_SP_Revert), (wx.ACCEL_ALT, ord('U'), wb_ids.id_SP_Update), (wx.ACCEL_NORMAL, wx.WXK_DELETE, wb_ids.id_SP_Delete), - (wx.ACCEL_NORMAL, wx.WXK_RETURN, wb_ids.id_Return_Hotkey), + (wx.ACCEL_ALT, ord('O'), wb_ids.id_Shell_Open), + (wx.ACCEL_NORMAL, wx.WXK_TAB, wb_ids.id_NextControl), ] elif wx.Platform == '__WXMSW__': acc_init = [ @@ -93,9 +103,9 @@ (wx.ACCEL_CTRL, ord('R'), wb_ids.id_SP_Revert), (wx.ACCEL_CTRL, ord('U'), wb_ids.id_SP_Update), (wx.ACCEL_NORMAL, wx.WXK_DELETE, wb_ids.id_SP_Delete), - (wx.ACCEL_NORMAL, wx.WXK_RETURN, wb_ids.id_Return_Hotkey), + (wx.ACCEL_CTRL, ord('O'), wb_ids.id_Shell_Open), + (wx.ACCEL_NORMAL, wx.WXK_TAB, wb_ids.id_NextControl), ] - acc_init.append( (wx.ACCEL_CTRL, ord('O'), wb_ids.id_Shell_Open) ) else: # Unix acc_init = [ @@ -110,7 +120,8 @@ (wx.ACCEL_CTRL, ord('R'), wb_ids.id_SP_Revert), (wx.ACCEL_CTRL, ord('U'), wb_ids.id_SP_Update), (wx.ACCEL_NORMAL, wx.WXK_DELETE, wb_ids.id_SP_Delete), - (wx.ACCEL_NORMAL, wx.WXK_RETURN, wb_ids.id_Return_Hotkey), + (wx.ACCEL_CTRL, ord('O'), wb_ids.id_Shell_Open), + (wx.ACCEL_NORMAL, wx.WXK_TAB, wb_ids.id_NextControl), ] acc_tab = wx.AcceleratorTable( acc_init ) @@ -137,12 +148,28 @@ wx.EVT_SET_FOCUS( self.tree_ctrl, self.OnSetFocus ) wx.EVT_KILL_FOCUS( self.tree_ctrl, self.OnKillFocus ) + wx.EVT_MENU( self, wb_ids.id_NextControl, self.app.eventWrapper( self.OnNextControl ) ) + # start up by skipping ui updates until we have the tree control initialised self.__skip_update_ui = True def __repr__( self ): return '' % self.getSelectionProjectInfo() + def OnPaint( self, event ): + dc = wx.PaintDC( self ) + dc.Clear() + w, h = self.GetSize() + if self.FindFocus() == self.tree_ctrl: + print 'tree focus' + dc.SetPen( wx.Pen( "red", 1 ) ) + dc.DrawRectangle( 0, 0, w, h ) + else: + dc.SetPen( wx.Pen( "green", 1 ) ) + dc.DrawRectangle( 0, 0, w, h ) + print 'tree unfocus' + event.Skip() + def initFrame( self ): bookmark_pi = None @@ -180,13 +207,22 @@ self.gotoBookmark( self.last_position_bookmark_name ) def OnSetFocus( self, event ): - if wb_config.debug_selection: print 'Z: WbTreePanel OnSetFocus' + if wb_config.debug_selection: print 'ZT: WbTreePanel OnSetFocus' self.frame.setEventHandler( self ) + if wb_config.focus_ring: + self.Refresh() + + event.Skip() + def OnKillFocus( self, event ): - if wb_config.debug_selection: print 'Z: WbTreePanel OnKillFocus' + if wb_config.debug_selection: print 'ZT: WbTreePanel OnKillFocus' #self.frame.clearEventHandler() - pass + + if wb_config.focus_ring: + self.Refresh() + + event.Skip() def OnDragBegin( self, event ): #print 'WbTreePanel.OnDragBegin' @@ -196,6 +232,9 @@ #print 'WbTreePanel.OnDragEnd' pass + def OnNextControl( self, event ): + self.frame.list_panel.setFocusFilter() + def getSelectionProjectHandler( self ): item = self.tree_ctrl.GetSelection() if not item: @@ -307,7 +346,7 @@ child_item = self.tree_ctrl.AppendItem( this_item, project_info.project_name ) child_handler = provider.getProjectTreeItem( self.app, project_info ) self.tree_ctrl.SetPyData( child_item, child_handler ) - self.tree_ctrl.SetItemTextColour( child_item, wx.BLUE ) + self.tree_ctrl.SetItemTextColour( child_item, wb_config.colour_status_qqq ) self.tree_ctrl.SortChildren( this_item ) @@ -384,6 +423,10 @@ self.list_panel.OnActivateApp( is_active ) def refreshTree( self ): + # need to restore the tree event handler if its currently active + set_tree_handler = self.frame.isEventHandler( self ) + self.app.log.debug( 'refreshTree set_tree_handler %r' % set_tree_handler ) + item = self.tree_ctrl.GetSelection() if not item: return @@ -395,8 +438,13 @@ tree_pi.updateStatus() self.updateTreeItem( item ) + self.list_panel.drawList() + if set_tree_handler: + # restore handler + self.frame.setEventHandler( self ) + def expandSelectedTreeNode( self ): self.refreshTree() item = self.tree_ctrl.GetSelection() @@ -428,7 +476,7 @@ # Handler for when a new selection is made in the tree control child def OnTreeSelChanged( self, event ): - if wb_config.debug_selection: print 'Z: WbTreePanel OnTreeSelChanged __skip_update_ui', self.__skip_update_ui + if wb_config.debug_selection: print 'ZT: WbTreePanel OnTreeSelChanged __skip_update_ui', self.__skip_update_ui if self.__skip_update_ui: return @@ -436,7 +484,7 @@ self.changedSelection( tree_item ) def changedSelection( self, tree_item ): - if wb_config.debug_selection: print 'Z: WbTreePanel changedSelection' + if wb_config.debug_selection: print 'ZT: WbTreePanel changedSelection' self.frame.clearUpdateUiState() self.frame.setEventHandler( self ) @@ -531,6 +579,9 @@ # move the selection to the clicked on node point = event.GetPosition(); item, flags = self.tree_ctrl.HitTest( point ) + if not item: + return + self.tree_ctrl.SelectItem( item ) self.changedSelection( self.tree_ctrl.GetPyData( item ) ) @@ -899,6 +950,22 @@ self.app.log.debug("GetItemByName failed to find item") return None + def OnCompareItems( self, a_item, b_item ): + # sort case blind + + a_handler = self.GetPyData( a_item ) + a_pi = a_handler.getProjectInfo() + + b_handler = self.GetPyData( b_item ) + b_pi = b_handler.getProjectInfo() + + if a_handler.isProjectParent(): + # compare nodes in the root - projects + cmp( self.GetItemText( a_item ).lower(), self.GetItemText( b_item ).lower() ) + else: + # compare children of projects + return cmp( a_pi.wc_path.lower(), b_pi.wc_path.lower() ) + #-------------------------------------------------------------------------------- # # diff -urN --exclude=debian svn-workbench-1.5.0/Source/wb_version.py svn-workbench-1.5.1/Source/wb_version.py --- svn-workbench-1.5.0/Source/wb_version.py 2006-10-21 18:36:48.000000000 +0200 +++ svn-workbench-1.5.1/Source/wb_version.py 2007-02-03 22:48:41.000000000 +0100 @@ -13,5 +13,5 @@ ''' major = 1 minor = 5 -patch = 0 -build = 746 +patch = 1 +build = 827 diff -urN --exclude=debian svn-workbench-1.5.0/Source/win32.mak svn-workbench-1.5.1/Source/win32.mak --- svn-workbench-1.5.0/Source/win32.mak 2006-02-26 13:39:14.000000000 +0100 +++ svn-workbench-1.5.1/Source/win32.mak 2007-02-03 21:11:55.000000000 +0100 @@ -40,10 +40,10 @@ wb_version.py wb.rc: wb.rc.template ..\Builder\version.info - c:\python24\python ..\Builder\brand_version.py ..\Builder\version.info wb.rc.template + $(PYTHON) ..\Builder\brand_version.py ..\Builder\version.info wb.rc.template wb_version.py: wb_version.py.template ..\Builder\version.info - c:\python24\python ..\Builder\brand_version.py ..\Builder\version.info wb_version.py.template + $(PYTHON) ..\Builder\brand_version.py ..\Builder\version.info wb_version.py.template IMAGES = \ toolbar_images/add.png \ @@ -70,7 +70,7 @@ wb_images.py: make_wb_images.py $(IMAGES) - c:\python24\python make_wb_images.py wb_images.py $(IMAGES) + $(PYTHON) make_wb_images.py wb_images.py $(IMAGES) PYCHECKER_OPTIONS=--no-shadowbuiltin