WorkBench-1.8.2/000755 000765 000024 00000000000 13221441402 013657 5ustar00barrystaff000000 000000 WorkBench-1.8.2/INSTALL.html000644 000765 000024 00000002754 12204114361 015664 0ustar00barrystaff000000 000000 Installing pysvn Workbench

Installing pysvn Workbench

Prerequisites

Preparing to run WorkBench

Note: These steps are only necessary if you checked out the sources from the pysvn subversion repository. The Source kits have already had these steps run.

In the Source folder:

python make_wb_version.py
python make_wb_images.py

Running WorkBench

In the Source folder:

python wb_main.py

Troubleshooting

Check the output from wb_main.py. You can prevent wb_main.py redirecting stdout by running as:

python wb_main.py --noredirect

Windows does not have a usable stdout instead look in "%TEMP%\workbench.tmp" for Python messages and errors.

WorkBench-1.8.2/configure000755 000765 000024 00000001211 12566356017 015603 0ustar00barrystaff000000 000000 #!/bin/bash echo "Info: configure called with: $*" case $( uname -s ) in Darwin) echo "Use Mac application" exit 1 ;; Linux) CPE=$(cat /etc/system-release-cpe) case "$CPE" in cpe:/o:fedoraproject:fedora:*) echo "Info: Configuring for Fedora Linux system" rm -f Makefile ln -s Source/linux-rpmbuild.mak Makefile ;; *) echo "Error: no support for $CPE system. Please add support and contribute a patch to pysvn." exit 1 ;; esac ;; *) echo "Error: Unknown system $(uname -s). Please add support and contribute a patch to pysvn." exit 1 ;; esac WorkBench-1.8.2/Source/000755 000765 000024 00000000000 13221441402 015117 5ustar00barrystaff000000 000000 WorkBench-1.8.2/Tests/000755 000765 000024 00000000000 13221441402 014761 5ustar00barrystaff000000 000000 WorkBench-1.8.2/Kit/000755 000765 000024 00000000000 13221441402 014406 5ustar00barrystaff000000 000000 WorkBench-1.8.2/Docs/000755 000765 000024 00000000000 13221441402 014547 5ustar00barrystaff000000 000000 WorkBench-1.8.2/LICENSE.txt000644 000765 000024 00000004315 11724430522 015514 0ustar00barrystaff000000 000000 ================================================================= Copyright (C) 2003-2012 Barry A. Scott. All rights reserved. ================================================================= The Apache Software License, Version 1.1 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by Barry A. Scott http://www.barrys-emacs.org." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 4. The names "PySVN" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact barry@barrys-emacs.org. 5. Products derived from this software may not be called "PySVN", nor may "PySVN" appear in their name, without prior written permission of Barry Scott. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================================= WorkBench-1.8.2/Import/000755 000765 000024 00000000000 13221441402 015131 5ustar00barrystaff000000 000000 WorkBench-1.8.2/Builder/000755 000765 000024 00000000000 13221441402 015245 5ustar00barrystaff000000 000000 WorkBench-1.8.2/Builder/brand_version.py000755 000765 000024 00000002730 13070503173 020465 0ustar00barrystaff000000 000000 import sys import os import re def main( argv ): version_details = argv[1] input_filename = argv[2] output_filename = input_filename[:-len('.template')] # create dictionary of branding strings branding_info = {} for line in open( version_details ): line = line.strip() if len(line) == 0: continue if line.startswith( '#' ): continue key, value = [s.strip() for s in line.split('=',1)] branding_info[ key ] = value if branding_info['BUILD'] == 'devel': wc_svnversion = os.environ.get( 'WC_SVNVERSION', 'svnversion' ) exported_from = os.environ.get( 'PYSVN_EXPORTED_FROM', '..' ) build_revision = os.popen( '%s -c "%s" 2>&1' % (wc_svnversion, 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] else: build_revision = branding_info['BUILD'] print 'Info: revision %s' % build_revision revision, modifiers = re.compile( '(\d+)(.*)' ).search( build_revision ).groups() if modifiers: branding_info['BUILD'] = '0' else: branding_info['BUILD'] = revision # read all the input text text = open( input_filename, 'r' ).read() # and write of a branded version open( output_filename, 'w' ).write( text % branding_info ) return 0 if __name__ == '__main__': sys.exit( main( sys.argv ) ) WorkBench-1.8.2/Builder/version.info000644 000765 000024 00000000044 13072422411 017610 0ustar00barrystaff000000 000000 MAJOR=1 MINOR=8 PATCH=2 BUILD=devel WorkBench-1.8.2/Builder/win32.mak000644 000765 000024 00000000436 10016223602 016703 0ustar00barrystaff000000 000000 build: all test kit all: cd ..\Source && $(MAKE) -f win32.mak all clean: cd ..\Source && $(MAKE) -f win32.mak clean cd ..\kit\Win32 && $(MAKE) -f win32.mak clean kit: cd ..\kit\Win32 && $(MAKE) -f win32.mak all install: ..\kit\Win32\tmp\output\setup.exe test: WorkBench-1.8.2/Builder/linux.mak000644 000765 000024 00000001261 11471243125 017105 0ustar00barrystaff000000 000000 build: all kit all: workbench workbench: meinc_installer cd ../Source && $(MAKE) -f linux.mak build_bin # build the launcher # but get ride of the config.dat that is created as it # prevent correct creation of a usable config.dat meinc_installer: cd ../Import/MEINC_Installer/source/linux && $(PYTHON) Make.py && $(MAKE) && rm -f ../../config.dat clean: find .. -name '*.pyc' -exec rm {} ';' cd ../Source && $(MAKE) -f linux.mak clean cd ../Kit/Linux && rm -rf tmp cd ../Import/MEINC_Installer/source/linux && $(MAKE) clean rm -f ../Import/MEINC_Installer/support/run* rm -f ../Import/MEINC_Installer/config.dat kit: cd ../Kit/Linux && $(PYTHON) make_rpm.py install: test: WorkBench-1.8.2/Builder/builder_custom_init.cmd000644 000765 000024 00000003126 12701741704 022011 0ustar00barrystaff000000 000000 rem rem builder_custom_init.cmd rem if "%BUILDER_TOP_DIR%" == "" ( echo Error: BUILDER_TOP_DIR is not set exit /b 1 ) if not "%1" == "" set PYTHON_VER=%1 if "%PYTHON_VER%" == "" set PYTHON_VER=2.7 if not "%2" == "" set SVN_VER_MAJ_MIN=%2 if "%SVN_VER_MAJ_MIN%" == "" set /p SVN_VER_MAJ_MIN="Build against Subversion Version [maj.min]: " if "%SVN_VER_MAJ_MIN%" == "" goto :eof rem python2 does not work with cp65001 chcp 1252 rem Save CWD pushd . pushd ..\..\ReleaseEngineering\Windows call ..\..\ReleaseEngineering\Windows\build-config.cmd %PYTHON_VER% %SVN_VER_MAJ_MIN% win32 popd echo Info: In release engineering build mode TARGET %TARGET% set PYSVN_PYTHONPATH=%TARGET%\py-%PYTHON_VER%-pysvn\Source set PYTHONPATH=%TARGET%\py-%PYTHON_VER%-pysvn\Source;%BUILDER_TOP_DIR%\Source echo PYTHONPATH %PYTHONPATH% set MEINC_INSTALLER_DIR=%BUILDER_TOP_DIR%\Import\MEINC_Installer-%MEINC_INSTALLER_VER%-py%PYTHON_VER:.=%-win32 set INCLUDE=%MEINC_INSTALLER_DIR%;%INCLUDE% rem PATH rem must have %PYTHON% on the path as meinc installer uses the path to find python rem rem gettext etc from http://mlocati.github.io/gettext-iconv-windows/ rem installed into "c:\Program Files\gettext-iconv" rem rem the DLLs for the SVN we are building againstmust be on the path to allow rem the svn_version to be determined for /d %%X in (%PYTHON%) do PATH %%~dpX;c:\Program Files\gettext-iconv;%SVN_BIN%;%PATH% %PYTHON% -c "import sys;print 'Info: Python Version',sys.version" %PYTHON% -c "import pysvn;print 'Info: pysvn Version',pysvn.version,'svn version',pysvn.svn_version" popd WorkBench-1.8.2/Builder/builder_custom_init.sh000755 000765 000024 00000002320 12575044130 021654 0ustar00barrystaff000000 000000 #!/bin/echo Usage: . $0 # default to highest version we can find if no value in $1 and $2 if [ ! -z "$1" ] then PREF_VER=$1.$2 else if [ "$(uname)" = "Darwin" ] then # default to 2.3 on Mac OS X PREF_VER=2.7 else PREF_VER= fi fi for PY_VER in ${PREF_VER} 2.7 do # used in pick python to use in Builder driver makefile export PYTHON=$( which python${PY_VER} ) if [ -e "${PYTHON}" ] then PYSVN_PY_VER=${PY_VER%.*}${PY_VER#*.} break fi done unset PREF_VER if [ "$WC_SVNVERSION" = "" ] then export WC_SVNVERSION=svnversion fi export MEINC_INSTALLER_DIR=${BUILDER_TOP_DIR}/Import/MEINC_Installer for _DIR in ${BUILDER_TOP_DIR}/../Extension/Source ${TARGET}/py${PYSVN_PY_VER}_pysvn_python_org/Source do if [ -e "${_DIR}" ] then export PYTHONPATH=$(cd ${_DIR};pwd) export PYSVNLIB=$(cd ${_DIR};pwd) fi done unset _DIR echo "Info: PYTHON ${PYTHON}" ${PYTHON} -c "import sys;print 'Info: Python Version %r' % sys.version" ${PYTHON} -c "import pysvn;print 'Info: pysvn module',pysvn" ${PYTHON} -c "import pysvn;print 'Info: pysvn Version',pysvn.version" ${PYTHON} -c "import pysvn;print 'Info: svn version',pysvn.svn_version" WorkBench-1.8.2/Builder/copy-meinc-installer.cmd000644 000765 000024 00000000331 10543315465 022002 0ustar00barrystaff000000 000000 xcopy L:\wc\MEINC_Installer\Installer\* /s /e L:\wc\pysvn\trunk\pysvn\WorkBench\Import\MEINC_Installer /y /q xcopy L:\wc\MEINC_Installer\Common\* /s /e L:\wc\pysvn\trunk\pysvn\WorkBench\Import\MEINC_Installer /y /q WorkBench-1.8.2/Builder/make-devel-rpm-build.sh000755 000765 000024 00000000111 12672507512 021516 0ustar00barrystaff000000 000000 #!/bin/bash cd ${BUILDER_TOP_DIR}/Kit/Linux ./make-devel-src-rpm.sh "$@" WorkBench-1.8.2/Builder/macosx.mak000644 000765 000024 00000000234 12070136002 017226 0ustar00barrystaff000000 000000 build: cd ../Source && make -f macosx.mak all cd ../Kit/MacOSX && ./build.sh clean: cd ../Source && make -f macosx.mak clean rm -rf ../Kit/MacOSX/tmp WorkBench-1.8.2/Docs/WorkBench_files/000755 000765 000024 00000000000 13221441402 017613 5ustar00barrystaff000000 000000 WorkBench-1.8.2/Docs/WorkBench.html000644 000765 000024 00000077454 12615677152 017363 0ustar00barrystaff000000 000000 WorkBench for Subversion

WorkBench for Subversion

Contents

Getting started

Setup a Project

WorkBench requires you to create a Project for each of the subversion working copy that you wish to work on.

From the Project menu select Add... to start the Add Project Wizard.

Checkout the source files

After the Add project wizard has setup a new project for you checkout the sources.

Select the project on the tree control and then select the menu Actions Checkout.

Working with files

The Action menu will list all the commands that apply to the selected item in the tree control or the list control. You can also Right-Click (Ctrl-Click on the Mac) to get a pop-up menu.

WorkBench Window

The WorkBench windows is split into three main areas. The top area is like a file and folder explorer.

Use the left hand tree control to change between projects and folders and use the right hand list control to work with files. You can change the information shown using the View menu. The number of columns and order of columns in the list can be changed in the Preferences.

The bottom panel is used to show messages. In the example below the result of an Update.

Use the toolbar to access frequently used commands.

The status bar will show the progress of long running operations like checkin and update commands.

The filename filter is used to focus in on a few files. Choose Name to filter on filenames and choose Author to filter on author's name. Type a part of the name into the text box to show only files that contain what you type in the name.

Click the X button to clear the filter string and show all files.

How to...

Adding TAGs to the History command

WorkBench can optionally add the names of TAGs to the history command output.

Note: WorkBench uses the revision of the TAG to work out where in the history list the TAG should be shown. This works very well if TAGs are created from the HEAD of trunk.

Setup a project to list TAGs

  1. Select the project name
  2. Right-Click (Ctrl-Click on the Mac) on the project and choose the Update Project menu item.
  3. If the subversion repositories has TAGs check the Enable next to Subversion Tags URL. WorkBench will fill in the typical TAGs URL. Edit the URL if the repository uses different conventions.
  4. Click OK

Workbench Menus

All of the menu items in WorkBench are described below. Click on the menu item to scroll to its description.

Menu: File

Menu: File » Preferences...

Menu: File » Exit

Quit WorkBench

Menu: Edit

Using the Copy, Cut and Paste commands you can copy or move files and folders. Normally subversion will not allow you to copy of move Added or Modified files, but WorkBench does know how to correctly copy and move Added and Modified files.

Menu: Edit » Copy

Copy the selected folder or files in prepation for using paste to make a copy.

Menu: Edit » Cut

Cut the selected folder or files in prepation for using paste to move the files.

Menu: Edit » Paste

Select a destination folder and use paste to copy files that where selected with the Copy command. If you paste a file into a folder that already has a file with the same name you will be prompted for a new name to use.

Select a destination folder and use paste to move files that where selected with the Cut command. If you paste a file into a folder that already has a file with the same name you will be prompted for a new name to use.

Menu: Edit » Clear log

Clear the log messages windows.

Menu: View

Menu: View » Show Controlled files

Show Controlled files, files that are under the control of subversion, in the list panel.

Menu: View » Show Uncontrolled files

Show Uncontrolled files, files that are not under the control of subversion, in the list panel.

Menu: View » Show Ignored files

Show Ignored files, files that subversion noramally ignores, in the list panel. Ignored files are configured in the subversion config file.

Menu: View » Show Recursive files

Show files in sub-folders as well as the currently selected folder.

Warning: It can take a long time for WorkBench to get all the information about a large number of files from Subversion.

Menu: View » Refresh

Refresh the tree and list panels with the current state of files.

Menu: View » Automatic Refresh

Normally WorkBench will automatically refresh the tree and list panels. Turn off automatic refresh if you find that refreshing is very slow. This can happen if you have a large number of shown in the list panel.

Menu: Actions

Menu:Actions » Command Shell

Start a command shell which has the selected folder as its current working directory.

The Preferences allow you to customise how the Command Shell command works.

Menu: Actions » File Browser

Start a file browser at the selected folder.

The Preferences allow you to customise how the File Browser command works.

Menu: Actions » Edit

Edit the selected file using the editor configured in the Preferences.

Menu: Actions » Open

Open the selected file using the stanard operating system method (Windows and Mac OS X only).

Menu: Actions » Diff WC vs. BASE...

Show the differences between the selected files and the checked out BASE version. In other words show the work you have done.

The differences windows shows changed lines and changes within a line.

By default the diff window has collapsed the folds to hide unmodified text.

Use the + and - buttons to toggle the collapsing.

The a.b button toggles the showing of white-space. Useful when looking for changes in TAB and SPACE characters.

The up and down arrow buttons move you backwards and forwards through each change in the diff window.

Menu: Actions » Diff WC vs. HEAD...

Show the differences between the selected files and the current version in the repository. In other words show the work other people have checked in against your version.

Menu: Actions » Diff WC vs. branch origin BASE...

Show the differences between the selected files and their versions at the point this branch was made. In other words show the work that has been done in this branch.
An error message is displayed if the active project is not associated with a branch.

Menu: Actions » Diff WC vs. branch origin HEAD...

Show the differences between the selected files and their latest versions in the main line of development this branch was copied from.
An error message is displayed if the active project is not associated with a branch.

Menu: Actions » Conflict

The Conflict menu items help you work with a file has conflicts between your edits and the version updated from the repository.

Menu: Actions » Conflict » Diff Conflict Old vs. Mine...

Diff between the Old version of the file and your current working copy version.

Menu: Actions » Conflict » Diff Conflict Mine vs. New...

Diff between your current working copy version and the New version of the file.

Menu: Actions » Conflict » Diff Conflict Old vs. New...

Diff between the Old version of the file and the New version of the file.

Menu: Actions » Conflict » Resolved Conflict

Tell Subversion that you have resolved the conflict.

Menu: Actions » Annotate...

Show an annotated listing of the selected file showing the origin of each line in the file. For each line the author, date and revision is listed.

Use this command to discover when a line of interest was introduced into a file.

Menu: Actions » Log history...

List the log history of the selected files or folder. You can choose to show all the log messages, the last few messages or the messages since a particular date.

The Log History window shows an abbreviated listing of all the log messages. Select a log message to display the full log message and the changed paths.

By selecting a single revision you can click the Diff button to view the differences between your copy of the file and the choosen revision.

By selecting two revisions you can click the Diff button to view the differences between the pair of selected revisitions.

Menu: Actions » Information...

Show information about the selected files or folder. The information about the entry, any lock held and working copy details.

Menu: Actions » Properties...

Show the Subversion properties of the selected files or folder.

In the dialog box you can change the properties. The known Subversion properties are all listed and any user defined. Change the check boxes to add or remove a property. Fill in the bottom pair of text boxes to define your own property and value.

In the example above a user defined proptery named "foo" has been added with the value "bar".

Menu: Actions » Update

Update the selected files or folder from the Subversion repository. The update will proceed in the background allowing you to do something else in Work Bench.

The status bar will show the progress of the update.

Menu: Actions » Checkout

Checkout files and folders from the repository to populate the working copy.

Menu: Actions » Checkin...

Search the selected files or folder for changes to checkin and display the results in a Checkin Window.

This is the same Checkin window is described that the Report Changes command creates.

Menu: Actions » Lock...

Take out a lock against the selected files. The dialog box allows a lock comment to be entered. If the lock is already held by another user and you wish to override the other users lock check the Force option.

Menu: Actions » Unlock...

Revoke a lock against the selected files. The dialog box allows you to confirm that you wish to unlock the file. You can unlock a file that is locked by another user by checking the Force option.

Menu: Actions » New File...

Pop up a dialog that allows you to name a new file and choose a template to initialised it from.

The new file is scheduler for addition to the repository.

Menu: Actions » Make directory...

Pop up a dialog that allows you to name a new directory.

The new directory is scheduler for addition to the repository.

Menu: Actions » Add

Schedule the selected files or folder to be added to the repository.

When adding a folder a dialog pops up that allows you to set the force option. Use force to override some of the checks Subversion makes and allow the addition to proceed.

Menu: Actions » Rename

The Rename command can work on controlled and uncontrolled files.

Pop up a dialog that allows you to change the name of the selected files and folders.

Work Bench can rename controlled files that have been added or modified.

Menu: Actions » Delete...

The Delete command can work on controlled and uncontrolled files.

Uncontrolled files are deleted. Controlled files are scheduled for deletion for the repository.

A dialog pops up to confirm the deletion.

Menu: Actions » Revert...

The Revert command allows you to undo additions and modification to files.

Added files are left as uncontrolled files. Edits to modified files are lost after a Revert.

A dialog pops up to confirm the revertion.

Menu: Actions » Clean up

If Work Bench or a Subversion command did not complete it is possible to leave the working copy in a locked state (state L).

Use the Clean Up command to clean up the working copy, removing locks and resuming unfinished operations.

Menu: Actions » Create tag...

Create a tag of the current folder in the project's tags folder.

The tags folder can be configured via the Projects menu Update project... dialog.

Menu: Actions » Create branch...

Create a branch of the current folder in the project's branches folder.

The branches folder can be configured via the Projects menu Update project... dialog.

Menu: Reports

Menu: Reports » Working copy Locks...

Search the selected Working Copy folder and all its sub-folders for locks and display any found in a new window.

The Working Copy Lock Report window allows you to Lock and Unlock the files as well as a set of other useful commands. Use the toolbar buttons or Context-Menu to access commands.

Menu: Reports » Repository Locks...

Search the selected folder and all its sub-folders in the repository for locks and display any found in a new window.

The Repository Lock Report window allows you to Lock and Unlock the files as well as a set of other useful commands. Use the toolbar buttons or Context-Menu to access commands.

Menu: Reports » Changes...

Search the selected Working Copy folder and all its sub-folders for changes and display any found in a new window.

The Check in window allows you to Check in the changes found to the repository.

You can also use commands from this window like Diff, Properties, Annotate and Log History to confirm that the changes are as you expect and help you write a suitable log message.

Work Bench remembers the last log message you entered and can insert it for you when you click the Insert Last Message button.

Use the exclude button to exclude a file from being checked in. Use the include button to reverse an exclusion.

Menu: Reports » Updates...

Search the selected folder and all its sub-folders in the repository for changes and display any found in a new window.

By using the exclude and include buttons you can update a subset of the available changes.

Menu: Reports » Branch changes...

Creates a list of all changes in and below the current folder that were made in the current branch.
An error message is displayed if the active project is not associated with a branch or if no changes were made to that branch in or below the current folder.

Menu: Bookmarks

Menu: Bookmarks » Add

Add the currently selected folder to the book marks.

Use Bookmarks » Manage to edit the properties of the book marks.

Menu: Bookmarks » Manage...

Pop up a dialog that allows you to edit the properties of any bookmark.

The dialog lists the book marks in the same order that they will be displayed in the Bookmarks menu.

Select a book mark and click the Properties button to edit the properties.

You can change the name of the bookmark and put it into a folder (sub-menu).

Menu: Bookmarks » bookmark 1

Your book marks appear here at the bottom of the Bookmarks menu.

Menu: Project

Menu: Project » Add...

The Add Project wizard guides you to add an existing working copy to Work Bench or create a new working copy.

Menu: Project » Update...

Menu: Project » Delete...

Pop up a dialog to confirm the deletion of the selected project.

This does not delete the working copy files, it only removes the project from Work Bench.

Menu: Help

Menu: Help » About...

Pop up a dialog that shows the version of WorkBench, Subversion, wxPython and Python.

WorkBench-1.8.2/Docs/developer-guide.html000644 000765 000024 00000015656 10404567254 020550 0ustar00barrystaff000000 000000 WorkBench Developer's Guide

WorkBench Developer's Guide

This is the source of information for developers working on WorkBench.

The contents will be enhanced and extended based in feed back sent to dev@pysvn.tigris.org.

Architecture

Threading

Workbench uses two threads.

The foreground thread operates the GUI and only fast operations should be performed in event handlers otherwise the responsiveness of the UI will suffer.

Slow operations are performed on a background thread. The background thread processes work that is added to a work queue.

No GUI operations are allowed to be performed on the background thread. This is because on some platforms the GUI will crash.

An event handler typcially needs to do some work on the foreground thread and some on the background thread.

Arranging to move between threads can involve a lot of code in the event handlers. However Workbench has greatly simplified the coding of event handlers by using using python generators.

The threading and event helper code is in wb_app.py and wb_background_thread.py. To understand the code you will need to be familier with the __call__() special method, how to call a function with arbitary args and keywords and python generators and the yield keyword.

Any event handler that needs access to the background thread is wrapped via the eventWrapper() function. An example from wb_frame.py:

wx.EVT_MENU( self, wb_ids.id_SP_History, self.app.eventWrapper( self.OnSpHistory ) )

The eventWrapper function wraps the self.OnSpHistory function inside an EventScheduling object.

Inside the event handler move to the background by:

yield self.app.backgroundProcess

And move to the foreground by:

yield self.app.foregroundProcess

The project_info object has two pysvn client objects, client_fg for use on the foreground thread and client_bg for use on the background thread. You must use the client object that matches the thread the code is running on otherwise pysvn will raise an exception that the it is already in use.

In this example from wb_subversion_list_handler_common.py you can see that the function yields to the background before calling annotate using the client_bg object. Then after annotate has completed the function yields to the foreground to do GUI operations, creating the AnnotateFrame.

Notice the use of the ok variable that carries status until the function is back in the foreground where it can react by calling appropiate GUI operations.

def Cmd_File_Annotate( self, all_rows ):
    for filename in [self.getFilename( row ) for row in all_rows]:
        self.app.setProgress( 'Annotating %(count)d', 0 )

        self.app.setAction( 'Annotate %s...' % filename )

        yield self.app.backgroundProcess

        ok = False
        try:
            annotation = self.project_info.client_bg.annotate( filename )
            ok = True
        except pysvn.ClientError, e:
            self.app.log_client_error( e )

        yield self.app.foregroundProcess

        if not ok:
            break

        h_frame = wb_subversion_annotate.AnnotateFrame( self.app, self.project_info, filename, annotation )
        h_frame.Show( True )

    self.app.clearProgress()
    self.app.setAction( 'Ready' )

Development

Code style

If you wish to contribute code to WorkBench please keep the style inline with the existing code.

Element Style Example
module name Lower case with underscore (_) to seperate words. wb_app.py
class Camel case with initial uppercase letter. class WbApp
def Camel case with initial lowercase letter. def setAction():
variables Lower case with underscore (_) to seperate words. current_file = 'a.txt'
plural variable names Use the all_ prefix for plural variables, do not append s, its to easy to confuse singluar with plural. all_files = []
Compare to None Always use is or is not to test a for None. Use of == can trigger unexpected calls to __eq__ and __cmp__ class methods. if status.entry is None:

Running WorkBench from SVN trunk

In the Sources folder run one of the make files to create the generated source files (wb_version.py and wb_images.py).

Unless you are trying to create binary images of WorkBench use:

make -f wb_common.mak wb_version.py wb_images.py

Now you can run WorkBench using one of the helper scripts.

PlatformCommand
Windowsrun_wb.cmd
Unixwb.sh
Mac OS Xrun_wb.sh

Debugging WorkBench

WorkBench has a number of features to aid debugging:

WorkBench-1.8.2/Docs/WorkBench_files/wb_project_add_select_wc.png000644 000765 000024 00000023626 10425464456 025351 0ustar00barrystaff000000 000000 PNG  IHDRgyU0sRGBgAMA a cHRMz&u0`:pQ< pHYs+&IDATx^ݿ$G7yה#x3:S 2vc}X599b!p283`d,Ac81ڐQ~#;G73g$CUVddēYj_[ @> ˳;OY~=Ӵ}ŋ7ub#@Y a2|PEfzyy @@JƔUpv~Y]XEzj#@S `JÔ)@Ej>EEX<3) S&d,3ee@i>^.L:=ϟb;Oٟ_R&d,syXRby"}n껳yb#@sxoR^^~//߽|nKeRɯJGIǔ)+oS7= @ [}tϊ/B[Y˧R#d-5Z|wNm @`fEj~d՟jo_\|WlҫL*HͫpL)ySiM@J?=OW_>v񗯊?yPʤE?YN %|r?=)j)k[STD"5o@ZW~A{g'Ys2Վ8E(R)5_e6,v^mUd d:H2;M_RҮʩ,AzrYL#Pgk}5+@.P>.3v'8p72oH2RJڳ'?*5\/~6jG5v^'YWvU@{t) zpV˗{TMNMgj[[,zoyZ~`!7 MZ }ef쯝 0?|Ej>,Sj*JV wSEvu,WiwAk`;cw\wVцVwCqw:G|Ζʐ*o)~ZyŇV^f=λySmj_y4*e)uRigmڮY{ @`)HͫzdV?U{7Q؊%e'7g?Q{, &SqfUmWɯ~liuKz_7^t_6X+yK~#-/ש/yو=28_YՖ65!7k"sյ7eeiX.Z/5OѪUmEjUsKS_#@^Uj,0mUf'mwjOyHqmUj=?ooViT^ >w)gl7= -P|ۇOχU@XmTۇ{7x{o@oU&;;DzJ2Xv%p\{` @Լ C2Ӄvok&SV^B{vy~UlWe]/>*S\Vjy*P;ݝU+lހ)t^X+p=(C@FJ"Ū:5?}xrљ݊)\ۛLYy_VGUyHػ@Ga mV|L^򿗫o~Ӫ[y p _'E ,o0UM>M_>MjڝJʪ6UnNUS '׋:)s薎JVxi9$@,^-zz)Bm @@@s H)yC۴\[ @@J2+T0 @RšDfTʴHMIR3*eE$@Q2"@RSj @ԌJa @)5  @@T@jF̰ @Ԕ @ * 5RfX @@jJM Q)3, 5& HͨRDfT HMIR3*ex"VU6ȱD~u˷sKV,QH!iJRs@{4qSCv3g fgL;#5,,6 :jwȠ(L`RsjflbiUw0OW?kzGd2تԔVTWZ;8{D)H%ˈ줇/ 5'e_m08G8tįw33g\? #.=מ.>KL1{S&.<^[A6)e["WH g=o"l'5)pcyINm HM9^`TQ`m˂)ȿsN5;\ QX% 5gq]m{Ea.>N kH Tc(5s`~XKE 5:vvvlCl= HM9I a.S8>wasuo[&:_t&n>FVۣYh4-Mt~úmU@jNʌ^sj\\̜k)Cg_|mj3IF05#Z[y2\@jJͩk_'/Z"3fbc4 ]kN\fN7vC+ N2Cf&4ԜL"+q)2e'Mfڑ_g9N mqPRSj 0L ?u0:%e␯ؠ@ n\U#򯾄wRGs)5 6L9E@j1 S<͝Qj1  7ǥ_KQ@j1  @@T@jFqN Y)5  @@T@jF6;[QԔ @ * 5R8'fجԔ @ * 5RQ@jJM Qci3lV@jJM /~i#@G*pyy9h1:55| @ij: @5O Hw*LRDfTj&"@ARSj @ԌJ (LRDfTj&"@ARSj @ԌJ (L9 ,^/?bdBNѩ$5&hyx?}7O}ӇX[, RsY8v)2?퍖/]~ :Rsϩol8@*NRjUɌ"Lԣԯr5(8Ss6s7!@`6)ky>MIZ-֜彨S\95/<7 pRos@>x`* k@@S@j(5)BԀ+q R3޸m{־G t/@q}zYHFn65+F{og_c5fp;5Юqx@& 羚mU- LH7"pd}6evV ZMˤo) WO7sHݯ5HWfch x茟L~[՗}!WV^ϡyߎϾÃ-uΰY Ug33! po:S*dz\^lwږD Jjf> NYo=ڕ4>t|W[&'j VخM*a̞|xM;謶? `'f~ИtvsҞ![.Й[1(v>nX8ԬΧ~{;zBM*YLHJӋͤG}+TCkJ3%%2K} ZoWR?{&5}On|V52SF W35]tWW=ej_z2kk^{p3(CK;wH#"ok"UmUo#S Nj|nc,vElt>8Qj C.7Iͣ# 5@D;5.gIcS'UM 6+ 5[yEI;FF:Ssf9ZZs#o @;5^.fIMNt9mRXS3ݝ6@?=ŜWߟ~~Y ke  @`'_}:<~ru9GjJMX#p\>?}<|l*5A  @!ZVW\ŶRu|ԵAWZDfTjdDaԔ @ * 5R4 RDfTjdDaԔ @ * 5R4 RDfTjdDaԔ @ * 5R4 RDfTjdDaԔ @ * 5R4 RD=5 95_}u^%FT8|n8%;0-X#z)S/ڷh4C#Ԍ2۾Z}Y*03Km_L/Ae}7N1(?ϢJR=umF;kc`ZOr5\6*|oWof4[UooʟZȵw6\"k\ި6fA57Ơ_u0s\ y{7woB|u};ʝ~wkF6nO/xh7&eI_ ~e2.o-o FO&S?qLt8SsPyuFZ$Ffx ia&-' Qi!  lЍ0i mL81%ZkOgh:_pl73Y<}\e}hkDz6u{i\ަnD`оwY*W jV]Ƭe-Z-Ֆ*Ϊɑ7l}%5OgjNo=1ӽ>v>iOڙ=C윸4lh3u:Oǟ^*ߢKZڣ2k` Ȟ!. 5,5~hԔ @ * 5Ryn P HMIR3*eE$@Q2"@RSj @ԌJa @)5  @@T@jF̰ @Ԕ @ * 5RfX @@jJM Q)3, 5& HͨRDfT HMIR3*eE$@Q2"@RSj @ԌJa @)5  @@T@jF̰ @Ԕ @ * 5RfX @@jJM Q)3, 5& HͨRDfT HMIR3*eE$@Q2"@RSj @ԌJa @)5  @@T@jF̰ @Ԕ @ * 5RfX @@jJM Q)3, 5& HͨRDfT HMIR3*eE$@Q2"@RSj @ԌJa @)5  @@T@jF̰ @Ԕ @ * 5RfX @@jJM Q)3, 5& HͨRDfT HMIR3*eE$@Q2"@RSj @ԌJa @)5  @@T@jF̰ @Ԕ @ * 5RfX @@jJM Q)3, 5& HͨRDfT HMIR3*eE$@Q2"@RSj @ԌJa @)5  @@T@jF̰ @Ԕ @ * 5RfX @@jJM Q)3, 5& HͨRDfT HMIR3*eE$@Q2"@RSj @ԌJa @)5  @@T@jF̰ @Ԕ @ * 5RfX @@jJM Q)3, 5& HͨRDfT HMIR3*eE$@Q2"@RSj @ԌJa @)5  @@T@jF̰ @Ԕ @ * 5RfX @@jJM Q)3, 5& HͨRDfT HMIR3*eE$@Q2"@RSj @ԌJa @)5  @@T@jF̰ @Ԕ @ * 5RfX @@jJM Q)3, 5& HͨRDfT HMIR3*eE$@Q2"@RSj @ԌJa @)5  @@T@jF̰ @Ԕ @ * 5RfX @@jJM Q)3, 5& HͨRDfT HMIR3*eE$@Q2"@RSj @ԌJa @)5  @@T@jF̰ @Ԕ @ * 5RfX @@jJM /~i#@̯fSӇ @ ^k[)'@R3` RDfT HMIR3*eE$@Q2"@RSj @ԌJa @)5  @@T@jF̰ @Ԕ @ * 5RfX @@jJM6/xX>[t퇓 ;Eel̾tݗ/s"Z.O=͓yl|g/7Edλk#>w_jJM6,B%E'<}}~9ŻԣԯrY >~vӖzTvpmj/57<^ 0{*iy2,?(k\]wdv[9+5";HLey_)5YSK͙__b$V -朚Z3u\SbmjJMINKjZ뫵~g5w:+@`<ȧe\5AX%ӯrd 5&RsƖ+X*,)Fzj~sC-[^oX&7?[k;QyԈt 3X  l<5ԙ8Vr'58HnR"ؚ~}7FNVj K#p;r]VR 7{ǷKbXk*5vlI=`^~h+hXj{y״~LD/:W9RjV?k(j۹shۚ}C+iݰ%3ˈEYR3Rmwq3״g' TL_~Ԗ́W(M6j(_טFj{C7jgkCz4h9ef`yH55ZsQ4n<w1z+G=U LU {9]F,]iM]_kMINnS|vw+)Ǜ=zzɡmkJMk|JCz4h9V?|5m[kNLᝅQl05q:-#dڇt'C5$@`?Uj.՚HͯVl:z4(5g#Rs?Q,4VnSb [}Y>YuvƤ 8dȤlXJͳs:Ss6{k [Rj~ӓN\i{IЦmew쯯V^|篾?Qj~?q\^uÏ}/f]w0G?'s X.凟>K(ǖaxY_/%}}kvy,V2j_wBuk_U3z6#w_jJM QXL#  @`RSj @ԌJmur8 )5  @@T@jFb U)5  @@T@jF:yQ9Ԕ @ * 5RG1 Hت@35Ie#@)%ӯVL \i r`Y"oIENDB`WorkBench-1.8.2/Docs/WorkBench_files/wb_info_dialog.png000644 000765 000024 00000047146 10417734221 023307 0ustar00barrystaff000000 000000 PNG  IHDR,Ur89sRGBgAMA a cHRMz&u0`:pQ<MIDATx^흿-Ivk5e`5d Y!! 1rfa!C2z7ƃgPm4T FeQFex*9'NFIq+o82fe O^CF$/JK?^I/ɯ  0"$욢b7 LF &x$@_ߦJ￿ۻ' @`I+I.I1I4⡓>'@'|y{ @#HrIIJ9]AiT߽q @`>O?wKbhN*KXNzHW% @ ,ï~ﺶ&/SSLIIy?]l @r/ǧ'M[N7)o.~m @@O?7w?{s/Oۿ>~zJiRS$T\RB  E |ӏ?<4#'u2O>JS7zwigi+/Z (!Y{BrB: aB$pI~\5Ya+:Sʔ$,߿Pv;+}k]!9וZJ+$#w o-\Mdd0bUOGaTMsɴ鋔 $?>y(Ia{|i4p/tzWr{^w|ji7B0J61=$c#2;nut%rƖd-iz}$N$ׯ=I-yg 󩴻igIB/ 2ԧ6A˞\L l/\cuߵ;`D2ZVT iT-mOG BG#DrsЫR)W[piyl/$2LO|4ft"hU]\J}<\GD틁]<]o2H{sd/ <+={?(YB:M~t~9IBy?V'??L\iC( )%Br2[2VEUhejUAAg|bm` pI$lirܣ\)oAw՗rS^-eߢL֒_˷T2O5uh/VT+*/q!4Ұ >1)r`;sP_QBe~({,뤕^ܩM>봽)WPyKT͖pȷeS6&SEJq Am9]( V+hxl za>#(x@`uh~? JF9y%mr텇, }}sͭC|k[J) Ổ_R֒nUmūfB9Wg;[WarصB!pZR]^⛬)i(:}믟$tM|~-CM͜ u+K!vUg'24-`Șm5j6S-(TBWZmzB%% [MzaUriKyNۻUNU}:U~-?%;<%΅<|(kUfISROu= Qe[[ 6<τd$y4JݢZ4HiysRa;!l'8lᏮҖDvS}NE%m;p?&=JlcPy1V@`Y1ߜҾRt,>,ߥE);wBrQ!,@]eP-Jyn%&G;F3 @`b졾[ʕzgBir@@Rϳ6}_q x!Oӏ~+6@ t%VҜ[6@ tz.ʫj @@"Abb9  $ @`1Hh1ABB $z΀ @, ]Zn.͔ Ċ:a ,!;1*{,$;sGT488V ][t҉+)ZUğ ×rm~;jƝC+GgB2k]$ڤIBϜ mЙ(!l]T;WBJw-9m %B@_#Ko*=ŝ E q@X$-- I -3?9C$ @`1Hh1;8 $!O >Ho!H?O y mC {%*gX WXBS%-])4  %TS *]v> bH Z@Bjn$tgN^e9.>O//rjT"VɥvU\xVڷ:0l{ %:-c @B HH͍j3: o߬_NPvf ٩ 춗$&PѤ{N /Pm\jrஒZAwFXQ–Muۮ&5>G B M.!IH?Vp'Y5̍Z$H 1JB`HhJ;ɰ+K3!5@עꂹOK힫vZ 9Hhn %ȥ,'W*Ev} ]sR͢P Cusņ(3ΰm F-*YEm-E67(0$PHϪZXc'ccrAbOG@%H@`AH -8:$~ ~@`AH -8:$U ޞvC mRBG~@`X۞m{v i p\H r@`qH - 8.$m {k܊vٻo&-Aȵ{؞-vvNfP: r4JtZPz-Uil-g;+ZbsKa jE>sOy:y$ø v -,7zGus}CqkӋZ()oך%J(= Hhs#9*'qe{h_, Wuɓhkq =7 Q^Z+d*lu~(r n#ۢ>A A}@@ ;[rgurP;UKs/#Cq#KvVO6;21jwPwkye]j2ʉu|(+%q ~̭ -(u~d $Չc,VnϷ8"9Hha o傇ݹ GlMB L-SRi`|.FbO0UHHcuj-UR4Ch[]+Zͅh4jLcJe:@)/ZXB;PAO+Iz0chSshe0j L xFҗk_iƟWّ̱*$Bk&9 Z&"6MG 1@(NdmQҵuv j0-3!E~VLL}"$^]#e#WH(rh(_)[-- QIZAfljd3hm0 IUZ^>,5ҕH5TJoTiOVj~1j@\O*gˇʎPO߷LI<r~33G,jq,jXt&B@BHE=PgX[ƈر d)SWf,H(P  !$4bw(@ !$ԯǐ;Q ~V%@.¯^IZK/+{18x MP2!B(5 Y*&'䝌  !$@`1Hh%;yϸp>FR>$%Te^B,N ؖ J^!pzXf3Zάw\Xܫ@F4l58@Yl[bv'ʡ] q CJ%ɝCeT+G=G͙j9J)R]<+Yv2%PQm-n^@nEmڣl`+N]3 EkskuFJȝibݡ_ yDϗk6ϫuU<mw\qmEBAiJ-vvcYdƢjUQR6ʅԡc Z!).}X[_P9<t)Ze@vķ j锐UGw9Nb>ʡ\"?ZaZJN4FTB{ &(D69W`,"ъ$j3W;vW NjXu.h[7$TLҩLzqdcG$FuX0mG=ܥ%Tk`33& wX&PczK*M(p@㔱=LS4 -+Nzl5pI,3QLl'|b y^JV_窔K\ޚWV2&˝EC]a t]B\ݶF@@`a u~QEc^\*a'GZPp?QƙٕW:# p&$|$Й !З6$4 c+  0${EtwQ2 H q@Xڪy2ݥn7ɐEyR<[zqv@`g&C\C`9n{wL3P !$4w>@BB#(a7XKvKX@y6eOmXmPPΚ lOB&c]7T!negnwg y:o쎛l2AMEXB}M[fmQNc3s$ɺo$V)~恌ضK`*i2%T{Pm]3wܴnѭZFX{q ".('Vo=:Ru@b؏^#ԣ_+G>T| ktjg?o5(?ڞV)[l̬6LM eT|7!(VJWx-c i ؉_3D 6OIPKilHc\u+RBv*W1 Dwpw F&P6;'ΚMݐZ7~'Y ՍRe6)r(ʉFh^v&@JK.*3!;j[嘾r0ڒբ3@;#/ ❎LSd[>k)ĹF(6)!i\k I%XJk$4Ku%dOM2 .c@`P s]LXvRWBw?|'#(!SHj nJ^nܯ:/L Y6$[BrZST$jжN !R+rrNqd{ ^,!\,rn @BQq8h PC7!  i !$4MϢT@ j&$ 0 $Y @BB ݄$!*=gA$ |w.!~M_}Q^6(W!SH@=VC$7YT@`n,m@=s 0$FJ@?$r@GBz]mx: @r$) ܱ,H>āMuP. 0$ZD-3'Ny(/!ua!cتmZ1Z O`y wk51*i8|~Ql䝲qf R{]wC,,Q 0*32^9Jma12emd FKH5S ̍!nNdU xcxЀ,)H`pSk` ɭLXB2~5Qӣ;%@K`a Ι fQerwcs&4XBR31HBz@`R+PFĕi-&F}KhI66 MztQ8 I` كvvE6E5F]GK.=(%f$bLJ-vr@`V%G(@W.X^IN/+5fۍݔ&C{%7v<ǧi&=$ !NH uv@BBS-ʅ I`&o2:{6 MPB} }p3axK.vK!j; e8D & Y9[<@}Tİ%gB]`6׾;:I`V 'wiD\D1?J0ZY3[yK-u΄Ji UBe$C]SwOJlQ1+) ے50 B`sfP qs]By2w:2~巤- :1C%0jk;}LJJ*vKB9 }~@BbZQ1 $8 ,F mUB{}<F@~A~R}_dR)lsr'o Q d[;b@`T,m@ zP $|T @ !!@8Y >'gt'%8MLR !|\m: j~DDrIԄKu[ x9z ]Bj&egyܟ_S׫b3rG`=ͪԊՒ[xP[zQ" ]Br|)xٮNQ96Fة:URKH OwzԨL,xmSvnend3`v}8%d PrXMoUN\Bx9N-d5wu 6з('krihJNٞ~]+@`6)2pWN+gB\Drr\<'}B5Vq%궔f_ȵƙyuXJ{Az.ɉL\A4rV׀9a&&N/j;s!vRE2Ac1Pb f]G'@%K-;I!pH=vRHG' !wnN!@BHh} @BHݜ&Bk%Z&qA *?pA" |w.zMrq}Q^6#@$ɉCp ! I dRn3!; @# eF9? @`׸:i) F q ,F -~ * ZH AbbW{bB`f# ,F -~ * ZH AbbW{bB`f# ,F -~ * ZH AbfWkc %R~~@s@BMB : ,4[iS3%Zgo *@3hzQRD@p$rAzkP$h  ]9Lh t&ڤ{&"0rn9CX+!u%B v:@YBJ 3H   t@@ !!@XZ S@$ @`1Hhn- #9$>@}pԾG!|@BK( qBG&Б?m&pD ]\\2JQ- Rӧw\.Ps&%fK>eO6P:Cz<ƨ *r&?G)5PYފKV ;TCFgm- 5]KXs-6MI3|@`擐ǕB䊙UNMB%UdEX ҶQ-ڌrUjڭfbN/JwZE`/OwB$T]+SýRk(!UogT!ԠF jH]- ;&ơl$.Xu6/;clTba r|ϯ;ERHٕPڣ =Mr HPjhP^% ExP[k;',JEL $0ٺZ"/LyHf?"y%R ݒvɍݟUs#{u;G2Ԧe8WT qMo9ÕFA`&PJ L2@`:HhWAB* LA JBStʄ 0$] @BB$#b! %t|@җ.Յӯ^IN/+v|4Zr2FgC] BB;4*$wCKHjċ|'xwd0蕸ȩ&%JSmĶTs~N[j{Ȉ)! + J-ku! Cϸ*|"AWҨaK9:c Q{l!`b}7\NT/蜳T4%dGC;r'<@XA);ЫdA`xjA'֝)2`iT#x Zg#Vz}XEgXZd%T3SAړU[qORq%w!8 s$4x`UƵ[T 2qdvP#P I5'ӝuBʬo/X2nnO;,~NHtaB5Ӿ.{ |VTj9Mf"%ԚZv3$T[Vn'#~vHhNR$`QW&R°%o: 0Arx?@)`ؒXKI8k\A̶d!U؀=Jk5P=Krq4jݰ\n紟0XI!ؕ貹{ M?QX@zoW*}ӷRBZ&)z !$H$ !$tNO!@BHh=H @BK'C82yKuoJyy[v@̄ nM!<@BHn{#H5}I @}.>m&m[JR=Hhao N-))I(M&uUBV2=( ,4sb&t2{ S_ eQ;oPˌ!MЃ J(%[D'lvQ&@P N8顝K]ʱ#5k)wQ,}H !;ڰ"Z!z]AP=&!t!C4:L,tkܫ vc&eV.^- _-wp;mdJ\>h77J)V`˞XJBn Bl;=aÇt>v hbD'ɰj:'~5WP!br?iJR]yBd:F;qH8,$m KZU YcY{Y=|qACj'spwᏆڛ^MV 娹N, [tI,GK01 CHȮY> J ܊zIȭ R%Nr $.G?`6K^ PFڶrSmaVlfwВ8HvjoeYԥatVڼЍm"}@BHh=i:GNWL !wOBN !qBB+BB{X1$V= ;$i bH { wH >@`Zq$4@`{} !$Ih !$>N @BHhݓ @BHh}A+&Њ'A{'8VL !wOBN !qBB+BB{X1$V= ;$i bH { wH >@`Zq$4@`{} !$Ih !$>N @BHhݓ @BHh}A+&Њ'A{'8VL !wOBN !qBB+BB{X1$V= ;$i bH { wH >@`Zq$4@`{} !$Ih !$>N @BHhݓ @BHh}A+&Њ'A{'8VL !wOBN !qBB+BB{X1$V= ;^M?oGw7)Y}Q^6JH @`Twi.o7i}R2ABO:'xw'=HmIMYNY?7\#!$ԷO K(zH+$8 CYBaui3/#!$4N@[s$t%BB8!$tl2J{ y&7Pj ,MIB7I<7y+=ABd -}P? 0,4 !i fB"q?@K(zCb9Nw"$C8e?j}V4BBtJ&RBϗÕ ZR? ;5 @`S2І#?Yn, Hh'J6BB !$NK~ I$tqqBZ^0J@`@ XGʎVp, $4DBLT MBrbΓI%*ʯ72YYŖ>Vv=ya=r͵~KN !z?j*$nOcS+xRB=E6J?E64X(2 QS뤒@NPEl!r̨l$#I-ɐ,y~KN $|)k΄:%l'G̀%P9e9 -zQ9 p"HH-mojȯKR9&%ok!*  !jY? :]Gd@:fϧ* !$H1 !$t̞O!U@BHh c@BH=VC v%=[!xx @`~$5A@h&rN N%gnOB@`-ZK_$@:`Z Y%< ^s-ZK"@]$gQOk&OS\SH%>.x*vW;mZdnuWթʯ{Hd[ NvT i)s6 i<-=*뉖nu2Y/1R@ؼYms T9a?um:˔g23"r&Pk%Lnbsw[$ GMoVTBd\rHۖ倮ggK$Vm9ΆLVKcQ͔A _)W$9Zj >Za$y )҃SCRԄQ3eli6+՘kGyWBk!ۦ͟jZ%KZʬ̜$uAP6/!;apZ::g968`\{~잾`JG"S͠ h,! ZiCW Q~Y U3A\}%dТq+%Agڒcqmg0T #ضwNٽKʸrBJ*R{B"5 3R]J=M.̙:%()-ShLtQ, 0=v%\ $TM]kPKkʼ]K\R9QoY9ˌ4% 0H6w.[e"3v 6 :%dbm]BB-hXC @`$e!TcJjidq^rT,n 8 e\VԦ%Y~Q,ϼF׹G*D%3![ h{`ZZL(!5Dv)әx lŸZSNK"  FHh;& ƞG2@$Vw&@`HhЂBB30TK !q{AA !݅%v%>a!#b\T_jz&i/(T gE\;1mK d2ą P 0vbt*f8`BB(J ЃBB= I!K Ѐ; ~Ŝqn2@i!!y|_vCO=N)!57]epƾ$[w&hQw|TǶWvv܏)emWI6)$*!mIȎj` w;Z=r%+^/qhHV|LJr0CP\s[wO)G娩U#ߕaؐLH+gèLKz䚴TL6VaQ\k#~~p$$4k7I2tL !P !5t+%eչWKS, qUA9fR^&J eE- XYB R9IvgޣΙT^Y]MB <9:G!jxZӒ2"BQP'ƀ]7&]j#@BMr ]L !ks$NZf0cbDof ;iC<+vc-"mjPJq'.2M|a-ɭ(Ǿ(&tH{J-%MP+iӴZT% ƭm23Yj  zF/8 BBkBB4X$'8$i* 6H O p HhWC8,yKuoJyF!@~&FG说b2~@s O dRn@ Q5t'Ap*!@`*@bb|nBl! ,F -~ j fH Abb|nBl! ,F -~ j fH Abb|nBlnY/nJRsN9BB%py?_yOoIM;: ߙd@ IdÞԢԮ3?A$ LK I(́.ߖu9BBvs>B JKp_OjQj-zx uH34XEB@L{AL̄AX=G }3 hDI K ߏ]<|'fB?S Nr}SvwG4P*ABw\< d } }?vr{Ki-EHhoeDIQB8I$UZޓFuqVO Kw' ޏ%Ө,+E1b&@`nqKQ=ɢڻݜ}L KW?̈́nw;ھTZjKj3!T@`%= Rmk$[$η3b!%ߞ$t>Ԗ"$  }y{ sE- H ]}q !!@ $Cv}ǣV_?S#B@`B_ߤ'azfK-/>?70ƙ91y! }{{z?NcKs 1 bݧzCds?dU2 U,m[|y O !!@XZ g@` h }˴  0$tTE? ƨXG󓯾~诧8@@@  R51I䭓oF /_@@@ I/;)usbf~Y\"/` aI/;p*i?#$D0HaĨA"/mexKzd2@y#|CD HaN2כp2e'O o>~r/8@@@g_>U#ǯԒ AD^ӟV}@@ D oc^/|~4՟/_=|S:BPVq$b٣jA[1}P+ԍ6i$h/Ywp:b/@ǧ?ϫ߇O+~ImބTnlI#X~/~uIw(«L(IKvS,<}|?>y~bopQQg@@@)|r{ZXneI#A6dO_|ٯN86~_oz_ eBɮbI AXxJuۏX|A@@%ueCej?>RIH~'p jAė:e'|U<&N/^8;tO unϫ&it1#owO[d%OLX!ꉕfL.po un$|_d'RSQI6hNn$3ƮrBоԁcK ZچNE 4FNH\;t i>o cWӳ M׆4GW FˍGծIt=M, s34 J34Ds^"vri*r;頟Sv_|5߉eVb.G?էw;_{w\u3_*21ǬVjFOvyX|U%Ic-Hj3L'10s]3K*!u"Q]xU"Q) {$sg-e(CŒưljfjsE6(w S)ϺDV^bI տ~؉*H2}?6Z.Z۰ݖ9 /M\Y+[v#Hc8~(#=ޥUtğ3d(CF :%3U =3]X[v~$ u#=O#U#~#n1ͣɫuew:$J,_QXF*O=#u1^i*.zPcxsx-y<3H}w:dۧ}P+ԍ5i|(]ptw,!w o2|*yqq]M{=( 6޽ y xfF}'ߜ=BN,I#=) _> :$99HXv[xc?u:OIHlj#Ψ :{=(nG;{A`&DĪ9iG64KH˾G,jx-a9S[!t%J9>jDFgQ/VK]$r `F HXNvt  {DX' @"Z,Û= 5@ b a! ` @@!  K|   ,WVggNqt5ޑ j7A@@NQ^>~vO?jH5z J>}v~0WgZڄqa=  mvaOyz@J%?(b}(Dzgyz==%H%~h6!bI  o%rw aW,omm XXc8  0P `oxD Xioja,I`qb4R}JdB,bѩ i->Mv3qCS q@*7 _eRh@P\; S|khB#M# m->WY@,Z#0X,_ ^K MA,/1"N`qbTb9h@@P\Xҁe]6b|:byс@bye]6zO:{ꐎ?q]]GpwYX}ǓW|z@b7Շ\^\ Ergجo? W?#)}0/K(A@,/o..Th{.ZB8-aI"{#y͂$r|q|uR4MK5{{,bX~CCˉ\C@FDS(:# $F"&#yR8̈bi^P.]rP.&cސ2šwjR!F[ݾN5{pö4 ar>g$mA XP b1)etfDc(*^ݫ+k5/ś \]:nx j)~ɉ67ƿ1B |\2bZm-_Z-b_%\h,_8%&ڣ(!GWW]au  p\X؅U|x #rmFl6o,W/֍.J4Jw.JaXY(  n0erPN֍5nax,w2U3N_Zli80c3V*4 Z܋Ȗmo%ta9q98v0xf'2$yySdOO<b[qԪq6VygN 6.%:.\60&up~f4JD E()CyPS zƆ< ![ͽ=.}Xڳ0IJhXΘ^yE]..jf7F5"ن*KKKvW.q/+:#9~b8s> 2]Y(mAXҬxn evwE͖mD'Ӻ^yo5:(pC)K1FW7sB,G\EU dQƆ +Q:R⚯z= gKuܲV,cW$Ӎ6XzqI2~ΝeO2\[HSB6*z\A_(IaQؐe@2/ #2r%WeRU2sLG ڻ=}PMUFktE C4k7Hy'S rɉ`Tێ0Lpb9s^Lb΁iK@,"   D 0YV+PX 42U89mFfu&f^K6MWH/u]쯃7k}m^ђ<+5y/2Yg򦀧S`t8r1*{d$c!29J&15df`g=TCl[xifT}.Fu͢$+-:eu9jZm:I9o&'1ܴqS'&2V_ڶ6Lu^|ؑt,g,QQ 3(:}sC` h~.UߧYfL0w߷\gYjK#eLIN(VS3!<^LE1#}Ctl[ {"R#XvKGО96)FRfȱΥQ!)ב*jڅUU2IJ28盭2d\#ܤݩU4bQ,s WSbݤӷ_,OEqXt#bz:rB(J\vȥqhNKKD>j/  0Q ǫ*.9j*eiv1#QRN y3pm1BTtƤ0!9lZ-M,eUFse41Ֆ} i'Yj6Tn6֍ChѦ!yzlezRe[GԹoZˌIAg=*4GdU keac rΜTD-2Exf `r:s_LS$Cˬ;ٗG_ Iw,r<)uaH`ݨqՏTqzO2AAQ퀤9Htc WwVX:AG,?95 0XF  {A` 4M`y ՞&7id2yĒ, nfe\ҷ,:R=t }c?`l\.ڜ^c7#\,+ԎTTU1Rbl!6M]j#0XRidSLkUt2K9{brv[fɼ1Ryy Ȩu֛K]_<^Q+XƝj6qnK63iFC:(5.ŧbޑKurPEJ{8'sO/yB#s %#u'ѰI8W`+Șquq]ŕvfyVf^qhg6A+(F\;RzŘ$KHTU?@$bd.t3$7grib˛ rKL=j9t7bq KJLuhu@nnVTY(7y`SCNZ[D[d.tN+5&t+7C]axU]Ѱm; ϛA rsZ@}Zll$a0 IK+ØGXY|Z4W;j%_LR' KuUɶ}S#::m͍]#ETo:ͭfZG,QpqA,T.A톨9Nj\ԩzߗ:='eE&qj4ϠK#eL<8X̕>3eR_ C/ oEBf:7hSjn($O LʕϙԫVX`;rP9HMJ5t.SIlFal<JM]ؓwlxsDxڦJn[hZT/{ 2%b&lSsG,p~ya_΂$`oiYUVX:X*   G F  6?ߟ>$^ZG}{ £<<`L@˯~zqˎUxC߃ e@#m2.Y邱8͐G3i/ MОT=Bz0/j3.>Ɔ1%:y=d}!QyucmedF ͇ټAgoӑUNVbiL9mD[Ѩ%;\, Ü"J$WE1xĒMjw̕Ffw!Ϊ{ _;KOJYzqlWL+\) ql:^e%B,A1Sw4*ב*Fʫ Xl 9VBS'KFũDfnRsN:cG'E)(7 d0`f3dYTcx2GS&/69gO"v!BmhNF.T&I SEϐebbmdTǩR,00VD&'~ڣZ=7UIʜ^fn\L=?7u0/FxA#Svm;S57ys%Ib~ {i/yvn CrJ.RQ(א'Z4ėʴ;S\%# $[&Xr#9a`Q?O#Z$JU>b*5;YkeѴ 5P 6#!A"ӎtirX\]GxvfT-!g];X䩅u8;mIܳ05G _T(.dr>m"<1' W6Nylj]A9.;7bidpu8:U5R809z&_.4ӂ>Lr/dI v^έrp\*:Ɇ0ƫS/IžyAZTw;c@5#ٳx$X5 .d(\BF2SIXM`U6R)BւA#1D258]FsU ݆~YTSu.3t amʜz< 6"WsAX bgrLAIb81/E%K&i?e @` ?ceK˖/ZA[ ~^//ΉTJv4&l ́#hL +/,¸H{Gqd9%0pr .nޠ\:͛X*t\zppʓo_EW嶧Ǜh~|Go1 nɹ{L?9}|upGߝoKC᷏Ϟ?zT;K=.8f/#_;cUGeۉBB>fGejt@@؉ۋۖ/'Fbn1.DGxFʃb}@f/^ŅNNByĒz4'|C,l68@`B>NX.|Db`S;Ai\ ,E b"@ bmxO^hK dׇyt_ Cq+=xߵ#fj;g[pZ& %piȱGx3˭xoh:Z=%Ϟfpph@@ @,!   P D@@r~ K۰x@@)rj׃vXOvY0Yb=  Y[Mz);KFlW/t  @K   SՌѧ}UOպpLOTŒ%Oe6A@@ _,K%/ri!{@@bIJM6,@@˨7zoĝb)39yА1C<} Kd0!Qq`θ91JUèzQ0)c(*3C0L̴MXh "p%igr/ǎ#)Y2)D*IP^Dn&Ĝ<´qCb_SΙCTC)%؋ba,w"3϶V5u13WjC Ж@˰kbI׏z9r d[[~1u>+%!8V챸4aRjRVYSxc#Î \X} XbK2Qz6 ;%:m?]zJ;˜Jl A$IIRq#D MB˝tN1WC 0N/E†;3Βez4hJeo^11 c]0jzJL`6)~6#)sU/mHK./38&Ly<^27GrQb>f 3/M[r5>wY=;TA#  c=K{86oO8ݥy^wF<]}8# Q/ qg1cE( pHKݬ^KOx$iz3@+Ԝ.A]gFX1VDZ{6LSh9 ,H6ܢk{\;\^^|N?/D,UiȞkx; Co$׫bߡ9wxQ@`Q%4ɓLDi?Ms,m05$-2^QInt8~ZWt%=cSIUcv^3Ēh&EB|5W&I,MESs1qFM*ИȾ*V*orXhҦ4GETRCi-i9uW,ݹ2:"%l"-xq3-r6AXCggJ,ΒiTݏzd9AJmVImnޑ^R@$2&G7X$b1ZfI?הGh]uGFDuY(Gglr)Nq9 fc gN)LN,i u3S@b) Ha,rNIYt6 XX7N    P@@X@,'@5e5:T8c4   PMbYA@X/< VOA@@h @,o60pb   K@,o~H@@@/,,az @@6ʃ,,az @@8w1"z @@@@)sgy֭oV 跳 “itlPALc䁅  @ K:4KD9@C^TeIѭg}3IO8lUs hM*bilIm;vvҤC+  А@{4>4XeqZi@n'4  0#bYk? 5>̽1keɜ15X7T*5\>GKuD{9   0bՅj&ln)Q<ʧ3@#%O,o6GA@G,7t @+e+L@bR@N;K@@@;a7}9@@f'%S   %v   0vXOvVQ@@f'%S   hgy:6?<~rZ8?}I(ޝ+e@?Kzs@X~ՋsOb0 e@I~vZ8"?N,?9AI&"GR̶` Gxu'K>J "e_Lby eI86 {Ab!Х/[)'l.3biâdZYP*H_XE Ѐ@I,odR( sg9l5v&\ޤ#(eHÃ2c;ȴL 'yvf։Mzv{`0B /9*4*wW1,̥hsd%ǓsI/,WHJW'Kc_bK,^3k@@O\rnY{lTM|˳gI)x3Ke.;EvbyhI1ri%/9eo+T,:&LzΌ%izcawqHiXz ʀS,}avm! Y2OGKW>MWcRX>XZx@`_" b2,,ov^N* ;i!߶N;%|fI?L夠qe  D_ȷREs D,oMUl:$.7Emyc\e;%NQ %,5iTggim< =OYb-!8DC(v?T #81&H*#ZsY2\.ܨG'R,{{\>gN.}r__pw.@7uOoƃ@k;??9 BJ\=?޽|r ?}T=߻_o 2)_nDKdz=Bo^?ʐ~|o_~\|w;(:*7w$n)7rIܢrw7͛3Ջu4< $ `F'  L%w>ݬ6l#LGU}JV - o8] oAks Dvw+.F, ' Q6,K LAj8Ӝ(O_+9PXx,T~գCE *q}3fEKU`9bYlяb`bN@\LOi+\n+jWnh#l` ndl呃Nw jcmSYb(U?HT0_#r@[:XX5{zNr )*r,ۣs11ScbR Lry.gpk{fw4Wd2A'0ΒegryRL/H3zUϙNi cjq:1G(4}QK@=(  0;r K@RS2l9bYliK A ygGLI*m h.'Sݤho|ƈ6VFa%Xh;VLv9nx6ڠ0Flf,BHWϨFRc5F{d%̶tȹU#o.1 Pn+$ǐb*g*m@bRWltR+ooO ) 4Z|ګ[9K<]ʴr lGB32R9(mlja/_0MXXi#*GujhjgU sG; pP,/5ĒmP<:'5&/)ʏ!U:=r۲j1zHjynDKwKI6r 8QT +\<< ðpʏAT3s;%ߛWrgxl u D!Q1'sSs{;|KQݍ!t **1s:#VCCEz$vu, AeեH3r0 9˽KRb$Y7udy1Z[;qw vգFEIJH bil*]էZ[fQn- B9hDb$8\s^pbd  E%EAI`ކ_/8>|r'\UíeݝO^&-<} FyX8(֫# ׯGߞa¿(<X9 B~-3cl_K}W,s'IB*_&k%s99P@ by{˓oP,_X:ۣb\5g[,Ӣx0x5X]o-$fo/.qq_#o)lH@\LO&JSVW{7rcDa+0M mG݊NB`+hZXTr֦=JϹF5c|D mD%6\Kb9 խ&)V(|4@$ǐb*g*2=:'L6:×BepI3T\3vzOU5n !;` G4d E; Аb)ӢGj.P<< DrRbIabm#*>.u!.^bi뇭%Ҵ$pdk-P,{-Շ0m%z'դ4jWB{[^-2[sTҖ^ ,c&{ zų0l2IQZXX3ߞvmŮXLѫYΐ gTא:_H՗#J`1Æ"%A:$ψ<+U'"=K9v@2G=ian59ܥ4m1憜˧ Fs aI5C*dmP7f:RT4>sơ0{S9^uE/ǐa Mf(˶#5# -7udy1Z[;qwrP~@,+0X[J'VVd_Y΀\/Ն'BX.!0@@`y ,Xs@@G`R~1S#,wr3>cS_nm/Ӥ^2 WS]1[N+hrKU7#*~ >@(utbXo7Q,=.WN1gxaAW3s[U!Vd(xbɶ vHNRQ,+h{56=;9Œ--L:4V_퍯F (” U8Y]ut2|!G$wZ+#7"fQ'gQȠ뗾s А=tiѡH6@o괵ɺPRzi.ξXXʬ;/)3pѹr髶Ұ=^luWJ|~5'jdcjQګC |G~*4LR]xfmc+ gm|lU;1CIJ4 SQ,O!15?:Ic%XQ%dz^}U1\R`'ӟL#qlq'vWIA>]) bŏ P2j0 Ŝ\P W BDK2 =nL^S,PH)9McLu=dy1Z[;qwB$p[,Ό*RnTWέt/%ufZM3oMF@x@,0@@@M8{  IJ25$/AǃXa<@%$p$ud㫋K}"e4>A bx \cU&4iY Zlax; *$g2>HÃb)t"iy2J] zKQ,$dɮHϟgse>e>&vt{<TzɔrLǦG@KOi+hn+=ҁ}F (L  mG݊N#SLmFu;6bU9\G9רF/|ri1Mz1bL̦<91ua8Z fut VXJN,si=u[8'9S9K,ۣs1\, `YbR ,z&jg7kON5DuZIaD"fvb)Ӣ3k[Z)22ٍ|YgjuO#Rjt[d/ぐ[Gx(v?L #; RU{k\I qԤgM^BWPXTӚ*cgik)  W 7Ӡ/< ִ&W|]śV$gVaУ 6ݝ64"dAP*УIŲ݉hI "eQQ@'S޷asƅ6'<`ah ؉ۋ $QNbTc|qB|4.Ve.}!#D/Y sqzM%89[ndPy]T3 `XFQ;$#2vbL$tB1J`'>ıb)'%}iTo*-@RUSRwq2z񝤗y2EZtz)f'\g4^.鴀bebT i|^҅^nd2 QBEyĒ$ xf`lAc byIFQ\$i~,fz'/+ 'ކ "G؀@@LV-)?H67\&NB x{x NC q$O%UDžY,v|% D'ǻ{_7 *cIJ/~^\ǒ@@O}xǻ~:|2U#1%rOu,^^7볟4 ĵۿ>~sxo_$l=KGb{Z_. & ;~V_O`ArėTo+xm>2>ΡU*"C&{{[?ߔ"btۘ*r坧[rR|LTKd3dϟQ @`B0WxzSiV~fzemYuq$9TGU@@2rOj>֏K jA[ۊ<wAt;0fFt-̳0^B|nrO?aC֫_RIZ]~}pyM~7wMy;n[qe$١bC: @1 iShdj l㛵^V=^WUӷyyZDA{ZwͧRaqfejRSES@G=J;:&$セڬ.廿~o7N{{[:~\a4UmL7s6X$@ X<9&Uyp$-O{Jyi~*ޭN~*wqchZYuSˋz# * \{=-+\I#zD%e=v̠-hKSX%q)q7Na ˭8mG @ {׿r->u%N~_Joo=y2u\/Yc7Z8:F2Kȳ0 G  D@yz|+[X?PItJJ|޼Ͷwy>GqsF69=-ydxTO$4 JmW,N~6N8y\}͏>8T2I @Egѡxua'%JJғ~쟿Tq @hiYI)ie/)1/ftvA@SO^w_v+~飣Mt,' Ё@ОiMJO__tI@0 i_j&%8Mb*VxI&MtX@[IXOsJCH@Ozscs5 yUV䊧wn9۬)t{YAV@^(M 0s-<-euKӵ7)EiSӦMC7WF]p陟4H@O!#\A_OuL7;OוnݡKOt]O}:@`Zxz q9#8fBi\Un<2zZX5lag~|@ m<횻+dY²,+[<]f!x:A(#Y3z"W >RVJY e\,`sAZփBW/y6 zgG_E6SZSi%}ܚe=i vs}-̃y܊Z Onj_(W@Zxz}?hO_@Zxzu/ZJ`j@ m-<|?hO_@Zxzj3~Оh)O?! {Z\5PGsv@)qufF\ET 6X_,tڽL @ Vx:֞ 0xzL!@ Vx:֞ 0xzL!@ Vcx 9}%p7rdaW;{d!P=Ft'ZKz=~/ |'_kT];DT#zכd-ȖYU$7ˋ g;X_oe p0r"C@,\AOJzkBd pYnU6^evb%h24qg/W2v Cߙ٬ƾw|cd7x~֐}Yu}ttbicGսgdiunĩ#4YY4( llrӻMbAM%k5SI+owhxLNV!fqxd~KkdmW}+Yl}0Z=dj2~nfE!k-`FSՈ֖.C/&?d܎FX7n56R5cL_f6'8Yvdb64_sl86z.\=w0/޾ߥj~G  LTn38zl) w d6u=fd]O2e. _5J?Џ#U„z=f^R c0xêag\^y3dd7ӛmzAj~G  BSn*xlkppO{?棎x޼ cHA:I5Sfޟy6jp? Tqo?1 #^ 1 i/^Ӆ0 ujwF78ͯlZyif3*%UD͢>=0==U/^_e$Ý2l.CG?IO{`6k>'/2jb+OTOkA(C]ikr7c9&h i0Vl#Ӆ-t;ћ_L%Tս㷛"XOt᪺U})Lnd0CVt8YOZD&z.9Z/@3$* @=]Pni1%A-Em3w8igrZJ΍iя[U&lXt1L(A@ٲ.Y+C 7-[./kBǥi9 @=͏p@$H{=]Vm7kZhOnu|x{+[=5Dq.,9Nnɜ5ĝ*M{YBmrE|&6&oNńkt: xz# ZH4/^0ӝ7>^{d 2eO(*&zzl1\͙ǽϧfq/^9Pfe֑s9r>1O@Rߟ.[KӗsZ&=`<}X [ Y6M\meRaڙh^_ח,C:I@T;k+X\OhP"m6z-F*÷"˜#{seX Нe̷tazϩGZܵ_bL{[Q1nIUӟmM'ڑ3C@tT;+۩uӳ-6wfAoұwEܚD?nGvMO>2OqFw#9!Ng̓w"*$_|F (Oϰ4yPvJzd$A5z:I4jxzs+OϭCh/x?Srӌ SW_Ob<=}P!!'f OOϜ ǠDG`+ޯ . ?x{dRXT\VVɥ':[[B۪7wk?B 0z:u5?ĹE/DZɶyݚ˾ݭCH5P:7:i)_dNMQ@Ne3Ǚ-tOoK| 6ͼᱍx#̴"UeXZWc{A<h.gn ݸ0U+5z.\OSy wޟa/]^%i0ɪ <݅inhMWTyڼI>hbb=x [^iGq 4 Ӆ7/+N!e+3xყ`$@O^Sy.T_4;:h[A{z>]5E' בd4d|xz|8<=ӷOO`xz䉧311(ϯ <=>Iy7{O?4 T)5SDCvP<{Xd6uf_K#~dŐ 1TLTjO~īU"sP>z$%j'BY"K@5?fxĥGz<_._/N|Z79Y:<&U"6&/*uvGU37z!Xz(#AR$\eY%@3L Yd-PǩLri=N+jYݞhrdB /eȖ4,'d p=ZyzN;֜ {朚 GvΞS{%ѳH>t/m^{?mN e˃_<]0ӵ#OrOQ{g~ I U]! Y37<=](/6'2g;ɹ zA_#M}BΨ{kz?\o9X>ܰ4}o_r"M"1Lf):vs'Q]k)XO;gi>§ i/N%%rxё&zZw\b|j.c+AG3#{hmӜ,o`'xڲeq˻b^6#mϪN"} >L([Kjw.cvJ |V!TS~oh[tz]XWl_ O}p',s7OR1d~xnG]&~X{n)GRpi@ t?SWmz!0Ȗ5 &.{ygWZŬp#O3DT}݋MKMu j}s[Oi[oRR^zo2 qx9I76:CKpn%6tu/ t#|\&+n]HZ OWhG2z{IZ0e ^}] kUp1{wf&{{B GZaP#[$:yz 3vJ́*ӌ( efot녀 >ˊ,aaM9"'2|+<kQjxz'8g!ЍƍT-Ex:>D;vh;<=yexz:4=~Nrg倧>mɿf`IOuO9Y*<|QNHQiBZquZ,O3Yt¿N;$KO%H0 ӮtE˲Jf<ٓNg=%&-|Ziw5?ms<=df{!*/[u?d%x!&9xg.[69n.帩V1kWaOǵM9|)< i fƙ;NOⶔ?o)f\I}*hTd iN OET5W*Pu-Bg"iH2jcZ `T1%=7YɎ% /HZN)5ӉJOJvPi~ LzfQL Et3=i6 @7<!@#CK tod@ -Cz8t}m :~d @?\uz} @ 0^.<  qy @hOV  @@@TXO{T@ BxOC%.-!@xOC%"!@xOC%"!@xOCjZo.G pA7A7a&J! <dH@._|JڞY <2@>B%=SaGvՁ3[RCtgK$pϹ:6um 6rOMi(֑MzڼnLvL=|r]i|٫eB^~ x:s{@`_|cyUA֎>lwZz:! <!K`nͶ;QF~kpWN"l4tgKd@H p p& C@i< @oZ]p$`{O 9D @ e2ɑ26 F. @.lbYV IENDB`WorkBench-1.8.2/Docs/WorkBench_files/wb_log_history_dialog.png000644 000765 000024 00000017157 10417734221 024715 0ustar00barrystaff000000 000000 PNG  IHDRMT1LsRGBgAMA a cHRMz&u0`:pQ<IDATx^1$ npK:;Ë. <1ǜ38m Ł^4 q` z``6X z : :pխVK*#v{U_'UUmz7FWy_'^P '?['Ωh! }P9bq_W9mHDDћ =Qp?;rʫSᕟӾ Zz<9}@O`?=tSZS¸K՟ld<(Fc۔&O( ` c uι)էh_6cNj9m'̮W`H.C @1(,K`oQ>v_wy^u.vF%kX$sΗ(շ?0yԽo8fΫ6*9@V w5r^(O(O7eː+`]}SDj>ܼg7eCr#Tިs.W0ž.";1ܞߓz(B[͊W ?:Gp΅5{֗nxo>w;eSsQޢ|*WմXw\#/R>R]sKE| -ԗ[.lߍYn*RF=ay)ٸb5~긽m V6zL囃y($"S}ڋD߯9_6cv), A0-G:bAwA>0 |:s^6}}waDݫqtvOg3̲E~oEeWָ͵)y#*k*aP&6)ORE( 0f(uWC'vka+gY&/ѽnJ ̈́ž"WY<.bF@ {c_S;X}*nki|a:/25Z+ͭDҿ>f}ڋq&pΆ./#^b{Fe2MuQx{:9r/(ebm!Gݷ^ z9_/(M9꾅}#D*S'QO{yaw(gG?@(s?$~ oikN7[D@|;(C\sTs(ߝ=z`6Uhsx<7;hd xpNx2:AW Sc#t1r.wG-odr?Rϕc) U],T*[Gt)䟿o!_#h:|Ne|ՄȹױHmd8|_x:er_>\9긐SWkU+ ܅|c'%HAWs{-4.ќ TGDWtU¹gp0<8[zׄەcFի]sD絝H/rUs}rCN^ oѫ)ܯUUW0?ϕ\㜣Nynu4\pCom<^5#&E~ p  8;yG0sQs(2q)p1 VŶ=θNM51\kŵbJgr!g\0'uLouVp_qUi\ g8b ('\ZyuM^uq١-4Zj$<97^7iE#~pY]8snwϻs;Kq(H@LΩ;|;8OR0C"sC$vFJh8RV#8o;ʑ@ p`8"vxFN.rUR|r/dvO+;mFyhhRaYs2]]cssyzN0^=A-}SٮGֻ͹:u<ܾ^mb"!.tӵ\e%؍X,\mډF*;5'JV'S=Uיmyۜc}8;WXõuh*Ss)P y8,` 8甗K< _P@ y-=+P@k n8]P@; !Z,UGtLV?c*;݋ZY|Qyrn Ztr~Gi@ڹ ?U]_FO;<Ε4nZιYmS۞Z Gj9Ի-0?dٲase~xX`ZndXo{*ɪ/* w vHUqLNk/ys柖Q>G٪i̳>C6Ι>>ύǪ25 Ѹ|b01{lT.V֕׮(qN 8Dҥw8v; spN(]*y/2_- sp(0NDsp^霌Kf\kkp!PdF4Hy4s2=<EXg#98/tN6GZ98 TT 0a${;@ڸ^*)|༵Ɓ<(GP)KyA:ΕFxg|u۵ތɃ/_zO[\:-p微q7ɬwbN#'/.g'Oi m/ZOSs{Q Iٱaí sKXv,"`>xr:|5X /nO/8E%$ 9wC0Q4Ga3Drp1?9wlv@*xyD<}M;G Otcى9#ܼKM>ny8 Y&'g,W<:~2ٜjB;SѸ(rɀs.y\/6tɍ4\_f-G_sXr5o1+aJ,h|+G*[f =<Բ4, o4'guTGNn r>|*B-ʠ֎})=5x|ˎϛvh|;Fsrū//\^ɟv>Ou %+)=:sч6<<6XjdDt 3Էxqx<2ﰓtn)Rtᒇe<'Gtu.ќ|~?S&_B>|H^5&iLFr>R"vpm9=9'[htu.ќ1=ʗДNAWW]]`\un{@q;nj?G:]x99 <}"$iFd)|{XOKk~C;|w_#.7 987Lq;?XƷƾa{z<ctvK t5ZS8F4 s_, ++d,6T;ik$Ѹ <#.lȎw9S% yq_% yq_% yq_<+Ϋ*߲vA؞W[7h9L\Wvv7>#?C;Fr4hǾqqù AM>QyAݯ0S}S<8ϐs=y6 byP;ܔ0PU#);aRxjX7,  ]fM6/b"w۫aWqR r86)=<-q^9, ␴=몆\ 9UYq.ԛ$aݎU"qoI;L&ӓAy>=ꗣ8D{sp^n98D{sp^n98D{sp^n98D{sp^n98D{sp^n98D{sp^n98D{sp^n98D{sp^n98D{sp^n98D{n8'Q@ P'WӧxWU(7Ndu~5@R?_p9N(`|' 8DA#*9_p9N(`|' 8DA#*9_p9N(`|' 8DA#*΋|v<~{3QtE@0x8ϟsᳳ_(ߎ w,S p%٣](;.r*+/WI_y݌<΋2v_!ӹ9S##yAY%p~Ccy [5圩уoE p^c\9rԈY,2+p6|~vlҌkp€у6aooa98?:_r>/%WxtM|ޤ1e&i߆HpΏRpnDZllrϚۃLTChHjǘAE75Wh_!F{ ܌ތs*5}z'Z}ѓY6<:X^LLj<(#G; n:Nm}ؗrH5yJゐhL1Kw=2s7 *3< ( spWܠ8P T_i 9)@\ h*< @(+)4WIENDB`WorkBench-1.8.2/Docs/WorkBench_files/wb_main_window.png000644 000765 000024 00000100042 10417734221 023331 0ustar00barrystaff000000 000000 PNG  IHDR.sRGBgAMA a cHRMz&u0`:pQ<IDATx^?h[I o ob [ab [!Ra5B|b1)lmIL`R"E@iJ \"A [>sFg̙GGG#f̿|gΜkѫZ^\!   Y%0?ZѠ!ݾw~x٤#   ##et5p ?wκ 6h(%J>:>;=:   %P89P;<(DB?~2mkӑ ΁ځ摒*H8^O(!MkV Y"   Pvy*qZHVюOG/t$@$@$@$"h篃N TЕj}4^HHHHE@7 l aeDr%4|~0|w4t<> ªBhzcynnё d ֘9C( dɾpoANz~?>s;Lsb" sYJbD"ЃýzD/~"^wIs{ѭf\_fq=! B$@$@$0iOJH9 4M?{{G.4 ~S;CICPHizwzޕ$ȟxk*Im_w0567'i6,,,?OupaqN˃919C @C#RٹwqJ"zNě6$#Đ8a'B =I>!%(Zn;EYs9zq]^2[ugNFT? z`|r(P\.y/Đ] OZOWđ7CtbcK4= @5 HNZ1$GdH b`<_"zHADNDi n =I +=$w^-}5(!|\PzHoRT<+ϥ#E݃S&_ɕCa$@$@$0nZ'HrfGMzh$CƯPWu=+'\Ё5h㏕+7"%dBj _5rȥK;ȵu1+kG[8Ѯ4S)-=H桹#F$0ux#GEI]1r1z(axz(Db L9}HspX5urK,=^Yz.ۇlq{|WECF7XHNF.'O 6q85˔bH?GTK8z$ l<߷:R۫OB!^MPAι0|oʍs{~pz_^^||KQmWȈSyeSb(YO1v] Hc8R: 6 ;O]z(nI*B="I$]tg[k*h^Ff mȉWAWs\^F>#*"z*XϞ<6;&t$0FHdo;Cԫ/DG⹌DHՆ"JҳxH@]O=BMG$@$@$@Bʜt^:BXs!3~ۂz(V/#Fz;! x@?:?ou%P$6 qsSpB =+=$ SF)+xшDFEGSJe u WMf:BXHHI!ʉde)\~d F 񓖼g\LT!$F 'RC$@$0>Pl>LyDiq롴1?"_CGӃ($CZ = ȥ ㈝ J/;lϧʃ% tweЋkDIӥxB҉q!hv*WAR%[[^Ƃ Do$@$@%NJ3k"4f gzȪ-S^ l"z(mZ Kf+]T\9M7zWB*1F3 LHH&H }/kV3l&JTuWʯHG}˜жSɜzKcӱΠ3ju!գ! 2f"zȯRiDAfAt@dg:4CK^CzWPڲ2 $3! G=iOT,P@x*e?Hi!౑U .+"Cl QH͆4\! HH` C;|уw0 uˮWp3t!*"3Re0Q)]b U, ĂIѨkhʖD`NHH`R衱惑F ,-cӞN{ 2$@L o \7"G5s8*Gq"5&=&A$@$Pz$ TJ$@$@$0TJ33$@$@$0CC$@$@$@N |p#:   ͇e)IHH>zt$@$@$@$0(HHHмZIHHHHH ԟ~Zۺ~'iu՛[aqב[qyL[C!ΰ"PZm_&YJXArO WBtKHLL >=\|('(crlj$Fhx@Zsp?/⧴O=Bΰi qŅ僟nniĜs\AV.Ο;',n׭6;xV_'.'x(V-e\ڣǫp"1f$'11dѥN*^^u)LaH:x|AK򿕥*b Gxcny={VG;VыRhft ]X~ݵ﫻.\+*o'I9<#HC䔨\Xެ~A a$6Hm4Vyum:0oV=.Z}]=2u?ubdFpZ/*y,YI+Ep/{o7ԋNsf>}ߑd&R?3:YMY&/v{yEaQH=A =$ [mvR$_752X7$ k5a]]1Ja{H x)?t4"ZuK*=1ÚƯ-Ma+B̦PiV(!ݏrk(srMh+Bn!80zN'TѳF!p jb!P4cD} J1,A 6нKb! $!Q_60g? ;@a%$z%z=I+,Q+ٶ1튒28^-d醽]ԇҝ ykk"ͫ(ozkRRf ?",e5u/I93< ω! eQݐȺB0Ww9!*M7 m<ܹg-PfZ+Q=ۭp|oRED՚X_iÎ?,4hG/KӾCh4ÖL{`zfpvy+C"]隺m~Nz۹+=t?r-- aUH 񓬇RvP1zJ j i~CC" árR]:̩( CjfH A= E7\ srBP57m/&~XՐ3I b'i3!ʉ Zj~0COgn83kZC碋9~Uo prKN~`~smAI%4rYH+e }[.Jzlt^m7l---f`+aC|tO&&Z)Ԍt~9b@T>SIT^,1CABlKU-0!]O\=s0٩G"l#d5B>u>tBV0ӵbCuXuDR٥6:mT􏞵rcs14eYmPCJ}44c('8 _.҇zޤ&$<* VNR?1PL@Ѹ:E+e0.!t!aJp+V[~+kɧخܹppEM4zSڃ\&:KK r|c̳v LE Xdws Stp3ֱ MP6P79^+|cV~N%00z Qՠ@94*vY!۴ %xwrrϵVҚ¶EWDk_ "MO[ kI5Yn@ mow z蒃!\#,8$)jx*dȺ%Ѯ)6nuzFT!U']ơ}ըH7DWzXǜZNxЊVX]l[ÿwV,o7 b ZOYhxc LARbjAMSk8CwLIHR̭XK";ː\&}.؇p&X CB=ܓEߡ[w !(9I-S_P~3C!,[w:UJ[1ɻ+kL*2b{]AɁ$50C#t1]ɚۭZL.1qzH1\4GyL5aHy'[T{W]zH&gs=k"?CBN$keXaf{۔!lp=|.;ss@}8jbj@tkop'{(Hn2@PAɋKBtvfу_p9z,0'r=|) e"8CASITPNZγVõuhٺ܇$6} 9H =p%qPXq6886B'"LT8l=6P:W_;vaK1IP7t_67#%$Km?o@*5a{+hwq.媃G׌@0u捝e-8x\O!e7áaP`5[߹jVkj;]ظeo8Z\ϓJrxOK#Sq++uu'l0:w 8hL:p>=n*j@O؅X=4l. Kr򄾵H!S3_M=zo ;` w/mEWd:ݷO6Z_{^}n/ssx1p༁DTz(jbơ۝0@}B!p[(j$2+Ч lyغbd}rty%):.lf_՛됏q7E>}99D_Z줆Qu]]b%E~>?!DZSEn{F-mzM )Ud}iѭ胝:-=t_!lBv`2pn^Ypl.?!5N:Pz3q3هCY^1[e{yE<` b}rZ=$̐a9!tE24?4B(4*HY$:*b'7L.o1!73g*Fp(kP a+囌"$~Eg ޴YFeB1L<Wë:oOCЯ+^I S2MTn7iQ `X^viUn­Wv~[΍}p.ݖMB}ڕe-R= HG{G`9Pׇ͗O;on^Vg z&!ե?]=B(@xL?m~4#*!PJ5/Jϐ? oml 99sc׵͕O+{ߋdHKZ}iޝ+ItjjRk=qyt]a[϶/g wg\t%lr׎x};8~~YZ>ssbCxjL"}8o- E?~9~?rlh EC>-ٸ|2hùVMIZg)zHJHL rrRT{H>uou5΃Ziu;f}xfnjxG]NgzGJ"aߕr[ )HE'xȋG zӭ练O;0$zc) jrjWK:F!oC ^j}%T F&qG[&UaxB"V7[X q cG/|GdHNnƧcۭ# _e[}(i gL<ρzۆT!d!n,^WEh1ϣMʄӶTJY/aZ A [Ef˧fbR6[-,-V}b2 eWNX3 qybfCaFzbevibtrya+t~=Trƍww᷵Ka:l{O" ?jR)8 w/D[ae!%ں;r2sR4kta@)L~( w]>,o GxzjW59/ʛf衽ZM:r9Ę{M=oJCF1z# BMH*!ȹR"0d<=k&޻;{+ġ0IV vzWOW~{T]$&{s&7(U^z; K.-a{ϔ!`j|NYdkcoc&zQ2K FrW <$P~j!uu!xγ aC^Z~-,I14\Z-cR,gD_7i{) ͻDwsy[[n wgFy8x&4JNGGwn .H ?? ?._// .Z<#ݣQ fvaKd (z4GL F"\Ms}2q`1d"|ӸzC;~Y3P%C)YG2K&Iُ+rW!E r@orß7jh5LCB<9w0:2Z 6T5&MA@*+Li vqLwcqXֽ;Yfk;Û3-=P2"k=y:=V6AщۆݕlR)I̭O[oK8Hy<'V.cksCWB;4|Pw8$dC5)NĻ^39U9)E48;B!¦zG*ϮaY=ԾvPIH~\}zؖ g҆G}ƚ HyH$ ~-&gԎCWcHA}_ۼ!ֈ x\&"+Q<Kӟܒ!xo_ +q":;D.F52af3OZqm%:־Qk[k-1ŲWCbR>r 79][zV_>>!Z!nc919B$X呪HDﷇ;}u:tbuu^9J5{)f^N:Ҳ!/K>?@+doK%4(]$N*Ϯ F/h(Vd*P!!gğl0f]H"{i ,WA AHS Szhr[2(!NW!2ǵZ?/k5!譟V{C?"/p7#ycwRUWD_w dtKB'٥j~HTBGokptdG!ͻ=Yd;i~Z dZ?6.!aR"3޵-#rt"_dYB#UWnH ])[XNnCKadK"C2ϭ7s]hp Epۅ  V,Tk7:/5{~h+_!F:m [*vi\!C]/~یzc32|X\a<v\z!ۨ>[|ݺg#xGaa\!4 Eb6|\?eȆ_LvUH*!_R "rlx`Gs. AR9|Ztܪ !HnqC$ī!Fgj= UV+v!x챐Dc~4~e;;D&M衤E[_/--`+j HFgȉ0eW< e=IHwu%>e ohrhep Z]G  'Ҿ.-[ތPE! 8i]K`A>ZLl"hޚ}ASJT+oAClIW/h򬔵|yC

d I`rͧtOR:C,┬~[;cn;_ I48][]J.A=[hX}'=;Dy[b~A4JNmKIB>䏧;KfPA.#D2<$mtsԎ|@Z$m0®{Aaϐs,z=#YS)X9ChQ,4Lˍݟc"C1Lqi?_[;"1}s~o~9JH!,A A qO)~m̸K2H}$ϛh]H>j19؛lJo_k=C024>=7@Y ˩Zœ͔-VԼz(D <]5]~_ۿ~pF 6rwaGvN2^&BZ) qRd3nay֡{!ڕtr0~r9xQ!,A`!FSC*WG5m M Y&J A0C%Qib%lu ̀>`ݸySyXY,3upgm+eϖZ>shL%"|X,úП~/$tүnph0-js\1zO :2^m1fZ\/K\Œ(|ՀJ KLW9!ϋmt$.\F'wPdvhrTMҫF+a)i/ =. $CRmU$j<KD?y&REIxF 63b};E4޷~RsӒ ynlZqvsQ?fE;= ̟ݒl'Fm3ͥ`=$8tq?2t7rD[\ iƭ'Y^(χvwZjVM%2J<[GALElK'%ΟH!;`*ĭW.“FDՈæ o)jty[XJ2_ğGo!+}<{nU(0m\v2;Lckx2=Poi=FR0TomPPX qk!OT' 6'p-$xuJު>n )fr{VZtng`KεbE$<]~!m- I+9b/u#o`QPB~HHH@z(TU   q"  wCơ' L!!   y'@=4-`;sK$@$@ @=D=D$@$@$0z(ƫz7U~s=x Mq!' $K4r:|z97x^HHA }(D̰ H=4>8IHȥ'BIE顣t$@?U(z& **:~{vl;\L5*}e&EJArه:* L&fޏСR!(bݛzK~;ظ?_b>LH=$ z/ttv.-L|Ps_9!N=4-E+PL(@+8a%F3YCST(=_u|eV+NzMb߻bS/8ӟ;?d~<:TL*=9)1d|b-k3YA9I 4161C*d̟Oox(C%?E衯}؄0_z0u!!1Pq-OiW!|θM4yf&61 9<ݻCB~&\{13z( 1mJ?CJ9 m,-i_=kq9m?6S!KlvK;LcLWu=$CJ+s&X!'{9zȸ?yHRL"!0Ɛ9ov[ =9m5Zoᎄ;zH6n1ں=t0^jC2 Cz[{ʾI LF ކc۶O읟SErX(;?z$vYcVn$rmKι_֮رh~=jՉl.5n16|w=?gc;oX7^^=XdT33ВPq!cI+w;1Ǧb gp1T#\1Qyl ƥ WuO_ yRoF )Pm/C-x]8<3$= $q(nF1gȫ B=TƉGR5=C19QyFӪ_.LJ5WbU~\0CJ*X+ڕ1cJ,y]cҩ2QgĶG sC=Y+f`&9;q Bt*DpS%D$§b f˹Qi_vؓPeP؛7MPKzJH=TTϔzit~POs+:BApMF@ I:gp sHLHo!mΊN 6p/zYbu@Bj''Ɛ$[ȩW*6E!ڇfed( ^q^Ht^?ށX {eF1`D=#,@(yڲ<ǁKOB*vEYriZsAn7c\ Ch.银n />x:Įz=Nr3 Rl!r@\+N*`77%ls0V$Wl>QI+g]7= Xe!cCl銊'3T+sz11PV_"0l7#ybz sZLPD@*=4%b&I|yP\˖S͵o51E LD3#WΡ9eɷSMEjb&g)KT>! CZ6˟$>)RdP'@=T:bO=s7Cfo8@JQU.H +ƒ' A@?f^Us+o%1 EG!ęY9]!O*HCQz(P"^$  dC_8x"Cv}(v ̵RF=T}iE=TZGeB$@$@c%E t;g-N[TzH.CD\/Zkd$@$@Ȣ:ֻF-ܑp'Gh#C{ϛJLHH@cI= pnSa+cNҞ?h¼1-C<4ݛ&o9'  2 r{X_-oGP!;`x$3uλ< H?G3w>"hңgY6Kz(UzO .`j!䘏as?zYigfY: (qd"val B<.WeeX,Ɩ!Q߀k IH VTCsX| =̓9X3C27ރtA}(m; F`jP11=Öȭ\S+C3ӀY p`rV?c?"iuӄ3xśQ +\yi"ҿ#տIɸAڇ=8!@F$@9 LJUNiIU{.Gl.m!lyD=TxCb$@$Kn'B_ݵǏi2!] O!{sn*XCPI@VAC)];zH) :g= _Rg L_~ <RG=- _WC) Ә8cz$r z(VTSiWwJשRg LzaHTI={#J%D R͎yeO2$gUsm溜cC{ HFMUz2Czh[5KG$P.=8.>~WJL衯}~z0ueU q +XkNU\R(vhA1.\zCCHHp]ԃ/Kdj= }Jaz(}Hv,vZVc3r F.=d(!ڇ27bPlH=!c  LJUriz w$ mm@7$,I@_p1QEKɴ{E8|R?>i&:xxK| PU 0PW}(Cl'}%9[j?CHH@p!q`ߴ C9'  ћ._r]m zs󵋟Daҵo?}'ܙbaz(!T83=1w^qoHH%yvϻ5?[:wMD(n?5^-R~HO Hqه ,kHH`.6kZw z('s@ J!,?}J^/odnfr$@$@SM|{a`bH[Ujnff'@=4 3O$@ENHvC>ʗf| Nڇʗ3bN=4wHG$@$@%%}nP9 ĵP }I '$N (C≸Z-L??UrgO*yZX]1 @`z(:>J"͞?P|S4J 9]s)*]eiW+%hgcͨ:.͐Di 69G)•Dx/Ỹ1Rq'0jTeT3SDQ%/=m}(V(Bĸ7(V٫uvlTl'y|zCJni3E Nj g]z6VJk=ktZVc3Q)PT2p=d3\q[oN?c\x'G45_\9MJn Le*$ɩ(D2ijzsօK#Jj!y8Dw#Ƙ}b-C{:ֻF-ܑp'JKfE.[z(6CZ~ njpQbO'W24-by8=P-F7&5.=d5J_\b69K-,.=ꭉEκE(@=\ŕRهҞ?JlM)bAUy2]-v{ cMkƾt &u=$ m $ɩ.X3CF5N9o 4ƄaLzhU~eb'鍍zh0h ܗ>\sB,@1cMS)N)69*!۱z(=Fgκea_.KPee&QJeNӫf2瓲/dǬıã́iM69Ee)9wDyuI5>$! LJ%W9 '0>=6Dѯz:XưoI{FlHlQq"vĐR#@.bBz_jP3q,Ce6d>Ug ƧJ99ǻr$duj uQzxyƸ.-7g/V`XqQT'6*x|z׽u{nT8QSO9]HH%hzȥc=_~0_qq7!#tadLͥi\+ev䩮$C>VCvWF$@$0&a.=> d^tQ&f\f]aH])\%5l?N1RK@=d[TV=g*hH  $qf̥c7C_ aEu`ꞷC~Ake [lkPZ[]1=Tm3[HȀĨqJĉa]ꡐ2O 3 ֬t1ռkw~KՃ{%;oz=1-?_Yœ8CiCB]gm휵:fōrlqhёK3bbzo*d8c1NJɬrYbN71ÖwFz([$ΌPb}slk w!zȐD&uݣ_xPQ)?Q1pJ*c<=FLJ]W @I=#F2faUz) UeUoxc[ݕsxV-9hKXJioy%OvX5Ñkt `.=>ĬOZLy(?EPpYe# ]l#V$ Ni?fP-<7~ѠzAȼ(>1e !z{HlEֲ=k׵q/gӣK&׹Pb3A5JW~E3J#_N3C=4qi4ڃ1%So2 #q8h|#L8̥zN,\qRfT!=iz+m:xzdyC.1g%CML(rWEWKTjX;KgBvXϸ;)UZC\?"CI`|!P^B zن(\r cJ&"[!!;l(Hb ğqw;!RqrٍBnئk$IsfCe}zH^Jyʧ1%ד-W!k\5q-3>d*j*Oum,W|/lmXK(ubK!yR~v2FiO'\z(1vzmۇz iG`bZNsfCe(>4H%03eekdZNs']x㈳L!C' @\z('Cp}w.jk"833Ksڇ T@.=jjwCr>u~=̓9`*J- ȥRهbuxY)W?E~Tp#ӌ!B!M.0k, L5v1U_.=*O=P?>_xëKJ]&CJp;BimTL =tƅZ;ρ#fsAzjِѩo Lc,ueɸn'ohTOپQId OPZĐ(dTFq{;oO2I0CbyCsl#L0B>OH(U=W@+z/cd}g IGKJQlbL ,]<F>YoP >&`ꞷ ڽCu=zH)iCa?㸡uA"1RB`$mLϕ%;Ou]iޮRZ{뿣GaQSkP8=CDbJ4XaP#IJTC.=>$E{;m?6=zȣB= !R9̤ % R F@}Ggs@!jڈ"H vJ 7|mΪD=!|_`]5)G5S.h BfqЛHPCtNw[#No#2T_9]#EϤg!tz֘(YBG%(3VP>S-e'?:I "/Ywiq4P''V9]^} R78sTMMEz(vu32룡l&S>=W@l.Z5P6CBi: )]uW3Q%=m?m ?ƐKM6L}|zS9O2}[H~BȮwW&)&TzȞ/DBF*=GOr?hE`H@b3:I5ElCC/*3P89CR@OrR>+1DȈE(0umLcF6b=Nz/0Ѻ,2i,\!ɹu3gݹzZb%DxF!.=D/Crs 骈,'gK6Zvr.=T}{d.#PftyG1)V[Bub|9a-ETjb&IH >r*Иy   ȥ])fCSiN=*-z&  ȥ͓ؽA{@=ͼMU CrN([  ݘG.=4<Ѻv]7$LժR)P֕mf٬EʜmO =Ogx\Oldh[#99؂"밟ĆXcp'ĬKcu? uuvOn[CC٬8SkJ;lHנ*#1Xcu%dKzc*uZ϶T1x%2YbW}bC.=TN!{~5!WzP;Ev6rPMeVP>[Vʿ6yLK0U2m9?+0ی__Ĺ)\2=Q"@11Kv!zؘcGlWC.=T}H*!ӽH=$3CWϬSJ--3ۏ\6IP?R׸*ui [DC\Di$E{FuZVc=q3.̏! 5xl~詻\cIceP.ȩB@NԱ] 2 )L}slk wd?WAAUrw2ZTPomm \wF{EwӤߕI59O72ne?T0VŊئ;tߢZQzc{K'%6y;|zQɹer$@$@$"Kc ?Hε E)$6XAz(CClB$@$@U&KS09u 6*DSҩKeSWe0 @,\z{ x}"n%iLryP>z<^笛elH$@$@$KS$~7CH~7LG~KR9%T`<ן7?M]-3$@$0rC@rdz=!z#)d(c#:5r!C83 CΜ_؇`]d8=okҗ-GtR*cIHb CهhwCV C c{' lȥA9m5Zoᎄ; RkǾ_ ** 0  qȥʱ=H] S?ДVM$@$`ȥFsJHYV$?CfHH '\z^B>g}zXHH`RrIeڕ.C%P CUu P?A$@$0r顙 Bd'@Pvv I$@$P%CU zhЙ$ Cc&CT[+ @.=T}(!Yڰ&]^llflQQM?S' (@.=TT&PKR#,6{zhR. @rrC[5d=ۊt ð$Rns*"'D3Q  C&6B~7!=2 b*S UJ$@$!KfR[_d:&R^ۆ8 \z>_z0u%؇UL=TN`*$@$@&Kf=kCuZVczh܍#1~DD@$@$0rrJ9m5Zoᎄ; 2PLecuW͌* 0  qȥʱ=(MD?CHH`*CSQLRRwC`d$$@$@'Kckr0o$@$@rds& PdP$@$0r58m"CHHr*y+P  @ r5XBLoC[w9 N "9'@=4 ' !KcI{ΰV#LU CUHH ?\z(!1ΡvO}taTQHHz( T@.=T}H=J~=k%EC$Uz*=ꡊWG$@$H  L#7~Ԍ7zPeJ(gE GIH&B *> $M;ӽH=$6qИИ2Z  CU؇MKơy;zY9T槮YR mPi!!.ݳ6P9m?6S>4FL=4&HHdP9y6[pGFB %VP< C؇Ҟ?4)s.V:L$@3I I",T8pVI$@$PeP9!_)V&7ꡉW3@$@$P\z0%@=4uǜ r5!@=A$@$0r@Rd&@= T@.=T+U[U Pj! F ʖ$C ꡙJH@.=T&C漂Z|ꡱe$@$@ȥɥܦB=4Uς ȥʱG>R#\ţǾCy3X#\HHRrrJ"o._WGUK?VFk7+C^? 0\z4$+{ёzHΉ!qR4ܕ^ }w+C`HHdP9y}8̾+/=l!=JB*WYN z() L@.=T}Hv 9kuN[lzȐ>d"jvSH~Qz('= L@.=TN{:ֻF-ܑp'8նr8zHD*fIH`NC؇Ҟ?4'5Wb!) 1D=TbHH\r!bs.=d!9o*,> T@.=T}9{C\,; \zh@,Pe $@$@$K>T-3KRu?R'$#Rxsten@}H>x 'LeLN}q9A˞!oAěbf(E`S-QGS8Fi<@!R!uSbߝ?wz=w"&v0MzF%2LBgO?d;u==Fm%=,UܺZt7m.3`7޴Îr\ [ϭ̌23y-yP==glO)'BO;m!x]%F[ b9zz\M,HC6]}9KD3Ϙ>A\ bg҉l4zT L@j=r7򔟴 <粗Ҙb"Y0$V(=@R23ǴHHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&@=TaHHHJ @=Td&A$@$@$PiCfHHHC%@f$@$@$@&C~/ӑ HHrH-`=\tԏOMq;!q WN?# I qçG7-῁!H:A9'  ƧRW#=4t$@ Qq' BMCk7bPz07#=uЧ# G V~qIg\qmxyeVd''VБ # XbLjvSwdG=PQ(IHO@!CdLj=xqyCW%Ie5UCIH(J$4ʰd+iIY 6 /P52 pهbg,!4Mrb`6H =!$@$PX=0Cm*y& PQ ! T$>$ԓSw/: w^48 ' IJ F;xT~~H wM #:  2h?~p=:#   )& %ͥځqYV]k6޽mb4HHHH`6#mvglsލ\tg }K9yHHHrnqGsP\kCøEohvSZׅ؂9$@$@$@$P8ikz:L?=~j>< wvBXiO#}ũ!zH ` @1) خ KtsI@p$Ί>If@zjjpvO;ZXZbO$0f^8잶=XZbO$0f^U^DVg 4J9a40Id.8Pft H$0CNe3CisRd5? @18ٝyKKϗnӥ]ڍt"J[O$@c'C͓#8 ;/K7ΨZ|>U ŊqTuA$@$CbfdP$zG=Nb1|z8 T{ b=$`6~=Y 3LHZ@61]"kyQ)0cF$@ #zYgUOWa8_'0:MX x,fEb60"A,hƉAR`Q8`ݯfJ5U5==(DOMWuՌ4rssgc@$0zh;ٿ ӓ$@$  k;IӓS O@\,F6VJ29?OL @hW+KFӏ~iI+=.Χ7$@$  3d,ShN_O_ xR5I-X: qۉ솟8~1&A Pѳ1Û77ק7%)#%J-UX,^;z3 @5 nTz=J{/[>_]qF @&Oߏ+ Y)#%:/3M @.qLJf?M١JO翗))#%UW=@@mg~|[ZLl%O)PK-e__.ӿ~L~BZ ?~`SYeΓeI<][eV-IT1VKaiͿ?/@@@Z<=O+~U2?[b/.v2ՙ Uq[Y@`(O_Z;Oͱ~jD;zZ}JyU6ˉمM~muC@@SĵS [֞vRK0V=}>}pV<}^invr[+.S&_-o3MݦfvZ"zr'*F`uA LC% /M24e|==W6~OZW'u:GЎ^VJמ6o=]Wime;+n9Kv2U cX^̟5mS+V!$H@hgiR${%s$z ON3; :y~u?_Li2UM#F @>wݽ/M2RFe~wOjF|Xzza*ߟe'#<'_O9cXLS^;Mٽ,lAoH -ОZmekˁnn_WQӵy=>*lry"GKS|YUkV  3cM]?h9`I}! % X40*qޅ$&eea;/=}s @ =IQ[U^e|c7$@PM]]o|=<4j>tM P#@I(w跱Vu$1eĘ  IjI]@XO[M @ b[Ow_t @O`ӣg8"A e+\VuF @ em=sBX@l@%ӝn$!@xOC%?Nw=(=3}{I| :=-~_7ܜwܑ^OC'v鋻J:MHm=@XcQ{ZG@h@Eҧ.2ߍn>ѳѶ;i_hs쁐4Cf%YO'{K`%PӳO7v%@KMO;vYifA@/i)O?@x1C؀@OLOxzT G=-6U` wK!@`~aq$? @@XzY4g @{x9 @@<͹@HNwn  9 @ A4 @ ]x:ݹ!2@9p|L >O # [N_ק'X g{oooÚ]T t' #pxr5XS7{mE3obn(@UWiڗk<oJ 6 #r x@ [qm2fk2v,'6]طcʗyw;>3YOkZ,oOo"}@/.v2vV\ ?Ga;72!@`-EϢjGO'ҭwYe6'" @@ӾVb^; tHOƤb^t @O'ӼL@NQIO'5&^⿚oRzp {8vscFc(QV ȿ>|2=?IG0*^7.3M >hr9>NN3GN k< @ }x:9"B@]xzw瞑C@tsD =# !@`w $@#<=0@;C@n,vCJ 0@C"Хͷ냍uJ օo|^tԅ .=\I651}ڊ5C^)<2R#5ঌ>.l͞:q4[X˙N;pak),i+,Ո+v >Hӎ-W"f .[ꪬ W=]fk?>^ @ )yY,7Ӂt6ߎέ=>Ys'u  wyZqrMa=89 gfmJγf٥c   m^LUYO{ W:ol @&<꬧5o& Z~۫@տ]{3B tHxzG&aB#6ӻ2ӌ<=,M vS92ӛ]8!lZmA8<= 6ӻ2ӌ<=+gzKOv*z"0CHNbZO. 4NO74tCX 2$@#&4@j=s䔐"@ @GxOCH,<9Ga$Y;rTϊvNvyX ȳ<|!.UzMxzhWu 8='F80!Ea/0ȁ`VaV`f(@ }k3ROOTkkzP\'{heKS7 nߓӇ?>~   Zt5~m.^W @`$DڢnRB|\[o@$ QER=R;U6/=z1xvE QE Q$QQ*? ?I%iZ/Ed^ LĜ?:@xcQуճݾ q$L[. /@蓀xyӨ"OҪ ] ]~e$o̠OukUULykRDo2h}dF ܛ*N5ӛ}1OkikhL }J#}uH)zzM^DTV'_O^ϯ^s^a{W;Oe[V`,EmYѼo!m|nAuY~]@5iKýnkcSPsOyy0p# M%UյVesykΧ+oUˬ^By>bk !=ۘTZ#eA8G[=U1#sFtmuk]'X22Օhhӧ;WjH@#ʜֿ*KGK0jW{T26[-7ua}{4u`nK|3CgE 53q3u̺hVomuL~ed.^kRm[հ꠪]w$Xo!vi/n$gsC[)[_;)go6B`1նTndq,fmTgꬮ8b +sU7sܪyҙ0iXM0Y6\I[U/(t^&(- zrE[6ܰ[*IyFݢMx' -r*,͹mD[MdImwS1UMY X67t%z 6\͚(Q f'#-6ºtg[}J82yV(DZ߼P||LU|Fׅ,eDHB@\gGI2'@ 3孇ޔ>/K(ԸQ̽^-!kJ uSeN>@NG~Y)ht|*Qh|+rvC5E# 2QYM2X?Cἃ.ӛI%iBWKG rz $h|=BA؝@#%  YBx _=$@BYFG@4~F* @B L L:  !dBgґM  t@ =dl@d/  h&¼x[Y']Q-oX_ȴUR* h't)_ZC:`1>,-^kr+!BL5B{#0B@c!0v̈́7/ײ^5.T/dS1#XY u:~-,ݝޚ {S@ 'ͅ-isSdn ԕ(s+tdLЃ66#:gW=>U|?^$cA#%L\hbjhؒ5\̙F#=L6 C7{mnc$Ô?^9?)1@.5B~Y7*wa1_FT5~)j&{(@n fBY.|' MX4XD=!LB1[2!3iGh5 `@@&z&8BG2!@1,Na"FIYnH vB߿HếD#J h!,Aΰ l4zt5T H\Nf=(Ƶ}}XǎI@ -B[Nجk5sNU7BW2{o 7@]4zKz)VFW󏀽BV zBHK㗠ӵ&4}xHgwn*{]v@Cu :W\bt_7r?Dg' . =r :s6#f|Ρ9RԝT{Tob]!4:K@̈́8$"@hJЛfM|@B䱙}4 BG2!3a '!dBgґ}( &ޓ= @HBr ?\<)ޓ@Kp$C^HvHp# #N x /eGcƒYC+_GP@ {,!dBgґ=h  BG2!3Ƚn"@@@&zOy9@uj {4聀\zO!Џls9z6 q z̳s+)#N ׳MoPjhyzF)-'\~//:B@ 0vi^ksYwYuKڎS.P =d~ޥSzz@ н2Ӣ^r5Bk Sj[~d~J+Т߮>*Uǎd !#t[P- z{HwC )z7Ox4*ގ @=-e(sl\l߽]}s˛hͭ[#! `H&tEе-.N+@{hԧL B眄H'^{nɺncLK=r;[O\d$$R:{\Τ'XLϚ3$ @kL#Kzż*r$F $zu#Mb=9R24 $z\F* :4`ޅi!M@#$2@}BO @]ψ@CȄBϤ#\5!dBgґVPy@ !dBԑh@ n[IuaQ(EGHB#Hr(  F15I: @2ϯg0/VawhzCL 2ip:lsY..dPF@L@ @ ?6W "U]@ =dsߑĄ Lys56 zIzAйeMkaĭXÈ)I(I-isK% պBx^w-"{sNu !T$'NAV;#tsDl3hzh[Rq >աS$L@2/|/a;"csr_w+)vBr!.H hW,Jv;^ 3*Yu<zG$B#t{4 ̟moJfm#S@ =lz|EL8%Ҵ7%F|@K cg >iS 0i>w"ЏX>S@`:B?>?"ЏTk  LEe @"Џ?x砄 BG2!3$w2FM#t@@t䨇THB$tn -$:ґg aёm.G`B[s>@H#gK% F虜L4&L6Zeϡ܏}`P> 0>h%twŸ $j H-t7~!6>9Q*>*@ I#t^ըQfQx9zeszɚѲ9U5i4WUV96Ot 2!R-2ksU7B7Gj3.A7mEy&'̀@jɄiѣrsl> H' #LNH)tiY/ohfz#e>ԳzA ]\RW.=Q@@ ]n5kü,N%H&ǹ֭C? F]FByD# 0@@ww?>ZN| `M; Do<2 @!dBgґ- 0"CȄBϤ#G44j|%'2 -4xo^$=Aai~X"3 h@ Iz{v@%'If{Mak#^qS&FW@v8B7A`bz{Mak#^qS&FW@v8B7A`bz{Mak#^qS&FW@v8B7A`bz{Mak#^qS&FWH&tɈC5Ec(hG Zl!@ [ɟ+J/@@z3q p CȄBϤ#t'[@`D:B IGhAU!@@&z&y >B#": L L:rD BG2!3]!0˫ً>iN٨Y ]BB'INJ뽙O>G/n~sZ\ϊAhm`Ie,DarXEҮh|y6+[̮Ң4,zC`Da26x/HŲ42,]Ї~6:<2{., H {v-5zI#39-h t1 }T%lސB<(m X en# =fC:_ +7FC?{B"r>Kg^tiF]*lKcV9 tX B[("+/ZIԨ" Ο]O'v#AR gߜ>|syE6AZtr {E/?H藟a;Bi# (;?䶦'}h(u@>eM "mJGýtTVMW,FF@&z&8&@ t@ =xa'9 !dB_.dFG@.IOyA > YIENDB`WorkBench-1.8.2/Docs/WorkBench_files/wb_project_add_start_existing.png000644 000765 000024 00000022611 10425464456 026441 0ustar00barrystaff000000 000000 PNG  IHDRgyU0sRGBgAMA a cHRMz&u0`:pQ< pHYs+$IDATx^?hYqnK6|ٻp 6؁ ְo\ 7`&  &X tO0 'M`M0 tLwK*_>H:{NKŃ  @}8jo,m_~~oGGyl#@Y 05cU2ӧ~8> @ 1 gQA̟fŇ|yF@`ajb*cYTe2X0;}s~F@ajb*c'B3l=L:/9ϯb;'/WctTjb*ciϯLG81U2'ߟF2H[|lAh+wY:HUGub#@ g<_|/̿-wg>iϴgOj.*yZk9{M&7=[|Wiem;>ib?լxߞ @Rf|2y "-R~H쟞QE5@Zek}Ծ%}smqE T'v>݆V%%̫=QE52~{r]+)/T}N7Ho~}{>~ +3V E5ITv#̫}fT˟=}{~U,ؿCmոd]tw}w` ߋ @@E52˭S[ ռTju5mm:kgn/^k>X x }ݿ[xH ||#pl|6ퟎJV 7,b{"wF^h-~6:}.nz\wV츆55u>$)5ㇽܒ@!T7ge5,^t2qlyHQͫs^W3Vzܡa괝;5Y/bS;ָԲ퓴U j.2dV?U{׫Q؊%ڳOHF>lWLnN_Ƨgk,RciYޕ@zǧ/e5d,Ylfuj`~,v?^^ӵW4߯aS>K6%,WVsYe5S,oIۍp.o))>aU͟[UO߯N[?~輞 wC[xwǟχU .V[u{ڧwQDxU5:OJ}TO>־$wWc=Ws偍a( gkYN{rWh/m,~١-..Z곗-O~㜗T;΍۫Oا~m hE|mXX)pT>5 Te)*VmӴſa{ʫj->,bW"  @ͣ m-O[\1T{Y?gŎo~Ӫqǯ @׿~RT00Q:US+YgNVݾISmn^z9.n>|D:2ctT:u5s:@vxα[:*[,r)6 @O ;< p7y? @mTjmӪsnf#@e+Ta"@:TšDT3*eETS5  @@T@5RfX @@5UDT3*eETS5  @@T@5RfX @@5UDT3*eETS5  @@T@5RfX @@5UDT3*eETS5  @@T@5RfX @@5UDT3*eETS5  @@T@5RfXw.J iun/cڅ l_@5Uso*>puw[JǶ{y{8KPͽi~=njӈAo/Z3w%w} O`TS5I`O Ok9.\k ;O؝]]p5U;ך`qӾ1[>U=;JJs_ʋ%P jpj  xw|$5j{7oX ^jϾ 5cp_F9H@"Kz_"bq-]a_8-4G}u|W{&jV"Yq Z]y }~iW7h;_HGK-{ Sy_D?G*عU/++1м`<\# j@cËF5b^W38( ,7't}ǟ[]^j|supƆ'++}+/)ÝU5txj6,4Pkƚ_<".A}l|nbkTbG]Yr*v7]9iXymy>^vv^{ߋPyTs_qYtwhXG=U[' 3;PM$+[{rv%ӎڠj3=Kl_fBv/T@5U LA?ū9=P]%nVG5=Pj&XGgt6[0wA`Ϙ;LPM$@QՌJM8rPM$@QՌJ4W2&I6+q$Iid, 0M@5UDT3*5mV(I`j|}h#@{*pqq1*V3ݟ7 [;3kuך{=xOF "@QՌJؙPM$@QՌJe9i2(%I53d)IrdP 0J@5UDT3*5j2bgR@5UDT3*ɠ @`j& fTjd j& fT*IA @(TM PͨԨɈ  @ KTM PͨT&"@Q @ *QQ; @@ @ *Q,'METS5  @@T@5R&#v&@,TS5  @@T@5RYN F j @jFFMFLY j @jF4PM$@QՌJؙPM$@QՌJe9i2(%I53d)IrdP 0J@5UDT3*5j2bgR@5UDT3*ɠ @`j& fTjd j& fT*IA @(TM PͨԨɈ  @ KTM PͨT&"@Q @ *QQ; @@ @ *Q,'METS5  @@T@5R&#v&@,6V͓Ϟ|q?o?, f)??8xbgG`mg\V3 ֔dj.>_.?Rm}wk9̞F`j_f2/Gi.gˁ|mo @ "n5e_'3UY'^]4>S^o[N}[_vn0gk ؿj ەSiyه[l{d^|™ Vs`S4V=﷟;CX/k#S8.e`lc$B>u֑'fi'3Uy|ٷj,__o[3jhEhpҗekŴ\Ym_f^`jY_2KӿfȁJ7[5;^fߐt J VK&Տd..ߞOھ=+b2Ep^vf{:!>aBS`j&2/?2{3k~_j|"mv;C;P+X =/iY-}K~kX9+#jLkK&'f?7{^o {Aֹ۝\jfߡmE;׋/jFC lۿtH,0 PͨԖ3vP@5UDT3*SD[PM$@QՌJmy: @`TS5  @@T@5R;8qI eTM PͨԖ3vP@5UDT3*SD[PM$@QՌJmy: @`6VY$.mTW/g??pTؚ lJ'?~<Ywf}/+K8ymHD|auI#j]:9  pVXk:-'0V)7/j^y|ٷi/7닪0駱lso\dcٴ+.V3}2IJflZed?̋f^\<}ͪ'%Np_mvXoilUkRJXЦ_2I?1[/̖ooϧ m_ᆟGu\˱r۾D_S5;_kU?Bdg2utǟ&\L'J~/3I;k~__<"mkع/}^۵-ma3{o.w`_/)E鼀΅i_$@le8Ӛ2Il6./a\'\`3L;ժjn|^$[ڽUi / u @ #@QՌJM8PM$@QՌJe3Q2,I<1q d#Ifd  0Y@5UDT3*5yb@F@5UDT3*D@ @`j& fTjā j& fT* @dTM Pͨ䉉  @ TM PͨT6%!@ @ *Q @@6 @ *Ql&JBTS5  @@T@5R'&$@lTS5  @@T@5RL & j @jF&OLHj @jF(LPM$@QՌJM8PM$@QՌJe3Q2,I<1q d#Ifd  0Y@5UDT3*5yb@F@5UDT3*D@ @`j& fTjā j& fT* @dTM Pͨ䉉  @ TM PͨT6%!@ @ *Q @@6 @ *Ql&JBTS5  @@T@5R'&$@lTS5  @@T@5RL & j @jF&OLHj @jF(LPM$@QՌJM8PM$@QՌJe3Q2,I<1q d#Ifd  0Y@5UDT3*5yb@F@5UDT3*D@ @`j& fTjā j& fT* @dTM Pͨ䉉  @ TM PͨT6%!@ @ *Q @@6 @ *Ql&JBTS5  @@T@5R'&$@lTS5  @@T@5RL & j @jF&OLHj @jF(LPM$@QՌJM8PM$@QՌJe3Q2,I<1q d#Ifd  0Y@5UDT3*5yb@F@5UDT3*D@ @`j& fTjā j& fT* @dTM Pͨ䉉  @ TM PͨT6%!@ @ *Q @@6 @ *Ql&JBTS5  @@T@5R'&$@lTS5  @@T@5RL & j @jF&OLHj @jF(LPM$@QՌJM8PM$@QՌJe3Q2,I<1q d#Ifd  0Y@5UDFWCRbx: @@ׂH]`Zs  @dՌ~x2  @ TM PͨT6%!@ @ *Q @@6 @ *Ql&JBTS5  @@T@5R'&$@lTS5  @@T@5RL & j @jF&OLHj @`۳goN>>BN1HUsߖC>9=y}Ie-ٓ/}ѯ=`lg{>|TM6/yl6[|҈ҸjEN[Q _9O>=x` L57?|JV3%ڂ-j-X3{z?|c/?}?n9ʣJ:',Ϲ.jI6[FsmekͣP5SQefYydUB;9QyZAMU\S6bY~KuȚWm3hy`˅۫}Wf{8zX҈UsCn\jc7WwԷ&ܙeWh.v.OLf:j*9gNtcYQS߹q՜v_+/AԩTa=_'v#Z'Q̓jhԖP_SY_ZU'v\y_CV\!%hZs;U?CC=1]U:J5U;XՂjR-=g}YV|ԖwS\9p_eEF]L} ՜v_- ?P5" _+fە?oe5ڊgC.;ӬqcCyc>{3J;ZkNX÷eV.L5U;T5v}? f8k6UyfZ-ʣj~3N5O Cygɸd,j]j~_Ei~履fFI j5ʁvvT?تTQgTseȤrpgO+3 V6r|LffoN_[ @}Fe8wD3n[fE/C7G.k'@ <%&Fv΂%YTIܷ}- @`J/:^w//[bX/cbۿFʫj}r# @oų_w{\gmUud!@(y>?^̿;X:މ:fӢ8F%Ts7'/hZ @|g~z鳳ϞͿxXξXSNY碚UWۣ 09(Ofzz˟?Z3X[SEb4֏j^?`H|se`+qي&/PT?O|RކdbY嚱UQͲ]U3~$%jPNU\!@zx}k?Y=c@{^tV @-(OpF5˵pz2/)e࢒ʋ=~{zT,ؿBWmmxd]El|aZ6z*;a=w^r*XwhO~<=!~GYpUWM~mVk|$~-7>ûRz~:n ԟH噗;^6ֿYXqYjoʫj^`~,vN//~u:,6ilXNZjv˫'՞;l|ʇw)#@*^.*f$a,YfH,¹xܤjU5:=}7//V5*W>|m}þ ~itO{C-PeݣX~ dju(*&zǓ.yi(jR`WZΝ :b=gs醍!L^"MײN{?rhӟfgr{u_vhxՇuEk]lU}bίjᮭxh}uV?!璅RB`@*KQjoRN:+j?wbg$  @ͣXjR)}[՜s6oR-?[G["Po_IQha~ͣتeW,nToKliWw)p#._<,9vb۪WՌ'jK 2c*,Zy1׌g$B D+ϋ#@w+p{_?  J^U3nƬsnf!@Qɲ0 @ibM2"@ @ +Y)#, I2"@ @ +Y)#, I2"@ @ +Y)#, I2"@ @ +Y)#, I2"@ @ +Y)#;xpy&|vK>hj+ ٶ{!'p;#PoSKV':gܶ˛I얀jL3vuCgۗF Gh%\Sށv9?]PM%6L )p΁.5c{7wu&Ռ*W53q~0ZNi{,v隝tJ}ғ'TS5wL`IbKKjj){ÝY5_txj6L4oE߱Pkƚؼ~G]gfEc֨ĎL Y0ofֻtаܦuPMI|;gWdTbifVCfyɍgٷuWi\jd3ɳ3sB?ΉT#nU5;6So'}ÅAw>j;!ӗd9n}}1svi)phnnϰ}'^C}P]m}{wj&gSF?Z|8mLG ܡj&X:dn@[@5we|n_fm߾#j&mXmn(_ͱoZjn+/W4:zOTS5 ::{ /gUN!lV@5wsF j @jfV؊$IXɵ @j @ +YF%"@) j @jf4Vr- j& fVjQ @`JVs} QQQ_q|໣:~~<&@@5#|-o/_?lr6_ݞ}ןWΟXq6d??_.>۾U[ݧvq[u{[uMu_,sǯ>al6_=AS^Ry3́YVްn߱ 0V`j7͸1O~_~{o"?9uڧWx|B[>7mYVmmUc&W:tcd>qƉI:޹ O> @`֭f܀o)H|ÿ=dus;_qȶf{:N7Z P2cpX*n5l2#7/j\4?׬玍tYDjiƖ0С|:'2L߬~!.}YX5!Hf|O|-3n,3yh_׬90wYxqH լG֙k*YƓo=Sӛ"H@5pƜ2~$c6_ˌ~7pd훟[5rL[`c՜6#@!;{q=  @TS5  @@V@5R6q ZTM Pͬ|ܚj& fV2D[+Iځ#@[PM$@YJ@Ɓ @`kTS5  @@V@5R[;qb pk @ +jf[ 8 TW/g?wGO؏G8i$@w"jF?lv~vvv{}g9 @v֭fܘ9e;Q:iLīv>pо3K^IS[*XaO҆su䋃1ۘeɜۓ:wRN5_7X;,+oUd XZfl=Q7'N*\4>l7֬>ۘ9wx~F;p?>PHI> N[k,=6M>FXq6Zf#?G8cd5xT0H{ZLX hI7tS$"UAG?>}f9nB`j<2cv2oNslcj焬3ěf=*cmy ^cWٴw5p&7̶O܄ՌY%_G]jgd紦=Bg3u}]uWS͛wh,6~Ȥ%Sb9??|{÷@ՙ훥 ;p>kV120c?IJ`jƎ'22Loځlb7n}oշJunvU:Ooun7ko\f_Ct@_#gI@5pƜ2~$cv}qcvn_s;g]= 0mT3&{hzlXy;ABX5ﭠ '@#<'\) j @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf @j& fV 0Ij @ l @ k[)NTw @jf @j& fVj @jf @j& fVj @jf @j& fVj @jf P =9y{|6ׇ'7TS\Io>_j&7"pᤖʢi>ѯ=tod=|ռ׋`:&,-Sy;ywW|TM6/S)% \Qq]j,l>΋˟P2=\jnbH &/uqTz5_/aRX̊߹x]Zjf:b}ñSW4#qɍSm\sD5<2wh.vNOLfZ5_<{?~_YS}8:G,2d+|5kI6:P}+==TML`jmxW٨wE5Z" ;՟UjjͥǪot q-qE暣yQ=4޸u*R;{0 @`jV/49H5\?fYwgx}//ߩOrʊ:+TscudJYcܙլߌ]ud5wn~O*~`ŷj4엛\tYrSuG3L+ǵk|˴V;1ռ 6Uͪzu~]`w\7MU|ݯǯýYVE5N5[ڛ+#j pgͭ_U4Q՜ƅWWwzaAV>VۛhgG5AlRM J5\xu,4-Jn7><~{:ejN«0 IK7jqEU5?9%kay~|ɔ3o)l^SG2⺊qÏ'|?ggY}w>?_26Z\|фy\jn"9i 9٤2l6{}_ ӬǟXeռߗIq|~~<sK|ުK(y\x_j2Ɂ#@aTM PͬPM$@YJa @TM PͬPM$@YJa @TM hVh D%7~z#@.iXAd?iPg#IENDB`WorkBench-1.8.2/Docs/WorkBench_files/wb_project_update.png000644 000765 000024 00000021154 10425464456 024045 0ustar00barrystaff000000 000000 PNG  IHDR~fU>sRGBgAMA a cHRMz&u0`:pQ< pHYs+!IDATx^=$ə3eq3?@}s 1D'gXb kFœ0cF`3zYCFk!3FoVTG_49CUV|>OO߿ߩOy @DO||R]xǾMHʯ>\]?ɊjEGׯ @ A>n$JEJ_x_A ݧWw{|w_q͓x JI)?Ի7k~e @ @@yon?O_dn%I#)w굆=UW @}Û.oe|طUmǼyKI/:ޗoof~<4}NiLaS_2)!@mzz? C=n~>{Rru5z}^ԑ]2vdNK@ IeLw*N@Nw7޽Oӥu^KQ7_('Ko C$si1ĐC;>pC&t}xqui&~xѩ;"ܣzw<e{a&T!NՉu ^O=$:6)^V9x @}{qbVe_^UI/$rײm{5jz'hL|Ux.'wS~1zF՝4r{1F{vkvw,zͰQDoVNv3؜Bҵhrz`N\/S@8_>ԻߌSw{O&$E$=,8_wNyUﱏǁs~Ɠ}K{/Wu9藜ҜpY%q8!)| ]ǻT5_]DG"7Gts|SFJlbM]c7oU]U_KᲮ)yK+!B@#:eWxl:5{d;~=kݻnԮOsۼ9m`cDvڗb49}f  nײoֲFv%MO|/}9+7&{)^'3V2H>l2&1 pz֐b偿_d>t/YaۛVo?;$6tyii`q,(6;洿YI fmѽdeޛݽߎ+Sڕd^>B@ϟw*o?{ :R{To6j<vgt 1EձٕJC3RNO@A(D 'zi;t\J^ j @$ o0I.k{,=D @XMf lz|/ @X^Y6@" 5G D K@(Jt zQ/ @([7s=@E PoQ @ԻS(%GNd9E P굝WVq@gN`feZqڨP6OcXxC@K䫧fD\qQo TN@3ҙ7dEE'rձlp Io2q-mH!@ ȴ`z: 9Q@R} 3!Pz339HtZ"PZ&xwu8|VK@_#@%zFO @q& @`P/_ @(JŽ ͆  ^ @(J=㤉 @`P/ %zfCP/ %zqDQ Q5V4!jF=:٨A^>` P380w',#8ŲȡCu@̣}_'̔dI\ǾzxA@g^LfjN<5@u@w'Z5ʴa1[쫶'  VʙQT:Q.h 0ԋz cb;PM& V%{p1AԫWG3Q 6Hz6X+b̈~!N۫^]ozۏzR5np=SN?rY3vئ ){՛yYP1> /c1v<5@u@p773u9MW6^⾒z}j_:BjS;ѹmO!zQ^SN(llv NJת2mQ7Wǩ~D1Hy.kJ&zQ뷽QԢYfW XGuK[H ԋzK2AGpQ0hp]:3W024Q:cBwg^O[~^of9:` XEgџ-qaZ!P/E}s˟ u^۸z׹@E@QޢA@59@r{$d()2 kozQoS]| K^@@Q(ީ @yB@Eq7? K^@Vޘ=.Qo'\' Jȣg~Sb\@Qo3 qmdSY@BNGS)Bg!@`^n58Y _bzQ+7 L E)vyWE 6f @w\{3A&[²A Fej@g%l:̜CN`wp@^@ ۈz8ҨK< x/13&|)Q~iJ.^c#+^?K!}' Ի {L{O/S;ٵDٝNQ/]nԠd@`"MI %WjlCAuv^1vqs1z?QċZ׉vIt\EgQoZ˙ؕ^ff`~ɮ;mS"XZ#B#̮^'r?N{Lb8rmXLuc%(:eb6`RDZSj/Xu VfwjwZgqXtr@Ng ܦz/K39rbp7 m'b!_ R1{3AW7)G2 LWĬc_1 L;+$~h!Z^ֈ9iWQoNdtsN2@WR,-!rdfbroX\я_A 4SVo~^(]bֽF[rCkPO39cS%!{;qfOHO '0]N?~ӪIzcגv^6"Kf$ΓDr{^D$@˙\<>rRl2γ=|*P/m*F#T  "zQ/tɐT^9 TNw67yYy\:u.J~@8EͪqC79S ^ԋz!@(ԋzpx@@B@Q"y;R k ù5ܐB G@z- @`o$9a P/ %zf@@ P-\/@1k TzToΧw@\T 4vvd/Xk@`ԻWj_.F.rA&Y PjIN2g̔ U͘_;-s>P  0HZ0O#Es*ؼ %uDۃo]Ԡ6\G:gyLVϾ~2p-yF.TN̞өUK^loW1KًSfPP{b#D! YkЗel9')w2҅F̉N̞CT1u;9=rS ɩwtd4P !:ioiv̜ bcu"rGK75hJcM%#fL̞CZJYam; mA Mw8hӄbmɽCm?oണjuiqNaE0S3Dc`^'6DNj2 t8ؗbI4Ubnyڝ洼$4]s ;c z/= "z^' LA/ Ǥػֽޘ-Ap{GCߣ=1]k5X WbC@Nv_<~c+-NMbD1o-tsMbOA^!q/ԻzlWGiktIĠDAtefASUMMĐZp e0\ *2]0\zz1MD.YrPj#ZE7WAja#ƈDt5 l-qȲ ԋz]\xT 3ԋzQoSy8 ԋz\vB ^ۚzf P9=3 6^9AqS۔w<6D^@@Q( Mh* ,D^@@Q(&P @"zQ/ @([&e4"zQ/ @([B( @ P-L( Ύ.^ g9).%`~ @|n z748[Y_РwD#;rJ0OBg;qlB9jIS&ꝫ%1^5 >4gK ޜm!mVZX8QkҾ2(k)XF!!1O+tWzc jzO,vt($PICz=z;׻B< $ԛ5> u?3Ȉm2\ѪjcoX 3gaD9klu2;z{#T Wm՟FƸ z5Z'E˹GzY*Zp-SwgPގ|c@URBt޹\zӓ+@ebs^g=rZ\sN4X~N'%6`W? VOb^w JF$LWoLюDY{WJXDlB|߯ՒYEw@*Q iɦ~C$*@c 7su}A@[rQoc#݁@7vOw;z}C؄Qo6͔^hZS@3&wШXCb=MDG 0+יoΊ~9Vڀz!zQoSq0mfl#hE A5@59@r{dy+wYy\IP6ApzyU4_#1 P/E PEEOI P9ԋzQ/ % ⑷#]>S#p.znU>y" rj{C u5^@XE ƫ;  41oY @BYi @%K} qP/ %z7?" D^@@Q(fm@ P-{\ ԋz!@E ޢ[@G^@E Zd)X^M [tFh~gkFz7IEw7Mn BQo{1AJb[ӅvB]^9Ej!z@BQoǵCP/mVX@Qwzڟf#S  k?diL7JXxW\kSe:(ԋz!B`Q-kwNIBlkwNM/r5@`So GUX"N@XEE,^o;舃M@rwƷYF~\a\,8#c@XE4D6^w Po-\q zﮚMjzzQ& ,^c_C1NW۬Z L(ջջVsVi6+@(;۬ꔮiz^W5 XEDX@QޢmN^ԋz!%`+7.] {%DūO i DQn#H2p.:mxkwtV _B'"!@`P/ %zLCtBp뗲  jͧywFA PA b!@ q^ IENDB`WorkBench-1.8.2/Docs/WorkBench_files/wb_project_add_project_name_new.png000644 000765 000024 00000021451 10425464456 026712 0ustar00barrystaff000000 000000 PNG  IHDRgyU0sRGBgAMA a cHRMz&u0`:pQ< pHYs+"IDATx^ݿ$}uP'?@ P D ġ *] X%up`/P0 L`L;[TLOUuSyzݻwd# @O~=i?g楉\@ajbǛnz'3͟ ʘyU6LlUo7uD.S u00511 Tß?埮^^ @% &2vL\k tjˤ^]]lN/^7wRv^]'TM5_Mo_IO>K|Gן)͓f_L}LL|yo_ @Rn~}~<4u3⃴l*U7/x2 @ lg7?|o?/6i4gg6ռc/k/|􉦉M Ϯ}pO>LGi4fnq5xN _u~p.fSz&?yOKmyRU?7GOg|{f)X?J|nC۩O/aϙT+JJzK9}_w3H_>^}'55o]ۺf3 @ l7LռǏy?fn.U2о߷/jn8>CӇeKޕlh?nl{ܞ)C`S< gj픇ݔBjw*򡚛?S[Mud{^kӱǻe5o߰gkg   @`}|o7|(Y8j?MҲ=j~&ݱ}e)֮ -;})]w؆jX:/eow$E{ O囮tg-:ɃM1fȿ|ݯvpԼbyԮJU Tvr'Swq--YR+оJFÔPjK~?*V\RoO%^f&[N_Hݖw/^LqvLU_[jC5G0auޤ|柮:vbpT)U-ז_kWXȷ @`.rmNh﷕fJ&iʯ2G(tl}Xa_ͯ޾*u3? ~ݯ6_pO?cSxj @6wYޛ4}@v]4ラ|&z7_WWoO}}|V?>Ϙǻ^I~5]qn]M @jކssӵc~ݧ"rʻ;ofNouס7<|{;O~_4nwVhw3dOh ym7~b|{lZ2*pwRNM& ,]54ui7}S+n1\H8o|4u M̟rtWywS~7]5<8T00>}뽴Ԧ}L|fMw_Ƿ>X[|SuuZ)|Gz]8wRiپL7Ԓ @]8wRi~R+5gJH|?/6VQ5O>6 @@C5MtyR%V>D k PͨaPM$@QՌJa @TM PͨPM$@QՌJa @TM PͨPM$@QՌJa @TM PͨPM$@QՌJa @TM PͨPM$@QՌJamv{n"OޞmmdSC`hW!ޢP|szpNg%oYN1+N+cӪNr=)Ej.^уEwJj+P7c=waǖmqid˱^@Ec={GF'A=_ 'phT L๾q7x3'* "[lZOޝC4\|35-5c5y/w՗cc]zvIHfd*/PM\@c; }2 ޽3rX՘ۮF<J@5ߌz=dc^ 2nk1qڹz[u XmƑ|:hoI^<Zͱ:/.C# ~fھ^U5> mW3r/w+\@5ߌNj֛7'rր5<q^_6`Gh쪱k?9׋O{馅'Myf\+x5YQ?lJ-|jnl/xv]b&pܘ,]`6ǟe]Z1ڈ܋_k41m!\|3wJK4dnc Oc=u[#˘RTS5/0c5P5NJ3[i,v*NfCu)'JIP7ëyjwe9ךŃ+]<76X{эB{Xj&zM@.Ϙ^FTk jMw.$b \jvƼO0$@`ev񜕝,I@5w;cz @% j @jF.yle  @PM$@QՌJg @TM PͨPM$@QՌJa @TM Pͨط?}a"@ xNC}/ \Vs'33XךyOvPMDT3*` JTM Pͨ*MvvPM$@QՌJ413R@5UDT3*A"@TS5  @@T@5R; FLPM$@QՌJhд[f n]!pyj>SGJ5ǂgQt fW5Us}oy{D^'NcFm^&@j>~?NKD Y>Rʹ/e+?f[3Ts~ͻ쫙iX1jN 檪ծo^n'o:3 -?E9;f .|a P͵U3b}%ZdOWqq9xY$ݷx5wo.fWx[O+Z>L \j.EX؊PKu_ԗ}j*.sjsFnk!SWZ|{(p^In^j.|y9u<]F٧Ey1j^YTҫϵjeMf}Yi.صf{c"lmo0k >?;j.lXNXq)f#Q[?_Ku)?-QMgU&Te%\ޙP q.g|Ym|**ߡ]f pTM<S PTM\f pTͳfz pV35J}⠞`)  XSfyP}e  p4)#suLhE\kefXY\u' U`>E0"6?D;XKpCP_m \V5VD<8+Y7/l;T"uWٛ2Lqu8XƁVͱ4େ [588!@@\Iqc1؅i7[~5gC28"j3onQ ;w9 8f},?vorN&cưe:cSc7j*gKƕ\[WgqY_ixF##+8j5shkY}N/֓ץXE6` [E3 `E9ry6c p3x%7v1Zh[|]w > @ (j]hn5UgEqlXH\[>+jUԿsVvgN n<3rºuw= iȢ[G%>8vi`o=]60xYG!5V+a78tBXGCTsa^{n=0~ٗ۞n\!pL\X5\ Pj @jF  @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @| Xw#XtyN.V`k͋ @@5i  PͨPM$@QՌJa @TM PͨPM$@QՌJa @TM PͨPM$@QՌJa @TM PͨA~='k>Ы?WM$@`ftVZ]5ZnVs7Tw|Us3Bpm6+UsWs}.R_\w/Y;˩:WZS/ 8j.'8_,b (3bsUI ΰ[K0N4\;; z{X- >hj%l;kNR qD5U^lu?ᚻfw!Q:9e EiךcgO\s2QͱRλw؞wf}Ǵsp@`ϳj~A9xJVqj>k͝YȬZs1:  s,K`T}ΰ{X7ܯs^<l+Mx7j p_>sc iYB5PG NTo !@`j<:x=5ee~rs< Zm\{UyXyByy9a5mQM$@yahlq5G5Op8m#p<Ϣ@Bΰ뛞OW|6 )Y rQוZH5}|US 8Zs]ӃlQg51 0;Jwbm DTM PͨDPM$@QՌJ;Z6Xj& fTjc"L j @@Y~2=d"@jT/-ǟth UӰ?ZǘJ'IENDB`WorkBench-1.8.2/Docs/WorkBench_files/wb_checkin_window.png000644 000765 000024 00000046030 10417734435 024026 0ustar00barrystaff000000 000000 PNG  IHDR}yeh sRGBgAMA a cHRMz&u0`:pQ<KIDATx^=h\S*nWpn-"pC E4ƤE)pPR"40ȅa].pE =YF3e9{of>3g5O!@hB`s)tmqO᷻;?|H @ Th4]<9 H[^k~}=hf@JOVY;w|kEߏ  4! =J[Jm5ɯc~Z6i!@pVÑvJs1㽟  y ޝV J-#{ܮHaR =z6@V/N{g N)R)B+Fv~ܗ=~x۲8R))tG@<|jmV7%U J |7 &{J`Jg;Afg' @`$zNPZ[Vݽwǿ쌟쎞.ӣ>Wr$GI@hqU_ AN#7o݃]UЄ&oC&'ieJYr@t,@@&+'+Xw,.oD9#޵WJY=*Ro\7?4*;9Ŷ\~|z =A)SHL@*MuwY)/Ir#Fwޙw'M1֧GnӁG5նߣgGO{=^r+~kKn8ngSĩ; @KLnJimQ}#)#I$_]8&:Jm898/\Z]/=~~(/`m\b~^E88~v8 @LUEğ\ߵUH,<yF~=^$iTmtד<~[~k~(3FgG-O/gX#6`gq59 @sB>tjZFY F'Fw/ɪ'VwTJ󼬻ZwVҴ@+v7n>q1'ќMst_vXiR#[ϗ3QAW'*(t!8T2hmR݉X ƺ;^tM\|T:U5>ymnpC;;#vNU,Tya? \!* SE5_WVْF*oD>-`]w=) u+ yzdhzM`+Xyt+׌^Nb?;X| bg&dD|'SK"k8U6w߈zwH ( Rݝ0d&qYS# DH= w&1imkZ'' ]]Ht]+~P-eu}֏tgP8a]f @F`rOX.cv~T,O^]J TV{tk׮wmkWVũK(OX3oۿpiE +3!@`~L~; Ow3_TJk+)}_3;^tlUq8ٶMbAUj;]gwO$WQKY+!f1G.׍݋N3dZȌ58z&U M`:q;؏=t$RZ)F|VStX:(ydn穤Hmuη~ DF2~9Jim.1~`u7)|j+oy2ԚTwuw<>#@愀$w2;Vo4:*5Ml)]O^k?'޶/D oE@|hUjP*Z01bjP*W++/OI]!@` HjzagFvP*kňZJi}R۩+/X#@ 0;R N@!Kݯw ̂tBwˡ v HaR:3._A|.n= @;nwA]t ʺk>u*| O3~ t׹_`a}/3BA@S u{4 . t_-mzKu76  %&TwXƷM"Gp@x7U ='E!@ T.7C5o,@@T^q Y@w{', X]t t;֋5# 0 { @m{@%L{ |wOC @@w^@#vǚ@B#p' `ӜF-(wA|;1 1!7 {[k?X[;eژg!TgPJKӗs8tNm6?کCЮ>=TQ?]_dmn*ht]Aњ0$- . Tݛ v6b}ͻ]YemUIJ+EN/]XˊIfݯn5x4X&A[>?A!4~xW7nx3TtĄ p4הu7z; N~}rW_BԮ3+?}&|}W9P)y5,GvWF$M_ɶi\yO߯h$}IB@)n ŕ>|P22=k6߿vJ)3{ϓ̘땕^0>vm=A I9lYUyKL{ގ8ZWm~Q*֛ I;5qFqLn#O(BhCw }\O{?z坛߼zJz7lmJ; [oi6Dzn61*(nL9Z::uyVeJriJ;>>Η6z{8޹-Hoiru񅃕vПQCS  Y}+jT*;tw: ''Zw/q'v1YЪZ?PsEӡ@(OeTNOtK7_Z]]@M*O{RY 4xJyeP/AîW s)YIJ+E.W\T43k!ڿ79Tjbݖw#]+2"S:Lh[hn޺k 3@j[`4UM »_l|GK GH\'_IHtW2 q&n Oia}%T^r̓?OFcގ`82wٵBlhvvK]PB;1w~:"Gw??SM'8Յ^?Z,S hx ]nNp}gvĖ" T]9^36V\`Bكwrή8@AuGb9sA~qi](SռTz$R򮹵ju7l?>×'GGp|5tZBk59Bd-{UoHqvS1k*xNwXY9x2%},s z+Vu_o<#sֿ*NeW{VqmVT'SIw}pݝ8yʫRR|G/;3ͻTWUnrdž[.2K ϮvtwkVG>0m{] RB-\Zqmd5xri*]V ro|uczd ˷VP'Zx;>x=y붦)+Dw'eCYE::x)~`T*d|;`'R̓Ƌz5)יa^i%S@3%P]w\Um{%=:esõ+w>ljՔ߿Y+q_{yw5iYU6r<m9^Y ؟ s kp'>=vrk_t[Jl7Қ'+Y5?ߘʭMsqHrXG~uۋ>DweXwuT\wX2 .W~V.JVoύJ F7cm7&AkkY{oͅUm}mn(k@M 5W]ٽ7z9uc\OCђHtuC_ZrqksƗJ}u{F_=Ԑ讜o~<ӑY<+D}TNn~-nʮw;J_;(]gU7:X2XA^ܲnZ?ѫw7meM[Y?nwQ%msM]L ] cB⮂\}-r{VBq%Sުf-&P]ܽ<ut&VÛuek*hU~L9¸׼5EhlZa4&KvHj-ͶήYpLVJ/*rʜiY,$6! j;ßׇ?~mց%UuW潆_ߵ/frksL<:yhOZ]h?7>bپTv\>opTW%\ܵ[,oỤ?=bfH70Z牶μX*>1|4¢̺u)no.*UUG¾o^t+Q1.a֣J֙{$lLw]8|>?3Յ]fVCs벹vT+7 ċVwK㮝a^U&Uiu6fU߶Z0}ufKEWCպ>t{㍓o<|צO6kRJTK9hw/Z֋e*it;?!]_ihZ{+M\M)龪&1X}`R+-`U]s}ѻkK %{;9l{dэg^J{%֫[3F<foV07d5[J WomIw_DŽn5}pϛ'>ag.8[X2{R}7-I{3 ߛ&Ţ{3hYt+rzWy<69q&DsX !;kٹy_޿-_iU[wԬE*"VKV'/;kI[ `%)9w[i\lsdZ+s}/kQ!]W .~OѳKw>2̻{"u즬0=]9!h@M-^j6F/d'k#t,Y~f[m.$xsO4(/뇣WFwm_wV0&"#>gnk]J^zH~|Ar;r!wShu׻E6O}"_^]]h`|'Lf\U]@tQ;gf_AܶۓnJO'^V%5or`+\^w? 6}vE͙>ITŏN~0~O*nGg"1!"k?< :-[}TK¯OU@QYW#w˭Hx9߽׫DRwזm%=u'PSw2~:jlANͧ St$^ɭ*)2lfa4-m5VMv-,=?y ^sf$gW{mjM>NޠY<63?LUeɭg'zoֺ&GE>M-5_mjUjZ5u u%_IfabmyfC2(I sUpwm5{4UZŵ C6l&=!DkV?A%QbLkKrkz _@lDx]Uypӕwfȸ UV[ꮕލO]=#4p$7Ws#Z4K"$CLrXoa~6JR_獪Zҫ.Ӯ?)8ssMdY=rs5+JzlQ.͟ӝFEcQj{zw(wlVgvVf.z%fjo |~_3~xSOaܼeg2>ԥ 魃  tW"'_J.fJwk?+_m J9lHY-Rwm\gM6KsN kʐ6S;uȼ2H'˧ݺv uRVd> @ j5̥.oܤWV\jkO?ޱ/e;@r $ȷk I @@ta D. @ĩ @` . @;nwxF @ B#v:q*D4@Xb. @%Q5@H$ ޯ@` "A>Ns螀Ni_w/9BU-ȍh/|@>te .C+R~@wr oy]}˽E v-XjX߆S*t0ҶHkLqq1:VpVA9hD%|ϴ⺤mfJj'B4fαRL]su`gqu7$XŴ3O ^Of%S5,0K2⮜@cV(_*Ŏ?ch%\0Sjԥ(JԨTS3<1"8]by[[w띕tⓢh7hH+Ymiq'v`:y,ݔ'fY Gī*yZgCJ_M ]]lmj;'ۖFNk[?\o ̑1Nwln˴`ͫ#7Z"J#e,ygXF%/Qƍ;'8yw3g?gkAZfuPfM(i1c\E"Ĝi^JJ8w*4Y-y6,~Uhւ[Zih▼E+nڬ▼F3uv%@^bBTzY h F@ Wddn2*"B` s8 t,twZ:@U 0oW`5~l!@hB`{5Ӗ~kQB Iݝ? @({7t, @`Y ^ /Ϗʎ6JtwN5{#SӅe fMh>}p݆a{[OW ;? ^$SYwJCXb]7{G/Rէ@#wkź b4B+O ѵ$S28cݭD ^^m+Z[n .va @]^C] @.y]YW>|Ȯ3F㔀8 @w/7#ox˹@U x|Cvf|\߭l\+l]ED.VwO^$6؟t7* @X$ V3`;} 6at7)q @'GZ:Ռئ*ˡ NnݭĖ y;z;.8КwZU?8 Utw7]^iEhyumR׵k{3uWr+ DW?]@J OwONGArК: efJDt7]tX"C%y݆vS{ {@ZC@^ [ Wظd @sG WwGiggn/Nߒn~i0;s@fHTc^݃w$ @wW>w٠p %|H1 ^ 6j6th˝g㳒W( !0<[g0v|`Ot @ "0zޫgBȻJfYJ@($0\ؿh8x}{skQIxr{y{JqMf^@Ph[KM݄5N@%[.cNOICPi&%$ݫ_gO7Ǥ,ֳ9m1eGoR-4ş@*-fQ--O~Зj=HwGlG).5:Si~yȢ*7!P'_pWa[$QTM,ߌ\v}ݍ;Lu$8BNöhEˀKKNtu^?qNb(1Dq?@kj~v "Owe RԚ3t3K>GIo4kPD7ضGnfYݮ] Γ~ʄ)^ qJ 6nZ:P4l?S\Fw"'EF[pu2tQz^Ϡ ⠄4s!xqKX'j.y[uhn/lq2$Uuwձe]+W=ݪ& 坂FvE e]uVLm2P4lXدGKૻZi5߬3Z2-4nҧsy۬@i\uV@Ѱ-LkvݸסIg.Xq=G N*Uי JAdJ֫bg9EöƄ< ĵΛ#UAS" t7Iwg\2|,:#0m1e L3BwԷʹ#b| 0OC[CEd9tt?}j>^Z@b ]R}_~;ruW k ]srw*   @@8:\`uW.x|6F;x5t=k; @]: @;IĺwVڲfjF 7n}k"E֙՟.솵ʹaA-\U-8Z/[o|J)qiӤǧI5PAy ٱӧ^2ǴxtU\ZªbU?& ͔JavnJ:3XYknSX-є?O(gd4"8i،Jؽ982 vٶhwNАЇ04|ͩ3#cYA;鲍ȫ@CA/ R]4!l*P̪t҂r6ce(=3Gт!(oK^#ID Iw'w\{F ?& P^g͌yQ!3肢'L~7f~iUa :m~} :pT e wqբϷػ21.v1dV0q#3-.so 3TmM)28;],ct!]jz92"})n@wޘ7Jy3*oWEx*\%ft2x@'sPsRwx evRM͔ 4y>U9?ΨJUgP|"rZŧ[JWL[*qexJ^)e&NI̵#yn:-=[ǣLA͔üwAR΁Rm.`J~uJGٵct7)*^Z31<)0 Ա{Ŗ טNq&Κ@6>oEm\JbCۓ$s{o9 W/^Uݵ6*2OY6㠋sSZ,2?Qw[/[J<nFS\[4\{@:h8x}݃%GŽ_6韶nd]Rg-Ew}*()RHĚ* Ą Ptw燽t W5剗^= T3Y,'HqYZF3fU:K [;qa^rOVw˶$kg}D +w n\LkAvqɃ⦌ g=A2 PݓCI ڶ!SYwfv]֊ZӪ缇S<@Nw/ Nhm{EZ"kCU m-; 'Zh?L@KM _}ݥ T /.ߠ @@Z D\w' @` n,~=-;~F6㻦KKx3)q$mJK\yJKH@`t1\J䏼)ۉzR ){'6Nf)(m\<@/t׵P4EkSqVR5Mtd+@zS'5=S?fb' 7VwON'w7NMdGtI23x/]+>EU*(= 2ZF)v~vyW0Z\3?l 4' !aw(WwC @@w^@#vǺ @` . @;nwq^F!@]. @vgLX E$ ݱ^ye v ޯ@d6 @" Fw{T!W1W2d㣻 0%`utw6( AS{]z @&靨oV➜:k=5+,L!-oT%fJ \; +$2 dXw'{;}}ܦBnR)qfѡKu7/B'Z%&AN؄yJפj@O\n;Yv>. d,NJ W7(FO))֥@̮Dv!Lݕ0jrwg#T@fH[*2nd 5R`2QR(fv@c"(hК)( -20yqz8Qe\!s=qݩNooKMY:S1H^ Cj,HM$Z%OJMvI4Nt٠AR9몪@xv)8kp$"k]Vw}Ob{"-փqo ŃK.QfF)vfi \w5e`Rf6E~s.f0.v1甮XR)]ݸ6f_z]F +sv3J*zu1` Nɺ`4/IU5%t u7]D[w3'(2> HW85@]ݺlvHn3#7SZ[i|*-. q.CyP8f`n Y̙d:aҢ1FkxܸT:I`a@w&vM @>>u ]'?@3tϭO!@knLsSw@5q >@w&vM @>>u ]'?@3tϭO!@knLsSw@5q >@w&vM @>>u ]'?@3tϭO!@knLsSw@5q >@w&vM @>>u ]'?@3tϭO!@knLsSw@5q >@w&vM @>>u ]'?@3tϭO!@knLsSw@5q >@w&vM @>>u ]'?@3tϭO!@knLsSw@5q >@w&vM @>>u ]'?@3tϭO!@knLsSw@5q >@w&vM @>>u ]'?@3tϭO!@kѰR6ޯӡˡiiKSJ Lnmp0^EPLAwP@TwߍN7RcVqONn-Df LXw'{;}}.S. ) t'Gn-Df Lvœ/SWL}@@w=~~dعco.]ې ,s=Jbmp76_Aw tO|Y.nMC ߽|!Sw8/KT &}wn #pYw/n`vSW& #.uې ,LKhy7{eZq>A*C`Luy#ݽvZAFJY,6զgAP_|L#U-UtK6Q nXQ I+@j`˷:sf)Dw{8 QeTwu7k IjX `kxnmm^YZۺlSKhq\Ogጻ a|ԷlgfZZ੻U0{w{5RY8Vinpđ6!7sS Ft7EQ0=3N˺;z;jew ɋXs W9.1I`NcHʰ90[ͦPqHw3JS|fkYwzCTPmm;pIy' \.Ύn$(jfyET/aL K!`uw7i8#g0NcZRBQ]T&nbF)p(*g Sި S'8vۈ!?BqKF~AQROٓK;]_yヿ?sՍFr6,'X(]JjÓ(BOv`#$mV7[ZBJKk$n?\j ;O;K3 _CY*sؔ+jMDQ>Qjf+;w=A ,|R7eߨyq)h6t$0'f>1'oǮ`O%4dx%W3qţzb&"m>;x5$P%Aw9RkX}b.Qݝ:V*apt2./I.pڂ. -Bsn?\j _w$sbIE*FkgG~?;GԯoѠ:תc|R<§'rjW;5W[8rgaXo-MPi˩,%#|GG{k>sBmgމ' XɘC;z^,Cg癐kڻ@@ta14Ru ]<0@@wX7%aB#vzh] @ :_u^?m7 3}  \n4!@$EEmIENDB`WorkBench-1.8.2/Docs/WorkBench_files/wb_project_add_new_wc.png000644 000765 000024 00000022071 10425464456 024654 0ustar00barrystaff000000 000000 PNG  IHDRgyU0sRGBgAMA a cHRMz&u0`:pQ< pHYs+#IDATx^=%}qP' $Pb,AX;(0e@0 @F`,(X t8ꮮ>Կkg)T=;w޽{?\O>|t?OA>gyi"@,jtL?~]D}A2>p^}}&_]ovs뫿\oM @2RS SS@Φy&zۗxm"@,j؆3yLc뫫I߽O;lO_x/ߜ;RSv^]'Ț&}n_lIO>K|GןԔIs~QZY/SS%S+|W& 1_|p_6v_|m5R%{/^߯L @`cM5ÛO?7{~E3 |~IsmS%_}=k~ߥh @R??7ifhIs6f& 9T'Ϯgڄ3u7W5ɳ4Z=HQ߿(_^I?anvR yj趝?ކ۩KfaϙjٖWLIJzr :ΐ~A4 hp͓Y5N\Ԅ3Uvz?N}^8jf?MҲj~&}b0"wӬv,?lmlٮw68gsg΁}(y{QvИ/{'6R%ėojކеl~"M5PWo+PE;CjgN/bkvmj`B[H{jNmT]\gKKe;ʻOh?aJߖlgx(Sj>˖v~-{HK짾=@L&u}y /vWw]/dolLV[[jC5`?ȽI毮'?"ق#`T|Ūʖwk?vk.Wȷ#@nm5SپG}]Ya]5zaWvG\}-1? M-|ޤ{Ob7uy^Z}|u_ͷ_M5}qۇ?7WR\~˕'-l^m8]:/G}.W+}LWWi^n;tz÷?vTӻն+λWz{4sz y߁rZ2)&@WbtW:MCgujzy?sww0__,gv.#z/Mm5CwR>_nnޝ7=5=8n*Z^Z}S+|_ttO]Ӫ)B o^;Ҳ]?%  @ _;ҲݑV=kϔP 0&ZP= p\G|ɋg?DLڦΫL @@)*ٶI& @`P@5=X @jF  @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTj @jF @j& fTjK#'?ptiNa7t7ڭ68ݘi^s%U.nG܁Cnn^)NV0yW&+T:{ލcl\sp}3야SPKf1~>ez"G'^Cm딯SP͋ffi?fa ˧YOG>۬܇ 6mm,WNhc]y|49g:\J:1[T"iFyՓ xnLdrI%etlPR\&\=58[03ˮ~)}&?K߭ܚ&UYV&[U5Gcz*OMGg5Ͻcot@-rddmnГgDKe$J^fOY@5UsT r[D%{, e=Ff!4(UY9)|֬XwˠAf]ރ{ܶjt5'Apق[/2뮚A`bgi`Yً̼`ϥP˪dlo3)+Ьgw7)e|mYWwC>k=SԳfJIT ETr̞vX͕ݚrq@YEvyeB N>G\oVb?k.)\pNW GYRzYA?c'7X>gz*xuՃPL+ac+ynU^_9ԓyAnЃF5;}?~Can=-&87ZvS:Yͱ1BJltrn"esP fvռ4Icd 1ͪfEr 7era0ٲ0#H&W@ir^ ?}ռjq]'oUe{|Sͭخ~5Js^͌zrle_2On׭N_@5/]K8,ۓeK]Yj p "λ}F@5t8,p՜!I=n:Usإͯ_\ڵx i~j @jF.g$H  @`L@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDV/L @L޽{7zm5CW՜13 @Xyo  @,Ռx  @` @ *QMPM$@QՌJ6)IA @,TM PͨԬ  "{Δj&R{n69q^5] Kef/ᮚiX2n]H}WֿLؕje5wؿeT?2O}몹 +ՌITsePƗ|4,_\ɇfümeGfSȯ|5n["pDUo'cN>ɧRٟ,ܕ5լ?u9lG{YkO. PT>&L`M5G[>s(R99 \ծfʧɷE&i.G`q5wucV@5+O_h?E.Yf쫦j @@M U3}Og Tӻj+۞TE^Xg\L"I:q$@ j @jF7rf8TM PͨԹ'O@5UDT3*5 @j @jFed?  @` @ *Q\E@5UDT3*u. IPM$@QS'O7d850kOGllOV#>71Xr'I 3\ӰĂfG].|k]fV=\ڕG'2sMq=_y'xd7s9Ǭfr̳5Vn vc)XT| +xp+f# V5ˡk9fٍ~׻evۍ ۽lWn1r.[G)sԿN^}lZsvd7+gXWeݿ/f'}|wkV 7yyc)Gcwr3F1x*֝l2b{zy^W9oǬ5dwòDZlu\]g\؎ :_U8jF㞧`n{N >N.L\6s_p,Byf<c p @ *QK[9vhTS5  @@T@5RY @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDT3*eETS5  @@T@5RFX @@5UDfW/L @`޽?OϮfZ @Vv\Mj @yRf7 I2"@ @ *Q)#, I2"@ @ *Q)#, I2"@ @ *Q)#, I]lOlz7~뇯;_ 7WVd57yrAW5u _핣;H57|~UsM^jSm(Tӳ^ p<Ӛj~qM`CCJ5US5 8 VsO^3Թv\Usy{Ow5G(O fnw,>gФ\p'k=j&Gy5ꬿl5wuh1 WG_L@`;fd V{v?uwYlyfG:̳)patlXIvR^v7zBv߮L>-96X*b٧փㆱ'v]:l6g*_Ys(;`أU֒KbM9[L R"P3xTD sC壼e%kj|֬7rl$Qz<]c0R!jvdpvuW̎n';_niʓYS/ 8jb~ǔ5=U{)+" =U5'̲!jjα{rjj&Gw5Ϛp2x__r<w;8ay~yF5M 컚GD[_#S8 5#k6yŚKֲ!gzUS5U#\H5v{ӳNu5p8ӑ&tW^N#j)Rm_TM*՜t5@b=?yNVB)ooT PͨizR@5UDT3*uȱm @i j @jFNsc @ @ *QCelNS@5UDj~ۗ% P Ji9 @]5 + @P@rtIENDB`WorkBench-1.8.2/Docs/WorkBench_files/wb_project_add_url.png000644 000765 000024 00000021301 10425464456 024167 0ustar00barrystaff000000 000000 PNG  IHDRgyU0sRGBgAMA a cHRMz&u0`:pQ< pHYs+"*IDATx^ݽ$}uP'?@ P D ġ *] X%up`/P0 L`LMM<꧟,2S/O}O߽{I׿?1d|S5_OAZ>gyi!@,j]tL_~]D}@2>p^}}&_]ovs뫿\O- @2RS SS@Φy&zۗxm!@,j؆3L7m뫫I߽w;|_뒶JMLelyuXR1j_~ pNYO?z_NZӏVͶ~*ZPW߾ @3H7ayhiWiV#UW/߼gBLϞ_~xG7wϯhW:iʹϞ5ռc/{/|M 87T>/>?CZ?Y~u:if6_BN _uZ~M8S{KzR؟%0)05?ᗽ9@%?ėojކyӵ[?l~&M5POOKWvN7g/?`mS;G @\R|ヲK~2?U{Viێ%7~Oߨ}Xҧj+/v#郻j~dO?l,V[jC5G0nqޤ|xOޯ3& [fїjnonc  m ZV3% aZw#iyGMmvUo/v5jWmù?xpph @y 4~7io?ul-iラj~&z7_WWo_&N~'ZɝdNwↃ)@[p6ouL/}iIVZyۯz,N}yuV۪n۝?# =Zyx@z-Y, ]K@F+KSnw{34_7l;Zy_ͯnvsKW͓A @=V3|Y?-rV]5rusok|G4׿~`ja|{i}L|ffݾ~o?~u/>8I iWxB/߼lù뒶Jv}|f$@ uI[m3KL) @`N 򡚕?/G+?y'[ @@C5ӛ+  0Hl[ 0)n  @@T@5R] @@5UDT3*eETS5  @@T@5RfX @@5UDT3*eETS5  @@T@5RfX @@5UDT3*eETS5  @@T@5RfX @@5UDT3*eETS5  @@T@5RfX @@5UDT3*u3'_k4Idtj:۞fb'әWa1L~u<~ g|RvtT͜@˷=Jp8M W;D5/2M@gX]/9y%ڕUszyTY~K/ǃ/Wq<{zT3NjY;j.Wsⶃ߆k=paw2YN+Gqom[''}wvj.Wss_$Z ,`\킧8|6s^ze8N>F":Jd; i+jfJ8u۹jt;]N0{5V;l围xF}Kt/F~ T=4P-x+0xj;Hwbc/әSu|'檙U~Tj.֋=jZrȉfLK5#/f՜]>Nek䠲ݧfEtȔ[g<|p-g0`~0c|CgҿK[{Wr\&ʹMwGY^_Kރj[qMIGwb5έq_CǓ _&CҿRW_"g8lGË \29ӕ8^s=sQϿP/9xjFޑu6~eޏ}b}{q#:[Us"ד}/oH]y.3aEY'sIV8{T?|)ٞ]wyɬnRKމjtϬ\K5j:ɻq2) |fw\/s7:GV3ӹT1#h~ xB0~m^(VJٟQ=Vi]v۰Ǔ_6Sḁ4ɧaj>[%).בd'Sbt=7q1x ;9G:5Uլ[ f{9_ M Ak G\ {Ӟ_\\)]erz!(w>3!<ß!rr!Tͭrwhghp|}qA~+>qc~u#iI*%YſC{Zo=(۞OM jn_L,=\jnuw}}UMΝ@n+SլaT.Y@5»ܾj!@@UiҰU9.y?}5/y> Ykչ깱M@5UDT3*U|x @j& fT  @6TM PͨTm!@ @ *Q3G$@TS5  @@T@5Rw j @jFhjPM$@QՌJ61(/I*?qDM@5UDT3*U|x @j& fT  @6TM PͨTm!@ @ *Q3G$@TS5  @@T@5Rw j @jFhjPM$@QՌJ61(/I*?qDM@5UDT3*U|x @j& fT  @6TM PͨTm!@ @ *Q3G$@TS5  @@T@5Rw j @jFhjPM$@QՌJ61(/I*?qDM@5UDT3*U|x @j& fT  @6TM PͨTm!@ @ *Q3G$@TS5  @@T@5Rw j @jFhjPM$@QՌJ61(/I*?qDM@5UDT3*U|x @j& fT  @6TM PͨTm!@ @ *Q3G$@TS5  @@T@5Rw j @jFhjPM$@QՌJ61(/I*?qDM@5UDT3*U|x @j& fT  @6TM PͨTm!@ @ *Q3G$@TS5  @@T@5Rw j @jFhjPM$@QՌJ61(/I*?qDM@5UDT3*U|x @j& fT  @6TM PͨTm!@ @ *Q3G$@TS5  @@T@5Rw j @jFhjPM$@QՌJ61(/I*?qDM@5UDT3*U|x @j& fT  @6TM PͨTm!@ @ *Q3G$@TS5  @@T@5Rw j @jFhjPM$@QՌJ61(/I*?qDM@5UDT3*U|x @j& fT  @6TM PͨTm!@ @ *Q3G$@TS5  @@T@5Rw j @jFhjPM$@QՌJ61(/I*?qDM@5UDT3*U|x @j& fT  @6TM PͨTm!@ @ *Q3G$@TS5  @@T@5Rw j @jFhjPM$@QՌJ61(/I*?qDM@5UDT3*U|x @j& fT  @6TM PͨTm!@ @ *Q3G$@TS5  @@T@5Rw j @jFhjPM$@QՌJ61(/I*?qDM@5UDT3*U|x @j& fT  @6TM PͨTm!@ @ *Q3G$@TS5  @@T@5Rw j @jFhjPM$@QՌJ61(/I*?qDM@5UDT3*U|x @j& fT  @6TM PͨTm!@ @ *Q3G$@TS5  @@T@5Rw j @jFhjع?}a!@g)ݻ|wfڣ_ @\6fm7C |YldDjPMDT3*U|x @j& fT  @6TM PͨTm!@ @ *Q3G$@TS5  @@T@5Rw j @jFhjPM$@QՌJ61 ؞׳=}r/~󧯚Izs}uO_ZM@`DKw_'OdT2%LLZYK_Sz'O$uҬ'sJ&EKQb# vȉƭd,tW-E  RRD ^g-+ힸ^vfs/tHr0? Ck)oѡ%0C%ƮrǕu-I-BUXTKjJL%h)*i: *0jࡲRRr t'fIn1%+rTSNɭ‰-%w k#3pWpdBBAׇ1Gh|DHKhiqܰf*,GKxɇ ЎOKiCLј# Z4VKvcԒHTpEK/ K%jB kh%({-Zr3cܧR,[!&%i4;m3eIZ? @wKRBi)Y DjɹpڽR:v6RV~bcH;i ZBE O@K'YE$p MC6iZƷ'IENDB`WorkBench-1.8.2/Docs/WorkBench_files/wb_main_window_recursive.png000644 000765 000024 00000102271 10417734221 025426 0ustar00barrystaff000000 000000 PNG  IHDR.sRGBgAMA a cHRMz&u0`:pQ< pHYs+"IDATx^?\Ƕ/Pp@ W_7'Wpk81N`;Ġ@ Ġ00 `' t8vLMZg;gշVڻw׌̷x P @Swu/!J?=;z:r#}\~%m#-UU(mPT @(@(K|]&*<}o@U0}z՜QIC_=@"U2HV]$B4 @(P_RȬDP6SbYC>YR~*EeNUR៎>%ݠϿ};"!+*O" nO9 }>ƅ3"'tQ&-Aln%CpwBkR-8p"j+p2ogt))yxhUj>uyhD%0{{>_οOxVMoI3Gw~uo>}m:O!n k,& YzombQsr@L%LP @Vy85DjkQ1+I!~C@DŽ!3F9LDA˦'׉{7Ƿo<}7~ ѧEZ!ME40Gz4O61ߚ;FѶJt+8 #?:7ѯ5*~#2eYP֧5.x `ޚ)$jҗCtC{Wn: }/_#?6kJԯOCWk#3Ǜ_mjKRy(i ^ok(X}Gl;=E>U&TC+ JEtӋJV!q(;COhuDiʪTrCJ7ol9KA0D[D ݸRG+Y[S#]*j.<^3&eǡP AwĦCv^O_'5*iy|4(I<ݧRͽLO/9JD?tVF߮W)ы[ߺ[jQRG4dx &6EKfb$ߴЬGm&YX ǡP lg]i?FǪ$|j]Y5 7]}H]}{..~mAжXڈxOs\i7EEf]JP`z3ْ*ffj0?2ymv5;$h: AVb=W:@dC ?oRTVr]e% \_O("65$t i! 䡳9= @( ʼ&*EeUU*i!_GvvCtryP @FBG_RjxhU*!9싆or/TC]P1jwU/>=hUB@h/ǡCU`瑢DB%4($JMTJ[犇hh#~C(RioU/JQYLJDJQYDACkD!A(P`h ]PHP @(!A(P`8 \]E;|9͢P @(Q2F}P @(LCP @(xh(P5?FܒUJ%u֐P @ xVSa$u}0Om_n'E(kI8IX#)"ɓUgU*+jUXސd  @(S vl IM[C5'o%kA*yNFJCIP @U}  >۩mp_H:-eJa[fw(zxȪGҖXD]s[1x\=ԍzG6ۋ̾x +T ٠P` PTJBЊj.yC.RҢ8Oh+L!#q!d]a/L r3a[kT {0fr7`B<$T9û?aEtݝ78i0=͔:KmDU/چ$P @+P2:.@9@<>3E`E2eYRjȐtK@gP @( eu*̄ Lh fkAqo%B/DJ\$'ޒZ!@(@ T!}-6ft*W:*oc]tla{CƀZu %n&u`P @Z $P&QP @(zx P ]=WxcP Ex @(A:P @(@_ A(P` `h܋^B(aHP @(!`P @(0tCCB(!P @(W NTO$?x(y(Ruax"Ͷ%umEz=*'H?DwMU@2'TGQ|LKc]KGUP@-.xBKVqhukCZzto~~zO্ph.O;J> ;IVyŴ>#ҔLmW.#rBlP:|ytj][0!uiP|U;ы>? !5򀺭_VTտl?Ͻ鋿|AVeZTw:тn@nIC*%l"{pt,y Xy(juJVM`R:sUL⒥+0*]"<;6O7fȦP>i]ʞc-mK4QY!Z9XxRhfؐ(gA<5 b͉ÂRCEMB‚VVKZ7!o#ZH"GEQe]vqTwhiR/ Uܹl"tҤ/tWjuSXO!y=L"Na\7S&+Tvhdzk.!4b*Ƒ*2#J?^b#(擷ln_>*읹4JFgBTqI8{[ ^U:bj1F⡳kAh_'/7n s_׼> }_ntNw%*RDD>]ʽju\գ)u :fӺӺ\mJvA-u o͑9pQUB }vJl5%i{Ew!!jnjZpɾlDh;a=C2!1"G"֛T,6ChRIUg;u!IZϟ:sd  &kBF}wq0QsVÅ]XQ"~>։2_ISVy`292B7keRvk3 սvKlFU-Af ??" E+N\zy{\CtQZ<*ҘяeVg8sjˋR `-A>:iwTEYWn.J杍'a[\ z錾,tsRRW`}z& H"ٽ׺Z2ށ6:LQR5_G+ffrIT Zz/Lq[OeJhzrB֖,-WmPȔ}|@7CD!*"$ wMKrMWǨQ~٣vWOXndF- (PE实نZ$lKJ~#i(_}/"K{}X^A<%9^e),>@7:ѝFbpP c)&ڮ:E@IeT'1,w7ɂ:ZEc-!CGͳ< ![f(i3mCtG}͏~|]7QZQ`͝n xJ̏HDP뀞*ՅfQ<ڰV&Y9E$+;ܛNia mG^i*{ڻWIlxFGA.炁^Cm`b"scCoy\PW+̙Tu1t{/=yhUCKw ;z?=|4uWJ4i&Ԥ;Qt.(B>:C<9CjO-ּ+Qd2菼lD< o] %[2(Gt ]}&m=nKw:?ftnx=Asu;a"74|ͫSӅ2xHۧ2%7o,JJtr͗7v\nw;Dk_&t*hz:Vu8ڣ{[GBnwzP'yR'Obj{iXҴl:~+gaUV7}w{a/\wGi?z3 ϗ/^|cV_,zPoS*bNc^-oL!Ќ7; sr#\  W!MB Kc֢"/~W(=hy6>=zoԫ _DoW+ W10oxᝌ:{@޼ЧZ^:[nԶ,#ˇ#ja(M#;n'M'_Z~q>#Joo-A6cM5~;{,wOoyӍųu*<%󄁏4W'yO63@]Tnz!jOJB֣76WTHj㡟7Ϗ&=Ho0ŠڡLH:<*{GPҨ\yB44x템9j 5*V %9QhEEfhi=Ea$B3#[ןi'b J&~'׳yG_5O]{|y"!ڝ-(.j&^s;DMԳw'4W~m"8z\i $$:oyrkcM@9tU0Ȏr?D<R7P70H賻c=Q }tAE"DEd>!}{z,ߏ9QA3{uL١%+ LXI2~Z]-I2L:RB4sȪ("^['|gI$N]+c+[$Hm&9ԩhfs\ׄjaЯ !$_.WQ""4Q[5D'P/~硝?ݚ<<[ dDB-z#겹'6ƣ/\^rE2\6C/Q72_(^hbhԔ̜a|-0,*^vq HO辜&yVHDw yH</N6ϧcA~i0_DɃ'Sd,!6'[ *r*3\(r^t٤$RZlM%^(ɼbJDUPaF66! 4 ]n `k}Gv.]*HvA1+eO~ͽ/?yP%̥ZpS$R/*P#YYuV̐0auP`ϟ0TtKD.yh`IҮ;˛̪MAkZ⤊!uMWl4IAymg,TÎ~9T!IU㙼|snu fo~}k<}6 +:/N4b.3{/!yj& ѤR}bmB_gh>`W%qo{*G_S{諻tG6];>-V% {I sHjL("#h +jjki4uDaU$v_'-n/^nVW^XOl;dj\MʞTCbU>{G%]ܬnNLt{ϕ9g=^Q7>+ed^)ܦvK_KKPF%/ey~|u`h[tn:&!|"E 톚Է?.yY~, %4V E?TK+e}^/nv d8X?DW\ ]ۈT׻;y#R^XgŽ_ob@MsPQ BcJ΅yAl<Z% նyIx 52˭onɫ攬ڶL}oQeeC?oC:(k]TSP*ӿ>HTВժŢSr?ۚJ/h9[`P)Z7?;l& X+zotWCKߥ/ʝ2NWVxZj{ɈBb]Ku[/-A>-5jh`RN=njȋ"'>ꮱmU̶xURyj<^h @(f) mw <|cң~(P' CP @( z0 @(P @+4P @(A(CW CܗdCmU`OOgO^"}ܓPT5̣_BLOf2cA(E2@jU|0&ACֹbsCRTp$*T ĩMߍoǯ)4{XCH%*P^C Yocho970{[K<"Ps!AB! x5ʛh(T8СlVZwZR\f LW<fzEƐD,CKO QZ_:Vş"'>XZC)^ ! LU^k y< 4_]QZh\0kiku9K%nՊw5y_VkT6!3K?VUK I}/WlBkג.-7:hfOHx(o#ĥ:f <5:xVĬClB=Ç̧ y^5#tB "=ߵ_/Db])^ 涛qߤ([8ЩX\=塨&E2F奘xVnVVtPY RTQybda4>ޟ< sk:ά2ވrWLZfI `kҍ2=-So}lC. s|ǵ@Ӂ| :5|^ *(>L[&!K]F;$>>T@wМ}\ ReJjv+E ˥JsJxR-,=n)+ mj[ޠjdH:Zas-fIz>u/e`z7 7@BjP6OvΣx]CB='²l>D,OވUkͲh<}C=aѫQ<:klqXPKv9<;G>{Xny+Tg1$u)5<`z2H9)?xEmA(<>=[PUTaCMIK(l+M}(ZU`cx({'`T_JjqXJRy l uoݴ^%-9RJw?_GCkw<@ryJGkoܻR?T[`Po`~oZI=)[m!17\'RC9%![xH*A( Jg9u;.yDJH֓CnxG˅*ҽ$2y¤͜^[B.I3wCI#ɻ4YC: P IK>Q x(kb,LN ]1O&Juk%Kk΄`Ҭ]%3[kvt%G-Jjxy0Ȃ!z  P @B<4;[0")}S5JdCj#kNC ?rh6yYe57f3{V7Ѩ$Z2wCY`LU#C. RE~(@A~`k7'CC Nf2qz<Oގ<n8K= .9Yb2yڔ]<=a 5D P @ l %gn4~s8~MI$x7ueKayd;+gk xh @(ɼJZP6h7e;t{jMTLw^[Q!2DC(@ y:xm!6!8G(soyhvvE5O{F(@!ܙq%?d]\\PQQ;+_X|h~BgjmvCd-h"A(@u̕vv2*͈*lW;/KGz?(+P,64Y{]x *p:{r,m^) ㋇zڝ9gJh @(6Zγ[}DQhgI@<ԫ cW!,~l%P @.XX~߷sz/]UqIub{ت ys$(4=J`Uè @(P)EJ2o44#kvP7 '30}?#jIx fͶ$9 ^^Js[LqsɢkK$քx]k4/B b+,>Yfkt( w!y+xl mK'Mq Cŭ#x(i(C*kR[j̤%Y-X H ]PF+ɍ]ƇxZ ,`±lr_m6c$6CV7]kr}WļټR8B{,!jC!F״Gn.Z۬W󢕾>e}̷Fmٯ,lu-P [ǖ]1BQ8^R11uC|CfI0uYm<71Z3=`wX&|?#އ TK桖&ܒ}<:E YBސ O&su`D>KKm4U~ @(z'!'/i|(⡖l] -Ī!ZgKjZN*'7)?Qu DP @(P])HJsJ^b+塤!T4#:=esx%BϐP TWSJں==i blIjlGB A(@E ypMeS"B;em1IK"Q_B}8"Jgn4~s8~MI<nvt뚚ƠN! m5CKjkv9}jfAwe XguS! Iרlz7bhDa;w lQ22[FEɫkc׫mR`fv"]PC~Pк 4}v[+K6Ia I+ S䘏I+uuYnfP@"/ 9d9O$ӪP ĭ]BS⛕{rZZժGCղ۪g]@SnKjZ!ɲn{Yd-j, T̒ʘ4OݞJTŝB+IkT5d{*T5؊[¿o dw- B(P@yݚ[,q/ ,I -)0ŧkji:љxhsC(Xy%j=-GK P\ybI UqN1 Igp.a<TYI.VVKJڤT 1L'yNԣ$-n]V.Д4rUJ/۶+{nl[ ө!eioLrcitiM/]PKj7EЦP Jyݚ[v<C(@ tCt MYPGA(@n\};Gr+TU\_҅#i1-YM5}mUU譻*հy!!txH`Z]31TY`hy(C =7A^'%qBe?'#s./WLQ#QJ)e}DgO9|9"S5^{P`OFx*{vC^ZF2VARnP]*p8! ׵6!yW `!H6*I2GCAI.N ( Fat*z~Ҟ;IČ 5\jO ܞΡ3SedKA6I'ޜI/TIxvU06h«)l"ZntmV!ѥRyHя*FxGx^'4Ggc.x鍮#}ꋤr۽ֺdE;C4-G%śA{RTC8sJ_{HwZSSr\͡6x.mU?+!uk`[>/أ sC@ECmU:062$G Df(@Fx*wkole=JO@R #<P:cW CZP MTCt|(7u)V= Jr!dP UdLvg"2НT2<6_Ǜ=$wM{phD\u}uishv2`<VR%gUx?CnN(g"lxB9=Oߍ'oGࡵO }\岼40pQ #<P7Lߍoǯ)4{!=INf/Q|?Efֈg8ܠx VP<<P7CFR4x(*2@(@ d"꾇ZUQ2?TEyTᩈ2;}p72VA1WyӢ,ᩈdrѲ^kpo[;+h lᩈ:"~{v:UTQ"|(f@G@?<yA]،s%ϧ8mUyhkF-o].۪jT@b <=7xH]շW毛Yz;Lr{ =aSndx{jǓ p`G;h4j2@(R@:G*P7#J#ui!8Oi!~̮Żx[`!P` h }T*YG*PgC@-Azv:UP @?z5_G䗔y~6i寉Ü@۪#U`"n[J)!9=iflK)Kl]UrKp P @(?^MMݟ_B2P4"v+oXv!9KQ (!l ٠P`y2E@BRS;CVLǚ~Y>zد[;Cʉ%ub#M EI<ȭ0=m]jcj.G\CH=<h#IaFC NЖE @+C@ -6!P R`ym$) hC͝]ϚuHˮnSϧoHP @(e ~ӡ`!FA+xda4>ޟR88z= P @(q ތho"ŧ)#X䡌P @(@~M lP @(@yj(P l!P @(0t? h @(i }WHuD 4 @(q0ӓ槢2B!P @(I   B6(P`ch$sڄ5Z/,.%J׺Dպ}-A2:%"4k=yO:wܱs5He44ZITzxaYlP`<QOC5BSŞ 4m߆%|m(l=ZX#uBmum}yadeUE3$5gLvY]0ۼ6( hg[gw#VCtV"AYxz~YȌ:kfwrtx[j˨GU^3n2PO;c ^0k6K9ݰjzꑮS7+%5FVw,{"55L/ uûu&wry2ifCƫ]\7ct:%y@ h_D0y^%!k0GRNo E&+x7ph3_[w3(ڞ]C]I%3;o4 ?貂|([nFB`%" JBřE_y]ݜLC%_}aCύkR/̟!TGY+2#ۺ<=VVضĐg#3iit9{?4'wy"F/IV09Im5X[Qhsץt*` C8Yĕ]#Z2Jׁo0mڐnuBTy+j9C<,`z6l'[_o)pu(֨@h}(Y+$uh (C~(TA[YC]=ڂxhSF vB(m)jKY P lfgs9CgMf .P @(D q>qMxH,2A(}\.UZS:ÓD<$|i$ϩ- xḶ׺DZb= E~U%"HLzpaٶO:WnlƗq&RCז4SR},;d༕ו.~ZVxHIՆfЪ6=M6Zf \ގ֙-9Եz"kP"4Fe+7lIי.g'JA^VxHJwsޠ3et}0;\Ano̮J="]%Z| Y5#G<[@y($; d/ 9uٌku~㮹-zMwyZoFwwيw+MzFKr{ g<$o%.17ɋѹ5޻\kH YCw\'?:CǷRx(:텳.Llв[G|nSFo(ᵼ˃;wTҐA$:+~t((Y=@\_2N^CʻCihV ]1z WݗY\CY˘̔ AR]\< {]4S{$ B "D[yN7GLEv?\$熨o[MHPObZ%θ}v,.IhDV2?﹋T2Q0̥4#D]ަC9uOuPCNwѷ$ubdJq]uMss׵rBFLkyMe,tb&3 w1u(Ciw,64ff?F[~F UNV(ᙅ]EK:?CzvyN@e-0z3u6gS* ZIw65$鏞$47,R[0#W#='7ړ y+)i*IS&Ӓ塖DP x&!WƷQgbnmuMFP @(lC (P` ?P @(@(CW,)ϩ둅 %J׺Dպxxi)1&Gzڼ '+뮒A(L&RwJUy+HPҔH<%VIC޲fmXFrB}ϰ[ mz݉j}ya(CFNü V^X[aq(Ͷ6UDVxHJ:th}b^yk֫62NEڲY"!~)t jͰi֣_Ն,iͮޞt{jivRSӻl7] -AkC>lDp;̻΄Ɲ(7~>rίАKdw,BkEh4QGCP<ZwA(ܕ\h.L%؝lh.n`4}5CzzGhܘyh}y[ 3Yu5D24 uu k63B?EJǷ[x:aDj5*)=VӕiYkxڐ=컜qtI|z I1 C %FKK99X skoN<敂oRQ ;C&yUfRZuBm3ؐ ` 6EW( hBK@}uwIA/:-hEVw.xnCz|΅=]CGC9@ta6f%<T~Dg\0 Py_z2xHB51u/!;K^:{IPw"ʗ(1uOS4IXuQr҅,,]y=P޺K-rr#^py0Q%ڡ(+PT֫½Y緦z 7#n6ofjq=jH+]xi Z紀ӝ;1nSòy#^(Փ%~c>gY+xV"u$vkN)9@<ԒnDOj[9˶%ueZJ(z^V`htZi ,,QmQ^tnC 鄾zp}_h}#a [Mb$uj02@ CJ l6hW`ƯGA(q 6n`0P @(PYPeAQP @(q 6n`0P @(PYbN5=?z5&F|srD5^{P @(8oLOf)eIB(P`cPچP @(xP @u*xhvv!!&v_hzqW^P @(6N!u'/i|Ъa@PM6$(P Fh=KNAvӯ#B0?="/y(phn*MAP @W Ct-FEF9)??% F?ij`P @<22%ū :?_.Rx(oD)()춤Q.xJ @S \ A(@^ шZr?qռp (hVcq (0#D#-2nލpЇiM @#FC@(ݖC tz=G(0FoF45h A(@@R_~ӽdo]$(P l i. hW;/Gt^\d"66W#$(P *یyhniz2[Oin$/$(P *v2yK;F F0AR?HIw=DωRӌ`݇P @(P6';35jg!=[OsRGBgAMA a cHRMz&u0`:pQ<BIDATx^}$ɕ}3e XyO gHe> 19C#c14cK -ce twAphc46(c2d\cޓuN8q䏊.MVVĉ_Ȭq}? v2#JzgF@ "" ; @i RQjt:>t"͆l~v>" @)"HKP(5.?_ɋכWw_  @R% uJtezK@ @DTiNmlt4F3^<q@xx7Q)XF_MR+H]ton}  ûͷ_S)*<#廯{Dy(K*Ee#t+xu{^ӯW<@E]k(&*EeCu!pG=WW2 ']UxuK3#>ÇWO~򓟔[ @I{OOQt:\˫.)-塜,oxCzw=+to~7/W1@VwmD:G1BZaW(mr٥w?(i3  RQO Cgb@Mhgr9 ?|[J0-I R,љU'J!R5LNKh'#?ӻwc̖ϟ'!gbpF| 5Jul--E,fsyƭn\k5QeUjix0r-vy]D8ëHI4<%"aPu c*v;iRVhmsu a{3v3)7B ,$ Rgt:?Ig|SĻRIXt'w)[9T3xA78HRеRn!Ag٨H,pn1cbPJ gS( p3m\Ƶ֫-e$\vǠ7)2U㢿Rlc'W8Oi1- TMZW5CYzj@f}oRT6XtFt|I t$5p,2t';I@w7]&~ӟѵQ:DKL.7AHPy,;UdzeH[q9s}Ԃ^>֨q+;TLb6ṨWmW?F6vbj'IBWeW X~z2VZBWdtwg&p#$hǢYP3߿vKx'&H]tF;W-ѝa3?\E.3?51;iprUQ'XPKa-9,jUG'Vplܲ$w> WD*9@DxG pͅخp>k}eP嵨E)jw\4Trڴ9< <[%>"*Bw"%V .N7Aق\Kp#CR:S`yǨZ1}=~zo k q#Ag䬉o wxHF0tS)*~3^uCnΠp' bB^tݴ姶A 5>.Ȋj.NT#jՉ:\:bu;6ED"|+ 14dsU)s ۞̐.xoCAˏwjp.s<l?펎:磞FCu K·ՁΛ+'S8Χ*FH(#F`HO {{(͛=EF:nFhOt.EFJ*fRT7wF }>AX,AIiNcP/x[:|C!8mʥ{"aZEΫ0*6pn>NO͊3Ѳp_r>$#QjcPc-tEQ5` q^EV!V᪫tTLY":-f/OVOބh07 1g3FʱWh};7'wmЯnԟ`VUq-3]{ϢJ"xt'!er~ݛ;zO()jL>g8N?rOb搓%o_ m5! J+~&~7A@'\g7Q7-PPӸ]V+BY.ʀ Tj5P(rBl.Ah2RǠq``f8CظH&A1ś :DDFb49* 2WΏ;zn30 )TkHy 7tNjtB4HX&'H]쯛:n\^Cy;|c[aW ?N?JE< }(!"OEWcŋp g&XiHgT<@rUTUX )nҾxOs]l`hPi/sr  :+G.'XSq-v%oEE{Ӯ=(Ћ]eG||`٘ST:8j~ Ƶlڴ0I "py{ n4J9Q6UvSnNsPKl0h1GFy3Jac:{7 S.t[ϻhv1T34Xpf8I v._ׇJ)hyu/^MTʆ&b{Ưߣt)[RWj{7)i@rF"4uo7毺.w:$aP%wFÓYtFNj,(b*@;W-]gݭJ5[8S<ֳo/b EΠ+yvRn*Y5āuY( KA3^.I3|J*^5}04=|HP Iꌓ8'*J!NNj|c( @`@gtƠ @ ,K͂ǀ@~ xH@@@E3 /NW@"3V@tP 茕u8 C:c<,a -g۵h茩} pt`}ĦrkZɉL lF B0lfh1'R9qT(ҷƪBl93T̐5|J[C)uwIJ'fRiu! xՀڒ+^%sK[s(r#Hb2Hm*sѲܘ7X8a@ $- ?Qm 0rbu 9TqʔuF t V)uuL 9ɥcpAgDUXPirT5fa >V4\\t"fjAJˊE }6|VA΅_!sb .VQUeM:U@~᫪ 4VSbCc* Tv(çyH轟{Ia]| IJKFzsn?:vI-)¹Iųa Vs*s1i *4t[ΐrD_y=A6B"R@cͻk3(ĹX2Qr]wrU2QfJ+ni~ <uIA9[9 p 18.<\g|DW6/MbNvYP|[sL94|pUO5%!zu깑s=+,ȑu!RO JK/vUn2@#ٞ3*e.j  c~Yv;')C3'Wc)˔Y6 S`uZJC(#z.LUT^9tdb(4 RUaieX9 ]Ѳm1}0N!&RמƵ]4K5 ^ު!*MGK%EWՂo=ZMci*E Xj?F ܥ^yLa"G=*Ut\E83pRȣ.')Fj]E|&yAe{/U1k]ΰc$*Q02_β>)QnD&aB`7{B b>;KqF` K!p# uq'"4(>h3DN @A Nq"@>EpE:EG$_> Ԯ93v{G=8Xpj0kofșOmo{/ 9z)gq(cR`Ś(Q31b( c8Cg0:c cY0=p3Ç:è8Ɂځ:!קv6 u3F ~I:nVg#g~s0y G-lZ Z4e~OI4mcLN!r-`i3Kʍ^Qvh]g \`^@@7<43QtMI*|SUrƯ]V0Dgk̡B:זAN 'GĬbhlNAstpk*H\A BN>FN >n_o(E!÷ 7cjCe[uVW&`uHg4nSX&3:PM9T-Y"PHvDSMgUU]{!Ղh]gE~jT"}|{E$432FdSgFaʱeʵ$:#Ur*^KU CW '9#Q3ܫ{#' ,ȈR#SgĿ¯NI]| eq]uj; I1}QqD ˆ Nr֥nJ.˴덞:u~wE:cĂ !29uF1 0y~ ;6EBgXu)6@y;m:؞ l.P NfT@ N,<@`8h` @(Zn D<@woOPv`yQ3:Ym43#@Aɳjj633 [h]g`u F{OU/QuTzW(ZX$v̓#Co~KRls^7<<~:3g]p҇+JsCJsRaPh#oQ1- licbC5TDEU@`@(ZXT:2Q`{gv^Ö-)ܟqTO(u3RWg#19&3 1df<68±BGT3p[ʹiIFvn_ӊ *DѺΠV٥}!'}|ݤj!bN })̚r ,&͌[! bZ::r'Y'hX֝UEu]d& @in?u'Mh?*2uc NUQ^&uuʠ3ӿXL3hu@xs8fSՒS]!N:GC)7;`Zo([@qSNr(2ޟ O3rbu^Ԟj J8PgVa:W5 NQ3w X3e[ϰd8|-Is4yWZ3R {|,'sy,"#kf9)(衾;uAإƐCY rL˔S\79ZUcv=OœY1{ N#Z-iՙrEњZQCQgßLL'xc{'vepcpSh]gE2P8)3Z}q zV@?DѺ~  DLiPg12h]g`?AALiPg1ԲZDѺdьόir@Kb!ϘmT(ZX,40=`^h]g`?cY!o>@` DѺΈ6`ƀ6(ۓķXc# @UW[) `8_Qkn wD|}`B׌(N3濆! 1J:}Ň߷Cmo?/?z(&|Mb_+KDA댸WQ` u?Cl?V Oc%"l7ݥw$?U@lIpE:ߟu@T t 3 BaDUGT$(BmL0>Sg!\ؚ 0 *Q3]yQFgA1 c 08u!3}tuA@R8i<'1ڢ3hK8:Cd6BI;^i>axDM_1ʞ+8Ψ?E8`EZCA #i3Ē&~wZ= DѺΈUp>` h]gXV'ghIpG:qAAA uuG4Z FGA 0@# u:![ !ࠏu(8uՉ  DA 00@q PFA N@`Ѡc-e8br N#ޟڸAÁ@ Md8@T)p:#}T~|;~呝"@8c O0i(N3|o_D1(JAgW')Qat灀>F*cl88D+GA [sنN0AA 0F;O(Z#g h( ࠏuA&EA }q҈q pG:" θ}}{?wqNa0~8C`3P4 L(Z}W'Sl}432zm#" uF߫#L }432ƪpE::cx:pG:/cj FAwq6 |Aq3rOZtQМsիjo-pf ,㠏)tJ1F!NA*6cl^  Z u) 3Tpx~Gsa5CjQm$3Orx+ƧQm#vxeuJ2e0FвsQ鱠&;$:uAh7BD%Kac0?sqL#\V5s2TckA͕8CQHTcR9QPHUD{e}"ZA댸i ~> u쀲P8[T~3z>rSPsf{Jrs&F@2~h$X),tFd(N3G^73Pwڵ219 %2^UHMUoHtr3{Xx0Y09S#1#ETwxtD:C0CNs:/c3bn "\Q1ZiN4V;NLC^͙iV·Rjwa i8jvHiSZo>~ w$ы5v04|M_]V6)1&Z+ov#Rq#euzHGH$<Džm-y\:xsܘS*\ ,缃>Bd Jk_h1 |eu>q.ī'3t:zE01:>ӶW?C ,δ(Zv3g=9.T^.F^KaЪA6>¨X9%!*hV^<:5\gXͮ^|1PeDI_(erc1wZQ1Ƨ)ݷ :_W&]vD, ;Fz>3f EiZ=3WpkaR Sga.'RAPXs.HWm<0׃@)/ʕ y!2ٹQ>BgP1r]n0Ɖ#jsua`xM|~@8&Z.gg_VwD1Θ1#QW0F);Z715eV t81lwrtTAї1"4.w9edfݱ gSzw,>[څŌ>eIȹc=pEN# 8C/Of? +L֍(ZɺѠc8EQ3a~CpG:1$PTpE:j#}43\g ^mU&MPOa7r:j ͉qxR-:5UDS\qtG:cEQL3HI?+M҉r5O5C7"n#BgԤ:`Rc Q:e,gT;eGA gL0DѺpNԥ'·т*wQ=HlTM ^K,e<Ċ}("H]M튎lY#ի8T%TDѠp3 ,8:#pX.3r*>7vGA b8V!&j*霚yP1BT,TC ն O* /rG: @`8ΈGMܫUmXtNqR˅Y0fy-n"F5ZNqεUT7 3x*r/zH-0tG:F 5{zNNLE0%*YLjTUƕZ}jG}njk«<:vACk ;\3943;p\1 9G _k l~s$_Z?=i{PP5hZLfYfuyjV=Ql9M UT-kGϰA@s:csS%9y?U;A ܟ1$QTpgƈU쐡g|Ďs|]>L7`] h]g`uE;O>`S?sDѺck}439ODѺZT 3x;3wAxb(Z1>` p:Vjܓ@e uו>пǛ"oϤPh ~^6oo~Q3 ࠏtF kS ~agM!Wܸq"fӴNyI:y@:3Q]-yc8uѩv #ࠏ)t[d^:C]ߖIHTmQ |:#{PRI9MA;q'x?A 9}3Qo.-Ӫ.ϝ,L΋wAgT9 akQ[ao{Z|8ȾqtDѺ0d@d}pS#ࠏ)tF#kU׵Ů#W|/s- ĖCvǠK!!BG?),/K;uE!^(]g}؍/sJC g,482cXV qSYsE: &Ի6 DѺhyu焀>`sIADѺ~Fq}43ghT;8uI;O}43hpE:GX gW_\Wҗח^Quc%f Q3:9U0޵!@YN(:kϜE<Z߶̐CQ3:|Bg}0q#pzgtE:''T*1~FxTWլW{݌.[{BxPfjƋ zknŋ*IW=ڌ ZJsW^@IP5\ ]:8u%_ > Ԩ)3 "#\O)?0t1 ͭn-=;*Uilv9ޢrQN/!zRMSWkaʂ<h]gW'Dq 6"}l4(gӪX ʘg+h\6~+tBӅ~/zQ]N宬/\zqE:ñ:Yns pB"}lR/bg&Vu@Se?tr\UN3:(r0)^w>Vh]g4v G+D ݛ I8$qf:5ܮan2c%ca*vx83YW؅zZ碷(vmu#m~TrVd.$O^rA "}ܾPBo'x_k 0kgSi<(ZcNy ۻ<43Zf&yPu$͜RQ31u"\d3^z6DѺhyu2[" 0*}!DF:1C5# uV'kh}NrucΘA]+DA NVhIpt:y'! uV'B8PѠc,4RpE:K >t}ܽ?0#>9C?xeBgay{ځS%rOY06vln\SzqwXE1p1HI=j1|   :ݻC !͝}Mk(c\7Rx ߵ:1pu,eE:Qh:[$ Tȍ0@1~Faւc@ɍ`  `A`X| )e<*:ݱ>a` ih !eDH'r): 6@PGJ:Og1 Kx2tMH"~ugp+X>a` p 0Q,Cg7SGL_W a ,Y>ɍ`598yby:L{!a?cx8²H*[]εB{^ ؆#"0QL3_[~ ϟn]i3o_ҷDqdЄ-SQH#:灦9aqh?nC 틅W2Jnure8茙c՝>#Mu1Nxp`YCꌨ|XVv>F7Bg{dACs ݏZMmvJwo^>>g { "Ѝ-ڷ`E`\go_̭ZRA_=yfI\m6vs55Jp FtƐW` 43E1^D[gH3]~r@,O_RlNO[_\# At˗ܴ3nE@ BۣͫDuwrwMܷo[-]H@ @ ȟe/-x}su1 ,o^z}vc CʍeC! tvr C ;X3 \9 ND @g<} 0gTp#֎IM*ȏT(xOSTs>YlSMg$ΝRXcX3d@3gďǤ+fkWEXalLQc}2ym+ڭ rЧ}?Wr/R MQ9 ψo8GW3{Ajw/R:LuryQ=PPB3}:f7u0SuT6IYGƮ\niRAXQVVYͤ+d5>ёtƐ$vxvZݺ8f7[Ga'K7=\I¬JX>)sVgUTUw;8:#TՖ}gZD}uɋ\EgT5Cu"p0cvpH8cHwz^dwTP@̓,2rwJ[(Z]aO"8uC!L"s >GрXkʼ'Cd&ћWKMf*3z.*??.2܏HX4ybxF`=h^grΰ(@3g"d豝2EmL:I3͛p;A"~ܼ8z|x->"͓g 98j%%Qz#<;|:;$ tl3ܟq& `A GAHpuۣ  Agс {"r3o2䎛no@&2%W7 ra5@ :q $3 $ @ D״AF.g$ @ΰ<@ #7@ Po+G4w\IIENDB`WorkBench-1.8.2/Docs/WorkBench_files/wb_project_add_project_name.png000644 000765 000024 00000022744 10425464456 026047 0ustar00barrystaff000000 000000 PNG  IHDRgyU0sRGBgAMA a cHRMz&u0`:pQ< pHYs+%MIDATx^ݱ%ɝ'>Lg#?⌵X ` 'G4cac!1z A1=1jc2(c2ΨʊȌ|Y*}y/22U_.//d?^ @cxwRч/>ӫWFY a6|Efz / @)lR3}Dgŏ?} @H90ebJ$g?|Ff_l @RLLg'B3=>;k21?w؞-21%cg%%fo~۾xc#@[ϯR^^~//{}î-I%?(oR>LYyo8y6 1~|v7^c[Yj~ywF6&Ф>ſ?䣋||ϚmEz7I%S?|ФU8|e;6 5|f7/ڷRT)&5p}m#@Hw~y~A/PLmiOV OG5yR޽.;=۝Vv(ؼ@R|RL6;.2yS2դfMORҮt 7u-AL3`;}5+@\I} ΔW۝ȼ)Ӕ&5ۀK)gh}uʭK& f[vTou U;xng:X{lO,%@m4j˃{ݾu&5or*emj6?S[l:;{wl{Z2?0/|}MnSmK:5Ͼ?|[7yk5@۾M ,o^:I͛|LYywD7ݾtwUV=z]NUR+O Ω[:*mj:IQ HgƣҘ 椨9Ťr*\@%˯uR_P-܀\}fl.ܳ S;@ݷFoa9 :TyH^>8ѠX;2; y38} HMz٩98WyoDH!;S3zzZ}}'0O@j>3]-`jV\ w*cڱ\,VomWԙD=+іR}ٌԔ>{C9,_Id*KFF稕ܦkF G% 5W~:Hcv.a=_ƍ=qϡz\|:ܫeJ^Ƴ 7* 5vX: ~rǵ޳֝UU=|[Oȳܝ 8\}f:xY6o' 5^K̓ )5W/Uc}fop:/Y_/{tF\}f<6|OZbk&/|sYOcI]p3xRSj^`\)^`=MQz,3eey_kN*GL-$ 5WnIR_1Yy[apYnb|~gf'up9)" 5L2*C|0)yT0t^jVT2Pj'N@j>3˦fT6ge?<̦>y4WgѕvXԔf LM땎M@. 5gn#R=@$0M;G82F̣9tvP`O0$@`cRSj& C@FL7 Ԕ @ * 5Rze'@ (pjc-{nyo`*xGH3ʻtMDF?:צ "ۖ18d*=-ڣnv/f8)Oy;^\UVe.0TiF.R®|spOzwoG屽_1Jvt9LK{Mkg!{F"pjzU9qp+gc ѼX#~Lr\\Vkj˿՜qAWmF˗K&tpp`28)çfSG^UX= ~1ʳ<`ֳ6 NꩶsPU9ĩ'3R~v$;oGW"Rs,*2vF9{$꽠 ^ԃf^g[cůQiD=L.?˛ap3&y#@!Oͱpp!2T4,,ޘ\zPE7@G"mp~Ml@)ߥs&? ֍,+v0O3jg|b<)`%bK , FU**_SFj{23Ffz[wS HVN3 @DfTSKԔ @ * 5R\ @@jJM Q)3, 5& HͨRDfT HMI=5gk -ͱFmSr#%Zڳ?6/S.~5/Vw'9v^QSO:BDڶg{o>':lj]Hʏ_tCD"*sL\,? vacyԞt%XHǻYWpnٿƔ-s4F;'ROZoxvM{I >vg; FOO+P62\McڗXk+q7z-\U>Y]WVR_r+N;O=Jj͜ߝ]niK=j;35}}ݗRRU EfG_ߟ_w7Lm__s"#HLOeȼӏԯ󳔚M|}>ܕRSj i)l:xSsWj>xq '~(R󫭭5_\knJMI <|"OSo^5uMjVE[{^߱ Q%KE $@0ݨz{c9cwv-N'SzHy iކa]g8L~uBܭo{yɩmǟ5kͩLN}z4i9tco+0vȌYk K#`jcbݣμ"OO85{^S`=LjXvg%;d>R0u]j|sIt0%hRjnmG$@0=l2}}g)N=:x rIY}/{`W~HÌt-mHԔF O~~qoRh~fzgnok;7m Wӧo>~ ۟U?p-躃U_j@2ο?{W?%"I/~Hw>;KNj]a=/~He#ݶϏkV. @e5q$@cRDfTPH)  @2֚8 @1HMIR3*u s(}$@ה @`ke @/o. P LS,? @@]:5M+ @Qkj+?IENDB`WorkBench-1.8.2/Docs/WorkBench_files/wb_prefs_editor.png000644 000765 000024 00000016427 10417734221 023520 0ustar00barrystaff000000 000000 PNG  IHDR~sRGBgAMA a cHRMz&u0`:pQ<IDATx^?Ge/T'—= N }>8#='fQ cQ :0U X%U X`a6PЁ6@ܯfjjtOwM׷COMuկgvfٞm@$Zsí=99@@Ŀbacc=#iyy~j^` FDI*&K?ɇT @臀W+KFӇiI+=O.N^_ @蓀W,,.֪;ϐbZn'2Dsyi  0>?ߟ&K,,.VNR"V>}"wO_^8#A І1cU2RcKL,^;<}fr9  D٣_*zV+d_ݺxˋ @hI@yÃףgKr6U)#%O:/5鷓i @m k??d$Od/I)A}|;ўZ6~OZWEyzY'NӒil?mK5iZ].t  k'<=OZ':_d_yq{r9x/J罵tk%M1]^f1S] [/>+dr ]Ĺ99v$-OU۾Mڏv]&Χg./UZ rTb4j<*'6}ajVۦ0;CX  687"]^IJZHZS)cNUv%u֠oH -ОZ> ?^z_k))*I?f*S6_|)|i/Vͪa47s<ɷ7Ѽ]! ӒN,fF@{F%s?.w]H Ҥ ,|i^Iv @hO@o_IҞom-,7N>$Y6#A Оm/(}맿ܓF;Vu${ɾy%H@@8+O_4 @O`Ϟ?@@+O˭pYY'  Va @BOs .<p! @x .0M3+ffrTխ]LTe<9 ϩ"X@xmx~:8QΔU]c;u:O4fv)o Oyk@ B <=zȩ%ܻht*]$%OEa:sduޟN,7I͒usj6iݖӨPI`楢.ڜt+"$Ӄ:M0׏:%~1J3/<t@OW~kשI]gU(`M1]Ү߶OG>> ]" 2It.urJ_G3t@Ӳ>@J_x:iM!xZu;vG}޶ߧ$@؀s~GFbbuismN* 0zO%^OoimG66i}\:+lz™2YyOW" @l6R{,5oߢmq3/4' @}OdUVR"dbۋQۋv{5wwv0xZ͖ϑOs?,!̅P~d=Cd uU3@`4nˣޕgms * &:Fly׌nNTݒ?-i8wۋnc6(,+Moy^UԼ?,9|g91lRQ%坒~lE9up:%Y4P$Z{tKʼnL&<'Φ:F_ Ld\C|oӕWKlXVG\OW!oK" m%i\4r4wΜ|\@ӏYye mQ?XS~ZIxzV5V䘚t(J>ՐyQzi~P-=mf1/\68:^em6kim<ӹ|m!MYzm]18jttXgߢPg_ /5gG,CsE>}U\ڶ7O<,Wg:\q[TyBY։?F)֨*rveY$WXM_p]Bd" |pJ<$Yye m?G_MͲyEsb_垬rQ1gKf秷HV~c=D$(@f"@%" i @ \x:ܱ!2@f@%0A *] @GˇzZLl9+caKe 2|4 :N}xd,1"#!lFOigmӹ>6v6짦B:¯iQ/15_m36;D '>y6m([tɇҹb6r7Y7Ww][zڏ y -vVҳ|\ܩ> 8 1xz'֝Tq5 PIOӕB!1@;GO&- 4@"tO3r9Ff "Pyܩlx>G!^vcF z9T 4@"O4j=@`\8yf4S'4'O<ӌff q䉧{L@  0.dڇ@@?vn T?[(4@@ϑ  ^P@ \xOC%.-!@xOC%"!@xOC%"!@xOCr:L]LL;SCQr4tgYK@`̮ONn=8ҭ'ҵfg8ifdħH@$h~yrJ_MIOi@3JlDW#W!m8ipFӗ( ;r{4+i#ү4iii< @ \ ,Nx:s:4"IOi@L;齽M =>]tS%,m#`hj?xM0vOT(%UO*dtO:ղZ<8:tH`YiRjYP؏ݶhCZTXj/*cjh Oi@Jx:uDw! <}LN7:ͶyRa[Oo;$*@`G OO.1 vɈR[O +sG/ 萀ŻRKO Oi@J@<}뫃7o&cJϤ_Ǧk# 7tW/ة t>Ͽ,͚&~Hܾ{(?\1$+i%4i$Bx:ٕ5}F I}Di=Y6^Zşn2Z\4؎F 8`,^IENDB`WorkBench-1.8.2/Docs/WorkBench_files/wb_annotate_window.png000644 000765 000024 00000046601 10417734221 024230 0ustar00barrystaff000000 000000 PNG  IHDRf=sRGBgAMA a cHRMz&u0`:pQ<LIDATx^}=e׵fL &Ouhxalp` B/xF!FL@C+C;AuP8@A 8(z]kk=ֹkַ~oթ/߽oŋ?? @P}Ǖ˿^^|wo~ѧy'@M#=zwn]\\:Kۧߞ@ !=Q|"_>M׽tܿf_.N_@ 6@D'2KGVzcߞ?q F zDݧo`(?g~@  "L#g:Mxǿ;@$>|zUQJ^ԒD?>. @ ! ;}T$V!J>D?{_zڕdgӪD3=!OO|p  qWZf (I}|zG淋%}]N_>"}w_y>n 8lg_|s %;_񇗟=/]|$$掠4LwgO^g=vz7%@0_hҪ_Q%/~}>]E~+$ɝu'? 7|Oӿ lx*^6_}*TI"x]W8qvr;ݔ|ZU#?{W'?Ov4=/o/H#Pȝd|dRtck[7!E|!G~YT"xZ2jliC0l$jUrŀTIws{"w򣆉w7_s#V툿pnO(HԞ~z+^鷧/N՝w1d;Z&L?81Is~gL :^h+Gj1Ulcw@S[1KmX57 kҪ6GDE?u.#щ_??=7I_?Owo*ĿC[s[u\F暘Zw{YrݔG1m GDmbn%fdz]?lǙk5y%X|MC͍@>N{<$u:;wo#Jvܟ_^緮4+IM%%}z˃9yrݗ>r7wX.Hu"|$ RKydgݶZP_Ts¼rOSHn=V&`7UOU7- z& *j`ZS:Ծ$u$=jZ+A ?۳+?ΗJ2[ػTI"} gg&*x%OGWftϧۃ|ELJ,Ryo{Uzޔ|.z5UWXw vgUmD 4&e(XQw73 CZЩ%eUB^*/mܓ3L ֖Db~gDM_uwFx]!.{5AlPr;1/X|zNZIQ>{%$3$לYxRJ5ػ}wn&B>%kꕿ?o?~p7MK>ݗps;\i]kl wm+TI&hSeS0K-J":U["E-b[dwOPUtk@9?[w9B$dVV[S<fM70qz)ZݯKU;ov/O4?UN_])뗟o*0&uҗAzrߞo~͏7?xkw즇;e~u0LeODAIX:KeR7G,Il(hһάց :ɟ1|$YY-ۂ_m=d2lJ2kDz]uVݑ)o _ԪExZ:i̚l,$URJr2_]S>c%~oog̞ /?IQb?|__q:r|H?׬w zq~[MŽrVlv",T#wč%&ZQ43me LL~N21T4WIyMJo":ٗ2^R8I&gE5UH:*o-gFeGSjiX[4jmU cB+/V/IOlfVW~]iՎJ%eEw\.zwu%o٫svݿGٛӡoޓXI/Xy+IQHjY,dP%@1Ov7)`kg43`w-=7A'h^&?حAEx$Q5r:XbᗼӗSܤU c #Mŝ pտz>.Sb.λD4]OVL*~V~0˛ }+|~?=_+xWoo/JZ}$AuY&7K&U$0)_S -(EA)-6 UHw&2^NdpHa\,ʗ)<ܑ\e8[ز*U-WTF4dڳz7ǝz}wփճ"~Uimv)澢*O3}go%G '?Cpյo罹.r?l@{&RO0oH.o9] ojiKl1*tȻ,Fj28qMy{s{sv_"w;[_ݡ%G9;JAa<14Znrr#zMuwzYŋU!}_?mg=|ô*;DPEፒ]&y߽v#=ל' ˃3!P O.3mcw7>A~Yq?zUimmݳ?y2񇮟ӵ~/ ?}5"ph<6j{Hۃ/S|g[-{zgG^iՎaDO~':_?yZ#~B oQ߯>oHrz)ɪp͏@nmjփrTo}*@`/Xy 6yLۭWZf;(NI+*df%90 Hi\^NV!J3^iUZKCğ>H@ D?7~ @`Y>~@ Dğt?u  80'OtQ? w@P@` !?`c@  8"Ŀ@/>/O*'?~ه ~"a A y|,Xo&0 @&g /, b(a=*H 7( 4VJF$08 SM޽{,9\اk"Hb9/..I_E&rU$&U&ZK Z݁EfG\LG9.12@(r%^'$zj(U#*.s C\>^竰~~Q{"\0rR@̔KrJ7-`T-j ]fMtHovGg!#̊@5dbC98 ~{u~wώOG<%TJIEWjySu$8EX !PMqV}VaBϿ9+^YՏXD,!:(JT1_|>UU'~u:} 3jo:N1kHPL_XpX]>%~ghv͎W'~ö5;ۀX 񗳬bS?v0h*lsNrLca0=q#.A20턆&ZNBʔrǃ*,3v!ڶkyq\ʣU8 !54p)df @`d?{u?}j_yj'ηYa @`U$OǏ}z=E'V@ Fw2_ӥ(. @`YO@ ,#K@ , e@ pm&ӜUBcXoc׍;GίҫnYK"a $ jfz:,U+דoJ[M֯ZcW)D`I◌2cTe ·eHpM3[:v/t<"^H_ ?I2_:梨*M* ,Q?;E/Pti*;2|Y,s+?uYT !z4DpkMlf>K`و)e%@?i[7 q[ƷlD *ЁrZyaNU-GG&>:Z8JVD}zጹ4ZOHWRQNgE$jl;°>^@`Iϝ&k}5BC6D6{}::`SBQj-2kM8+O}%$aBƵj:>nVqW-Vc  ,Ll4!Q)ɆnۧD8taK:k^l5Yu!~ V;#5Xd}eC: l`oq}{UJjGOiCLqa 7TOCJk/[9 ʸ6?q62D&QY=ziC#[]`dRC~,Lr.32l/e sIN$,l/Ol6KI/#A5Śmd[iT픡wªIj(hԯ`WE䥳` sBS+qK_2Iֻ@X1oD{1߁F 㪕:<0nKm?xph- @Q*@rߤ@`"@`#,LsSX Neiz1蝕S!$s5y!wTmVHdlTN{,@`&$~9g:PGdpk _pGi|UӲ*⅔$mB6ow&c!k@`u))N_q@}2NzEVϑZb$|qRšCuxQ䃓\ؑ`֔<EoNp$U[PQ5,X"AeP [ Tz+3,ܖĮʡ;ğ,NO/H*oVͪ :#F3ʩ@OՑfK[-ƭ#!bn_u""t 6&~ofE;AAF}yR`9}Z3_rM=*raS+^Ыr '@`#h:>}RSm`dzOs)o:U[ftPQ 8˦` N ğ?d%oB$kܑ7Gm:]427q3-X(1|Z:P3KU, h/e$* ۩|%ٚH@`: t0@v|wށ? F @ 1@ PA o &A`ao}k OMteP0`$R&w%$3o΃pYVy!@,qxЇK.[ ݁B KdQBv"ߝՀ/`ϯ,|͗MDŚ` CG\v"u\Ա:m$|aw,tY4dG(/OG!ڵ6F8BquLUEe{){ mlj*JIV˝FY,T1$9Xw3wqFYE<)U;ʪF<2,UYpmU,;i,aGENjfwN!MdT`$CDsF UL,XVH:rPyefG6&FX7ڶFV`m>ܰ~?9PA o? @* > ߤ@`"]}+Έ |ߪpC~&DZfeS65C6-Jǽ6Z},qj`R$wxJkϻG]cS,2w/+](!9_6MD /ҡcڥ# ,Iԛp*3%!7KX;[P *hwIBUJlI 9T˶*M A.UMřCmK1ng;]άG;׌ZyW#e,ީ#־Zuj3*r];qZ2pGW JAhW^Pi .ẉ2G"푒;4d"8:)SnVMvN:z&l.0ܞBk3&3`ɽ^C:Jq#ՂgV˨*BZ;NA j*EsDZC)WEZF\$]n#.4*vJX3sr)H,Ù6֠ŋ`+nlYjT"T 0Z4Uatc@~KeJS+_V~ڪܱ0憃Z}߻` }.QJkO/݌e,CԊ];2"v oriaD-?X}9ŇbN0Ft75Oq6\PJq*8jU}BiI(=u)$_U;ǘp/(L[^s5ߴVS8:|VD\FNZ*J% Y"V!mc{E4V'L%&0P^Dw:]RͽHė)j%j$mJTB #ѡak#oݤJp,i|GVw"V>f [Uj;%rjSka'e{nw Ё:FhZVzE5@X@8ȃ>p - ~d@#BD@ @|mAA^Zw+wge v;?Bm| &~Evu"*^c!A<}Ozf%'?MM&O)*&Ul#wx,%ٝ Y4˗ 9v]DU.3b6k}f0UxYU缈kKUGwIt<Ō˦Lȝ*F'|7—MR[X0 )+ζ@vHK{QQOv0IZZӉ# ͞[lu/{(|]s#Hvv"L[KiFGt,|tʬib(f#B+Նv"v3~tKժETf&jr%NfWKΈOj:!6Y,ղgT,a^"~O\"#ozT(zJF DCjfw:\SV9SrPlPώ8[9[ NN~($s[Z",]>_ry9o"L ljh6a8 j;N>XcT*Y@S S,I4Z`ӑNe9PKeTc2^< $.>k\Hm :~”"cb+S:rW9luʚUZOͪ[.&,ȼl.9Pg8՞{Q5t/ؾLfX;E6g^ή,{Vs)6Ȫ#V3 H{Y/)Yǔ`\"mnSNgihx:Ju(3TkY&W)? ~`DƔuvr#0񧔕U*oNSeYTMػAhdFZ+fֲE~D^5@FMx4c$OppSGFQ}i pyYNw JY}C n$O"Pt᱆™Xg jX 4q"?@p1-"ZЂ"pqqGO<^I,ɟsO>?IJҋ{*}^0. ?gÓXG @5"?Yvyrw}w\t4sV}~itV*gH+ߊI@&ߵ{!p>"xZ\g9Tr"9:?{5G*W~#η<[֪!/D j._S?yDjeo3ez+QM¦ِM~AB t{5OBl@/&tpYEf <3V+Hf>W'N2#$o٨J4"ЗXN*ÍW*Wڵ oI2.qxewL"~ 5Ցg~9w"ӆ兜c"i INm 3e:)b&Љ㟃cnUbڦÃSJ`Πli^OL-F}F;Um#eY ioawoUdD˙˙|٫~;%WȜq,-H1 [u cOtL @ 7n5+WkV\#dd* J~"n!.i l\$GFRuwyXZĿoϪW |ʹfW|d s--/ƛ>p ՍT7U誾6aDҦzXWcG 2"8֗{^օ:ñNlA _/~gOW_$L6Ek &\GDJ`F7a@  #  M F  j7&gʁ@,}`vx&#YC-?ZɅ=4Cf5;ɖ-!_⏜ɇ,":R5,+Tw"RGFsБ{\tUoU!+RAM Q26CRےg Ukp(jmܗP[wc4,Qۘ*3>˙C3 _)'ӖM¦ms`<4O4|vvZ>/'~? 4 VLjPun:dVݔ_xrg/Zyu$RL0pX9`Fq-Մc9&Adk5KZR[ja[}6,ʗN%ZN&( q5եk*}_*xrSFY_$e:KU5ak꣪vmrH,I99$<"Q.*oS0314~lML.v@[y[FFsGEAZJPbƴ* n-ieZf`j7j:dOM0WL ܊ܙɘ #`.U V(Eʼn-:Τ$2Pd 3ՔbtNUbUC2OӉ߲`*ߩaVKH1T}Q[FdDk-rv+|HG@GUGvh<;;C@u4a^59XStYgwގe4@Z5w|liy1թ|9Ш  >J}PnZ"Uk~Fܑ PG˘"! 5Gڲ0pA%2Qb][0ZH)g-H*ګ)'vePndCwo׸fl;jiiG[,7F<ճ$K2i;[1^OSa)f~$YNr,`kȨDt:5 VF[_-cvGfl,4 TeU 2h@%lS1BjKKԜ_9TEKӻ-jh}@$3.^VA淰VܽUZ?"WՒt%?d euⱈYNT*:MG妕jYR_t |Krsqvt`QWsRM,Z5FqdXt 6jޗYOBV}BX/g9. "V1OYjeE}d+WH6ȎVTXzU@ p8u5֓s]O wXC`mU>3ozR_X=NU @pN, @* *D@ p87˹߳`"X%ݵٽp0!0GC(钬yc=%pDb-eJ}#:BKQ$P,Q8݋HGd9jk3o:al/eX$ueHՍ#:[SٹʵN°%2 _CqSD{%!58uLWNC!_2uXtJJk uşHdwWG(_ _˔f>Z_Ses2h nkB8.⯶"4G=pgYG=SP;C,dtk˖;'*2gȔg,˱wJ4 -)so'e,It(vE[[6jgWl7~ O.ӳQ^<{tSȔz[e_v:GD2I4 kDVŶ&̈́3n__;Qvvuh@ #ZЩ@CwDr'38[$j6d`%ܤ$2"UUAwAtQϡ [^?2ǀr Hמ!חFtk}‘G~#ݳOCdPsڋӼ]h\"T >-MJ~=::8l4( JG9zϗy;VlБliyq О$CPHuS |5%Լeve>D LT"^\V| &,=Sr[<@0`Y&e?-<&@UlV"o'ZX?vXyj5G7( '@d@ G W@7v7@wMu/V/>hRyu;۶.\NMϿsS|~iSyI-{()b^A e}ɨKdԬ8NB>E39ahntmHM O&pBN bgկj|DaJ.LN{R!8ILej'jTTe}f>HJ$vE0qr:.Gu2S\cGČ;U@`aw=ʛ৅ܮI¹6d;~?RYTrux$ܑ'βK+ (ҹy(4 U"P% -7.nZN/Q5@̽骪i7jEsU%ӵ$ۊX~iElJ2^-XvWyia˒iBI#YMu93YɿZ{}3lqE%#5˨6 S/~5\숈_TTa;+]Ƌ3lFƑWB3Dp'T? ּ ljP-4zSrZ#Dm\sN2c'~î#Tro߱QعǢE{\uzꗼG'uUz`l UhX2OWs%S_}֗S*R#l+vDG9x6eq|kgbɬςקw-NN3SCMT "#) KCRdL%sLE6vr| kPmh_&>veuŦjmav .LgOG.?Rʈk B(Җ*wlY P_Gz|-hnbTmkU-mZ5/ޠdYɔ>Fedj#6sa^[4Xr@8A/_Z\ !g׷J~챵`?Sh@ Z@ @ 0xL j64M<@ /'~PGY|Od |C|KG}Y'35%[W!$B*5rG~Ջ`b3=t 8 {Rnl*ɱc5K#w!5|٦U%]rn$iFjMZܙXR,L&+i7R,JQ:VfQNZ*tFR|SaQi :(tkZPxf O˟6QEจ-VHգ=ϱll+fJx(w$|:t޵66fVmVzL<+D?!fNұ@Yߧ֔"*y+rX-6:E )2Iɬ(`;C؍ąAF}'lSqNF,B$S=(8T=m:i@#"~>޺8M$~ٱRN,"~"҈ѭ֯1a 0O更;гʗT4{Q 5c2.] ԑ|`~5eK$ v _Nb^8j@`)&>};x@B~!@ ? @lc@񇡂 @#@ M  & 6X&Gfzz6GxYgt;lqbCD'5*|0tR݅a$0"]`2T= fܖM.`9: lE`YgJ*+D'z1;ҋ,NJiP%_:>^H"Q )a-/O? CHnH`X i?aKrKiFHS-F1^dnvYˈ+1#V;]*!QO;l%jf~9tljUH%~gpgK2#WǨN.坹'$`+@mNQ(tCJ,=jggW?sК$TϵqL,## Yayҍ3oA$'#~]P:QI?ϐE`鰭sjU>\>Bq/L)iIeE\v0OHtq I kV !QG:3@`NDuN$~\8#qӊ:0x#68Nw[T6g 7Vg E`a/ڡ聀&On6& *rd],VP VqF?o<[6H[9zԋ~r@{x9Sr ,LqC!ٍ@Ow@ r..O_=/O*'?~_wIr3O[9l ׀$7O_E&$ ` kD 2+H X Յ B8 S3IrkD$"3OmZt5,,qqYf!~V䡣{Lz6{Q 'NSZZOEtR}UӬĤ͑XvQk¿##HnႳ$bC^ -_d1t)fڱ1s&rE1r"VPVFg DL;ԈNJyBlFzdf"`;n5L7;gDO\[0O(ҝvVŦ8RU>VJ'~%aڰ4gTGhoU=k*K/TiCنlqϡ'=TS]Z}2fD­1Mb̮;Ya -ZHg,UQ2IdX& qM5FE 6%4VhU l,7'#:O=OWaz: U} YŪT&(GTBWRsg#_K ݱʱFTQ^jzH :eeO:K6ULT(h>0reN8#5Og!U৐, ]%NztSkm.|/"i}i@Z|iw ZjqBS?Jˎ;Ey\n}~P57txc" :4KīiRw ET);fI5}Gqu_ʸ~\r;P,[T^Nڰ%5@,Xj-y'X}t$~v0M Xk)8ƺ`qB_UJbP kjz; H#EXk}~oCGHt w-e,SB'T{~w#8;lf`V9hGRiھjR}RJ/C&Uk)RtRNL8WSoCʔFFl4W5UՈT[6kS{UY G8$ʻ*TF+ NV{%CC6etWݴJ,}[%u%K֟4?6w!۩MN\XVa>VP VPhyL>3UETM gnyuԚgkVDN-M6Gʂ**C5XIс; FMolIE ꝅlgSGLr֑a{E2JݺD&IZn?hSP X msn ce3ч@lڦ О'W'v|{@ 0 D7>ѣϞT=:?$/pm]@h&ԏ_M~'I$OS}'~yR8IH?}qN,ϟ>G }tp?} y$[%ʧl* "{%&j&ׯ//qmqn!F`wBnO^2. Bp`\_//p  jkL}ٸ˻? gk)WxK\@ 0[-bM< S**I~C]\֕{x5Ӱ:N @A@=![ "_ŬrpM) LIU]/*Y0:luߴS@įe|? 纚`.]Z2@:q=  f@ dRd!~WY A@`(Z<'C}wKNS6 ,$W~OoϤؒw=A6W7Z!8Wѭ"`U~\ʼnbn=%}s@}n]L%M`>X! ~t% 4'y}g/Ϟ}}k=ݓYʁnj(u'}ٓG/?3|ID2D'))@`:u?}'h@ G 7 O@l? !?`oe@ 0&7p OoH~KWk\@ #p 8lO<@ T?PәIENDB`WorkBench-1.8.2/Kit/MacOSX/000755 000765 000024 00000000000 13221441402 015500 5ustar00barrystaff000000 000000 WorkBench-1.8.2/Kit/Linux/000755 000765 000024 00000000000 13221441402 015505 5ustar00barrystaff000000 000000 WorkBench-1.8.2/Kit/Win32/000755 000765 000024 00000000000 13221441402 015310 5ustar00barrystaff000000 000000 WorkBench-1.8.2/Kit/Win32/win32.mak000644 000765 000024 00000001267 12701741752 016767 0ustar00barrystaff000000 000000 all: kit kit: tmp\workbench.iss info_before.txt copy_setup.cmd copy ..\..\LICENSE.txt tmp\workbench_LICENSE.txt copy ..\..\Source\bin\wb.exe tmp\WorkBench.exe copy ..\..\Source\bin\wb.exe.manifest tmp\WorkBench.exe.manifest copy k:\subversion\vcredist_x86_2008.exe tmp "c:\Program Files (x86)\Inno Setup 5\ISCC.exe" tmp\workbench.iss tmp\setup_copy.cmd info_before.txt: tmp\workbench.iss copy_setup.cmd: tmp\workbench.iss tmp\workbench.iss: setup_kit_files.py workbench.iss if exist tmp rmdir /s /q tmp mkdir tmp $(PYTHON) setup_kit_files.py debug: "c:\Program Files (x86)\Inno Setup 5\Compil32.exe" tmp\workbench.iss clean: if exist tmp rmdir /s /q tmp WorkBench-1.8.2/Kit/Win32/setup_kit_files.py000644 000765 000024 00000004677 12710627174 021107 0ustar00barrystaff000000 000000 print 'Info: setup_version_handling.py' import sys sys.path.insert( 0, '..\\..\\Source') import wb_version import pysvn import time import os import datetime workbench_version_string = '%d.%d.%d-%d' % (wb_version.major, wb_version.minor, wb_version.patch, wb_version.build) python_version_string = '%d.%d.%d' % (sys.version_info[0], sys.version_info[1], sys.version_info[2]) pysvn_version_string = '%d.%d.%d' % (pysvn.version[0], pysvn.version[1], pysvn.version[2]) svn_version_package_string = '%d%d%d' % (pysvn.svn_version[0], pysvn.svn_version[1], pysvn.svn_version[2]) svn_version_string = '%d.%d.%d' % (pysvn.svn_version[0], pysvn.svn_version[1], pysvn.svn_version[2]) build_time = time.time() build_time_str = time.strftime( '%d-%b-%Y %H:%M', time.localtime( build_time ) ) print 'Info: Create info_before.txt' f = file('tmp\\info_before.txt','w') f.write( '''WorkBench %s for Subversion %s Barry Scott %s ''' % (workbench_version_string, svn_version_string, build_time_str) ) f.close() print 'Info: Creating workbench-branded.iss from workbench.iss' f = open( 'workbench.iss', 'r' ) inno_contents = f.read() f.close() syms = { 'year': '%d' % (datetime.datetime.now().year,), 'version': workbench_version_string } f = open( r'tmp\workbench.iss', 'w' ) f.write( inno_contents % syms ) for filename in os.listdir('..\\..\\source\\bin\\support'): if filename.lower() not in ['msvcp60.dll','support']: f.write( 'Source: "..\\..\\..\\source\\bin\\support\\%s"; Flags: ignoreversion; DestDir: "{app}\\Support";\n' % (filename,) ) f.write( 'Source: "..\\..\\..\\docs\\WorkBench.html"; DestDir: "{app}";\n' ) docs_files_dir = '..\\..\\docs\\WorkBench_files' for filename in os.listdir( docs_files_dir ): if os.path.isfile( os.path.join( docs_files_dir, filename ) ): f.write( 'Source: "..\\..\\..\\docs\\WorkBench_files\\%s"; DestDir: "{app}\\WorkBench_files";\n' % filename ) locale_files_dir = '..\\..\\Source\\locale' for lang in os.listdir( locale_files_dir ): f.write( 'Source: "..\\..\\..\\Source\\locale\\%s\LC_MESSAGES\\pysvn_workbench.mo"; ' 'DestDir: "{app}\\locale\\%s\LC_MESSAGES";\n' % (lang, lang) ) f.close() print 'Info: Create setup_copy.cmd' f = file( 'tmp\\setup_copy.cmd', 'w' ) f.write( 'copy tmp\\Output\\setup.exe tmp\\Output\\pysvn-workbench-svn%s-%s.exe\n' % (svn_version_package_string, workbench_version_string) ) f.close() WorkBench-1.8.2/Kit/Win32/workbench.iss000644 000765 000024 00000002261 12701531545 020025 0ustar00barrystaff000000 000000 ; ; -- workbench.iss -- ; [Code] function InitializeSetup(): Boolean; begin Result := true; end; [Setup] AppName=WorkBench AppVerName=WorkBench %(version)s AppCopyright=Copyright (C) 2003-%(year)s Barry A. Scott DefaultDirName={pf}\PySVN\WorkBench DefaultGroupName=WorkBench for Subversion UninstallDisplayIcon={app}\WorkBench.exe DisableStartupPrompt=yes InfoBeforeFile=info_before.txt Compression=bzip/9 [Icons] Name: "{group}\WorkBench"; Filename: "{app}\WorkBench.exe"; Name: "{group}\Documentation"; Filename: "{app}\workbench.html"; Name: "{group}\License"; Filename: "{app}\workbench_LICENSE.txt"; Name: "{group}\Web Site"; Filename: "http://pysvn.tigris.org"; [Run] Filename: {tmp}\vcredist_x86_2008.exe; Parameters: "/q"; StatusMsg: Installing VC++ 2008 Win32 Redistributables... [Files] Source: "workbench_LICENSE.txt"; DestDir: "{app}"; Source: "..\..\..\Docs\WorkBench.html"; DestDir: "{app}"; Source: "WorkBench.exe"; DestDir: "{app}"; Flags: ignoreversion; Source: "WorkBench.exe.manifest"; DestDir: "{app}"; Flags: ignoreversion; Source: "vcredist_x86_2008.exe"; DestDir: {tmp}; Flags: deleteafterinstall ; more file are appending here WorkBench-1.8.2/Kit/Linux/pysvn-workbench.spec000644 000765 000024 00000002573 12672511355 021545 0ustar00barrystaff000000 000000 Name: pysvn-workbench Version: 1.7.1 Release: 1%{?dist} Summary: PySVN WorkBench IDE License: ASL 1.1 URL: http://pysvn.tigris.org/ Source0: http://pysvn.barrys-emacs.org/source_kits/%{name}-%{version}.tar.gz BuildArch: noarch BuildRequires: python Requires: wxPython >= 2.8 Requires: pysvn >= 1.8.0 Requires: gettext %description PySVN WorkBench Features * Easy to learn and use * Most subversion client operations in a GUI * Enhanced subversion operations (rename of modified files etc) * Support software development workflow * Builtin GUI diff showing line and character diffs * Ability to diff between revisions of a file's history * Runs on Windows, Mac OS X and Unix platforms * Implemented in Python, allowing customization %prep %setup -q -n %{name}-%{version} %build %configure make all PYTHON=%{__python} %install rm -rf $RPM_BUILD_ROOT %make_install %files %defattr(-, root, root, -) %doc Docs/WorkBench.html Docs/WorkBench_files/*.png %license LICENSE.txt /usr/bin/pysvn-workbench /usr/share/pysvn-workbench /usr/share/applications/pysvn-workbench.desktop %attr(0644,root,root) %{_mandir}/man1/pysvn-workbench.1.gz %changelog * Thu Mar 17 2016 barry scott 1.7.1-1 - Take upstream fixes * Sun Aug 23 2015 barry scott 1.7.0-1 - First version WorkBench-1.8.2/Kit/Linux/pysvn-workbench.1000644 000765 000024 00000001651 12615713720 020744 0ustar00barrystaff000000 000000 .TH pysvn-workbench 1 "2 Nov 2015" "" "pysvn-workbench man page" .SH NAME pysvn\-workbench \- WorkBench is a GUI for working with Subversion .SH SYNOPSIS WorkBench is a GUI for working with Subversion .SH DESCRIPTION WorkBench is a GUI for working with Subversion. PySVN WorkBench Features * Easy to learn and use * Most subversion client operations in a GUI * Enhanced subversion operations (rename of modified files etc) * Support software development workflow * Builtin GUI diff showing line and character diffs * Ability to diff between revisions of a file's history * Runs on Windows, Mac OS X and Unix platforms * Implemented in Python, allowing customization Detailed help is available as HTML that is installed in: file:///usr/share/doc/pysvn-workbench/WorkBench.html .SH OPTIONS None .SH SEE ALSO svn(1) .SH BUGS Please report any bugs on http://pysvn.tigris.org .SH AUTHOR Barry Scott (barry@barrys-emacs.org) WorkBench-1.8.2/Kit/Linux/make-devel-src-rpm.sh000755 000765 000024 00000002716 12672507512 021463 0ustar00barrystaff000000 000000 #!/bin/bash # # make-devel-src-rpm.sh # CMD="$1" echo "Info: Creating source tarball" . ${BUILDER_TOP_DIR}/Builder/version.info V=${MAJOR}.${MINOR}.${PATCH} rm -rf /tmp/pysvn-workbench-${V} BUILD=$( svnversion .. ) KIT_BASENAME=pysvn-workbench-${V} rm -rf tmp mkdir -p tmp pushd tmp svn export --quiet ${BUILDER_TOP_DIR} ${KIT_BASENAME} cat <pysvn-workbench-${V}/Builder/version.info MAJOR=${MAJOR} MINOR=${MINOR} PATCH=${PATCH} BUILD=${BUILD} EOF tar czf ${KIT_BASENAME}.tar.gz ${KIT_BASENAME} popd echo "Info: Creating SRPM for ${KIT_BASENAME}" sudo \ mock \ --buildsrpm --dnf \ --spec pysvn-workbench.spec \ --sources tmp/${KIT_BASENAME}.tar.gz MOCK_ROOT=$( sudo mock -p ) MOCK_BUILD_DIR=${MOCK_ROOT}/builddir/build ls -l ${MOCK_BUILD_DIR}/SRPMS set $(tr : ' ' tmp/${KIT_BASENAME}.tar.gz sudo \ mock \ --buildsrpm --dnf \ --spec pysvn-workbench.spec \ --sources tmp/${KIT_BASENAME}.tar.gz MOCK_ROOT=$( sudo mock -p ) MOCK_BUILD_DIR=${MOCK_ROOT}/builddir/build ls -l ${MOCK_BUILD_DIR}/SRPMS set $(tr : ' ' ) """ __all__ = ["BundleBuilder", "BundleBuilderError", "AppBuilder", "buildapp"] from warnings import warnpy3k warnpy3k("In 3.x, the bundlebuilder module is removed.", stacklevel=2) import sys import os, errno, shutil import imp, marshal import re from copy import deepcopy import getopt from plistlib import Plist from types import FunctionType as function class BundleBuilderError(Exception): pass class Defaults: """Class attributes that don't start with an underscore and are not functions or classmethods are (deep)copied to self.__dict__. This allows for mutable default values. """ def __init__(self, **kwargs): defaults = self._getDefaults() defaults.update(kwargs) self.__dict__.update(defaults) def _getDefaults(cls): defaults = {} for base in cls.__bases__: if hasattr(base, "_getDefaults"): defaults.update(base._getDefaults()) for name, value in cls.__dict__.items(): if name[0] != "_" and not isinstance(value, (function, classmethod)): defaults[name] = deepcopy(value) return defaults _getDefaults = classmethod(_getDefaults) class BundleBuilder(Defaults): """BundleBuilder is a barebones class for assembling bundles. It knows nothing about executables or icons, it only copies files and creates the PkgInfo and Info.plist files. """ # (Note that Defaults.__init__ (deep)copies these values to # instance variables. Mutable defaults are therefore safe.) # Name of the bundle, with or without extension. name = None # The property list ("plist") plist = Plist(CFBundleDevelopmentRegion = "English", CFBundleInfoDictionaryVersion = "6.0") # The type of the bundle. type = "BNDL" # The creator code of the bundle. creator = None # the CFBundleIdentifier (this is used for the preferences file name) bundle_id = None # List of files that have to be copied to /Contents/Resources. resources = [] # List of (src, dest) tuples; dest should be a path relative to the bundle # (eg. "Contents/Resources/MyStuff/SomeFile.ext). files = [] # List of shared libraries (dylibs, Frameworks) to bundle with the app # will be placed in Contents/Frameworks libs = [] # Directory where the bundle will be assembled. builddir = "build" # Make symlinks instead copying files. This is handy during debugging, but # makes the bundle non-distributable. symlink = 0 # Verbosity level. verbosity = 1 # Destination root directory destroot = "" def setup(self): # XXX rethink self.name munging, this is brittle. self.name, ext = os.path.splitext(self.name) if not ext: ext = ".bundle" bundleextension = ext # misc (derived) attributes self.bundlepath = pathjoin(self.builddir, self.name + bundleextension) plist = self.plist plist.CFBundleName = self.name plist.CFBundlePackageType = self.type if self.creator is None: if hasattr(plist, "CFBundleSignature"): self.creator = plist.CFBundleSignature else: self.creator = "????" plist.CFBundleSignature = self.creator if self.bundle_id: plist.CFBundleIdentifier = self.bundle_id elif not hasattr(plist, "CFBundleIdentifier"): plist.CFBundleIdentifier = self.name def build(self): """Build the bundle.""" builddir = self.builddir if builddir and not os.path.exists(builddir): os.mkdir(builddir) self.message("Building %s" % repr(self.bundlepath), 1) if os.path.exists(self.bundlepath): shutil.rmtree(self.bundlepath) if os.path.exists(self.bundlepath + '~'): shutil.rmtree(self.bundlepath + '~') bp = self.bundlepath # Create the app bundle in a temporary location and then # rename the completed bundle. This way the Finder will # never see an incomplete bundle (where it might pick up # and cache the wrong meta data) self.bundlepath = bp + '~' try: os.mkdir(self.bundlepath) self.preProcess() self._copyFiles() self._addMetaFiles() self.postProcess() os.rename(self.bundlepath, bp) finally: self.bundlepath = bp self.message("Done.", 1) def preProcess(self): """Hook for subclasses.""" pass def postProcess(self): """Hook for subclasses.""" pass def _addMetaFiles(self): contents = pathjoin(self.bundlepath, "Contents") makedirs(contents) # # Write Contents/PkgInfo assert len(self.type) == len(self.creator) == 4, \ "type and creator must be 4-byte strings." pkginfo = pathjoin(contents, "PkgInfo") f = open(pkginfo, "wb") f.write(self.type + self.creator) f.close() # # Write Contents/Info.plist infoplist = pathjoin(contents, "Info.plist") self.plist.write(infoplist) def _copyFiles(self): files = self.files[:] for path in self.resources: files.append((path, pathjoin("Contents", "Resources", os.path.basename(path)))) for path in self.libs: files.append((path, pathjoin("Contents", "Frameworks", os.path.basename(path)))) if self.symlink: self.message("Making symbolic links", 1) msg = "Making symlink from" else: self.message("Copying files", 1) msg = "Copying" files.sort() for src, dst in files: if os.path.isdir(src): self.message("%s %s/ to %s/" % (msg, src, dst), 2) else: self.message("%s %s to %s" % (msg, src, dst), 2) dst = pathjoin(self.bundlepath, dst) if self.symlink: symlink(src, dst, mkdirs=1) else: copy(src, dst, mkdirs=1) def message(self, msg, level=0): if level <= self.verbosity: indent = "" if level > 1: indent = (level - 1) * " " sys.stderr.write(indent + msg + "\n") def report(self): # XXX something decent pass if __debug__: PYC_EXT = ".pyc" else: PYC_EXT = ".pyo" MAGIC = imp.get_magic() USE_ZIPIMPORT = "zipimport" in sys.builtin_module_names # For standalone apps, we have our own minimal site.py. We don't need # all the cruft of the real site.py. SITE_PY = """\ import sys if not %(semi_standalone)s: del sys.path[1:] # sys.path[0] is Contents/Resources/ """ if USE_ZIPIMPORT: ZIP_ARCHIVE = "Modules.zip" SITE_PY += "sys.path.append(sys.path[0] + '/%s')\n" % ZIP_ARCHIVE def getPycData(fullname, code, ispkg): if ispkg: fullname += ".__init__" path = fullname.replace(".", os.sep) + PYC_EXT return path, MAGIC + '\0\0\0\0' + marshal.dumps(code) # # Extension modules can't be in the modules zip archive, so a placeholder # is added instead, that loads the extension from a specified location. # EXT_LOADER = """\ def __load(): import imp, sys, os for p in sys.path: path = os.path.join(p, "%(filename)s") if os.path.exists(path): break else: assert 0, "file not found: %(filename)s" mod = imp.load_dynamic("%(name)s", path) __load() del __load """ MAYMISS_MODULES = ['mac', 'os2', 'nt', 'ntpath', 'dos', 'dospath', 'win32api', 'ce', '_winreg', 'nturl2path', 'sitecustomize', 'org.python.core', 'riscos', 'riscosenviron', 'riscospath' ] STRIP_EXEC = "/usr/bin/strip" # # We're using a stock interpreter to run the app, yet we need # a way to pass the Python main program to the interpreter. The # bootstrapping script fires up the interpreter with the right # arguments. os.execve() is used as OSX doesn't like us to # start a real new process. Also, the executable name must match # the CFBundleExecutable value in the Info.plist, so we lie # deliberately with argv[0]. The actual Python executable is # passed in an environment variable so we can "repair" # sys.executable later. # BOOTSTRAP_SCRIPT = """\ #!%(hashbang)s import sys, os execdir = os.path.dirname(sys.argv[0]) executable = os.path.join(execdir, "%(executable)s") resdir = os.path.join(os.path.dirname(execdir), "Resources") libdir = os.path.join(os.path.dirname(execdir), "Frameworks") mainprogram = os.path.join(resdir, "%(mainprogram)s") sys.argv.insert(1, mainprogram) if %(standalone)s or %(semi_standalone)s: os.environ["PYTHONPATH"] = resdir if %(standalone)s: os.environ["PYTHONHOME"] = resdir else: pypath = os.getenv("PYTHONPATH", "") if pypath: pypath = ":" + pypath os.environ["PYTHONPATH"] = resdir + pypath os.environ["PYTHONEXECUTABLE"] = executable os.environ["DYLD_LIBRARY_PATH"] = libdir os.environ["DYLD_FRAMEWORK_PATH"] = libdir os.execve(executable, sys.argv, os.environ) """ # # Optional wrapper that converts "dropped files" into sys.argv values. # ARGV_EMULATOR = """\ import argvemulator, os argvemulator.ArgvCollector().mainloop() execfile(os.path.join(os.path.split(__file__)[0], "%(realmainprogram)s")) """ # # When building a standalone app with Python.framework, we need to copy # a subset from Python.framework to the bundle. The following list # specifies exactly what items we'll copy. # PYTHONFRAMEWORKGOODIES = [ "Python", # the Python core library "Resources/English.lproj", "Resources/Info.plist", ] def isFramework(): return sys.exec_prefix.find("Python.framework") > 0 LIB = os.path.join(sys.prefix, "lib", "python" + sys.version[:3]) SITE_PACKAGES = os.path.join(LIB, "site-packages") class AppBuilder(BundleBuilder): # Override type of the bundle. type = "APPL" # platform, name of the subfolder of Contents that contains the executable. platform = "MacOS" # A Python main program. If this argument is given, the main # executable in the bundle will be a small wrapper that invokes # the main program. (XXX Discuss why.) mainprogram = None # The main executable. If a Python main program is specified # the executable will be copied to Resources and be invoked # by the wrapper program mentioned above. Otherwise it will # simply be used as the main executable. executable = None # The name of the main nib, for Cocoa apps. *Must* be specified # when building a Cocoa app. nibname = None # The name of the icon file to be copied to Resources and used for # the Finder icon. iconfile = None # Symlink the executable instead of copying it. symlink_exec = 0 # If True, build standalone app. standalone = 0 # If True, build semi-standalone app (only includes third-party modules). semi_standalone = 0 # If set, use this for #! lines in stead of sys.executable python = None # If True, add a real main program that emulates sys.argv before calling # mainprogram argv_emulation = 0 # The following attributes are only used when building a standalone app. # Exclude these modules. excludeModules = [] # Include these modules. includeModules = [] # Include these packages. includePackages = [] # Strip binaries from debug info. strip = 0 # Found Python modules: [(name, codeobject, ispkg), ...] pymodules = [] # Modules that modulefinder couldn't find: missingModules = [] maybeMissingModules = [] def setup(self): if ((self.standalone or self.semi_standalone) and self.mainprogram is None): raise BundleBuilderError, ("must specify 'mainprogram' when " "building a standalone application.") if self.mainprogram is None and self.executable is None: raise BundleBuilderError, ("must specify either or both of " "'executable' and 'mainprogram'") self.execdir = pathjoin("Contents", self.platform) if self.name is not None: pass elif self.mainprogram is not None: self.name = os.path.splitext(os.path.basename(self.mainprogram))[0] elif executable is not None: self.name = os.path.splitext(os.path.basename(self.executable))[0] if self.name[-4:] != ".app": self.name += ".app" if self.executable is None: if not self.standalone and not isFramework(): self.symlink_exec = 1 if self.python: self.executable = self.python else: self.executable = sys.executable if self.nibname: self.plist.NSMainNibFile = self.nibname if not hasattr(self.plist, "NSPrincipalClass"): self.plist.NSPrincipalClass = "NSApplication" if self.standalone and isFramework(): self.addPythonFramework() BundleBuilder.setup(self) self.plist.CFBundleExecutable = self.name if self.standalone or self.semi_standalone: self.findDependencies() def preProcess(self): resdir = "Contents/Resources" if self.executable is not None: if self.mainprogram is None: execname = self.name else: execname = os.path.basename(self.executable) execpath = pathjoin(self.execdir, execname) if not self.symlink_exec: self.files.append((self.destroot + self.executable, execpath)) self.execpath = execpath if self.mainprogram is not None: mainprogram = os.path.basename(self.mainprogram) self.files.append((self.mainprogram, pathjoin(resdir, mainprogram))) if self.argv_emulation: # Change the main program, and create the helper main program (which # does argv collection and then calls the real main). # Also update the included modules (if we're creating a standalone # program) and the plist realmainprogram = mainprogram mainprogram = '__argvemulator_' + mainprogram resdirpath = pathjoin(self.bundlepath, resdir) mainprogrampath = pathjoin(resdirpath, mainprogram) makedirs(resdirpath) open(mainprogrampath, "w").write(ARGV_EMULATOR % locals()) if self.standalone or self.semi_standalone: self.includeModules.append("argvemulator") self.includeModules.append("os") if not self.plist.has_key("CFBundleDocumentTypes"): self.plist["CFBundleDocumentTypes"] = [ { "CFBundleTypeOSTypes" : [ "****", "fold", "disk"], "CFBundleTypeRole": "Viewer"}] # Write bootstrap script executable = os.path.basename(self.executable) execdir = pathjoin(self.bundlepath, self.execdir) bootstrappath = pathjoin(execdir, self.name) makedirs(execdir) if self.standalone or self.semi_standalone: # XXX we're screwed when the end user has deleted # /usr/bin/python hashbang = "/usr/bin/python" elif self.python: hashbang = self.python else: hashbang = os.path.realpath(sys.executable) standalone = self.standalone semi_standalone = self.semi_standalone open(bootstrappath, "w").write(BOOTSTRAP_SCRIPT % locals()) os.chmod(bootstrappath, 0775) if self.iconfile is not None: iconbase = os.path.basename(self.iconfile) self.plist.CFBundleIconFile = iconbase self.files.append((self.iconfile, pathjoin(resdir, iconbase))) def postProcess(self): if self.standalone or self.semi_standalone: self.addPythonModules() if self.strip and not self.symlink: self.stripBinaries() if self.symlink_exec and self.executable: self.message("Symlinking executable %s to %s" % (self.executable, self.execpath), 2) dst = pathjoin(self.bundlepath, self.execpath) makedirs(os.path.dirname(dst)) os.symlink(os.path.abspath(self.executable), dst) if self.missingModules or self.maybeMissingModules: self.reportMissing() def addPythonFramework(self): # If we're building a standalone app with Python.framework, # include a minimal subset of Python.framework, *unless* # Python.framework was specified manually in self.libs. for lib in self.libs: if os.path.basename(lib) == "Python.framework": # a Python.framework was specified as a library return frameworkpath = sys.exec_prefix[:sys.exec_prefix.find( "Python.framework") + len("Python.framework")] version = sys.version[:3] frameworkpath = pathjoin(frameworkpath, "Versions", version) destbase = pathjoin("Contents", "Frameworks", "Python.framework", "Versions", version) for item in PYTHONFRAMEWORKGOODIES: src = pathjoin(frameworkpath, item) dst = pathjoin(destbase, item) self.files.append((src, dst)) def _getSiteCode(self): return compile(SITE_PY % {"semi_standalone": self.semi_standalone}, "<-bundlebuilder.py->", "exec") def addPythonModules(self): self.message("Adding Python modules", 1) if USE_ZIPIMPORT: # Create a zip file containing all modules as pyc. import zipfile relpath = pathjoin("Contents", "Resources", ZIP_ARCHIVE) abspath = pathjoin(self.bundlepath, relpath) zf = zipfile.ZipFile(abspath, "w", zipfile.ZIP_DEFLATED) for name, code, ispkg in self.pymodules: self.message("Adding Python module %s" % name, 2) path, pyc = getPycData(name, code, ispkg) zf.writestr(path, pyc) zf.close() # add site.pyc sitepath = pathjoin(self.bundlepath, "Contents", "Resources", "site" + PYC_EXT) writePyc(self._getSiteCode(), sitepath) else: # Create individual .pyc files. for name, code, ispkg in self.pymodules: if ispkg: name += ".__init__" path = name.split(".") path = pathjoin("Contents", "Resources", *path) + PYC_EXT if ispkg: self.message("Adding Python package %s" % path, 2) else: self.message("Adding Python module %s" % path, 2) abspath = pathjoin(self.bundlepath, path) makedirs(os.path.dirname(abspath)) writePyc(code, abspath) def stripBinaries(self): if not os.path.exists(STRIP_EXEC): self.message("Error: can't strip binaries: no strip program at " "%s" % STRIP_EXEC, 0) else: import stat self.message("Stripping binaries", 1) def walk(top): for name in os.listdir(top): path = pathjoin(top, name) if os.path.islink(path): continue if os.path.isdir(path): walk(path) else: mod = os.stat(path)[stat.ST_MODE] if not (mod & 0100): continue relpath = path[len(self.bundlepath):] self.message("Stripping %s" % relpath, 2) inf, outf = os.popen4("%s -S \"%s\"" % (STRIP_EXEC, path)) output = outf.read().strip() if output: # usually not a real problem, like when we're # trying to strip a script self.message("Problem stripping %s:" % relpath, 3) self.message(output, 3) walk(self.bundlepath) def findDependencies(self): self.message("Finding module dependencies", 1) import modulefinder mf = modulefinder.ModuleFinder(excludes=self.excludeModules) if USE_ZIPIMPORT: # zipimport imports zlib, must add it manually mf.import_hook("zlib") # manually add our own site.py site = mf.add_module("site") site.__code__ = self._getSiteCode() mf.scan_code(site.__code__, site) # warnings.py gets imported implicitly from C mf.import_hook("warnings") includeModules = self.includeModules[:] for name in self.includePackages: includeModules.extend(findPackageContents(name).keys()) for name in includeModules: try: mf.import_hook(name) except ImportError: self.missingModules.append(name) mf.run_script(self.mainprogram) modules = mf.modules.items() modules.sort() for name, mod in modules: path = mod.__file__ if path and self.semi_standalone: # skip the standard library if path.startswith(LIB) and not path.startswith(SITE_PACKAGES): continue if path and mod.__code__ is None: # C extension filename = os.path.basename(path) pathitems = name.split(".")[:-1] + [filename] dstpath = pathjoin(*pathitems) if USE_ZIPIMPORT: if name != "zlib": # neatly pack all extension modules in a subdirectory, # except zlib, since it's neccesary for bootstrapping. dstpath = pathjoin("ExtensionModules", dstpath) # Python modules are stored in a Zip archive, but put # extensions in Contents/Resources/. Add a tiny "loader" # program in the Zip archive. Due to Thomas Heller. source = EXT_LOADER % {"name": name, "filename": dstpath} code = compile(source, "" % name, "exec") mod.__code__ = code self.files.append((path, pathjoin("Contents", "Resources", dstpath))) if mod.__code__ is not None: ispkg = mod.__path__ is not None if not USE_ZIPIMPORT or name != "site": # Our site.py is doing the bootstrapping, so we must # include a real .pyc file if USE_ZIPIMPORT is True. self.pymodules.append((name, mod.__code__, ispkg)) if hasattr(mf, "any_missing_maybe"): missing, maybe = mf.any_missing_maybe() else: missing = mf.any_missing() maybe = [] self.missingModules.extend(missing) self.maybeMissingModules.extend(maybe) def reportMissing(self): missing = [name for name in self.missingModules if name not in MAYMISS_MODULES] if self.maybeMissingModules: maybe = self.maybeMissingModules else: maybe = [name for name in missing if "." in name] missing = [name for name in missing if "." not in name] missing.sort() maybe.sort() if maybe: self.message("Warning: couldn't find the following submodules:", 1) self.message(" (Note that these could be false alarms -- " "it's not always", 1) self.message(" possible to distinguish between \"from package " "import submodule\" ", 1) self.message(" and \"from package import name\")", 1) for name in maybe: self.message(" ? " + name, 1) if missing: self.message("Warning: couldn't find the following modules:", 1) for name in missing: self.message(" ? " + name, 1) def report(self): # XXX something decent import pprint pprint.pprint(self.__dict__) if self.standalone or self.semi_standalone: self.reportMissing() # # Utilities. # SUFFIXES = [_suf for _suf, _mode, _tp in imp.get_suffixes()] identifierRE = re.compile(r"[_a-zA-z][_a-zA-Z0-9]*$") def findPackageContents(name, searchpath=None): head = name.split(".")[-1] if identifierRE.match(head) is None: return {} try: fp, path, (ext, mode, tp) = imp.find_module(head, searchpath) except ImportError: return {} modules = {name: None} if tp == imp.PKG_DIRECTORY and path: files = os.listdir(path) for sub in files: sub, ext = os.path.splitext(sub) fullname = name + "." + sub if sub != "__init__" and fullname not in modules: modules.update(findPackageContents(fullname, [path])) return modules def writePyc(code, path): f = open(path, "wb") f.write(MAGIC) f.write("\0" * 4) # don't bother about a time stamp marshal.dump(code, f) f.close() def copy(src, dst, mkdirs=0): """Copy a file or a directory.""" if mkdirs: makedirs(os.path.dirname(dst)) if os.path.isdir(src): shutil.copytree(src, dst, symlinks=1) else: shutil.copy2(src, dst) def copytodir(src, dstdir): """Copy a file or a directory to an existing directory.""" dst = pathjoin(dstdir, os.path.basename(src)) copy(src, dst) def makedirs(dir): """Make all directories leading up to 'dir' including the leaf directory. Don't moan if any path element already exists.""" try: os.makedirs(dir) except OSError, why: if why.errno != errno.EEXIST: raise def symlink(src, dst, mkdirs=0): """Copy a file or a directory.""" if not os.path.exists(src): raise IOError, "No such file or directory: '%s'" % src if mkdirs: makedirs(os.path.dirname(dst)) os.symlink(os.path.abspath(src), dst) def pathjoin(*args): """Safe wrapper for os.path.join: asserts that all but the first argument are relative paths.""" for seg in args[1:]: assert seg[0] != "/" return os.path.join(*args) cmdline_doc = """\ Usage: python bundlebuilder.py [options] command python mybuildscript.py [options] command Commands: build build the application report print a report Options: -b, --builddir=DIR the build directory; defaults to "build" -n, --name=NAME application name -r, --resource=FILE extra file or folder to be copied to Resources -f, --file=SRC:DST extra file or folder to be copied into the bundle; DST must be a path relative to the bundle root -e, --executable=FILE the executable to be used -m, --mainprogram=FILE the Python main program -a, --argv add a wrapper main program to create sys.argv -p, --plist=FILE .plist file (default: generate one) --nib=NAME main nib name -c, --creator=CCCC 4-char creator code (default: '????') --iconfile=FILE filename of the icon (an .icns file) to be used as the Finder icon --bundle-id=ID the CFBundleIdentifier, in reverse-dns format (eg. org.python.BuildApplet; this is used for the preferences file name) -l, --link symlink files/folder instead of copying them --link-exec symlink the executable instead of copying it --standalone build a standalone application, which is fully independent of a Python installation --semi-standalone build a standalone application, which depends on an installed Python, yet includes all third-party modules. --python=FILE Python to use in #! line in stead of current Python --lib=FILE shared library or framework to be copied into the bundle -x, --exclude=MODULE exclude module (with --(semi-)standalone) -i, --include=MODULE include module (with --(semi-)standalone) --package=PACKAGE include a whole package (with --(semi-)standalone) --strip strip binaries (remove debug info) -v, --verbose increase verbosity level -q, --quiet decrease verbosity level -h, --help print this message """ def usage(msg=None): if msg: print msg print cmdline_doc sys.exit(1) def main(builder=None): if builder is None: builder = AppBuilder(verbosity=1) shortopts = "b:n:r:f:e:m:c:p:lx:i:hvqa" longopts = ("builddir=", "name=", "resource=", "file=", "executable=", "mainprogram=", "creator=", "nib=", "plist=", "link", "link-exec", "help", "verbose", "quiet", "argv", "standalone", "exclude=", "include=", "package=", "strip", "iconfile=", "lib=", "python=", "semi-standalone", "bundle-id=", "destroot=") try: options, args = getopt.getopt(sys.argv[1:], shortopts, longopts) except getopt.error: usage() for opt, arg in options: if opt in ('-b', '--builddir'): builder.builddir = arg elif opt in ('-n', '--name'): builder.name = arg elif opt in ('-r', '--resource'): builder.resources.append(os.path.normpath(arg)) elif opt in ('-f', '--file'): srcdst = arg.split(':') if len(srcdst) != 2: usage("-f or --file argument must be two paths, " "separated by a colon") builder.files.append(srcdst) elif opt in ('-e', '--executable'): builder.executable = arg elif opt in ('-m', '--mainprogram'): builder.mainprogram = arg elif opt in ('-a', '--argv'): builder.argv_emulation = 1 elif opt in ('-c', '--creator'): builder.creator = arg elif opt == '--bundle-id': builder.bundle_id = arg elif opt == '--iconfile': builder.iconfile = arg elif opt == "--lib": builder.libs.append(os.path.normpath(arg)) elif opt == "--nib": builder.nibname = arg elif opt in ('-p', '--plist'): builder.plist = Plist.fromFile(arg) elif opt in ('-l', '--link'): builder.symlink = 1 elif opt == '--link-exec': builder.symlink_exec = 1 elif opt in ('-h', '--help'): usage() elif opt in ('-v', '--verbose'): builder.verbosity += 1 elif opt in ('-q', '--quiet'): builder.verbosity -= 1 elif opt == '--standalone': builder.standalone = 1 elif opt == '--semi-standalone': builder.semi_standalone = 1 elif opt == '--python': builder.python = arg elif opt in ('-x', '--exclude'): builder.excludeModules.append(arg) elif opt in ('-i', '--include'): builder.includeModules.append(arg) elif opt == '--package': builder.includePackages.append(arg) elif opt == '--strip': builder.strip = 1 elif opt == '--destroot': builder.destroot = arg if len(args) != 1: usage("Must specify one command ('build', 'report' or 'help')") command = args[0] if command == "build": builder.setup() builder.build() elif command == "report": builder.setup() builder.report() elif command == "help": usage() else: usage("Unknown command '%s'" % command) def buildapp(**kwargs): builder = AppBuilder(**kwargs) main(builder) if __name__ == "__main__": main() WorkBench-1.8.2/Kit/MacOSX/make_wb_bundle.py000644 000765 000024 00000006757 12772463601 021046 0ustar00barrystaff000000 000000 # # make_wb_bundle.py # import bundlebuilder import sys import pysvn import shutil import glob # make sure that we get 3.0 and not an earlier version if not hasattr(sys, 'frozen'): import wxversion wxversion.select( ['3.0'] ) import wx 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' ) # the pysvn package sys.path.append( '../../../Extension/Source' ) # Create the AppBuilder myapp = bundlebuilder.AppBuilder( verbosity=1 ) # Tell it where to find the main script - the one that loads on startup myapp.mainprogram = '../../Source/wb_main.py' myapp.standalone = 1 myapp.name = 'WorkBench.app' myapp.iconfile = '../../Source/wb.icns' # create the bundle here myapp.builddir = sys.argv[1] # includePackages forces certain packages to be added to the app bundle #myapp.includePackages.append("Menu") # Here you add supporting files and/or folders to your bundle myapp.files.append( ('../../Source/locale', 'Contents/Resources/locale') ) for locale_lang_root in glob.glob( '../../Source/locale/??' ): myapp.resources.append( os.path.join( locale_lang_root, 'LC_MESSAGES/pysvn_workbench.mo' ) ) # bundlebuilder does not yet have the capability to detect what shared libraries # are needed by your app - so in this case I am adding the wxPython libs manually py_ver = '%d.%d' % (sys.version_info[0], sys.version_info[1]) wx_ver = '%d.%d.%d.%d%s' % wx.VERSION wx_4ver = '%d.%d.%d.%d' % wx.VERSION[0:4] wx_4ver0 = '%d.%d.%d.%d.0' % wx.VERSION[0:4] wx_3ver = '%d.%d.%d' % wx.VERSION[0:3] wx_2ver = '%d.%d' % wx.VERSION[0:2] for libname_fmt in [ "/usr/local/lib/wxPython-%s/lib/libwx_osx_cocoau-%s.dylib", "/usr/local/lib/wxPython-%s/lib/libwx_osx_cocoau_gl-%s.dylib", ]: for args in [(wx_ver, wx_4ver0), (wx_ver, wx_4ver), (wx_ver, wx_3ver), (wx_ver, wx_2ver)]: lib_found = False libname = libname_fmt % args if os.path.exists( libname ): print 'Info: Manually adding lib %s' % libname myapp.libs.append( libname ) lib_found = True if not lib_found: raise ValueError( 'Cannot find lib %s' % 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/%s/Resources/English.lproj/Documentation' % py_ver ) print doc_path shutil.rmtree( doc_path ) except: traceback.print_exc( file=sys.stderr ) sys.exit( 1 ) sys.exit( 0 ) WorkBench-1.8.2/Kit/MacOSX/make_pkg.py000644 000765 000024 00000006704 12772463601 017656 0ustar00barrystaff000000 000000 # # make_pkg.py # import os print 'Info: setup version info' import sys sys.path.insert( 0, '../../Source') import wb_version import pysvn import time import subprocess proc = subprocess.Popen( 'uname -m', shell=True, close_fds=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) processor = proc.stdout.read().strip() rc = proc.wait() def os_system( cmd ): print 'Info: %s' % cmd sys.stdout.flush() if os.system( '%s 2>&1' % cmd ) == 0: return print 'Error: command failed' sys.exit( 1 ) pymaj, pymin, pypat, _, _ = sys.version_info wb_version_string = '%d.%d.%d-%d' % (wb_version.major, wb_version.minor, wb_version.patch, wb_version.build) python_version_string = '%d.%d.%d' % (pymaj, pymin, pypat) pysvnmaj, pysvnmin, pysvnpat, _ = pysvn.version pysvn_version_string = '%d.%d.%d' % (pysvn.version[0], pysvn.version[1], pysvn.version[2]) svn_version_package_string = '%d%d%d' % (pysvn.svn_version[0], pysvn.svn_version[1], pysvn.svn_version[2]) svn_version_string = '%d.%d.%d' % (pysvn.svn_version[0], pysvn.svn_version[1], pysvn.svn_version[2]) pkg_filename = 'pysvn_workbench_svn%s-%s-%s' % (svn_version_package_string, wb_version_string, processor) print 'Info: Packageing %s' % pkg_filename build_time = time.time() build_time_str = time.strftime( '%d-%b-%Y %H:%M', time.localtime( build_time ) ) year = time.strftime( '%Y', time.localtime( build_time ) ) tmpdir = os.path.join( os.getcwd(), 'tmp' ) if os.path.exists( tmpdir ): print 'Info: Clean up tmp directory' os_system( 'rm -rf tmp' ) print 'Info: Create directories' for kit_dir in [ tmpdir, os.path.join( tmpdir, '%s' % pkg_filename), ]: if not os.path.exists( kit_dir ): os.makedirs( kit_dir ) print 'Info: Copy files' for cp_src, cp_dst_dir_fmt in [ ('../../LICENSE.txt', '%s/License.txt' % pkg_filename), ('../../Docs/WorkBench.html', '%s/WorkBench.html' % pkg_filename), ]: if os.path.exists( cp_src ): cmd = 'cp -f %s tmp/%s' % (cp_src, cp_dst_dir_fmt % locals()) os_system( cmd ) else: print 'Error: cannot find %s' % cp_src sys.exit( 1 ) print 'Info: Export WorkBench_files' if os.path.exists( '../../Docs/WorkBench_files/.svn' ): os_system( 'svn export ../../Docs/WorkBench_files tmp/%s/WorkBench_files' % pkg_filename ) else: os_system( 'mkdir tmp/%s/WorkBench_files' % pkg_filename ) os_system( 'cp ../../Docs/WorkBench_files/* tmp/%s/WorkBench_files' % pkg_filename ) print 'Info: Create tmp/ReadMe.html' f = open('tmp/ReadMe.html','w') f.write('''

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

Copyright Barry A. Scott (c) 2003-%(year)s

Mail barry@barrys-emacs.org

Pysvn home http://pysvn.tigris.org

     Barry Scott

''' % locals() ) f.close() print 'Info: python bundle' os_system( '${PYTHON} -u make_wb_bundle.py tmp/%s' % pkg_filename) print 'Info: Make Disk Image' # if hdiutil exists with a -5341 error make the size bigger os_system( 'hdiutil create -size 200m -srcfolder tmp/%s tmp/tmp.dmg' % pkg_filename ) os_system( 'hdiutil convert tmp/tmp.dmg -format UDZO -imagekey zlib-level=9 ' '-o tmp/%s.dmg' % pkg_filename ) WorkBench-1.8.2/Kit/MacOSX/build.sh000755 000765 000024 00000000167 12617124047 017155 0ustar00barrystaff000000 000000 #!/bin/bash set -e STARTDIR=$(pwd) cd ../../Source make -f macosx.mak clean all cd $STARTDIR ${PYTHON} -u make_pkg.py WorkBench-1.8.2/Tests/copy_paste_test.sh000755 000765 000024 00000000702 12617124047 020537 0ustar00barrystaff000000 000000 #!/bin/bash rm -rf copy_paste mkdir -p copy_paste/repos svnadmin create --fs-type fsfs copy_paste/repos R=file://$PWD/copy_paste/repos svn mkdir -m "Init" $R/trunk svn checkout $R/trunk copy_paste/wc svn mkdir copy_paste/wc/original echo orig file 1 >copy_paste/wc/original/file1 echo orig file 2 >copy_paste/wc/original/file2 echo orig file 3 >copy_paste/wc/original/file3 svn add copy_paste/wc/original/* svn commit copy_paste/wc -m "populate" WorkBench-1.8.2/Source/wb_dialogs.py000644 000765 000024 00000075601 11507143125 017622 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_dialogs.py ''' import wx import os import pysvn import wb_subversion_utils import wb_platform_specific id_log_message_text = wx.NewId() id_last_log_message = wx.NewId() class DialogBuildingBlock(wx.Dialog): def __init__( self, parent, title ): wx.Dialog.__init__( self, parent, -1, title ) self.title = title self.all_filenames = [] self.add_filename_list_field = False self.add_log_message_field = False self.add_force_field = False def initControls( self ): self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.h_sizer = wx.BoxSizer( wx.HORIZONTAL ) self.filename_list = None if self.add_filename_list_field: self.filename_list = wx.ListCtrl( self, -1, wx.DefaultPosition, wx.Size( 600, 200 ), wx.LC_REPORT|wx.NO_BORDER ) self.filename_list.InsertColumn( 0, T_("Status") ) self.filename_list.SetColumnWidth( 0, 50 ) self.filename_list.InsertColumn( 1, T_("Filename") ) self.filename_list.SetColumnWidth( 1, 1000 ) for index, _ in enumerate( self.all_filenames ): self.filename_list.InsertStringItem( index, self.all_filenames[index][0] ) self.filename_list.SetStringItem( index, 1, self.all_filenames[index][1] ) self.log_message_ctrl = None if self.add_log_message_field: self.log_message_ctrl = wx.TextCtrl( self, id_log_message_text, size=wx.Size( 600, 200 ), style=wx.TE_MULTILINE ) self.log_message_ctrl.SetFocus() self.force_checkbox_ctrl = None if self.add_force_field: self.force_checkbox_ctrl = wx.CheckBox( self, -1, T_("Force") ) self.force_checkbox_ctrl.SetValue( False ) self.button_ok = wx.Button( self, wx.ID_OK, T_(" OK ") ) self.button_cancel = wx.Button( self, wx.ID_CANCEL, T_(" Cancel ") ) self.button_ok.SetDefault() self.initExtraButtons() self.h_sizer.Add( (60, 20), 1, wx.EXPAND) self.h_sizer.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15) self.h_sizer.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 2 ) if self.add_filename_list_field: self.v_sizer.Add( self.filename_list, 0, wx.EXPAND|wx.ALL, 5 ) if self.add_log_message_field: self.v_sizer.Add( self.log_message_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) if self.add_force_field: self.v_sizer.Add( self.force_checkbox_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer, 0, wx.EXPAND|wx.ALL, 5 ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() self.CentreOnParent() wx.EVT_BUTTON( self, wx.ID_OK, self.OnOk ) wx.EVT_BUTTON( self, wx.ID_CANCEL, self.OnCancel ) def initExtraButtons( self ): pass def OnOk( self, event ): self.EndModal( wx.ID_OK ) def OnCancel( self, event ): self.EndModal( wx.ID_CANCEL ) def getLogMessage( self ): return self.log_message_ctrl.GetValue() def getForce( self ): return self.force_checkbox_ctrl.GetValue() != 0 class ConfirmAction(DialogBuildingBlock): def __init__( self, parent, title, all_filenames, force_field=False ): DialogBuildingBlock.__init__( self, parent, title ) self.all_filenames = all_filenames self.add_filename_list_field = True self.add_force_field = force_field self.initControls() class LogMessage(DialogBuildingBlock): def __init__( self, parent, title, all_filenames, message_filename=None, force_field=False ): DialogBuildingBlock.__init__( self, parent, title ) self.all_filenames = all_filenames self.add_filename_list_field = True self.add_log_message_field = True self.add_force_field = force_field self.message_filename = message_filename self.last_log_message_text = None if self.message_filename is not None: try: self.last_log_message_text = wb_platform_specific.uOpen( self.message_filename, 'r' ).read().decode('utf-8').strip() except EnvironmentError: self.last_log_message_text = '' self.initControls() def initExtraButtons( self ): if self.last_log_message_text is not None: self.button_last_log_message = wx.Button( self, id_last_log_message, T_("Insert Last Message") ) self.button_last_log_message.Enable( len(self.last_log_message_text) > 0 ) self.h_sizer.Add( self.button_last_log_message ) self.button_ok.Enable( False ) wx.EVT_BUTTON( self, id_last_log_message, self.OnInsertLastLogMessage ) wx.EVT_TEXT( self, id_log_message_text, self.OnLogMessageChanged ) def OnInsertLastLogMessage( self, event ): self.log_message_ctrl.WriteText( self.last_log_message_text ) self.button_ok.Enable( True ) def OnLogMessageChanged( self, event ): self.button_ok.Enable( len( self.log_message_ctrl.GetValue().strip() ) > 0 ) def OnOk( self, event ): self.EndModal( wx.ID_OK ) try: wb_platform_specific.uOpen( self.message_filename, 'w' ).write( self.getLogMessage().encode('utf-8') ) except (IOError,OSError): pass class GetCredentials(wx.Dialog): def __init__( self, parent, title, username, may_save ): wx.Dialog.__init__( self, parent, -1, title ) self.g_sizer = wx.FlexGridSizer( 0, 2, 0, 0 ) self.g_sizer.AddGrowableCol( 1 ) self.border = wx.StaticBox( self, -1, T_('Credentials') ) self.box = wx.StaticBoxSizer( self.border, wx.VERTICAL ) self.box.Add( self.g_sizer, 0, wx.EXPAND ) self.username_label = wx.StaticText( self, -1, T_('Username:') ) self.username_ctrl = wx.TextCtrl( self, -1, username ) self.username_ctrl.SetFocus() self.username_ctrl.SetSelection( -1, -1 ) self.g_sizer.Add( self.username_label, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( self.username_ctrl, 0, wx.EXPAND|wx.EAST, 5 ) self.password_label = wx.StaticText(self, -1, T_('Password:') ) self.password_ctrl = wx.TextCtrl(self, -1, '', style=wx.TE_PASSWORD ) self.g_sizer.Add( self.password_label, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( self.password_ctrl, 0, wx.EXPAND|wx.EAST, 5 ) self.save_ctrl = wx.CheckBox( self, -1, T_("Always uses these credentials") ) self.save_ctrl.SetValue( may_save ) self.g_sizer.Add( self.save_ctrl, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( (1, 1) ) self.button_ok = wx.Button( self, wx.ID_OK, T_(' OK ') ) self.button_ok.SetDefault() self.button_cancel = wx.Button( self, wx.ID_CANCEL, T_(' Cancel ') ) self.h_sizer_buttons = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer_buttons.Add( (250, 20), 1, wx.EXPAND) self.h_sizer_buttons.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15 ) self.h_sizer_buttons.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 2 ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.v_sizer.Add( self.box, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer_buttons, 0, wx.EXPAND|wx.ALL, 5 ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() self.CentreOnParent() wx.EVT_BUTTON( self, wx.ID_OK, self.OnOk ) wx.EVT_BUTTON( self, wx.ID_CANCEL, self.OnCancel ) def OnOk( self, event ): self.EndModal( wx.ID_OK ) def OnCancel( self, event ): self.EndModal( wx.ID_CANCEL ) def getUsername( self ): return self.username_ctrl.GetValue() def getPassword( self ): return self.password_ctrl.GetValue() def getSaveCredentials( self ): return self.save_ctrl.GetValue() != 0 class GetServerTrust(wx.Dialog): def __init__( self, parent, realm, info_list, may_save ): wx.Dialog.__init__( self, parent, -1, T_('Trust server %s') % realm ) self.g_sizer = wx.FlexGridSizer( 0, 2, 0, 0 ) self.g_sizer.AddGrowableCol( 1 ) self.border = wx.StaticBox( self, -1, T_('Server Certificate') ) self.box = wx.StaticBoxSizer( self.border, wx.VERTICAL ) self.box.Add( self.g_sizer, 0, wx.EXPAND ) for key, value in info_list: self.addRow( key, value ) self.save_ctrl = wx.CheckBox( self, -1, T_("Always trust this server") ) self.save_ctrl.SetValue( may_save ) self.g_sizer.Add( self.save_ctrl, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( (1, 1) ) self.button_ok = wx.Button( self, wx.ID_OK, T_(' OK ') ) self.button_ok.SetDefault() self.button_cancel = wx.Button( self, wx.ID_CANCEL, T_(' Cancel ') ) self.h_sizer_buttons = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer_buttons.Add( (250, 20), 1, wx.EXPAND) self.h_sizer_buttons.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15 ) self.h_sizer_buttons.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 2 ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.v_sizer.Add( self.box, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer_buttons, 0, wx.EXPAND|wx.ALL, 5 ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() self.CentreOnParent() wx.EVT_BUTTON( self, wx.ID_OK, self.OnOk ) wx.EVT_BUTTON( self, wx.ID_CANCEL, self.OnCancel ) def addRow( self, label, value ): label_ctrl = wx.StaticText( self, -1, label, style=wx.ALIGN_RIGHT ) value_ctrl = wx.TextCtrl( self, -1, str(value), style=wx.TE_READONLY ) 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 ) def OnOk( self, event ): self.EndModal( wx.ID_OK ) def OnCancel( self, event ): self.EndModal( wx.ID_CANCEL ) def getSaveTrust( self ): return self.save_ctrl.GetValue() != 0 class AddDialog(wx.Dialog): def __init__( self, parent, title, filename, force=False, recursive=None ): wx.Dialog.__init__( self, parent, -1, title ) self.g_sizer = wx.FlexGridSizer( 0, 2, 0, 0 ) self.g_sizer.AddGrowableCol( 1 ) self.add_border = wx.StaticBox( self, -1, T_('Add') ) self.add_box = wx.StaticBoxSizer( self.add_border, wx.VERTICAL ) self.add_box.Add( self.g_sizer, 0, wx.EXPAND ) self.filename_text = wx.StaticText( self, -1, T_('From:') ) self.filename_ctrl = wx.StaticText( self, -1, filename ) self.g_sizer.Add( self.filename_text, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( self.filename_ctrl, 0, wx.EXPAND|wx.EAST, 5 ) self.force_ctrl = wx.CheckBox( self, -1, T_('Force Add') ) self.force_ctrl.SetValue( force ) self.g_sizer.Add( self.force_ctrl, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( (1, 1) ) if recursive is not None: self.recursive_ctrl = wx.CheckBox( self, -1, T_('Recursive Add') ) self.recursive_ctrl.SetValue( recursive ) self.g_sizer.Add( self.recursive_ctrl, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( (1, 1) ) else: self.recursive_ctrl = None self.button_ok = wx.Button( self, wx.ID_OK, T_(' OK ') ) self.button_ok.SetDefault() self.button_cancel = wx.Button( self, wx.ID_CANCEL, T_(' Cancel ') ) self.h_sizer_buttons = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer_buttons.Add( (150, 20), 1, wx.EXPAND ) self.h_sizer_buttons.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15 ) self.h_sizer_buttons.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 2 ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.v_sizer.Add( self.add_box, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer_buttons, 0, wx.EXPAND|wx.ALL, 5 ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() self.CentreOnParent() wx.EVT_BUTTON( self, wx.ID_OK, self.OnOk ) wx.EVT_BUTTON( self, wx.ID_CANCEL, self.OnCancel ) def OnOk( self, event ): self.EndModal( wx.ID_OK ) def OnCancel( self, event ): self.EndModal( wx.ID_CANCEL ) def getForce( self ): if self.force_ctrl is None: return False return self.force_ctrl.GetValue() != 0 def getRecursive( self ): if self.recursive_ctrl is None: return False return self.recursive_ctrl.GetValue() != 0 class RenameFile(wx.Dialog): def __init__( self, parent, title, old_filename, force=None ): wx.Dialog.__init__( self, parent, -1, title ) self.g_sizer = wx.FlexGridSizer( 0, 2, 0, 0 ) self.g_sizer.AddGrowableCol( 1 ) self.rename_border = wx.StaticBox( self, -1, T_('Rename') ) self.rename_box = wx.StaticBoxSizer( self.rename_border, wx.VERTICAL ) self.rename_box.Add( self.g_sizer, 0, wx.EXPAND ) self.old_filename_text = wx.StaticText( self, -1, T_('From:') ) self.old_filename_ctrl = wx.StaticText( self, -1, old_filename ) self.g_sizer.Add( self.old_filename_text, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( self.old_filename_ctrl, 0, wx.EXPAND|wx.EAST, 5 ) self.new_filename_text = wx.StaticText( self, -1, T_('To:') ) self.new_filename_ctrl = wx.TextCtrl( self, -1, old_filename ) self.new_filename_ctrl.SetSelection( -1, -1 ) self.new_filename_ctrl.SetFocus() self.g_sizer.Add( self.new_filename_text, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( self.new_filename_ctrl, 0, wx.EXPAND|wx.EAST, 5 ) if force is None: self.force_ctrl = None else: self.force_ctrl = wx.CheckBox( self, -1, T_("Force rename") ) self.force_ctrl.SetValue( force ) self.g_sizer.Add( self.force_ctrl, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( (1, 1) ) self.button_ok = wx.Button( self, wx.ID_OK, T_(' OK ') ) self.button_ok.SetDefault() self.button_cancel = wx.Button( self, wx.ID_CANCEL, T_(' Cancel ') ) self.h_sizer_buttons = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer_buttons.Add( (150, 20), 1, wx.EXPAND ) self.h_sizer_buttons.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15 ) self.h_sizer_buttons.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 2 ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.v_sizer.Add( self.rename_box, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer_buttons, 0, wx.EXPAND|wx.ALL, 5 ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() self.CentreOnParent() wx.EVT_BUTTON( self, wx.ID_OK, self.OnOk ) wx.EVT_BUTTON( self, wx.ID_CANCEL, self.OnCancel ) def OnOk( self, event ): self.EndModal( wx.ID_OK ) def OnCancel( self, event ): self.EndModal( wx.ID_CANCEL ) def getNewFilename( self ): return self.new_filename_ctrl.GetValue() def getForce( self ): if self.force_ctrl is None: return False return self.force_ctrl.GetValue() != 0 class GetFilename(wx.Dialog): def __init__( self, parent, title, border_title ): wx.Dialog.__init__( self, parent, -1, title ) self.g_sizer = wx.FlexGridSizer( 0, 2, 0, 0 ) self.g_sizer.AddGrowableCol( 1 ) self.rename_border = wx.StaticBox( self, -1, border_title ) self.rename_box = wx.StaticBoxSizer( self.rename_border, wx.VERTICAL ) self.rename_box.Add( self.g_sizer, 0, wx.EXPAND ) self.new_filename_text = wx.StaticText( self, -1, T_('Name:') ) self.new_filename_ctrl = wx.TextCtrl( self, -1, T_('New Folder') ) self.new_filename_ctrl.SetSelection( -1, -1 ) self.new_filename_ctrl.SetFocus() self.g_sizer.Add( self.new_filename_text, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( self.new_filename_ctrl, 0, wx.EXPAND|wx.EAST, 5 ) self.button_ok = wx.Button( self, wx.ID_OK, T_(' OK ') ) self.button_ok.SetDefault() self.button_cancel = wx.Button( self, wx.ID_CANCEL, T_(' Cancel ') ) self.h_sizer_buttons = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer_buttons.Add( (150, 20), 1, wx.EXPAND ) self.h_sizer_buttons.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15 ) self.h_sizer_buttons.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 2 ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.v_sizer.Add( self.rename_box, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer_buttons, 0, wx.EXPAND|wx.ALL, 5 ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() self.CentreOnParent() wx.EVT_BUTTON( self, wx.ID_OK, self.OnOk ) wx.EVT_BUTTON( self, wx.ID_CANCEL, self.OnCancel ) def OnOk( self, event ): self.EndModal( wx.ID_OK ) def OnCancel( self, event ): self.EndModal( wx.ID_CANCEL ) def getNewFilename( self ): return self.new_filename_ctrl.GetValue() class NewFile(wx.Dialog): template_suffix = '.template' def __init__( self, parent, template_dir ): wx.Dialog.__init__( self, parent, -1, T_('New File') ) self.g_sizer = wx.FlexGridSizer( 0, 2, 0, 0 ) self.g_sizer.AddGrowableCol( 1 ) self.newfile_border = wx.StaticBox( self, -1, T_('New File') ) self.newfile_box = wx.StaticBoxSizer( self.newfile_border, wx.VERTICAL ) self.newfile_box.Add( self.g_sizer, 0, wx.EXPAND ) self.newfile_text = wx.StaticText( self, -1, T_('New Filename:') ) self.newfile_ctrl = wx.TextCtrl( self, -1, '' ) self.newfile_ctrl.SetFocus() self.newfile_ctrl.SetSelection( -1, -1 ) self.g_sizer.Add( self.newfile_text, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 5 ) self.g_sizer.Add( self.newfile_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) try: self.template_file_list = [filename[:-len(self.template_suffix)] for filename in os.listdir( template_dir ) if filename.lower().endswith( self.template_suffix )] except EnvironmentError: self.template_file_list = [] self.template_text = wx.StaticText( self, -1, T_('Template:') ) self.template_list = wx.Choice( self, -1, choices=self.template_file_list ) self.template_list.SetSelection( 0 ) self.g_sizer.Add( self.template_text, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 5 ) self.g_sizer.Add( self.template_list, 0, wx.EXPAND|wx.ALL, 5 ) self.button_ok = wx.Button( self, wx.ID_OK, T_(' OK ') ) self.button_ok.SetDefault() self.button_cancel = wx.Button( self, wx.ID_CANCEL, T_(' Cancel ') ) self.h_sizer_buttons = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer_buttons.Add( (150, 20), 1, wx.EXPAND ) self.h_sizer_buttons.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15 ) self.h_sizer_buttons.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 2 ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.v_sizer.Add( self.newfile_box, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer_buttons, 0, wx.EXPAND|wx.ALL, 5 ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() self.CentreOnParent() wx.EVT_BUTTON( self, wx.ID_OK, self.OnOk ) wx.EVT_BUTTON( self, wx.ID_CANCEL, self.OnCancel ) def OnOk( self, event ): self.EndModal( wx.ID_OK ) def OnCancel( self, event ): self.EndModal( wx.ID_CANCEL ) def getNewFilename( self ): return self.newfile_ctrl.GetValue() def getTemplateFilename( self ): index = self.template_list.GetCurrentSelection() if index >= 0 and len(self.template_file_list) > index: return self.template_file_list[ index ] + self.template_suffix else: return None class UpdateTo(wx.Dialog): def __init__( self, parent, title ): wx.Dialog.__init__( self, parent, -1, title ) self.all_depth_types = [(pysvn.depth.empty, T_('Empty directory')) #,(pysvn.depth.exclude, T_('Exclude (not used yet)')) ,(pysvn.depth.files, T_('Children files only')) ,(pysvn.depth.immediates, T_('Immediate children')) ,(pysvn.depth.unknown, T_('Only already checked out descendants')) ,(pysvn.depth.infinity, T_('All descendants (Full recursion)'))] self.depth_enabled = wb_subversion_utils.version_info.has_depth self.g_sizer = wx.FlexGridSizer( 0, 2, 0, 0 ) self.g_sizer.AddGrowableCol( 1 ) self.revision_border = wx.StaticBox( self, -1, title ) self.revision_box = wx.StaticBoxSizer( self.revision_border, wx.VERTICAL ) # Line 1: checkbox for head revision self.head_checkbox_ctrl = wx.CheckBox( self, -1, T_("HEAD revision") ) self.head_checkbox_ctrl.SetValue( True ) self.revision_box.Add( self.head_checkbox_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) # Line 2: text entry for giving a revision no. manually self.revision_box.Add( self.g_sizer, 0, wx.EXPAND ) self.revision_text = wx.StaticText( self, -1, T_('Revision:') ) self.revision_ctrl = wx.TextCtrl( self, -1, '' ) self.revision_ctrl.SetSelection( -1, -1 ) self.revision_ctrl.Enable( False ) self.g_sizer.Add( self.revision_text, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 5 ) self.g_sizer.Add( self.revision_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) if self.depth_enabled: self.r_sizer = wx.FlexGridSizer( 0, 2, 0, 0 ) self.r_sizer.AddGrowableCol( 1 ) self.recursive_border = wx.StaticBox( self, -1, T_('Apply on') ) self.recursive_box = wx.StaticBoxSizer( self.recursive_border, wx.VERTICAL ) self.recursive_checkbox_ctrl = wx.CheckBox( self, -1, T_('Recursive (all)') ) self.recursive_checkbox_ctrl.SetValue( True ) self.recursive_box.Add( self.recursive_checkbox_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) self.recursive_box.Add( self.r_sizer, 0, wx.EXPAND ) self.depth_text = wx.StaticText( self, -1, T_('Depth:') ) self.depth_ctrl = wx.Choice( self, -1, choices=[name for depth, name in self.all_depth_types] ) for index, (depth, name) in enumerate( self.all_depth_types ): if depth == pysvn.depth.unknown: self.depth_ctrl.SetSelection( index ) self.depth_ctrl.Enable( False ) self.r_sizer.Add( self.depth_text, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 5 ) self.r_sizer.Add( self.depth_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) # Line 3: Ok/Cancel button self.button_ok = wx.Button( self, wx.ID_OK, T_(' OK ') ) self.button_ok.SetDefault() self.button_cancel = wx.Button( self, wx.ID_CANCEL, T_(' Cancel ') ) self.h_sizer_buttons = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer_buttons.Add( (150, 20), 1, wx.EXPAND ) self.h_sizer_buttons.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15 ) self.h_sizer_buttons.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 2 ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.v_sizer.Add( self.revision_box, 0, wx.EXPAND|wx.ALL, 5 ) if self.depth_enabled: self.v_sizer.Add( self.recursive_box, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer_buttons, 0, wx.EXPAND|wx.ALL, 5 ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() self.CentreOnParent() # Catch button events wx.EVT_BUTTON( self, wx.ID_OK, self.OnOk ) wx.EVT_BUTTON( self, wx.ID_CANCEL, self.OnCancel ) # Catch checkbox events wx.EVT_CHECKBOX ( self, self.head_checkbox_ctrl.GetId(), self.onHeadRevisionClicked ) if self.depth_enabled: wx.EVT_CHECKBOX ( self, self.recursive_checkbox_ctrl.GetId(), self.recursiveClicked ) def OnOk( self, event ): # Check revision value if not self.head_checkbox_ctrl.GetValue(): try: val = int( self.revision_ctrl.GetValue() ) if val < 1: wx.MessageBox( T_('Please enter a revision number > 0!'), style=wx.OK|wx.ICON_ERROR ) return except ValueError: wx.MessageBox( T_('Please enter digits only!'), style=wx.OK|wx.ICON_ERROR ) return self.EndModal( wx.ID_OK ) def OnCancel( self, event ): self.EndModal( wx.ID_CANCEL ) def onHeadRevisionClicked( self, event ): self.revision_ctrl.Enable( not self.head_checkbox_ctrl.GetValue() ) def getRevision( self ): if self.head_checkbox_ctrl.GetValue(): return pysvn.Revision( pysvn.opt_revision_kind.head ) else: rev = int( self.revision_ctrl.GetValue() ) return pysvn.Revision( pysvn.opt_revision_kind.number, rev ) def getSvnDepth( self ): if self.depth_enabled: return (self.getRecursive(), self.getDepth( pysvn.depth.unknown )) else: return (True, None) def recursiveClicked( self, event ): self.depth_ctrl.Enable( not self.recursive_checkbox_ctrl.GetValue() ) def getRecursive( self ): return self.recursive_checkbox_ctrl.GetValue() def getDepth( self, default ): if self.recursive_checkbox_ctrl.GetValue(): return default else: return self.all_depth_types[ self.depth_ctrl.GetSelection() ][0] class CopyUrl(wx.Dialog): def __init__( self, parent, app, title, copy_from_url, copy_to_url ): wx.Dialog.__init__( self, parent, -1, '%s %s' % (title, copy_from_url) ) self.app = app self.copy_to_url = copy_to_url p = self.app.prefs.getAdvanced() self.arbitrary_path = p.arbitrary_tag_branch self.g_sizer = wx.FlexGridSizer( 0, 2, 0, 0 ) self.g_sizer.AddGrowableCol( 1 ) self.copyurl_border = wx.StaticBox( self, -1, title ) self.copyurl_box = wx.StaticBoxSizer( self.copyurl_border, wx.VERTICAL ) self.copyurl_box.Add( self.g_sizer, 0, wx.EXPAND ) self.copy_from_label = wx.StaticText( self, -1, T_('Copy From:') ) self.copy_from_value = wx.StaticText( self, -1, copy_from_url ) self.copy_to_label = wx.StaticText( self, -1, T_('Copy To:') ) if not self.arbitrary_path: self.copy_to_root = wx.StaticText( self, -1, copy_to_url + '/' ) self.copy_to_leaf = wx.TextCtrl( self, -1, '', size=(300, -1) ) else: # should place repos_root_URL as a static prefix self.copy_to_browse = wx.StaticText( self, -1, '' ) self.copy_to_leaf = wx.TextCtrl( self, -1, copy_to_url + '/', size=(500, -1) ) self.copy_to_leaf.SetFocus() self.h_sizer_copy_to = wx.BoxSizer( wx.HORIZONTAL ) if not self.arbitrary_path: self.h_sizer_copy_to.Add( self.copy_to_root, 0, wx.EXPAND|wx.EAST, 15 ) self.h_sizer_copy_to.Add( self.copy_to_leaf, 0, wx.EXPAND|wx.EAST, 2 ) else: self.h_sizer_copy_to.Add( self.copy_to_leaf, 1, wx.EXPAND|wx.EAST, 2 ) self.h_sizer_copy_to.Add( self.copy_to_browse, 0, wx.EXPAND|wx.EAST, 2 ) self.g_sizer.Add( self.copy_from_label, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 5 ) self.g_sizer.Add( self.copy_from_value, 0, wx.EXPAND|wx.ALL, 5 ) self.g_sizer.Add( self.copy_to_label, 1, wx.EXPAND|wx.NORTH|wx.ALIGN_RIGHT, 5 ) self.g_sizer.Add( self.h_sizer_copy_to, 0, wx.EXPAND|wx.ALL, 5 ) self.label_ctrl = wx.StaticText( self, -1, T_('Log message'), style=wx.ALIGN_LEFT ) self.log_message_ctrl = wx.TextCtrl( self, -1, style=wx.TE_MULTILINE ) self.button_ok = wx.Button( self, wx.ID_OK, T_(' OK ') ) self.button_ok.SetDefault() self.button_ok.Enable( False ) self.button_cancel = wx.Button( self, wx.ID_CANCEL, T_(' Cancel ') ) self.h_sizer_buttons = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer_buttons.Add( (150, 20), 1, wx.EXPAND ) self.h_sizer_buttons.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15 ) self.h_sizer_buttons.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 2 ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.v_sizer.Add( self.copyurl_box, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.label_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.log_message_ctrl, 1, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer_buttons, 0, wx.EXPAND|wx.ALL, 5 ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() self.CentreOnParent() wx.EVT_TEXT( self, self.log_message_ctrl.GetId(), self.OnLogMessageChanged ) wx.EVT_BUTTON( self, wx.ID_OK, self.OnOk ) wx.EVT_BUTTON( self, wx.ID_CANCEL, self.OnCancel ) self.copy_to_label.SetFocus() def OnLogMessageChanged( self, event ): self.button_ok.Enable( len( self.log_message_ctrl.GetValue().strip() ) > 0 and len( self.copy_to_leaf.GetValue().strip() ) > 0 ) def OnOk( self, event ): self.EndModal( wx.ID_OK ) def OnCancel( self, event ): self.EndModal( wx.ID_CANCEL ) def getCopyTo( self ): if not self.arbitrary_path: return '%s/%s' % (self.copy_to_url, self.copy_to_leaf.GetValue().strip()) else: return self.copy_to_leaf.GetValue().strip() def getCopyToLeaf( self ): return self.copy_to_leaf.GetValue().strip() def getLogMessage( self ): return self.log_message_ctrl.GetValue() class CreateTag(CopyUrl): def __init__( self, parent, app, copy_from_url, copy_to_url ): CopyUrl.__init__( self, parent, app, T_('Create Tag'), copy_from_url, copy_to_url ) class CreateBranch(CopyUrl): def __init__( self, parent, app, copy_from_url, copy_to_url ): CopyUrl.__init__( self, parent, app, T_('Create Branch'), copy_from_url, copy_to_url ) WorkBench-1.8.2/Source/wb_subversion_report_revision_changes.py000644 000765 000024 00000030450 12617123054 025373 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2009-2011 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_subversion_report_revision_changes.py ''' import types import wx import pysvn import wb_config import wb_ids import wb_images import wb_exceptions import wb_list_panel_common import wb_subversion_diff import wb_subversion_utils import wb_subversion_list_handler_common import wb_subversion_annotate import wb_subversion_info_dialog import wb_subversion_history class ReportRevisionChangesFrame(wx.Frame): def __init__( self, app, project_info, all_files, info1, info2 ): rev_info = {'rev1': info1.revision.number ,'rev2': info2.revision.number} wx.Frame.__init__( self, None, -1, T_('Revision changes - r%(rev1)d vs. r%(rev2)d') % rev_info, size=(700,500) ) self.app = app self.info1 = info1 self.info2 = info2 self.menu_text = T_('Diff r%(rev1)d vs. r%(rev2)d...') % rev_info self.menu_actions = wx.Menu() self.menu_actions.Append( wb_ids.id_SP_DiffRevisionRevision, self.menu_text, self.menu_text ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SP_Annotate, T_('Annotate...'), T_('Annotate...') ) self.menu_actions.Append( wb_ids.id_SP_History, T_('Log history...'), T_('Log history...') ) self.menu_actions.Append( wb_ids.id_SP_Info, T_('Information...'), T_('Information...') ) self.menu_bar = wx.MenuBar() self.menu_bar.Append( self.menu_actions, T_('&Actions') ) self.SetMenuBar( self.menu_bar ) # Add tool bar t = self.CreateToolBar( name="main", style=wx.TB_HORIZONTAL ) bitmap_size = (32,32) t.SetToolBitmapSize( bitmap_size ) t.AddSimpleTool( wb_ids.id_SP_DiffRevisionRevision, wb_images.getBitmap( 'toolbar_images/diff.png', bitmap_size ), self.menu_text, self.menu_text ) t.AddSimpleTool( wb_ids.id_SP_History, wb_images.getBitmap( 'toolbar_images/history.png', bitmap_size ), T_('Show History log'), T_('Show History log') ) t.AddSimpleTool( wb_ids.id_SP_Info, wb_images.getBitmap( 'toolbar_images/info.png', bitmap_size ), T_('File Information'), T_('File Information') ) t.Realize() try_wrapper = wb_exceptions.TryWrapperFactory( self.app.log ) # Set the application icon self.SetIcon( wb_images.getIcon( 'wb.png' ) ) # create the individule panels self.panel_list = ReportRevisionChangesListPanel( app, self, self ) wx.EVT_CLOSE( self, self.OnCloseWindow ) wx.EVT_BUTTON( self.panel_list, wx.ID_OK, self.app.eventWrapper( self.OnOk ) ) self.project_info = ReportRevisionChangesProjectInfo( project_info, all_files ) self.list_handler = ReportRevisionChangesListHandler( self.app, self.panel_list, self.project_info, self.menu_text ) # draw the list - it updates the status info. self.panel_list.setHandler( self.list_handler ) wx.EVT_MENU( self, wb_ids.id_SP_Annotate, self.app.eventWrapper( self.OnSpAnnotate ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffRevisionRevision, self.app.eventWrapper( self.OnSpDiffRevisionRevision ) ) wx.EVT_MENU( self, wb_ids.id_SP_History, self.app.eventWrapper( self.OnSpHistory ) ) wx.EVT_MENU( self, wb_ids.id_SP_Info, self.app.eventWrapper( self.OnSpInfo ) ) def clearUpdateUiState( self ): pass def getUpdateUiState( self ): pass def setEventHandler( self, handler ): self.handler = handler def OnCloseWindow( self, event ): self.Destroy() def OnCancel( self, event ): self.Destroy() def OnOk( self, event ): self.Destroy() # command events def OnSpAnnotate( self, event ): for filename, url in [(self.list_handler.getFilename( row ), self.list_handler.getUrl( row )) for row in self.panel_list.getSelectedRows()]: self.app.setProgress( T_('Annotating %(count)d'), 0 ) self.app.setAction( T_('Annotate %s...') % filename ) yield self.app.backgroundProcess ok = False try: annotation = self.project_info.client_bg.annotate( url, peg_revision=self.info2.revision ) ok = True except pysvn.ClientError, e: self.app.log_client_error( e ) yield self.app.foregroundProcess if not ok: break h_frame = wb_subversion_annotate.AnnotateFrame( self.app, self.project_info, filename, annotation ) h_frame.Show( True ) self.app.clearProgress() self.app.setAction( T_('Ready') ) def OnSpDiffRevisionRevision( self, event ): for filename, url in [(self.list_handler.getFilename( row ), self.list_handler.getUrl( row )) for row in self.panel_list.getSelectedRows()]: info1 = self.info1.copy() info1.peg_path = url info1.title = filename + '@r' + str(info1.revision.number) info2 = self.info2.copy() info2.peg_path = url info2.title = filename + '@r' + str(info2.revision.number) self.app.setAction( T_('Diff -r%(rev1)d:%(rev2)d %(url)s...') % {'rev1': info1.revision.number ,'rev2': info2.revision.number ,'url': url} ) generator = wb_subversion_diff.subversionDiffFiles( self.app, self.project_info, info1, info2 ) 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 self.app.setAction( T_('Ready') ) def OnSpHistory( self, event ): dialog = wb_subversion_history.LogHistoryDialog( self.app, self.app.frame.tree_panel.tree_ctrl ) result = dialog.ShowModal() if result != wx.ID_OK: return for filename, url in [(self.list_handler.getFilename( row ), self.list_handler.getUrl( row )) for row in self.panel_list.getSelectedRows()]: self.app.setAction( 'Log history %s...' % filename ) yield self.app.backgroundProcess ok = False try: file_url, history_entries = wb_subversion_history.getHistoryEntries( self.project_info, url, dialog.getLimit(), dialog.getRevisionEnd(), dialog.getIncludeTags(), dialog.getRevisionStart() ) ok = True except pysvn.ClientError, e: self.app.log_client_error( e ) yield self.app.foregroundProcess if not ok: break h_frame = wb_subversion_history.HistoryFileFrame( self.app, self.project_info, filename, url, history_entries ) h_frame.Show( True ) self.app.setAction( T_('Ready') ) def OnSpInfo( self, event ): for filename, url in [(self.list_handler.getFilename( row ), self.list_handler.getUrl( row )) for row in self.panel_list.getSelectedRows()]: try: entry = self.project_info.client_fg.info2( url, peg_revision=self.info2.revision, recurse=False )[0][1] dialog = wb_subversion_info_dialog.InfoDialog( self.app, self.panel_list, filename, entry ) dialog.ShowModal() except pysvn.ClientError, e: self.app.log_client_error( e ) class ReportRevisionChangesListHandler(wb_subversion_list_handler_common.SubversionListHandlerCommon): def __init__( self, app, parent, project_info, menu_text ): wb_subversion_list_handler_common.SubversionListHandlerCommon.__init__( self, app, parent, project_info ) self.all_excluded_files = {} self.menu_text = menu_text def _getNameColPrefix( self ): return 0 # use the repos status in the report def getTextStatus( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) return status.repos_text_status # use the repos status in the report def getPropStatus( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) return status.repos_prop_status def statusFormatString( self, file ): text_code = wb_subversion_utils.wc_status_kind_map[ file.text_status ] prop_code = wb_subversion_utils.wc_status_kind_map[ file.prop_status ] if text_code == ' ' and prop_code != ' ': text_code = '_' return file.get('branch_text_states', '') + text_code + prop_code def setupColumnInfo( self ): self.column_info.setFrom( [T_('State'), T_('Name')], [5, 100] ) def statusColour( self, file ): # show that a file is on the exclude list if file.path in self.getAllGreyFilenames(): return wb_config.colour_status_disabled else: return wb_config.colour_status_normal def getContextMenu( self ): menu_template = \ [('', wb_ids.id_SP_DiffRevisionRevision, self.menu_text ) ,('-', 0, 0 ) ,('', wb_ids.id_SP_Annotate, T_('Annotate...') ) ,('', wb_ids.id_SP_History, T_('Log history...') ) ,('', wb_ids.id_SP_Info, T_('Information...') ) ] return wb_subversion_utils.populateMenu( wx.Menu(), menu_template ) def getAllGreyFilenames( self ): # show all excluded files in grey return self.all_excluded_files class ReportRevisionChangesProjectInfo: def __init__( self, project_info, all_files ): self.all_files = all_files self.need_properties = False self.project_name = project_info.project_name self.url = project_info.url self.wc_path = project_info.wc_path self.need_checkout = False self.need_upgrade = False self.client_fg = project_info.client_fg self.client_bg = project_info.client_bg def getTagsUrl( self, rel_url ): return None def setNeedProperties( self, need_properties ): self.need_properties = need_properties def updateStatus( self ): pass def getFilesStatus( self ): return self.all_files def getProperty( self, filename, prop_name ): return '' def getWorkingDir( self ): return self.wc_path class ReportRevisionChangesListPanel(wb_list_panel_common.WbListPanelCommon): def __init__( self, app, frame, parent ): wb_list_panel_common.WbListPanelCommon.__init__( self, app, frame, parent ) def addToSizer( self, v_sizer ): pass def getAcceleratorTableInit( self ): if wx.Platform == '__WXMAC__': acc_init =[ (wx.ACCEL_ALT, ord('D'), wb_ids.id_SP_DiffRevisionRevision), (wx.ACCEL_ALT, ord('L'), wb_ids.id_SP_History), ] elif wx.Platform == '__WXMSW__': acc_init =[ (wx.ACCEL_CTRL, ord('D'), wb_ids.id_SP_DiffRevisionRevision), (wx.ACCEL_CTRL, ord('L'), wb_ids.id_SP_History), ] else: # Unix acc_init =[ (wx.ACCEL_CTRL, ord('D'), wb_ids.id_SP_DiffWorkBase), (wx.ACCEL_CTRL, ord('L'), wb_ids.id_SP_History), ] return acc_init WorkBench-1.8.2/Source/wb_pychecker.py000644 000765 000024 00000001350 10505216164 020144 0ustar00barrystaff000000 000000 # # wb_pychecker.py # # helper to run pychecker on all the imports of workbench # import os import sys sys.path.insert( 0, os.environ['PYCHECKER_DIR'] ) class CountOutput: def __init__( self, f ): self.__f = f self.__write_count = 0 def write( self, data ): self.__write_count += data.count('\n') return self.__f.write( data ) def getWriteCount( self ): return self.__write_count def report(): count = sys.__stdout__.getWriteCount() print 'Info: %d lines' % count sys.exit( count != 0 ) os.environ['PYCHECKER'] = '--no-shadowbuiltin --config=./pycheckrc' import pychecker.checker sys.__stdout__ = CountOutput( sys.__stdout__ ) sys.stdout = sys.__stdout__ import wb_main WorkBench-1.8.2/Source/wb_shell_win32_commands.py000644 000765 000024 00000013377 12646464336 022233 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2011 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_shell_win32_commands.py ''' import win32process import win32con import win32api import os import string import subprocess import wb_platform_specific import tempfile def setupCommands(): pass def getTerminalProgramList(): return ['CMD'] def getFileBrowserProgramList(): return ['Explorer'] def EditFile( app, project_info, filename ): p = app.prefs.getEditor() if p.editor_image: if p.editor_options: command_line = (u'"%s" %s "%s"' % (p.editor_image, p.editor_options, filename)) else: command_line = (u'"%s" "%s"' % (p.editor_image, filename)) else: command_line = (u'"notepad.exe" "%s"' % (filename,)) app.log.info( command_line ) CreateProcess( app, command_line, project_info.getWorkingDir() ) def GuiDiffFiles( app, options ): cmd_line = '"%s" %s' % (app.prefs.getDiffTool().gui_diff_tool, options) app.log.info( cmd_line ) CreateProcess( app, cmd_line, os.path.curdir ) def ShellDiffFiles( app, options ): cmd_line = (u'"%s" %s' % (app.prefs.getDiffTool().shell_diff_tool ,options)) app.log.info( cmd_line ) return __run_command_with_output( cmd_line ) def ShellOpen( app, project_info, filename ): app.log.info( T_('Open %s') % filename ) try: win32api.ShellExecute( 0, 'open', filename, '', project_info.getWorkingDir(), win32con.SW_SHOWNORMAL ) except win32api.error, e: if e[0] == 31: app.log.error( T_('Unable to shell open %s\n' 'Is an application associated with this file type?') % filename ) else: app.log.error( T_('Unable to shell open %(filename)s - %(error)s') % {'filename': filename ,'error': e[2]} ) def CommandShell( app, project_info ): p = app.prefs.getShell() working_dir = project_info.getWorkingDir() # calc a title that is leaf to root so that the leaf shows up in a task bar first title = [] pi = project_info while pi: title.append( pi.project_name ) pi = pi.parent cmd_lines = [ u"@title %s\n" % (' '.join( title ),), u"@set PYTHONPATH=\n", u'@cd %s\n' % (working_dir,), u'@echo on\n', ] if len( p.shell_init_command ) > 0: cmd_lines.append( u'call %s\n' % (p.shell_init_command,) ) f = tempfile.NamedTemporaryFile( mode='w', delete=False, prefix='tmp-wb-shell', suffix='.cmd' ) app.all_temp_files.append( f.name ) for line in cmd_lines: f.write( line.encode( 'utf-8' ) ) f.close() command_line = u'"%s" /k "%s"' % (os.environ['ComSpec'], f.name) app.log.info( command_line ) CreateProcess( app, command_line, working_dir ) def FileBrowser( app, project_info ): command_line = (u'explorer.exe /e,%s' % (project_info.getWorkingDir(),)) app.log.info( command_line ) CreateProcess( app, command_line, project_info.getWorkingDir() ) def CreateProcess( app, command_line, current_dir ): if not ensureDirectory( app, current_dir ): return try: import ctypes old_value = ctypes.c_long( 0 ) try: ctypes.windll.kernel32.Wow64DisableWow64FsRedirection( ctypes.byref( old_value ) ) except: pass si = win32process.STARTUPINFO() h_process, h_thread, process_id, thread_id = win32process.CreateProcess( None, command_line, None, # processAttributes None, # threadAttributes , 0, # bInheritHandles , win32con.CREATE_NEW_CONSOLE, # dwCreationFlags , None, # newEnvironment , current_dir, # currentDirectory , si # startupinfo ) try: ctypes.windll.kernel32.Wow64RevertWow64FsRedirection( old_value ) except: pass except win32process.error, detail: app.log.error( T_('Create process failed for command - %(command)s\n' 'Reason %(reason)s') % {'command': command_line ,'reason': detail} ) def ensureDirectory( app, current_dir ): if not wb_platform_specific.uPathExists( current_dir ): try: os.makedirs( current_dir ) app.log.info( T_('Created directory %s') % current_dir ) except IOError, e: app.log.error( T_('Create directory %(dir)s - %(error)s') % {'dir': current_dir ,'error': e} ) return 0 elif not wb_platform_specific.uPathIsdir( current_dir ): app.log.error( T_('%s is not a directory') % current_dir ) return 0 return 1 def __run_command_with_output( command_line ): err_prefix = 'error running %s' % command_line try: proc = subprocess.Popen( command_line, bufsize=-1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) all_output = proc.stdout.read() proc.wait() return all_output except EnvironmentError, e: return '%s - %s' % (err_prefix, str(e)) WorkBench-1.8.2/Source/wb_shell_macosx_commands.py000644 000765 000024 00000016675 12617400453 022553 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_shell_macosx_commands.py ''' import os import signal import subprocess import xml.sax.saxutils import shlex import types import tempfile import wb_platform_specific __sigchld_handler_installed = False def setupCommands(): # install the sig child handler to get rid of the zombie processes global __sigchld_handler_installed if not __sigchld_handler_installed: signal.signal( signal.SIGCHLD, __sigchld_handler ) __sigchld_handler_installed = True def __sigchld_handler( signum, frame ): try: while True: pid, status = os.waitpid( -1, os.WNOHANG ) if pid == 0: break except OSError, e: pass def getTerminalProgramList(): return ['Terminal', 'iTerm', 'iTerm2 V3'] def getFileBrowserProgramList(): return ['Finder'] def GuiDiffFiles( app, args ): __run_command( app, app.prefs.getDiffTool().gui_diff_tool, args ) def ShellDiffFiles( app, args ): return __run_command_with_output( app, app.prefs.getDiffTool().shell_diff_tool, args ) def EditFile( app, project_info, filename ): p = app.prefs.getEditor() if p.editor_image: if p.editor_options: cmd = p.editor_image args = shlex.split( p.editor_options ) + [filename] else: cmd = p.editor_image args = [filename] else: cmd = '/usr/bin/open' args = ['-e', filename] cur_dir = os.getcwd() try: wb_platform_specific.uChdir( project_info.getWorkingDir() ) __run_command( app, cmd, args ) finally: wb_platform_specific.uChdir( cur_dir ) def ShellOpen( app, project_info, filename ): cur_dir = os.getcwd() try: wb_platform_specific.uChdir( project_info.getWorkingDir() ) __run_command( app, u'/usr/bin/open', [filename] ) finally: wb_platform_specific.uChdir( cur_dir ) def CommandShell( app, project_info ): p = app.prefs.getShell() if p.shell_terminal == 'iTerm': CommandShell_iTerm( app, project_info ) elif p.shell_terminal == 'iTerm2 V3': CommandShell_iTerm2_V3( app, project_info ) else: CommandShell_Terminal( app, project_info ) def CommandShell_iTerm( app, project_info ): p = app.prefs.getShell() working_dir = project_info.getWorkingDir() # calc a title that is leaf to root so that the leaf shows up in a task bar first title = [] pi = project_info while pi: title.append( pi.project_name ) pi = pi.parent commands = u'cd "%s"' % (working_dir.replace( '"', '\\\\"' ).replace( '$', '\\\\$' ),) if len( p.shell_init_command ) > 0: commands = commands + u';export WB_WD="$PWD"; . "%s"' % (p.shell_init_command.replace( '"', '\\\\"' ).replace( '$', '\\\\$' ),) contents = u''' tell application "iTerm" activate -- make a new terminal set work_bench_term to (make new terminal) -- talk to the new terminal tell work_bench_term activate current session launch session "Default Session" -- talk to the session tell the last session set name to "%s" -- execute a command exec command "/bin/bash" write text "%s" end tell end tell end ''' % (' '.join( title ).replace( '"', '\\"' ) ,commands.replace( '"', '\\"' )) f = tempfile.NamedTemporaryFile( mode='w', delete=False, prefix='tmp-wb-shell', suffix='.scpt' ) app.all_temp_files.append( f.name ) f.write( contents.encode( 'utf-8' ) ) f.close() __run_command( app, u'/usr/bin/osascript', [f.name] ) def CommandShell_iTerm2_V3( app, project_info ): p = app.prefs.getShell() working_dir = project_info.getWorkingDir() # calc a title that is leaf to root so that the leaf shows up in a task bar first title = [] pi = project_info while pi: title.append( pi.project_name ) pi = pi.parent commands = u'cd "%s"' % (working_dir.replace( '"', '\\\\"' ).replace( '$', '\\\\$' ),) if len( p.shell_init_command ) > 0: commands = commands + u';export WB_WD="$PWD"; . "%s"' % (p.shell_init_command.replace( '"', '\\\\"' ).replace( '$', '\\\\$' ),) contents = u''' tell application "iTerm" activate -- make a new terminal create window with default profile command "/bin/bash -l" tell current window tell current session set name to "%s" write text "%s" end tell end tell end tell ''' % (' '.join( title ).replace( '"', '\\"' ) ,commands.replace( '"', '\\"' )) f = tempfile.NamedTemporaryFile( mode='w', delete=False, prefix='tmp-wb-shell', suffix='.scpt' ) app.all_temp_files.append( f.name ) f.write( contents.encode( 'utf-8' ) ) f.close() __run_command( app, u'/usr/bin/osascript', [f.name] ) def CommandShell_Terminal( app, project_info ): p = app.prefs.getShell() working_dir = project_info.getWorkingDir() # calc a title that is leaf to root so that the leaf shows up in a task bar first title = [] pi = project_info while pi: title.append( pi.project_name ) pi = pi.parent commands = u"cd '%s'" % (working_dir,) if len( p.shell_init_command ) > 0: commands = commands + ";. '%s'\n" % (p.shell_init_command,) contents = u''' WindowSettings CustomTitle %s ExecutionString %s ''' % (xml.sax.saxutils.escape( ' '.join( title ) ) ,xml.sax.saxutils.escape( commands )) f = tempfile.NamedTemporaryFile( mode='w', delete=False, prefix='tmp-wb-term', suffix='.term' ) app.all_temp_files.append( f.name ) f.write( contents.encode( 'utf-8' ) ) f.close() __run_command( app, u'/usr/bin/open', [f.name] ) def FileBrowser( app, project_info ): __run_command( app, u'/usr/bin/open', [u'-a', u'Finder', project_info.getWorkingDir()] ) def __run_command( app, cmd, args ): app.log.info( '%s %s' % (cmd, ' '.join( args ) ) ) env = os.environ.copy() cmd = asUtf8( cmd ) args = [asUtf8( arg ) for arg in args] os.spawnvpe( os.P_NOWAIT, cmd, [cmd]+args, env ) def __run_command_with_output( app, cmd, args ): err_prefix = u'error running %s %s' % (cmd, ' '.join( args )) try: cmd = asUtf8( cmd ) args = [asUtf8( arg ) for arg in args] proc = subprocess.Popen( [cmd]+args, close_fds=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) output = proc.stdout.read() rc = proc.wait() return output except EnvironmentError, e: return '%s - %s' % (err_prefix, str(e)) def asUtf8( s ): if type( s ) == types.UnicodeType: return s.encode( 'utf-8' ) else: return s WorkBench-1.8.2/Source/wb-diff.sh000755 000765 000024 00000001020 12617124047 017000 0ustar00barrystaff000000 000000 #!/bin/bash export PYSVN_WORKBENCH_STDOUT_LOG=$(tty) SOURCEDIR=$( dirname $0 ) if [ "$PYTHONPATH" = "" ] then export PYTHONPATH=${SOURCEDIR} else export PYTHONPATH=${SOURCEDIR}:$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} ${SOURCEDIR}/wb_diff_main.py $* else ${PYTHON} ${SOURCEDIR}/wb_diff_main.py $* fi WorkBench-1.8.2/Source/wb_diff_difflib.py000644 000765 000024 00000016257 11507143125 020571 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_diff_difflib.py ''' import sys import re import difflib import wb_exceptions import wb_read_file # define what "junk" means def IS_LINE_JUNK(line, pat=None): return False def IS_CHARACTER_JUNK(ch, ws=" \t"): return False class Difference: 'Difference' def __init__( self, text_body ): self.text_body = text_body # meant for dumping lines def dump( self, fn, x, lo, hi ): for i in xrange(lo, hi): fn( x[i] ) def plain_replace( self, a, alo, ahi, b, blo, bhi ): assert alo < ahi and blo < bhi # dump the shorter block first -- reduces the burden on short-term # memory if the blocks are of very different sizes if bhi - blo < ahi - alo: self.dump(self.text_body.addInsertedLine, b, blo, bhi) self.dump(self.text_body.addDeletedLine, a, alo, ahi) else: self.dump(self.text_body.addDeletedLine, a, alo, ahi) self.dump(self.text_body.addInsertedLine, b, blo, bhi) # When replacing one block of lines with another, this guy searches # the blocks for *similar* lines; the best-matching pair (if any) is # used as a synch point, and intraline difference marking is done on # the similar pair. Lots of work, but often worth it. def fancy_replace( self, a, alo, ahi, b, blo, bhi): # don't synch up unless the lines have a similarity score of at # least cutoff; best_ratio tracks the best score seen so far best_ratio, cutoff = 0.51, 0.52 cruncher = difflib.SequenceMatcher(IS_CHARACTER_JUNK) eqi, eqj = None, None # 1st indices of equal lines (if any) # search for the pair that matches best without being identical # (identical lines must be junk lines, & we don't want to synch up # on junk -- unless we have to) for j in xrange(blo, bhi): bj = b[j] cruncher.set_seq2(bj) for i in xrange(alo, ahi): ai = a[i] if ai == bj: if eqi is None: eqi, eqj = i, j continue cruncher.set_seq1(ai) # computing similarity is expensive, so use the quick # upper bounds first -- have seen this speed up messy # compares by a factor of 3. # note that ratio() is only expensive to compute the first # time it's called on a sequence pair; the expensive part # of the computation is cached by cruncher if( cruncher.real_quick_ratio() > best_ratio and cruncher.quick_ratio() > best_ratio and cruncher.ratio() > best_ratio ): best_ratio, best_i, best_j = cruncher.ratio(), i, j if best_ratio < cutoff: # no non-identical "pretty close" pair if eqi is None: # no identical pair either -- treat it as a straight replace self.plain_replace( a, alo, ahi, b, blo, bhi ) return # no close pair, but an identical pair -- synch up on that best_i, best_j, best_ratio = eqi, eqj, 1.0 else: # there's a close pair, so forget the identical pair (if any) eqi = None # a[best_i] very similar to b[best_j]; eqi is None iff they're not # identical # pump out diffs from before the synch point self.fancy_helper( a, alo, best_i, b, blo, best_j ) # do intraline marking on the synch pair aelt, belt = a[ best_i ], b[ best_j ] if eqi is None: self.text_body.addChangedLineBegin() cruncher.set_seqs( aelt, belt ) for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes(): la, lb = ai2 - ai1, bj2 - bj1 if tag == 'replace': self.text_body.addChangedLineReplace( aelt[ai1:ai2], belt[bj1:bj2] ) elif tag == 'delete': self.text_body.addChangedLineDelete( aelt[ai1:ai2] ) elif tag == 'insert': self.text_body.addChangedLineInsert( belt[bj1:bj2] ) elif tag == 'equal': self.text_body.addChangedLineEqual( belt[bj1:bj2] ) else: raise ValueError, 'unknown tag ' + str(tag) self.text_body.addChangedLineEnd() else: self.text_body.addNormalLine( aelt ) # pump out diffs from after the synch point self.fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi) def fancy_helper( self, a, alo, ahi, b, blo, bhi): if alo < ahi: if blo < bhi: self.fancy_replace( a, alo, ahi, b, blo, bhi) else: self.dump(self.text_body.addDeletedLine, a, alo, ahi) elif blo < bhi: self.dump(self.text_body.addInsertedLine, b, blo, bhi) def fail(self, msg): out = sys.stderr.write out(msg + "\n\n") return 0 # filename can be a list of lines of the name of a file to open def filecompare( self, filename_left, filename_right ): if type(filename_left) == type([]): lines_left = filename_left else: try: lines_left = wb_read_file.readFileContentsAsUnicode( filename_left ).split('\n') except IOError, detail: print 'Error opening %s\n%s' % (filename_left, detail) return 0 if type(filename_right) == type([]): lines_right = filename_right else: try: lines_right = wb_read_file.readFileContentsAsUnicode( filename_right ).split('\n') except IOError, detail: print 'Error opening %s\n%s' % (filename_right, detail) return 0 lines_left = [eolRemoval( line ) for line in lines_left] lines_right = [eolRemoval( line ) for line in lines_right] matcher = difflib.SequenceMatcher( IS_LINE_JUNK, lines_left, lines_right ) for tag, left_lo, left_hi, right_lo, right_hi in matcher.get_opcodes(): if tag == 'replace': self.fancy_replace( lines_left, left_lo, left_hi, lines_right, right_lo, right_hi ) elif tag == 'delete': self.dump( self.text_body.addDeletedLine, lines_left, left_lo, left_hi ) elif tag == 'insert': self.dump( self.text_body.addInsertedLine, lines_right, right_lo, right_hi ) elif tag == 'equal': self.dump( self.text_body.addNormalLine, lines_left, left_lo, left_hi ) else: raise ValueError, 'unknown tag ' + str( tag ) self.text_body.addEnd() return 1 # need to strip any \n or \r thats on the end of the line def eolRemoval( line ): while line and line[-1] in ['\n','\r']: line = line[:-1] return line WorkBench-1.8.2/Source/make-po-file.cmd000644 000765 000024 00000000035 11261605162 020057 0ustar00barrystaff000000 000000 %PYTHON% make_po_file.py %1 WorkBench-1.8.2/Source/wb_subversion_utils.py000644 000765 000024 00000034400 13005657764 021625 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2011 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_subversion_utils.py ''' import pysvn import time import types import locale import wx import wb_exceptions class svn_version_info: # # Keep infos about the features of the available pysvn version # def __init__(self): self.notify_action_has_failed_lock = hasattr( pysvn.wc_notify_action, 'failed_lock' ) self.has_depth = hasattr( pysvn, 'depth' ) self.notify_action_has_property_events = hasattr( pysvn.wc_notify_action, 'property_added' ) self.notify_action_has_upgrade_events = hasattr( pysvn.wc_notify_action, 'upgraded_path' ) self.has_upgrade = self.notify_action_has_upgrade_events version_info = svn_version_info() def fmtDateTime( val ): encoding = locale.getlocale()[1] if type( val ) == types.FloatType: val = time.localtime( val ) return time.strftime( '%d-%b-%Y %H:%M:%S', val ).decode( encoding ) wc_status_kind_map = { pysvn.wc_status_kind.missing: '?', pysvn.wc_status_kind.added: 'A', pysvn.wc_status_kind.conflicted: 'C', pysvn.wc_status_kind.deleted: 'D', pysvn.wc_status_kind.external: 'X', pysvn.wc_status_kind.ignored: 'I', pysvn.wc_status_kind.incomplete: '!', pysvn.wc_status_kind.missing: '!', pysvn.wc_status_kind.merged: 'G', pysvn.wc_status_kind.modified: 'M', pysvn.wc_status_kind.none: ' ', pysvn.wc_status_kind.normal: ' ', pysvn.wc_status_kind.obstructed: '~', pysvn.wc_status_kind.replaced: 'R', pysvn.wc_status_kind.unversioned: '?', } # lookup the status and see if it means the file will be checked in wc_status_checkin_map = { pysvn.wc_status_kind.missing: False, pysvn.wc_status_kind.added: True, pysvn.wc_status_kind.conflicted: True, # allow user to see the conflicted file pysvn.wc_status_kind.deleted: True, pysvn.wc_status_kind.external: False, pysvn.wc_status_kind.ignored: False, pysvn.wc_status_kind.incomplete: False, pysvn.wc_status_kind.missing: False, pysvn.wc_status_kind.merged: True, pysvn.wc_status_kind.modified: True, pysvn.wc_status_kind.none: False, pysvn.wc_status_kind.normal: False, pysvn.wc_status_kind.obstructed: False, pysvn.wc_status_kind.replaced: True, pysvn.wc_status_kind.unversioned: False, } # return a value used to sort by status # 1-10 - text status # 11-20 - prop status # 21-30 - other status wc_status_kind_text_sort_map = { # need use to update pysvn.wc_status_kind.missing: 1, pysvn.wc_status_kind.incomplete: 1, pysvn.wc_status_kind.obstructed: 1, # user needs to sort this one out pysvn.wc_status_kind.conflicted: 2, # need user to checkin pysvn.wc_status_kind.deleted: 3, pysvn.wc_status_kind.added: 4, pysvn.wc_status_kind.modified: 4, # other controlled files pysvn.wc_status_kind.normal: -21, pysvn.wc_status_kind.external: -21, # uncontrolled but interesting files pysvn.wc_status_kind.unversioned: -22, # uncontrolled but uninteresting files pysvn.wc_status_kind.ignored: -23, # svn will not return these as the status of a file only of a change in a file pysvn.wc_status_kind.replaced: 0, pysvn.wc_status_kind.none: 0, pysvn.wc_status_kind.merged: 0, } prop_sort_offset = 10 def wc_notify_action_lookup( action ): if action in wc_notify_action_map: return wc_notify_action_map[ action ] return repr( action ) wc_notify_action_map = { pysvn.wc_notify_action.add: 'A', pysvn.wc_notify_action.commit_added: 'A', pysvn.wc_notify_action.commit_deleted: 'D', pysvn.wc_notify_action.commit_modified: 'M', pysvn.wc_notify_action.commit_postfix_txdelta: None, pysvn.wc_notify_action.commit_replaced: 'R', pysvn.wc_notify_action.copy: 'c', pysvn.wc_notify_action.delete: 'D', pysvn.wc_notify_action.failed_revert: 'F', pysvn.wc_notify_action.resolved: 'R', pysvn.wc_notify_action.restore: 'R', pysvn.wc_notify_action.revert: 'R', pysvn.wc_notify_action.skip: '?', pysvn.wc_notify_action.status_completed: None, pysvn.wc_notify_action.status_external: 'E', pysvn.wc_notify_action.update_add: 'A', pysvn.wc_notify_action.update_completed: None, pysvn.wc_notify_action.update_delete: 'D', pysvn.wc_notify_action.update_external: 'E', pysvn.wc_notify_action.update_update: 'U', pysvn.wc_notify_action.annotate_revision: 'a', } if version_info.notify_action_has_failed_lock: wc_notify_action_map[ pysvn.wc_notify_action.failed_lock ] = 'lock failed' wc_notify_action_map[ pysvn.wc_notify_action.failed_unlock ] = 'unlock failed' wc_notify_action_map[ pysvn.wc_notify_action.locked ] = 'Locked' wc_notify_action_map[ pysvn.wc_notify_action.unlocked ] = 'Unlocked' if version_info.notify_action_has_property_events: wc_notify_action_map[ pysvn.wc_notify_action.property_added ] = '_A' wc_notify_action_map[ pysvn.wc_notify_action.property_modified ] = '_M' wc_notify_action_map[ pysvn.wc_notify_action.property_deleted ] = '_D' wc_notify_action_map[ pysvn.wc_notify_action.property_deleted_nonexistent ] = 'property_deleted_nonexistent' wc_notify_action_map[ pysvn.wc_notify_action.revprop_set ] = 'revprop_set' wc_notify_action_map[ pysvn.wc_notify_action.revprop_deleted ] = 'revprop_deleted' wc_notify_action_map[ pysvn.wc_notify_action.merge_completed ] = 'merge_completed' wc_notify_action_map[ pysvn.wc_notify_action.tree_conflict ] = 'tree_conflict' wc_notify_action_map[ pysvn.wc_notify_action.failed_external ] = 'failed_external' if version_info.notify_action_has_upgrade_events: wc_notify_action_map[ pysvn.wc_notify_action.upgraded_path ] = 'upgraded_path' wc_notify_action_map[ pysvn.wc_notify_action.update_started ] = "update_started" wc_notify_action_map[ pysvn.wc_notify_action.update_skip_obstruction ] = "update_skip_obstruction" wc_notify_action_map[ pysvn.wc_notify_action.update_skip_working_only ] = "update_skip_working_only" wc_notify_action_map[ pysvn.wc_notify_action.update_external_removed ] = "update_external_removed" wc_notify_action_map[ pysvn.wc_notify_action.update_shadowed_add ] = "update_shadowed_add" wc_notify_action_map[ pysvn.wc_notify_action.update_shadowed_update ] = "update_shadowed_update" wc_notify_action_map[ pysvn.wc_notify_action.update_shadowed_delete ] = "update_shadowed_delete" wc_notify_action_map[ pysvn.wc_notify_action.merge_record_info ] = "merge_record_info" wc_notify_action_map[ pysvn.wc_notify_action.upgraded_path ] = "upgraded_path" wc_notify_action_map[ pysvn.wc_notify_action.merge_record_info_begin ] = "merge_record_info_begin" wc_notify_action_map[ pysvn.wc_notify_action.merge_elide_info ] = "merge_elide_info" wc_notify_action_map[ pysvn.wc_notify_action.patch ] = "patch" wc_notify_action_map[ pysvn.wc_notify_action.patch_applied_hunk ] = "patch_applied_hunk" wc_notify_action_map[ pysvn.wc_notify_action.patch_rejected_hunk ] = "patch_rejected_hunk" wc_notify_action_map[ pysvn.wc_notify_action.patch_hunk_already_applied ] = "patch_hunk_already_applied" wc_notify_action_map[ pysvn.wc_notify_action.commit_copied ] = "commit_copied" wc_notify_action_map[ pysvn.wc_notify_action.commit_copied_replaced ] = "commit_copied_replaced" wc_notify_action_map[ pysvn.wc_notify_action.url_redirect ] = "url_redirect" wc_notify_action_map[ pysvn.wc_notify_action.path_nonexistent ] = "path_nonexistent" wc_notify_action_map[ pysvn.wc_notify_action.exclude ] = "exclude" wc_notify_action_map[ pysvn.wc_notify_action.failed_conflict ] = "failed_conflict" wc_notify_action_map[ pysvn.wc_notify_action.failed_missing ] = "failed_missing" wc_notify_action_map[ pysvn.wc_notify_action.failed_out_of_date ] = "failed_out_of_date" wc_notify_action_map[ pysvn.wc_notify_action.failed_no_parent ] = "failed_no_parent" wc_notify_action_map[ pysvn.wc_notify_action.failed_locked ] = "failed_locked" wc_notify_action_map[ pysvn.wc_notify_action.failed_forbidden_by_server ] = "failed_forbidden_by_server" wc_notify_action_map[ pysvn.wc_notify_action.skip_conflicted ] = "skip_conflicted" def wc_notify_type_lookup( action ): if action in wc_notify_type_map: return wc_notify_type_map[ action ] return '?' wc_notify_type_map = { pysvn.wc_notify_action.add: 'A', pysvn.wc_notify_action.commit_added: 'C', pysvn.wc_notify_action.commit_deleted: 'C', pysvn.wc_notify_action.commit_modified: 'C', pysvn.wc_notify_action.commit_postfix_txdelta: None, pysvn.wc_notify_action.commit_replaced: 'C', pysvn.wc_notify_action.copy: 'A', pysvn.wc_notify_action.delete: 'A', pysvn.wc_notify_action.failed_revert: 'A', pysvn.wc_notify_action.resolved: 'A', pysvn.wc_notify_action.restore: 'A', pysvn.wc_notify_action.revert: 'A', pysvn.wc_notify_action.skip: '?', pysvn.wc_notify_action.status_completed: None, pysvn.wc_notify_action.status_external: 'A', pysvn.wc_notify_action.update_add: 'U', pysvn.wc_notify_action.update_completed: None, pysvn.wc_notify_action.update_delete: 'U', pysvn.wc_notify_action.update_external: 'U', pysvn.wc_notify_action.update_update: 'U', pysvn.wc_notify_action.annotate_revision: 'A', } if version_info.notify_action_has_failed_lock: wc_notify_type_map[ pysvn.wc_notify_action.failed_lock ] = None wc_notify_type_map[ pysvn.wc_notify_action.failed_unlock ] = None wc_notify_type_map[ pysvn.wc_notify_action.locked ] = None wc_notify_type_map[ pysvn.wc_notify_action.unlocked ] = None if version_info.notify_action_has_property_events: wc_notify_type_map[ pysvn.wc_notify_action.property_added ] = 'A' wc_notify_type_map[ pysvn.wc_notify_action.property_modified ] = 'M' wc_notify_type_map[ pysvn.wc_notify_action.property_deleted ] = 'D' wc_notify_type_map[ pysvn.wc_notify_action.property_deleted_nonexistent ] = None wc_notify_type_map[ pysvn.wc_notify_action.revprop_set ] = None wc_notify_type_map[ pysvn.wc_notify_action.revprop_deleted ] = None wc_notify_type_map[ pysvn.wc_notify_action.merge_completed ] = None wc_notify_type_map[ pysvn.wc_notify_action.tree_conflict ] = None wc_notify_type_map[ pysvn.wc_notify_action.failed_external ] = None if version_info.notify_action_has_upgrade_events: wc_notify_type_map[ pysvn.wc_notify_action.upgraded_path ] = None wc_notify_type_map[ pysvn.wc_notify_action.update_started ] = None wc_notify_type_map[ pysvn.wc_notify_action.update_skip_obstruction ] = None wc_notify_type_map[ pysvn.wc_notify_action.update_skip_working_only ] = None wc_notify_type_map[ pysvn.wc_notify_action.update_external_removed ] = None wc_notify_type_map[ pysvn.wc_notify_action.update_shadowed_add ] = None wc_notify_type_map[ pysvn.wc_notify_action.update_shadowed_update ] = None wc_notify_type_map[ pysvn.wc_notify_action.update_shadowed_delete ] = None wc_notify_type_map[ pysvn.wc_notify_action.merge_record_info ] = None wc_notify_type_map[ pysvn.wc_notify_action.upgraded_path ] = None wc_notify_type_map[ pysvn.wc_notify_action.merge_record_info_begin ] = None wc_notify_type_map[ pysvn.wc_notify_action.merge_elide_info ] = None wc_notify_type_map[ pysvn.wc_notify_action.patch ] = None wc_notify_type_map[ pysvn.wc_notify_action.patch_applied_hunk ] = None wc_notify_type_map[ pysvn.wc_notify_action.patch_rejected_hunk ] = None wc_notify_type_map[ pysvn.wc_notify_action.patch_hunk_already_applied ] = None wc_notify_type_map[ pysvn.wc_notify_action.commit_copied ] = None wc_notify_type_map[ pysvn.wc_notify_action.commit_copied_replaced ] = None wc_notify_type_map[ pysvn.wc_notify_action.url_redirect ] = None wc_notify_type_map[ pysvn.wc_notify_action.path_nonexistent ] = None wc_notify_type_map[ pysvn.wc_notify_action.exclude ] = None wc_notify_type_map[ pysvn.wc_notify_action.failed_conflict ] = '?' wc_notify_type_map[ pysvn.wc_notify_action.failed_missing ] = '?' wc_notify_type_map[ pysvn.wc_notify_action.failed_out_of_date ] = '?' wc_notify_type_map[ pysvn.wc_notify_action.failed_no_parent ] = '?' wc_notify_type_map[ pysvn.wc_notify_action.failed_locked ] = '?' wc_notify_type_map[ pysvn.wc_notify_action.failed_forbidden_by_server ] = '?' wc_notify_type_map[ pysvn.wc_notify_action.skip_conflicted ] = '?' # # format the concise status from file # def _status_format( file ): text_code = wc_status_kind_map[ file.text_status ] prop_code = wc_status_kind_map[ file.prop_status ] if text_code == ' ' and prop_code != ' ': text_code = '_' if (file.is_locked or file.is_copied or file.is_switched) and prop_code == ' ': prop_code = '_' lock_state = ' ' if file.entry is not None and hasattr( file.entry, 'lock_token' ): if file.entry.lock_token is not None: lock_state = 'K' state = '%s%s%s%s%s%s' % (text_code, prop_code, ' L'[ file.is_locked ], ' +'[ file.is_copied ], ' S'[ file.is_switched ], lock_state) return state.strip() def populateMenu( menu, contents ): for details in contents: if len(details) == 3: type, id, name = details cond = True else: type, id, name, cond = details if type == '-': if cond: menu.AppendSeparator() elif type == 'x': menu.AppendCheckItem( id, name ) menu.Enable( id, cond ) elif type == 'o': menu.AppendRadioItem( id, name ) menu.Enable( id, cond ) elif type == '': menu.Append( id, name ) menu.Enable( id, cond ) elif type == '>': # sub menu in the list in id menu.AppendMenu( id, name, populateMenu( wx.Menu(), cond ) ) else: raise wb_exceptions.InternalError( 'Unknown populateMenu contents (%s,%s,%s,%s)' % (repr(type),repr(id),repr(name),repr(cond)) ) return menu def by_path( a, b ): return cmp( a.path, b.path ) WorkBench-1.8.2/Source/wb_platform_macosx_specific.py000644 000765 000024 00000003266 11511055031 023232 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2004-2010 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_platform_macosx_specific.py ''' import os import types def getApplicationDir(): return os.path.join( os.environ['HOME'], 'Library/Preferences/org.tigris.pysvn.Workbench' ) def getLocalePath( app ): return os.path.join( os.environ.get( 'PYTHONHOME', app.app_dir ), 'locale' ) def getNullDevice(): return '/dev/null' def uPathExists( path ): if type(path) == types.UnicodeType: path = path.encode( 'utf-8' ) return os.path.exists( path ) def uPathIsdir( path ): if type(path) == types.UnicodeType: path = path.encode( 'utf-8' ) return os.path.isdir( path ) def uAccess( path, mode ): if type(path) == types.UnicodeType: path = path.encode( 'utf-8' ) return os.access( path, mode ) def uRemove( path ): if type(path) == types.UnicodeType: path = path.encode( 'utf-8' ) return os.remove( path ) def uRename( path1, path2 ): if type(path1) == types.UnicodeType: path1 = path1.encode( 'utf-8' ) if type(path2) == types.UnicodeType: path2 = path2.encode( 'utf-8' ) return os.rename( path1, path2 ) def uOpen( path, mode ): if type(path) == types.UnicodeType: path = path.encode( 'utf-8' ) return open( path, mode ) def uChdir( path ): if type(path) == types.UnicodeType: path = path.encode( 'utf-8' ) return os.chdir( path ) WorkBench-1.8.2/Source/make-pot-file.cmd000644 000765 000024 00000000033 11261605162 020241 0ustar00barrystaff000000 000000 %PYTHON% make_pot_file.py WorkBench-1.8.2/Source/wb_preferences_dialog.py000644 000765 000024 00000121506 11507143125 022014 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_preferences_dialog.py ''' import wx import wb_exceptions import os import wb_subversion_list_handler_common import wb_shell_commands import wb_dialogs import wb_tree_panel import wb_toolbars import wb_platform_specific class PreferencesDialog( wx.Dialog ): def __init__( self, parent, app ): wx.Dialog.__init__( self, parent, -1, T_('Preferences'), size=(400,200) ) self.app = app self.v_sizer = None # useful for debugging new pages try: self.initControls() except: app.log.exception( T_('PreferencesDialog') ) self.SetSizer( self.v_sizer ) self.Layout() self.Fit() self.CentreOnParent() def initControls( self ): self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.notebook = wx.Notebook( self ) self.v_sizer.Add( self.notebook, 0, wx.EXPAND|wx.ALL, 5 ) self.pages = [] self.pages.append( EditorPage( self.notebook, self.app ) ) self.pages.append( DiffToolPage( self.notebook, self.app ) ) self.pages.append( ShellPage( self.notebook, self.app ) ) self.pages.append( ListColumnsPage( self.notebook, self.app ) ) self.pages.append( LogHistoryPage( self.notebook, self.app ) ) self.pages.append( ToolbarPage( self.notebook, self.app ) ) # may want view page for editing the control file #self.pages.append( ViewPage( self.notebook, self.app ) ) self.pages.append( AdvancedPage( self.notebook, self.app ) ) self.button_ok = wx.Button( self, wx.ID_OK, T_(' OK ') ) self.button_ok.SetDefault() self.button_cancel = wx.Button( self, wx.ID_CANCEL, T_(' Cancel ') ) self.h_sizer = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer.Add( (1, 1), 1, wx.EXPAND ) self.h_sizer.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15 ) self.h_sizer.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 2 ) self.v_sizer.Add( self.h_sizer, 0, wx.EXPAND|wx.ALL, 5 ) wx.EVT_BUTTON( self, wx.ID_OK, self.OnOk ) wx.EVT_BUTTON( self, wx.ID_CANCEL, self.OnCancel ) def OnOk( self, event ): for page in self.pages: if not page.validate(): return for page in self.pages: page.savePreferences() self.EndModal( wx.ID_OK ) def OnCancel( self, event ): self.EndModal( wx.ID_CANCEL ) class PagePanel(wx.Panel): def __init__( self, notebook, title ): wx.Panel.__init__( self, notebook, -1, style = wx.NO_BORDER ) self.page_v_sizer = wx.BoxSizer( wx.VERTICAL ) self.page_v_sizer.Add( self.initControls(), 0, wx.EXPAND|wx.ALL, 5 ) self.SetSizer( self.page_v_sizer ) self.SetAutoLayout( True ) self.page_v_sizer.Fit( self ) self.Layout() notebook.AddPage( self, title ) def initControls( self ): raise wb_exceptions.InternalError('must override initControls') class EditorPage(PagePanel): def __init__( self, notebook, app ): self.app = app PagePanel.__init__( self, notebook, T_('Editor') ) def initControls( self ): p = self.app.prefs.getEditor() self.static_text1 = wx.StaticText( self, -1, T_('Editor: '), style=wx.ALIGN_RIGHT) self.text_ctrl_editor = wx.TextCtrl( self, -1, p.editor_image, wx.DefaultPosition, wx.Size(415, -1) ) self.static_text2 = wx.StaticText( self, -1, T_('Edit Arguments: '), style=wx.ALIGN_RIGHT) self.text_ctrl_edit_arg = wx.TextCtrl( self, -1, p.editor_options, wx.DefaultPosition, wx.Size(315, -1) ) self.browse_button = wx.Button( self, -1, T_(' Browse... ')) self.grid_sizer = wx.FlexGridSizer( 0, 3, 0, 0 ) self.grid_sizer.AddGrowableCol( 1 ) self.grid_sizer.Add( self.static_text1, 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( self.text_ctrl_editor, 0, wx.EXPAND|wx.ALL, 5 ) self.grid_sizer.Add( self.browse_button, 0, wx.EXPAND ) self.grid_sizer.Add( self.static_text2, 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( self.text_ctrl_edit_arg, 0, wx.EXPAND|wx.ALL, 5 ) self.grid_sizer.Add( (1, 1), 0, wx.EXPAND ) wx.EVT_BUTTON( self, self.browse_button.GetId(), self.OnBrowseExe ) return self.grid_sizer def savePreferences( self ): p = self.app.prefs.getEditor() p.editor_image = self.text_ctrl_editor.GetValue() p.editor_options = self.text_ctrl_edit_arg.GetValue() def OnBrowseExe( self, event ): if wx.Platform == '__WXMSW__': file_type = T_('Executable files (*.exe)|*.exe') elif wx.Platform == '__WXMAC__': file_type = T_('Applications|*.app|Executable files|*') else: file_type = T_('Executable files|*') filename = self.text_ctrl_editor.GetValue() file_dialog = wx.FileDialog( self, T_('Choose an Executable file'), os.path.dirname( filename ), os.path.basename( filename ), file_type, wx.OPEN ) if file_dialog.ShowModal() == wx.ID_OK: self.text_ctrl_editor.SetValue( file_dialog.GetPath() ) file_dialog.Destroy() def validate( self ): # allow no editor if len(self.text_ctrl_editor.GetValue()) == 0: return True # otherwise it must exist valid = False if wx.Platform == '__WXMAC__': valid = (wb_platform_specific.uAccess( self.text_ctrl_editor.GetValue(), os.X_OK ) or not wb_platform_specific.uPathIsdir( self.text_ctrl_editor.GetValue() ) ) elif wx.Platform == '__WXMSW__': valid = (wb_platform_specific.uPathExists( self.text_ctrl_editor.GetValue() ) or not wb_platform_specific.uPathIsdir( self.text_ctrl_editor.GetValue() ) ) else: valid = (wb_platform_specific.uAccess( self.text_ctrl_editor.GetValue(), os.X_OK ) or not wb_platform_specific.uPathIsdir( self.text_ctrl_editor.GetValue() ) ) if not valid: wx.MessageBox( T_('You must enter a valid editor executable'), T_('Warning'), wx.OK | wx.ICON_EXCLAMATION, self ) return False return True class DiffToolPage(PagePanel): def __init__( self, notebook, app ): self.app = app PagePanel.__init__( self, notebook, T_('Diff Tool') ) 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': ''} self.options = {'built-in': '', 'external-gui-diff': p.gui_diff_tool_options, 'external-shell-diff': p.shell_diff_tool_options, 'svn-diff': ''} self.mode_choice_values = [T_('Work Bench Diff'), T_('External GUI Diff Command'), T_('External Text Diff'), T_('SVN diff')] self.mode_choice = wx.Choice( self, -1, choices = self.mode_choice_values ) if p.diff_tool_mode not in self.mode_values: p.diff_tool_mode = 'built-in' self.mode = p.diff_tool_mode self.mode_choice.SetSelection( self.mode_values.index( p.diff_tool_mode ) ) self.text_ctrl_diff_tool = wx.TextCtrl( self, -1, self.diff_tools[self.mode], wx.DefaultPosition, wx.Size(415, -1) ) self.text_ctrl_options = wx.TextCtrl( self, -1, self.options[self.mode], wx.DefaultPosition, wx.Size(315, -1) ) self.browse_button = wx.Button( self, -1, T_(' Browse... ') ) self.grid_sizer = wx.FlexGridSizer( 0, 3, 0, 0 ) self.grid_sizer.AddGrowableCol( 1 ) self.grid_sizer.Add( wx.StaticText( self, -1, T_('Mode: '), style=wx.ALIGN_RIGHT), 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( self.mode_choice, 0, wx.ALIGN_LEFT, 5 ) self.grid_sizer.Add( (1, 1), 0, wx.EXPAND ) self.grid_sizer.Add( wx.StaticText( self, -1, T_('Diff Tool: '), style=wx.ALIGN_RIGHT), 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( self.text_ctrl_diff_tool, 0, wx.EXPAND|wx.ALL, 5 ) self.grid_sizer.Add( self.browse_button, 0, wx.EXPAND ) self.grid_sizer.Add( wx.StaticText( self, -1, T_('Tool Arguments: '), style=wx.ALIGN_RIGHT), 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( self.text_ctrl_options, 0, wx.EXPAND|wx.ALL, 5 ) self.grid_sizer.Add( (1, 1), 0, wx.EXPAND ) self.grid_sizer.Add( wx.StaticText( self, -1, T_('Use'), style=wx.ALIGN_RIGHT), 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( wx.StaticText( self, -1, T_('%nl for left file name, %nr for right file name,\n%tl for left title, %tr for right title'), style=wx.ALIGN_LEFT), 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( (1, 1), 0, wx.EXPAND ) self.OnModeChange( None ) wx.EVT_CHOICE( self, self.mode_choice.GetId(), self.OnModeChange ) wx.EVT_BUTTON( self, self.browse_button.GetId(), self.OnBrowseExe ) return self.grid_sizer def savePreferences( self ): self.OnModeChange( None ) p = self.app.prefs.getDiffTool() 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'] p.diff_tool_mode = self.mode 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 ) def OnBrowseExe( self, event ): if wx.Platform == '__WXMSW__': file_type = T_('Executable files (*.exe)|*.exe') elif wx.Platform == '__WXMAC__': file_type = T_('Applications|*.app|Executable files|*') else: file_type = T_('Executable files|*') filename = self.text_ctrl_diff_tool.GetValue() file_dialog = wx.FileDialog( self, T_('Choose an Executable file'), os.path.dirname( filename ), os.path.basename( filename ), file_type, wx.OPEN ) if file_dialog.ShowModal() == wx.ID_OK: self.text_ctrl_diff_tool.SetValue( file_dialog.GetPath() ) file_dialog.Destroy() def validate( self ): return True class ShellPage(PagePanel): def __init__( self, notebook, app ): self.app = app PagePanel.__init__( self, notebook, T_('Shell') ) def initControls( self ): p = self.app.prefs.getShell() self.static_text1 = wx.StaticText( self, -1, T_('Terminal Init Command: '), style=wx.ALIGN_RIGHT) self.text_ctrl_shell_init_command = wx.TextCtrl( self, -1, p.shell_init_command, wx.DefaultPosition, wx.Size(300, -1) ) self.grid_sizer = wx.FlexGridSizer( 0, 2, 0, 0 ) self.grid_sizer.AddGrowableCol( 1 ) terminal_program_list = wb_shell_commands.getTerminalProgramList() if len(terminal_program_list) > 0: self.static_text2 = wx.StaticText( self, -1, T_('Terminal Program: '), style=wx.ALIGN_RIGHT) self.terminal_program_list_ctrl = wx.Choice( self, -1, choices=terminal_program_list, size=(150,-1) ) if p.shell_terminal in terminal_program_list: self.terminal_program_list_ctrl.SetStringSelection( p.shell_terminal ) else: self.terminal_program_list_ctrl.SetStringSelection( terminal_program_list[0] ) self.grid_sizer.Add( self.static_text2, 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( self.terminal_program_list_ctrl, 0, wx.EXPAND|wx.ALL, 3 ) else: self.terminal_program_list_ctrl = None self.grid_sizer.Add( self.static_text1, 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( self.text_ctrl_shell_init_command, 0, wx.EXPAND|wx.ALL, 3 ) file_browser_program_list = wb_shell_commands.getFileBrowserProgramList() if len(file_browser_program_list) > 0: self.static_text3 = wx.StaticText( self, -1, T_('File Browser Program: '), style=wx.ALIGN_RIGHT) self.file_browser_program_list_ctrl = wx.Choice( self, -1, choices=file_browser_program_list, size=(150,-1) ) if p.shell_file_browser in file_browser_program_list: self.file_browser_program_list_ctrl.SetStringSelection( p.shell_file_browser ) else: self.file_browser_program_list_ctrl.SetStringSelection( file_browser_program_list[0] ) self.grid_sizer.Add( self.static_text3, 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( self.file_browser_program_list_ctrl, 0, wx.EXPAND|wx.ALL, 3 ) else: self.file_browser_program_list_ctrl = None self.grid_sizer.Add( (1, 1), 0, wx.EXPAND ) return self.grid_sizer def savePreferences( self ): p = self.app.prefs.getShell() p.shell_init_command = self.text_ctrl_shell_init_command.GetValue().encode('UTF-8') if self.file_browser_program_list_ctrl is not None: p.shell_file_browser = self.file_browser_program_list_ctrl.GetStringSelection().encode('UTF-8') if self.terminal_program_list_ctrl is not None: p.shell_terminal = self.terminal_program_list_ctrl.GetStringSelection().encode('UTF-8') def validate( self ): return True class ViewPage(PagePanel): id_add = wx.NewId() id_del = wx.NewId() id_filename_list = wx.NewId() id_pattern_text = wx.NewId() def __init__( self, notebook, app ): self.app = app PagePanel.__init__( self, notebook, T_('View') ) self.selected_item = None def initControls( self ): p = self.app.prefs.getView() self.filename_list = wx.ListCtrl( self, ViewPage.id_filename_list, wx.DefaultPosition, wx.Size( -1, 100 ), wx.LC_REPORT ) self.filename_list.InsertColumn( 0, T_('Exclude filename') ) self.filename_list.SetColumnWidth( 0, 400 ) for index, filename in enumerate( p.excluded_filenames_list ): self.filename_list.InsertStringItem( index, filename ) self.button_add = wx.Button( self, ViewPage.id_add, T_(' Add ') ) self.button_add.Enable( False ) self.button_del = wx.Button( self, ViewPage.id_del, T_(' Delete ') ) self.button_del.Enable( False ) self.pattern_text_ctrl = wx.TextCtrl( self, ViewPage.id_pattern_text, '', wx.DefaultPosition, wx.Size(200, -1), style=wx.TE_PROCESS_ENTER ) self.h_sizer = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer.Add( self.button_add, 0, wx.EXPAND|wx.EAST, 15 ) self.h_sizer.Add( self.pattern_text_ctrl, 0, wx.EXPAND|wx.EAST, 5 ) self.h_sizer.Add( self.button_del, 0, wx.EXPAND|wx.EAST, 5 ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.v_sizer.Add( self.filename_list, 0, wx.EXPAND|wx.EAST, 5 ) self.v_sizer.Add( self.h_sizer, 0, wx.EXPAND|wx.EAST, 5 ) wx.EVT_TEXT( self, ViewPage.id_pattern_text, self.OnPatternTextChanged ) wx.EVT_TEXT_ENTER( self, ViewPage.id_pattern_text, self.OnPatternTextEnter ) wx.EVT_BUTTON( self, ViewPage.id_add, self.OnAdd ) wx.EVT_BUTTON( self, ViewPage.id_del, self.OnDel ) wx.EVT_LIST_ITEM_SELECTED( self, ViewPage.id_filename_list, self.OnPatternSelected ) wx.EVT_LIST_ITEM_DESELECTED( self, ViewPage.id_filename_list, self.OnPatternDeselected ) return self.v_sizer def sortList( self, item1, item2 ): return cmp( self.filename_list.GetItemText( item1 ), self.filename_list.GetItemText( item2 ) ) def OnAdd( self, event ): pattern = self.pattern_text_ctrl.GetValue() if self.selected_item is None: self.filename_list.InsertStringItem( 0, pattern ) else: self.filename_list.SetItemText( self.selected_item, pattern ) self.filename_list.SortItems( self.sortList ) self.button_add.Enable( False ) def OnDel( self, event ): if self.selected_item is not None: self.filename_list.DeleteItem(self.selected_item ) self.selected_item = None self.button_del.Enable( False ) def OnPatternTextEnter( self, event ): if self.button_add.IsEnabled(): self.OnAdd( None ) def OnPatternTextChanged( self, event ): new_pattern = self.pattern_text_ctrl.GetValue() if len(new_pattern) == 0: self.button_add.Enable( False ) return # enable the add button if the text is not in the list already for index in range( self.filename_list.GetItemCount() ): pattern = self.filename_list.GetItemText( index ) if pattern == new_pattern: self.button_add.Enable( False ) return self.button_add.Enable( True ) def OnPatternSelected( self, event ): self.selected_item = event.m_itemIndex # put the selected text into the pattern ctrl pattern = self.filename_list.GetItemText( event.m_itemIndex ) self.pattern_text_ctrl.SetValue( pattern ) self.pattern_text_ctrl.SetSelection( -1, -1 ) # Enable the delete button self.button_del.Enable( True ) def OnPatternDeselected( self, event ): self.selected_item = None # Disable the delete button self.button_del.Enable( False ) def savePreferences( self ): p = self.app.prefs.getView() filename_list = [] for index in range( self.filename_list.GetItemCount() ): filename_list.append( self.filename_list.GetItemText( index ).encode('UTF-8') ) p.excluded_filenames_list = filename_list def validate( self ): return True class ListColumnsPage(PagePanel): id_exclude = wx.NewId() id_include = wx.NewId() id_move_up = wx.NewId() id_move_down = wx.NewId() id_excluded_list = wx.NewId() id_included_list = wx.NewId() id_width = wx.NewId() def __init__( self, notebook, app ): self.app = app self.selected_col_id = None self.column_info = wb_subversion_list_handler_common.ViewColumnInfo() PagePanel.__init__( self, notebook, T_('Columns') ) def initControls( self ): p = self.app.prefs.getView() self.column_info.setFromPreferenceData( p ) self.excluded_list = wx.ListCtrl( self, ListColumnsPage.id_excluded_list, wx.DefaultPosition, wx.Size( 200, 100 ), wx.LC_REPORT ) self.excluded_list.InsertColumn( 0, T_('Column') ) self.excluded_list.SetColumnWidth( 0, 100 ) self.excluded_list.InsertColumn( 1, T_('Width'), wx.LIST_FORMAT_RIGHT ) self.excluded_list.SetColumnWidth( 1, 80 ) self.included_list = wx.ListCtrl( self, ListColumnsPage.id_included_list, wx.DefaultPosition, wx.Size( 200, 100 ), wx.LC_REPORT ) self.included_list.InsertColumn( 0, T_('Column') ) self.included_list.SetColumnWidth( 0, 100 ) self.included_list.InsertColumn( 1, T_('Width'), wx.LIST_FORMAT_RIGHT ) self.included_list.SetColumnWidth( 1, 80 ) for name in self.column_info.getColumnOrder(): info = self.column_info.getInfoByName( name ) info.included = True index = self.included_list.GetItemCount() self.included_list.InsertStringItem( index, T_( info.label ) ) self.included_list.SetItemData( index, info.col_id ) self.included_list.SetStringItem( index, 1, str(info.width) ) for info in self.column_info.excludedInfo(): index = self.excluded_list.GetItemCount() self.excluded_list.InsertStringItem( index, T_( info.label ) ) self.excluded_list.SetItemData( index, info.col_id ) self.excluded_list.SetStringItem( index, 1, str(info.width) ) self.button_include = wx.Button( self, ListColumnsPage.id_include, T_(' Include --> ') ) self.button_include.Enable( False ) self.button_exclude = wx.Button( self, ListColumnsPage.id_exclude, T_(' <-- Exclude ') ) self.button_exclude.Enable( False ) self.button_up = wx.Button( self, ListColumnsPage.id_move_up, T_(' Move Up ') ) self.button_up.Enable( False ) self.button_down = wx.Button( self, ListColumnsPage.id_move_down, T_(' Move Down ') ) self.button_down.Enable( False ) self.width_text_ctrl = wx.TextCtrl( self, ListColumnsPage.id_width, '', wx.DefaultPosition, wx.Size(200, -1), style=wx.TE_PROCESS_ENTER|wx.TE_RIGHT ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.v_sizer.Add( self.button_include, 0, wx.EXPAND|wx.EAST, 5 ) self.v_sizer.Add( self.button_exclude, 0, wx.EXPAND|wx.EAST, 5 ) self.v_sizer.Add( self.width_text_ctrl, 0, wx.EXPAND|wx.EAST, 5 ) self.v_sizer.Add( self.button_up, 0, wx.EXPAND|wx.EAST, 5 ) self.v_sizer.Add( self.button_down, 0, wx.EXPAND|wx.EAST, 5 ) self.h_sizer = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer.Add( self.excluded_list, 0, wx.EXPAND|wx.WEST, 5 ) self.h_sizer.Add( self.v_sizer, 0, wx.EXPAND|wx.EAST, 5 ) self.h_sizer.Add( self.included_list, 0, wx.EXPAND|wx.EAST, 5 ) wx.EVT_BUTTON( self, ListColumnsPage.id_include, self.OnInclude ) wx.EVT_BUTTON( self, ListColumnsPage.id_exclude, self.OnExclude ) wx.EVT_BUTTON( self, ListColumnsPage.id_move_up, self.OnMoveUp ) wx.EVT_BUTTON( self, ListColumnsPage.id_move_down, self.OnMoveDown ) wx.EVT_TEXT_ENTER( self, ListColumnsPage.id_width, self.OnWidthTextEnter ) wx.EVT_LIST_ITEM_SELECTED( self, ListColumnsPage.id_excluded_list, self.OnExcludedListItemSelected ) wx.EVT_LIST_ITEM_DESELECTED( self, ListColumnsPage.id_excluded_list, self.OnExcludedListItemDeselected ) wx.EVT_LIST_ITEM_SELECTED( self, ListColumnsPage.id_included_list, self.OnIncludedListItemSelected ) wx.EVT_LIST_ITEM_DESELECTED( self, ListColumnsPage.id_included_list, self.OnIncludedListItemDeselected ) return self.h_sizer def OnInclude( self, event ): self.changeInclusionColumn( True, self.excluded_list, self.included_list ) def OnExclude( self, event ): self.changeInclusionColumn( False, self.included_list, self.excluded_list ) def changeInclusionColumn( self, include, from_list, to_list ): info = self.column_info.getInfoById( self.selected_col_id ) try: width = int(self.width_text_ctrl.GetValue()) except ValueError: wx.MessageBox( T_('Width for %(name)s must be an number between %(min)d and %(max)d') % {'name': T_( info.label ), 'min': info.min_width, 'max': info.max_width}, T_('Warning'), wx.OK | wx.ICON_EXCLAMATION, self ) return if( width >= info.min_width and width <= info.max_width ): info.width = width else: wx.MessageBox( T_('Width for %(name)s must be between %(min)d and %(max)d') % {'name': T_( info.label ), 'min': info.min_width, 'max': info.max_width}, T_('Warning'), wx.OK | wx.ICON_EXCLAMATION, self ) return info.include = include # remove from from_list from_index = from_list.FindItemData( -1, info.col_id ) from_list.DeleteItem( from_index ) # add to end of to_list index = to_list.GetItemCount() to_list.InsertStringItem( index, T_( info.label ) ) to_list.SetItemData( index, info.col_id ) to_list.SetStringItem( index, 1, str(info.width) ) def OnMoveUp( self, event ): self.moveColumn( -1 ) def OnMoveDown( self, event ): self.moveColumn( 1 ) def moveColumn( self, direction ): info = self.column_info.getInfoById( self.selected_col_id ) index = self.included_list.FindItemData( -1, info.col_id ) name = self.included_list.GetItemText( index ) self.included_list.DeleteItem( index ) index += direction self.included_list.InsertStringItem( index, T_( info.label ) ) self.included_list.SetItemData( index, info.col_id ) self.included_list.SetStringItem( index, 1, str( info.width ) ) self.included_list.SetItemState( index, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED ) # enable up and down if not at the ends item_count = self.included_list.GetItemCount() self.button_up.Enable( item_count > 1 and index != 0 ) self.button_down.Enable( item_count > 1 and index != (item_count-1) ) def OnWidthTextEnter( self, event ): info = self.column_info.getInfoById( self.selected_col_id ) try: width = int(self.width_text_ctrl.GetValue()) except ValueError: wx.MessageBox( T_('Width for %(name)s must be an number between %(min)d and %(max)d') % {'name': T_( info.label ), 'min': info.min_width, 'max': info.max_width}, T_('Warning'), wx.OK | wx.ICON_EXCLAMATION, self ) return if( width >= info.min_width and width <= info.max_width ): info.width = width index = self.included_list.FindItemData( -1, info.col_id ) self.included_list.SetStringItem( index, 1, str(width) ) else: wx.MessageBox( T_('Width for %(name)s must be between %(min)d and %(max)d') % {'name': T_( info.label ), 'min': info.min_width, 'max': info.max_width}, T_('Warning'), wx.OK | wx.ICON_EXCLAMATION, self ) def OnExcludedListItemSelected( self, event ): self.selected_col_id = self.excluded_list.GetItemData( event.m_itemIndex ) info = self.column_info.getInfoById( self.selected_col_id ) self.button_up.Enable( False ) self.button_down.Enable( False ) self.button_include.Enable( True ) self.button_exclude.Enable( False ) self.width_text_ctrl.SetValue( str(info.width) ) self.width_text_ctrl.Enable( True ) def OnExcludedListItemDeselected( self, event ): self.button_include.Enable( False ) self.width_text_ctrl.Enable( False ) self.button_up.Enable( False ) self.button_down.Enable( False ) def OnIncludedListItemSelected( self, event ): self.selected_col_id = self.included_list.GetItemData( event.m_itemIndex ) info = self.column_info.getInfoById( self.selected_col_id ) self.button_exclude.Enable( info.name != 'Name' ) self.button_include.Enable( False ) # enable up and down if not at the ends item_count = self.included_list.GetItemCount() self.button_up.Enable( item_count > 1 and event.m_itemIndex != 0 ) self.button_down.Enable( item_count > 1 and event.m_itemIndex != (item_count-1) ) self.width_text_ctrl.SetValue( str(info.width) ) self.width_text_ctrl.Enable( True ) def OnIncludedListItemDeselected( self, event ): self.button_exclude.Enable( False ) self.width_text_ctrl.Enable( False ) self.button_up.Enable( False ) self.button_down.Enable( False ) def savePreferences( self ): p = self.app.prefs.getView() column_order = [] for index in range( self.included_list.GetItemCount() ): col_id = self.included_list.GetItemData( index ) column_order.append( self.column_info.getNameById( col_id ) ) self.column_info.setColumnOrder( column_order ) p.column_order = self.column_info.getColumnOrder() p.column_widths = self.column_info.getColumnWidths() def validate( self ): info = self.column_info.getInfoByName( 'Name' ) if self.included_list.FindItemData( -1, info.col_id ) < 0: wx.MessageBox( T_('You must include the %s column') % (T_( info.label ),), T_('Warning'), wx.OK | wx.ICON_EXCLAMATION, self ) return False return True class LogHistoryPage(PagePanel): def __init__( self, notebook, app ): self.app = app PagePanel.__init__( self, notebook, T_('Log History') ) def initControls( self ): p = self.app.prefs.getLogHistory() self.mode_values = ['show_all', 'show_limit', 'show_since' ] self.mode_choice_values = [T_('Show all entries'), T_('Show only'), T_('Show since')] self.mode_label = wx.StaticText( self, -1, T_('Default mode: '), style=wx.ALIGN_RIGHT) self.mode_ctrl = wx.Choice( self, -1, choices = self.mode_choice_values ) self.mode_ctrl.SetSelection( self.mode_values.index( p.default_mode ) ) self.limit_label = wx.StaticText( self, -1, T_('Default limit: '), style=wx.ALIGN_RIGHT) self.limit_ctrl = wx.TextCtrl( self, -1, str( p.default_limit ), wx.DefaultPosition, wx.Size(415, -1) ) self.since_label = wx.StaticText( self, -1, T_('Default since interval (days): '), style=wx.ALIGN_RIGHT) self.since_ctrl = wx.TextCtrl( self, -1, str( p.default_since_days_interval ), wx.DefaultPosition, wx.Size(415, -1) ) self.tags_label = wx.StaticText( self, -1, T_('Default Include tags: '), style=wx.ALIGN_RIGHT) self.tags_ctrl = wx.CheckBox( self, -1, T_('Include tags in log history') ) self.tags_ctrl.SetValue( p.default_include_tags ) self.grid_sizer = wx.FlexGridSizer( 0, 2, 0, 0 ) self.grid_sizer.AddGrowableCol( 1 ) self.grid_sizer.Add( self.mode_label, 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( self.mode_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) self.grid_sizer.Add( self.limit_label, 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( self.limit_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) self.grid_sizer.Add( self.since_label, 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( self.since_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) self.grid_sizer.Add( self.tags_label, 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( self.tags_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) self.grid_sizer.Add( (1, 1), 0, wx.EXPAND ) return self.grid_sizer def savePreferences( self ): p = self.app.prefs.getLogHistory() p.default_mode = self.mode_values[ self.mode_ctrl.GetSelection() ] p.default_limit = int( self.limit_ctrl.GetValue() ) p.default_since_days_interval = int( self.since_ctrl.GetValue() ) p.default_include_tags = self.tags_ctrl.GetValue() def validate( self ): try: limit = int( self.limit_ctrl.GetValue() ) if limit < 1: raise ValueError( 'out of range' ) except ValueError: wx.MessageBox( T_('Limit must be greater then 0'), T_('Warning'), wx.OK | wx.ICON_EXCLAMATION, self ) return False try: since_days = int( self.since_ctrl.GetValue() ) if since_days < 1: raise ValueError( 'out of range' ) except ValueError: wx.MessageBox( T_('Since days must be greater then 0'), T_('Warning'), wx.OK | wx.ICON_EXCLAMATION, self ) return False return True class ToolbarPage(PagePanel): id_exclude = wx.NewId() id_include = wx.NewId() id_move_up = wx.NewId() id_move_down = wx.NewId() id_excluded_list = wx.NewId() id_included_list = wx.NewId() def __init__( self, notebook, app ): self.app = app self.selected_name = None PagePanel.__init__( self, notebook, T_('Toolbar') ) def initControls( self ): p = self.app.prefs.getToolbar() self.all_groups = wb_toolbars.toolbar_main.getAllGroupNames() self.group_order = p.group_order self.excluded_list = wx.ListCtrl( self, ToolbarPage.id_excluded_list, wx.DefaultPosition, wx.Size( 200, 100 ), wx.LC_REPORT ) self.excluded_list.InsertColumn( 0, T_('Toolbar Group') ) self.excluded_list.SetColumnWidth( 0, 100 ) self.included_list = wx.ListCtrl( self, ToolbarPage.id_included_list, wx.DefaultPosition, wx.Size( 200, 100 ), wx.LC_REPORT ) self.included_list.InsertColumn( 0, T_('Toolbar Group') ) self.included_list.SetColumnWidth( 0, 100 ) for name in self.group_order: index = self.included_list.GetItemCount() self.included_list.InsertStringItem( index, name ) for name in [name for name in self.all_groups if name not in self.group_order]: index = self.excluded_list.GetItemCount() self.excluded_list.InsertStringItem( index, name ) self.button_include = wx.Button( self, ToolbarPage.id_include, T_(' Include --> ') ) self.button_include.Enable( False ) self.button_exclude = wx.Button( self, ToolbarPage.id_exclude, T_(' <-- Exclude ') ) self.button_exclude.Enable( False ) self.button_up = wx.Button( self, ToolbarPage.id_move_up, T_(' Move Up ') ) self.button_up.Enable( False ) self.button_down = wx.Button( self, ToolbarPage.id_move_down, T_(' Move Down ') ) self.button_down.Enable( False ) # qqq self.enable_label = wx.StaticText( self, -1, T_('Display toolbar: '), style=wx.ALIGN_RIGHT ) self.enable_ctrl = wx.CheckBox( self, -1, T_('Enabled') ) self.enable_ctrl.SetValue( p.toolbar_enable ) self.horizontal_label = wx.StaticText( self, -1, T_('Orientation: '), style=wx.ALIGN_RIGHT ) self.horizontal_ctrl = wx.Choice( self, -1, choices=[T_('Horizontal'), T_('Vertical')] ) if p.horizontal_orientation: self.horizontal_ctrl.SetSelection( 0 ) else: self.horizontal_ctrl.SetSelection( 1 ) sizes = [T_('Small'), T_('Large'), T_('Huge')] pixtoidx = {64: 2, 32: 1, 16: 0} self.size_label = wx.StaticText( self, -1, T_('Icon size: '), style=wx.ALIGN_RIGHT ) self.ctrl_image_size = wx.Choice( self, -1, choices=sizes ) self.ctrl_image_size.SetSelection( pixtoidx.get( p.bitmap_size, 1 ) ) # build the sizers self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.v_sizer.Add( self.button_include, 0, wx.EXPAND|wx.EAST, 5 ) self.v_sizer.Add( self.button_exclude, 0, wx.EXPAND|wx.EAST, 5 ) self.v_sizer.Add( self.button_up, 0, wx.EXPAND|wx.EAST, 5 ) self.v_sizer.Add( self.button_down, 0, wx.EXPAND|wx.EAST, 5 ) self.h_sizer = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer.Add( self.excluded_list, 0, wx.EXPAND|wx.WEST, 5 ) self.h_sizer.Add( self.v_sizer, 0, wx.EXPAND|wx.EAST, 5 ) self.h_sizer.Add( self.included_list, 0, wx.EXPAND|wx.EAST, 5 ) self.grid_sizer = wx.FlexGridSizer( 0, 2, 0, 0 ) self.grid_sizer.AddGrowableCol( 1 ) self.grid_sizer.Add( self.enable_label, 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( self.enable_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) self.grid_sizer.Add( self.horizontal_label, 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( self.horizontal_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) self.grid_sizer.Add( self.size_label, 1, wx.EXPAND|wx.ALL, 3 ) self.grid_sizer.Add( self.ctrl_image_size, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer2 = wx.BoxSizer( wx.VERTICAL ) self.v_sizer2.Add( self.grid_sizer ) self.v_sizer2.Add( self.h_sizer ) wx.EVT_BUTTON( self, ToolbarPage.id_include, self.OnInclude ) wx.EVT_BUTTON( self, ToolbarPage.id_exclude, self.OnExclude ) wx.EVT_BUTTON( self, ToolbarPage.id_move_up, self.OnMoveUp ) wx.EVT_BUTTON( self, ToolbarPage.id_move_down, self.OnMoveDown ) wx.EVT_LIST_ITEM_SELECTED( self, ToolbarPage.id_excluded_list, self.OnExcludeSelected ) wx.EVT_LIST_ITEM_DESELECTED( self, ToolbarPage.id_excluded_list, self.OnExcludeDeselected ) wx.EVT_LIST_ITEM_SELECTED( self, ToolbarPage.id_included_list, self.OnIncludeSelected ) wx.EVT_LIST_ITEM_DESELECTED( self, ToolbarPage.id_included_list, self.OnIncludeDeselected ) return self.v_sizer2 def OnInclude( self, event ): self.changeInclusionGroup( self.excluded_list, self.included_list ) def OnExclude( self, event ): self.changeInclusionGroup( self.included_list, self.excluded_list ) def changeInclusionGroup( self, from_list, to_list ): # remove from from_list from_index = from_list.FindItem( -1, self.selected_name ) from_list.DeleteItem( from_index ) # add to end of to_list index = to_list.GetItemCount() to_list.InsertStringItem( index, self.selected_name ) def OnMoveUp( self, event ): self.moveColumn( -1 ) def OnMoveDown( self, event ): self.moveColumn( 1 ) def moveColumn( self, direction ): index = self.included_list.FindItem( -1, self.selected_name ) self.included_list.DeleteItem( index ) index += direction self.included_list.InsertStringItem( index, self.selected_name ) self.included_list.SetItemState( index, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED ) # enable up and down if not at the ends item_count = self.included_list.GetItemCount() self.button_up.Enable( item_count > 1 and index != 0 ) self.button_down.Enable( item_count > 1 and index != (item_count-1) ) def OnExcludeSelected( self, event ): self.button_up.Enable( False ) self.button_down.Enable( False ) self.button_include.Enable( True ) self.selected_name = self.excluded_list.GetItemText( event.m_itemIndex ) def OnExcludeDeselected( self, event ): self.button_include.Enable( False ) self.width_text_ctrl.Enable( False ) def OnIncludeSelected( self, event ): self.button_exclude.Enable( True ) self.button_include.Enable( False ) self.selected_name = self.included_list.GetItemText( event.m_itemIndex ) # enable up and down if no at the ends item_count = self.included_list.GetItemCount() self.button_up.Enable( item_count > 1 and event.m_itemIndex != 0 ) self.button_down.Enable( item_count > 1 and event.m_itemIndex != (item_count-1) ) def OnIncludeDeselected( self, event ): self.button_exclude.Enable( False ) def savePreferences( self ): p = self.app.prefs.getToolbar() p.toolbar_enable = self.enable_ctrl.GetValue() p = self.app.prefs.getToolbar() group_order = [] for index in range( self.included_list.GetItemCount() ): name = self.included_list.GetItemText( index ) group_order.append( name ) p.group_order = group_order p.horizontal_orientation = self.horizontal_ctrl.GetSelection() == 0 size_to_pixels = {0:16, 1:32, 2:64} p.bitmap_size = size_to_pixels[ self.ctrl_image_size.GetSelection() ] def validate( self ): if self.included_list.GetItemCount() == 0: wx.MessageBox( T_('You must include at least one Toolbar group'), T_('Warning'), wx.OK | wx.ICON_EXCLAMATION, self ) return False return True class AdvancedPage(PagePanel): def __init__( self, notebook, app ): self.app = app PagePanel.__init__( self, notebook, T_('Advanced') ) def initControls( self ): p = self.app.prefs.getAdvanced() self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.tagbranch_ctrl = wx.CheckBox( self, -1, T_("Allow arbitrary paths for tag/branch") ) self.tagbranch_ctrl.SetValue( p.arbitrary_tag_branch ) self.v_sizer.Add( self.tagbranch_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) return self.v_sizer def savePreferences( self ): p = self.app.prefs.getAdvanced() p.arbitrary_tag_branch = self.tagbranch_ctrl.GetValue() def validate( self ): return True WorkBench-1.8.2/Source/toolbar_images/000755 000765 000024 00000000000 13221441402 020106 5ustar00barrystaff000000 000000 WorkBench-1.8.2/Source/wb_subversion_annotate.py000644 000765 000024 00000012240 11507143125 022256 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_subversion_annotate.py ''' import wx import time import itertools import wb_images import wb_read_file import wb_subversion_utils class AnnotateFrame(wx.Frame): def __init__( self, app, project_info, filename, annotation ): wx.Frame.__init__( self, None, -1, T_("Annotation of %s") % filename ) self.panel = AnnotatePanel( self, app, project_info, filename, annotation ) # Set the application icon self.SetIcon( wb_images.getIcon( 'wb.png' ) ) wx.EVT_CLOSE( self, self.OnCloseWindow ) def OnCloseWindow( self, event ): self.Destroy() class AnnotatePanel(wx.Panel): col_line = 0 col_revision = 1 col_author = 2 col_date = 3 col_text = 4 def __init__( self, parent, app, project_info, filename, annotation ): wx.Panel.__init__( self, parent, -1 ) self.app = app self.project_info = project_info self.filename = filename self.annotation = annotation self.id_list = wx.NewId() self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.list_ctrl = wx.ListCtrl( self, self.id_list, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT|wx.NO_BORDER) self.list_ctrl.InsertColumn( self.col_line, T_("Line") ) self.list_ctrl.InsertColumn( self.col_revision, T_("Revision") ) self.list_ctrl.InsertColumn( self.col_author, T_("Author") ) self.list_ctrl.InsertColumn( self.col_date, T_("Date") ) self.list_ctrl.InsertColumn( self.col_text, T_("Text") ) char_width = 9 self.list_ctrl.SetColumnWidth( self.col_line, 6*char_width ) self.list_ctrl.SetColumnWidth( self.col_revision, 7*char_width ) self.list_ctrl.SetColumnWidth( self.col_author, 14*char_width ) self.list_ctrl.SetColumnWidth( self.col_date, 20*char_width ) self.list_ctrl.SetColumnWidth( self.col_text, 250*char_width ) self.v_sizer.Add( self.list_ctrl, 2, wx.EXPAND|wx.ALL, 5 ) wx.EVT_SIZE( self, self.OnSize ) self.initList() self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() #---------- Comment handlers ------------------------------------------------------------ def OnSize( self, event): w,h = self.GetClientSizeTuple() self.v_sizer.SetDimension( 0, 0, w, h ) def initList( self ): if len( self.annotation ) == 0: return encoding = wb_read_file.encodingFromContents( self.annotation[0]['line'] ) last_revision = None all_row_colours = itertools.cycle( (wx.Colour( 0xff, 0xff, 0xff ) ,wx.Colour( 0xf8, 0xf8, 0xf8 ) ) ) row_colour = all_row_colours.next() for index, entry in enumerate( self.annotation ): raw_line = entry['line'] try: line = raw_line.decode( encoding ) except UnicodeDecodeError: # fall back to latin-1 try: line = raw_line.decode( 'iso8859-1' ) except UnicodeDecodeError: # sigh this is hard. use the choosen encoding and replace chars in error line = raw_line.decode( encoding, 'replace' ) if '\t' in line: column = 1 char_list = [] for c in line: if c == '\t': char_list.append( ' ' ) column += 1 while (column%8) != 0: column += 1 char_list.append( ' ' ) else: char_list.append( c ) column += 1 tab_expanded_line = ''.join( char_list ) else: tab_expanded_line = line self.list_ctrl.InsertStringItem( index, str(entry['number']+1) ) if entry['revision'].number <= 0: revision = ' - ' else: revision = str( entry['revision'].number ) if revision != last_revision: # Only populate Revision, Author, Date columns when revision number changes. last_revision = revision row_colour = all_row_colours.next() entry_date = time.strptime( entry['date'].split('.')[0], '%Y-%m-%dT%H:%M:%S' ) self.list_ctrl.SetStringItem( index, self.col_revision, revision ) self.list_ctrl.SetStringItem( index, self.col_author, entry['author'] ) self.list_ctrl.SetStringItem( index, self.col_date, wb_subversion_utils.fmtDateTime( entry_date ) ) self.list_ctrl.SetStringItem( index, self.col_text, tab_expanded_line ) self.list_ctrl.SetItemBackgroundColour( index, row_colour ) WorkBench-1.8.2/Source/wb_images.py000644 000765 000024 00000535301 13221441402 017435 0ustar00barrystaff000000 000000 import wx import cStringIO def getBitmap( name, size=None ): return wx.BitmapFromImage( getImage( name, size ) ) def getImage( name, size=None ): stream = cStringIO.StringIO( images_by_filename[ name ] ) image = wx.ImageFromStream( stream ) if size is not None: w, h = size if image.GetWidth() != w or image.GetHeight() != h: image.Rescale( w, h ) return image def getIcon( name, size=None ): icon = wx.EmptyIcon() icon.CopyFromBitmap( getBitmap( name, size ) ) return icon images_by_filename = {} images_by_filename["toolbar_images/add.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq' '\xde\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\r\xf5IDATx\x9c\xcd\x9b{l\x14' 'G\x9e\xc7?\xdd=/{\xfc\x18\x8f\xb1M\xc66x1\x0b\t&\x07\x04\x08\xb1\t\x8e\xf7\x82n\x0f\x91\xac' '\xf6\xb4Y`\x13\xb41H(\xcaC9\xee\xa2l\x12r\xbap\xc7\xe5\xce:\x9d\xf3B\xb9 \x85,\xb0\x1b' '\x88\xf2P\xb4\xab(\xda({b/,\xaf\x10\x1e\x0egp\x0c\xac\x01\x9b1\x04\xf0\xa3g\xf0<<\xd3\xdd' '\xf7G\xcf\x8c{\xc63\xf6x<\xe3\xe4\'\xb7\xba\xaa\xbb\xa6\xba\xbe\xdf\xfa\xd6\xaf~\xd5]\x16\x98"+(' '(`\xc7\x8e\x1d\x14\x17\x17\xe3\xf7\xfb\t\x04\x02\xec\xdf\xbf\x9f\xdd\xbbwS\\\x08\xad\xcfB\x91\x056m\x03' 'y\x08>\xfe\xf8c\xccf3\x0f?\xfc0^\xaf7g\xed\x92rVs\x82\r\x0f\x0f\xd3\xd6\xd6\xc6\xf2\xe5' '\xcbc\xf9\xa2\xa2"\x82\xb7:\xd9\xfb_2\x7fs7\xcc)\x83\n\'|r\x08\xfa\xfb\xfby\xfd\xf5\xd7s' '\n\x1e\xa6\x88\x80\xc5\x8b\x17\xe3t:9w\xee\x1c===\xdc}\xf7\xdd\xf4\xf5\xf5\xe1\x97\x8f\xf2\xc6\xaf\x8e' 'SiW\xd0|z\xd9\x93g\xe0\xd3\xa3\xd0\xd5\xd5\xc5\xf0\xf0\xf0T4/\xb7\xb6f\xcd\x1a\x0e\x1f>\xcc\xd3' 'O?MQQ\x11\x00K\x96,\xe1\xd7\xaf\xdfG\xb8\x13B_A\xe8\x10\x04\x0f\xc0\xd5=\xb0l\xf6\xd4\xb6' '\xcf\x94\xcb\xca\xd7\xacYCkk+\x97.]\x02\xc0\xe3\xf1\x00\xf0\xf0\x8f\x8f\xf3\xe8\x8fA\xf3\x82\xa0\x80\n' '\xf8\x07@S\xe1\x9e\xd9P\\\x08K\x97\xc1\xe0Mx\xf7\x0f\xbaO\xc8\x95\xe5\x8c\x00#x\x8f\xc7\xc3\xe1\xc3' '\x87\x01\xf8\xf5\x7f\xc0\xa3\x7f\x07x@\x00T\x05B~P\xc3\xa0i\xb0y\x15\x14L\x07\xab\x03\xe4A\xf8\xc5' "\x8f`\xf5\xafrG\x82\x98\x8bJ\x13\xc1{\xbd^|>}\x90\xff\xf2'\xc4\xc6{\xd0\x03\xb7n\xe8\xe7\x90" '\x1f4\x05\xcan\x87\xc2\xe9p\xfc(|\xf1G\xbd\xec\x7f\xffc.Z\xa9[\xd6\tH\x06\xded2QP' 'P\x00\xc0\x1b\xfb@\xc8\x87Kg\xe1J7\x84\x14\x08\xc8\xba\x12\xca\xe6\x829O\xaf\xa7\xb2\x1aD\tz.' '\xc0\x9c\xe9\xb0#G$\x08\xd9\xac,\x15x\x93\xc9DGG\x07/\xbc\xf0\x02\x00\x0b\xee\x80\xaf;\xa0\xc8\x0e' "'\x7f\x03N\x0b\xd8\x8b\xa1\xe72\x9c8\n\x82\x08\x0b\x16\xc3\xcdo\xf5k\xa2\x04\xf3\x16\xc1\xbf\xef\x83}\xff" '\x93\xcd\x16gH\x80\xddn\xa7\xb1\xb1\x91\x95+W2m\xda4l6\x1b\x92$\xb1l\xd9\xb2\xa4\xe0\xa3\xc7' '\xe9\xd3\xa7iii\xa1\xbf\xbf\x1f\x80\xba\x1a8\xb2\x17\xf2,\xa0\x05\xe1\xd0\x9f\xe0\xe6\r\xfd\x19\xa2\x04\x8b\x96' '\xc0\xc5\xf3\xd0\xd7\x07\xd6|\xb8k\t\xfc\xe2_\xe0`{\xb6\xe0g\x10\x07444\xb0}\xfbv\x9a\x9a\x9a' 'p:\x9dX,\x16\xacV+V\xab\x95\xfe\xfe~\x82\xc1 \x1e\x8fg\x14\xf8\xc2\xc2B\xea\xeb\xeb)--' '%\x1c\x0es\xf1\xe2E\x1e_\x0f\xf7\xdd\x03\x97\xda\xe1\xc6U(-\x83\xeb\xdf\xea\n\x00\x1d\xf8_-\x00\xaf' '\x0c\x01\x1f\xf4\xcb\xb0\xe1\xa7\xf0\xa7Sp}0;\x04L\xc8\x07\xac_\xbf\x9em\xdb\xb6\xa1(\n\xb2,3' '<<\x8c(\x8aH\x92\x84\xc9dB\x92$\x14EI\n~\xee\xdc\xb9H\x92\x84\xdb\xed\x8e\xcd\x08\x1a\xe0\xf5' 'BG;\x9c?\x0f\x9e\x01\xa8\x9e\xa9\xf7\xbe(\x81\x12\x863\x1d0o>X\xac\x10\x1c\x82^7\xbc\xf14' '\x14\xe5O1\x01\r\r\rl\xd8\xb0\x01\x8f\xc7\x83\xaa\xaa\x88\xa28\n\xbc$I\xd8l\xb6\xa4\xe0\x15E\xa1' '\xad\xad\x8d\x9d;w\x12\x08\x04\x00\xf8\xcd\xc7\xa0JP6\x03$\x13t\xf7@\x89CW\x82h\xd2I\xf0\x0f' 'A\xcf5X\xb8P\xcf\x0f\xde\xd4\xcb\xfc\xf6\x85\xec\x10\x90\xd6\x10\xb0\xdb\xedl\xdf\xbe\x9d`0\x08\x90\x12\xbc' '\xc9d\xc2j\xb5b\xb3\xd9\xb0\xd9l\x94\x97\x97SSS\x83\xa2(\x1c;vl\xd4\xc2F\xf6\xc2\xb9\x8b\xf0' '\xd4&\xf8\xf6\x8a>\x13\x0c\x0e\xc2\xedsah\x08\xc2\n\x08\x02\xf8\x86 \xcf\x01\x15%00\x08r\x1f\xcc' '\xa9\x83\xdb\xcb\xe1\x0f_M\x01\x01+W\xae\xa4\xa9\xa9\x89@ \x80(\x8a\xf8|><\x1e\x0f\x0e\x87#\x0e' "\xbc\x91\x04\xa7\xd3IAA\x01\xe1p\x98\x13'N\xa4\\\xd5u^\x04\xef\x10\xac_\x07\xd7\xbaAU\xa1\x7f" '\x10\xe6\xfe\x10\xfa\x06\x00\x01D\x01<\x1e(s\x81E\x84!\xbf>\\V\xdc\x0bW{\xa1\xfdr\x8e\tx' '\xfc\xf1\xc7)++CQ\x14|>\x1f[\xb6la\xd7\xae]|\xfe\xf9\xe7tuu\xe1\xf7\xfb\xa9\xaa\xaa' '"???F\x08\x80\xa2(\xa8\xaa\xca\xc6\x8d\x1bq\xbb\xdd)\xeb?v\x1ajg\xc2\x8a\xe5p\xf3\xaaN' 'B0\x045\xd5:\t\x82\xa8OW\xb7|0\xb3\x06B\x01\xf0\x07tR\x1e\xfd\x19\xb4\x7f\x03\x17\xaefF' '@Z\xd3\xe0\x9e={())\x89\x81\xef\xea\xea\x1aU\xa6\xbe\xbe\x9eW_}\x95\xfc\xfc\xfcQ\xc3b\xdd' '\xbau\x1c=z\x14\x00\x97\xcbu\xdf\x04\xdb\xd8\xb6\xfb\x89^\x19\x00\r\nJaV\x05\x9c\xfb\x0b\xf8\x83P' 'P\x02\x95\xb7\xc1\xea\x7f\x803\xdd\x13\xac\x994\xd7\x026\x9bmL\xf0\x00G\x8e\x1c\x899\xbe\xc4a!\x08' ":\xcf.\x97k\x17\xd0<\xc16\xb6M\x9f\xc6\xa2\xeb\x03z\xc6'\xc3\xb5\x02\x98S\x0bg\xcf\x83\xcf\x03^" "'\xfc\xf6\x9f\xa0i3x|\x13\xab<-\x02.\\\xb8\xc0;\xef\xbc\x93\x12|\xd4\xda\xdb\xdb\xa9\xaf\xaf\x8f" '\x03/\x8aq\x13M3\xc0\xbd?oD\x03444M\x03@\xd3\xf4\xb4~M\xbf\xd7\xf9\xe73xoz' '\x17\xfe\xed\xbf\xba\x16|\xf2b\xef\xd7\xd1\x05Q\xffU(\xbc\x1dfWB\xd75\xb8\xde\x03uKa\xcdr' '\xd8\xf9\xc7\x1c\x10\xf0\xe2\x8b/\xa6U\xd9\x81\x03\x07hll\x8c\xcd\x10\xa2("\x08\x02G\x8e\x1c\x89+\xd7' '\xf0\xf3F4T\xd4\x08hU\x8d\xa6\xf5\xb3\xaa\xa9\xa8\xaa\x86\xbb\xa3\x07\xefM/\x80\xa3r:\x84\xdc\x10\x08' '\xe9u\xf4\x9c\x87\xb9\x8b\xa02\x00>\x01\x049\xb3!\x90\xd5\xc5\x90\xdb\xed\xc6l6\xc7\x81\x7f\xff\xfd\xf7G' '\x95\x8b\x02TU\x15EU\xf5\xbc\xa6\xa2hz^QU\x14M!"\x0e\x00n\x0c\xc3\x8c\n0\x9b#\x81' '\x92\t\xfa\xfb\xc1Q\ne\x12\xfc\xe7n8\xd29\xf16g\x8d\x80\xba\xba:^{\xed5\x04A\x88\x81\x97' 'e\x99\xd6\xd6\xd6Qe\xa3\x80c\xc0\xa3D\xa8*\xaa\xa6\xcf\x1c\xaa\xaa\xa2\xc7\x8a\xbay\xfa h\x87\x19\xa5' '`\x8e\x04I\xa5\xd3\xa0\xff&l\xde\t\xad\xbf\xcf\xac\xddYy!RWW\xc7G\x1f}Daa!\x82' ' \xc4\x9c\xdeK/\xbd\xc4\x95+WF\x95O%\xf9\x11Rtuh\x06\t\x88\x12\\\xeb\x81\xda;\xa0*' '\x0c\x92\x15\x06\xcf\xc2\xcfZ\xe0LO\xe6m\x9f\xb4\x02\x92\x81\x97e\x99\xcd\x9b7\xf3\xc1\x07\x1f$\xfdM*' '\xc9\xc7\xd2\xaa\x82\xa2*\x86\xfe\x87\x8a\xcaH(\xec\x01[!\x9c\xe9\x84\x95\xff<9\xf00I\x05\xcc\x9b7' '\x8f\x0f?\xfc0\x06\xde\xe3\xf1\xf0\xd9g\x9f\xd1\xda\xda\x9a\xb4\xe7\xa3\xa6\xc4) \xa1\xf7U\x15%\xa2\n\xa3' "\x130I\xe0r\x82\xd9\x0f\x9f\x1c\xd4e\xef\xf1O\xa6\xf5\x91z'\xf3cY\x96\xd9\xb8qcL\xf2\x89\xde" '>\x95\xa5\x92|\x94\x80\xa8_\xd0\x0c\x1aX\xfd\xf7\xb0\xfa^\xb8x\x11\xde\xffb2\xad\x8e\xb7I\x11\xe0v' "\xbb\xc7\x0cqSY\xcc\xf3'\xf4\xbc\xf1\x9a\xa2\xc6\xcf\x02\xffwA?\xb2mYq\x82.\x97\xab\x18X\x98" 'ny\xd50\xf5\x19%\x1f\xf5\xfeJ$mP\xc0B\x97\xcb5^\xb5\x83\xbd\xbd\xbd_O\xb4\xed\x93&\xc0' "\xe5r-\x00\xfe\x17p\xa4\xfb\x1b%\xae\xc7G$o<'\xcc\x02\xaf\xa5\xd9\x966\xa0\xa9\xb77\xb2vH" '\xc3\xb2\xa1\x80\xad\x80\xa3lf9\xd6|k\xa4\xcf\xb4\x98|54"\x7fD\x13\x8a\xaa$\x95|\xd4\x17(' '\x91X\xa0\xd0U\x8c\xaai\xa0i\x86z#\xe9\xc89\x9a\x0f\xc9\x01Brp!\xba\x12\xd3\xf6\x12\xd9 \xc0' '\x01p\xdf/\xef\xa7\xf2\x8e*\x83C\x1b\xc3\xcb\x1b{\xdf \xf9D%\xfc\xf0\x81;cd(\xb1r\xf1\xd3' "e\x94\xb0\xeb\x7f\xee\xa1\xefP\xea\x99'\x97\x04\x00\x18\xe4\x9c:\xb0\x19O\xf2J\x02Azt\xa8\xc4\xe2\x85" '\x18\xf8dDhjF\xed\xce\x1a\x01\x9a\xa6E\x1af\xec\xed\xb1\xbd|\xa2\xe4\x15M\x8b\x01\x8b\x12\x12\xeb\xe5' 'Q\xf9(\x19#\xe5\xbfS\x02\xa2r\x1e+\xb0\x19O\xf2qd\x8c#\xf9\xf8\xa8QW\xd9wK\x80\xa6\x19' 'Vv\xb9\x97|\xdcY\xfb\x1e\x0c\x818pS \xf9D\x12\xbe\x17\x04\xa8F`9\x96|t\xc1d,\xf7' '\xdd\x12\xa0\x8e\x80\x9a\n\xc9+J\x94\x18=\xfd=\xf1\x01\xca\x94I>Q\t9Q@UU\x15\xd5\xd5\xd5' '\xa3\xaeGW\x7f@l\xfbK_\xf7\r\xc2\xaa\x82f\x88\x05\xb4\xe8P0\x04G\xaa\xa6RTS\x92\x96\xe4' '\xfd\xd7n\x11\x0e\x84\xe2\x03\xa7\x14\xaaR\xbd\xa1\xec\x13\xb0v\xedZ\x9ey\xe6\x998\xc0\x89\x04\xdcu\xd7]' '\xbb\x81\xa6\x93\x1f~\x99\xf6C\x97\xff\xdb\xaa\xb4$\x7fc\xffe\x02WnM\x04\xcf pi"?\x18w' '\x08\x18\xc1&K\x9f:uj\xcf\xa2E\x8b\x00j\xd2x\xdeV\xd0\xbf\x18\xa5#y\xc3bh7\xe9\x01\xfb' ']oo\xef\x84>\x94\xa5\xe5\x03R\x91\x10\xcd\xb7\xb5\xb5\xedIu\xcf\x98\xbe\xf3\xce;\xb7\x02\x84\x13\xbcw' '*/opk\xbb;::\xbe\x18\xab\xfeU\xabVq\xf0\xe0\xc1t\xe0\xc4\xd9\x98\x04\x18_p\x8eGB' ':\xf7\xa2\x16V\x95\xb4\xbc\xbc\xf1\xa5\xe8x\xcfH\xf6\x9ct,\xe3!0\x99{\xfa\x10\x18\xdf\xcbkh)' '\xebL\x96\xcf\xc4\xd2z+\x9c\xc8p\xa22\xd2\xb9\xd7\xd33\xf2\xfa6\xac\x86\t\xab\x8a~(\xc6#\x1cw' '6*`\xfe\xfc\xf9\xbc\xf9\xe6\x9b)\x9f\x99\xa9\x8dI@6\x80\x03\xec\xd8\xb1\x83\x15+V\xc4\xf2\xa3\x00\xab' '\xd1\xb31\x1d\x8e#\xa0\xbb\xbb\x9b\xe7\x9f\x7f\x9e\xba\xba:N\x9f>\x9d\xf49\x99\xd8\x84\x150\x11\xe0\x1e\x8f' "\x87'\x9f|\x92-[\xb6\xc4\xb6\xc9\x02#@\x13\x00\xc7\x88\x89\xa4\x8dC`\xed\xda\xb5\xd8\xedv\xba\xbb\xbb" 'ihh`\xef\xde\xbdqm\xc8\xd4\xc6% \x13\xe0\xd1o\x04\x0f<\xf0\x00\xef\xbd\xf7\x1e\xa0\x7fb\x8fZ' '*\xc9\xeb\xe7\x91\xb4Q\x01\x8f<\xf2\x08\xbbv\xed\xa2\xbe\xbe\x1e\x80\xc7\x1e{\x8cw\xdf}7\xf7\n\xc8t' '\x18l\xd9\xb2\x85\xf6v}C\xdf\xf4\xe9\xd3\xd9\xbcys\xac\\*\xc9\x8f\xf8\x84\xa8\x0f\x18iGuu5' 'N\xa7\x93m\xdb\xb6\xf1\xdcs\xcf\x01\xd9!a\xdcip"\xe9h\xfe\xad\xb7\xdeb\xdf\xbe}\x80\x0e\xbe\xb9' '\xb9\x19\xbb\xdd\x1e+\xe3\xef\xf6\x8e\x0ee\r+\xc4\xd8\xd264\x12\xdfwz\n\xb8gn\x05\xbd=\x97X' '\xbdz5&\x93\x89\x97_~\x99g\x9f}\x96\x05\x0b\x16L\xfd4\x98jJ\x92e\x99\x96\x96\x16@\x97}' 'ss3\x85\x85\x85T\xcd\x9c\x05z4W3\xf8\xbb\xb1\x83\xb5\x04Y\x0e\x02m\xfb\xbf\x919\xd5=\xc4\xaa' 'y\xd3\xc8\x93\x06x\xf0\xc1\x07\xb9~\xfd:o\xbf\xfd6\x9b6m\xc2\xe1H\xfb\xad|\x9ce%\x124\xa6' '[ZZ\x90e\xfd\xb5\xfc\xbau\xeb\xb0\xdb\xedT\xb8\xaa9\x1d\x98\t\xd0\x84\x1e\x0e\xd7\xa4\xd9\xbeA`\xeb' '\xf3\xfbN\xc8\x7f\xb9|\x9d\x01_\x88}\xc7=4\xce\x10\x99]\xa2\xf0\xc4\x13Op\xea\xd4)\x8e\x1f?\x9e' 'fu\xa3-\xa3H01oLG\xa5_SSCmm-E%e\x9c\r\xd7rK\xb5\xf0\x93\xad\x9f' '\\\x9e\xed*\xdePS[\x895o\xc4)\x9a\x04(4\xabx\xbc\x01\xbe\r\xa8H\x02\x985-\xb6\x1fQ' '\x10E*\xca\x1c|s\xa1\x17\xf7\xb5\x01\x0et\x0b\xdc\x1a\nr\xcfl\x1bO=\xf5\x14\xcd\xcd\xcd\xb9!`' '"\xc0\x01>\xfd\xf4\xd3X\xef\xdf\x7f\xff\xfdX\xadV.Y\xe63\xa4Z1K\x02UN;?\x98]\x85' '\xc5f\xa5"\x0f\xa6\xe7\xeb\x87Y\x04\x10\tV\xe4\xf3\xdeU\xf0\xa8\xfa\xfe=\x8b\xaab\x0b)XT\x15\xb3' '$\xb1\xa0n\x06\xc5E\xf9\x9c=\xe7\xe6d\x9f\x1d\x9b-H}}=\xcb\x96-\xe3\xcb/\xd3_\x8d\x1a-' '\xed\xfd\x01\xe9x\xff\xe8b\xc4\xe1pP[[\xcb\x80m\x16C\x9a\r\x01p\xe4Yp\x96\x16S]b\xe5' '\xaf+ai9T\x17D\xc1\xebf\x15\xa1\xcc2\x92W$\x91@\x9e\x99!\xbb\x95\xa0EBC\xe0\x073' '\xcb\xa9\xbc\xcd\t\xc0W\xbd\x02\x1a"\x0f=\xf4PF\xe0\xc7% \n,\xddX \x1a\xa1\xcd\x9a5\x0b\x93' '\xc9\x84l\xae\xd0\x81\x99DLf\x13\x8bk\x8bXZ\x0e\xf9c\xe8\xce*\x8el^\x14\x0c\x87b\x16\t\xe4' '\x89\xa8\xa2\xc0\x9c\xda\xdb\xb0XL\x844\x11\xf7`\x98\xa5K\x97f\x04\x1e2p\x82n\xb7\x9b\xce\xce\xe4\xbb' '\x91\x06\x06\xf4\xcd|\xa5\xa5\xa5\xa8\xa6|\xc2b\x1eh\x1a\x16\xb3\x84p\xc7LB\x05y\xe3>\xcb\x1d\x8c<' '3yC\x08Y@\nKT\x94\x15\xd3\xe3\xee\xe3\x9aW\xe1G\xf3\xe6\xa4\x03#\xa9e\x1c\t\x8eY\xa9(' '\xa2\x98\xf4y_\xd04\n\x17\xceF\xb2\xe7qn\x9cM\x8cm\x1e\xf0\x86#\xbd.\xc4+\xc0\xf8d\xc9n' "\xa6\xac\xbc\x08A\x10\xf0\x874L\xa6\xcc_mN\xd8\tVWW\xc7\xbd'4\xde\x7f\xe5\x95Wb\xd7\xa4" '\xc8\x06\xc9\xe2Z\x17\xe6\xc2|4\rz\x02p`\x00\x1aKF?\xe7\xa8\x0c\xc7d\x90\x84\xd4\xfbw\x8dd' '\x94\xbb\x1c\xe4uZ\x10"{\x123\xb5\x8c"\xc1\xf1\xf2\xa2(\x92\'\x85\x01\x986\xa7\x12U\x03M\xd0?' '\x91\x1f\x92\xe1\xcc\x10\xcc\xc9\x07\x9b\xa8\xf7\xb8;\x08\xb7\x14]\x8e\xc9\xc6\xbf\x90\x84\x14\xc9$R]\xed\xa4\xd4' '\xec\x9b\x14\x01\x93Z\x0b$\xe6\xa3K^\x9d\x00\x05\x0b\xc3\x88\xe8\xdb\xddEF\x0eo\x18Nz\xe1\x88\x0cg' '\x87\xc0\xab\x8c\x96{\xe20 \xc95\x9bYdF\x99}R\xff_\x9c\xd148^^\x10\x04L&\x13\xa5' '\x92w\x04\xb8\xa0\xcb[\x12F\x13"\x18\xca\x08\x86s*2\xa2G\x91E\xa0\xa6\xbc \xeeeKV\tH' '\x9c\x06\xd3!"j\x92$Qm\x1dd\xd8\xeb\x8b\x03\x1bw\x08:0\xd1\x080\xd59\xe1P\x82!\xca\xc5' "a\xf2\xac\xe6\xd8\xf4\x9b\x89\xfd?&\xd1\xd4\x86\x96@\xb5'\x00\x00\x00\x00IEND\xaeB`\x82" ) images_by_filename["toolbar_images/checkin.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq' '\xde\x00\x00\x00\x06bKGD\x00\xf4\x00|\x00\x08\x17\xc7T\xe4\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00' '\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd6\x02\x0b\x0c#/\xb9\x102\xaf\x00\x00\x0c\x92I' 'DATx\xda\xed\x9akl\\ez\xc7\x7f\xef{\xc6\xf6\xd8\t\xe4b\x1b\x08,\x9b\xd0P\x91\r\r\xa4' '\r*\n\xcb\\\xa4"\x88 \xacT@[\xb5\xbb(\xdd\xcb\x97\xe5\x03\x95\xfa\xa1\xa1R\xa5%\x02>U\xa2' '\xda\xad*E\xa0J\x95X\x8a\xb2\x08\xa5H\xa0%\x8dB\xe7b\x82\r\x89gU\x9241\x82\xd8\xb1\x1d\xdb' 'c;\xcez\xe2\xfb\x9c\xf3\xf4\xc3{\xce\x99s<\x17_\xf0\x84 \xf1\xca\xd69\xf3\x9e\xdb<\xff\xe7\xff<' '\xff\xe7}\xce(\xae\xcfx\x06x\rh\xb8\x0e\xcfR\xc0$\xb0\x0b\x18_\xea\xe4\xc8u\x02`\xc7\x03\x0f<' '\xb0\xe9\xe0\xc1\x83h\xad\x11\x11\xb4\xd6\x00\xfe\xbe\x88\xa0\x94B)Uu\x1f(\xdbz\xfb\xde}\x86\x87\x87y' '\xe1\x85\x17\xd6\xe5\xf3\xf9{\x81\xcc\x8d\x02\x00\xad\xad\xad$\x93\xc9\x8aF\xacd\xbb\xd8\xf0\xc5\xf3\xbd\xbd\xbdD' '\xa3\xd1e\x7f\xaf\xeb\x06\x80\x88P,\x16\xbf\x92qKm\x01l\xdbFDn<\x00\x8aeY\x00\\\xbcx\x91K\x97.\xf9@zc\xdd' '\xbau<\xf4\xd0C\xab\x06\xe0\xa9\xeb Q\x8dAz666\xb2o\xdf\xbe\xaa\x94\xae6\x7f\xff\xfd\xf7\xb3' '{\xf7\xee\x8a9 x\xedJ\x01\xa8\xbbD\xbd\xf5\xd6[\x8c\x8d\x8d\xf9\xf7\xd5Z\x87\x0c\r\x9e\x1f4\xa2\x12' '\x18\xd5B%\x18\x0e\xeep\x96\x9d\x03\xea-Q]]]\x8c\x8d\x8d\x85\x8e\xf7\xf7\xf7\x97]\x13\x8dF\x99\x98' '\x98\xa0\xa5\xa5\x05\xa5\x14\xe3\xe3\xe3U\r\x0e\xceE"\x116l\xd8\x80R\x8a\x81\x81\x01l\xdb\x06\xd8\x02\xdc' '^#\xcf)`2r=$\xcaq\x9c\x90W\xe7\xe7\xe79y\xf2d\x99\xe7\xde\x7f\xff}\x8e\x1e=\xbaV' 'a\xf7\xdb\xe5\xa8s\xe4zJ\x94\x97\xcd\x9b\x9a\x9a8p\xe0@\xe8\x9a\xc6\xc6F\xde~\xfbm\x00\x8e\x1c9' '\xc2\xb5k\xd7j\x82_M9j\x1d\xf7>k\xad\x19\x1d\x1d\xe5\xf9\xe7\x9f/\xd1\xa3\x9e\x12\xe51 \x08P' '\xb1X,\x03\xc0\xfb\x0eO?\xfd4\x13\x13\x13e9a%EQ\xad\xad\xd6\x9a\xbe\xbe\xbe\xf2:\xa0^\x12' '\xe58N\x88]J)FFFB\xec\x88F\xa3\xcc\xce\xce\xfa\xd5\x9cm\xdb\x1c~\xafwM\xa5\xa8}c' "3\x07\x1e\xff\x1eb\x17}\x07\x94\x85@=$\xea\xd3O?\r\x19?==\xcd'\x9f|\x12\xfarMM" 'M\\\xbdz\xb5*\x95W;\xfe\xe6\xd1{\xf8\xe3;7\x86\xe6fgm\xff\xfee!P\x0f\x89Z|n' 'KK\x0b\x8f?\xfex\xd9\xdck\xaf\xbd\xf6\x95\x00h\x8ch\xfe\xe1\xc7{\xb0\xac0\x0b;\xcf\x0cs\xac\xab' '\xcf\xff|\xf0G\xf7\x97\x03PO\x89\xf2\xe29\x98`\x1b\x1b\x1bC!`YVU\xd5Yj\x1c|v\x0f' "\xd1\xc6Hh\xe1\xf5\xab\xdf\xfe\x9e?\\\x9b\xafZ\x92\x97\x01Po\x89z\xe2\x89'B\x05V>\x9f\x0f\x19" '\xdc\xdc\xdc\xcc\xfc\xfc\xfc\xb2\x00xp\xe7\xad\xec\xdb\xbb-4\xf7Ag/]gGV\xb48\x0b\xe5\x80z' 'J\xd4\xbb\xef\xbe\xeb+\x84\xd6\x9a\x85\x85\x05N\x9d:\xc5\xc2\xc2B(\x07xL\xa9\x06\xc0\xdf\xff\xf5\x9fr' 'SKch\xee_\xde\xca19=\xbf\xaaEY\xc5\x10\xa8\x87D}\xfe\xf9\xe7\xe4r9\xffs4\x1ae' '\xff\xfe\xfd\xa1$\x1a\x89Dx\xfd\xf5\xd7CR\x05\xf0t\xf2n\xfed{k\x99\x11\x87\xfe\xbd\xeb+/\xcf' 'C\x0c\xa8\xa7D}t&O\xcb\xa2d\xb881V\xda\xff\xe5\xcf\x1e\xacx\xbf\xe5\x18\xaf\x80?\xdbq\x0b' '{v\xdc\xc2\x96\xd6u\xa1c\xb6]E\x05\xea)QAV\xcc\xcc\xcc0::\x1a:\x16\x8dF\x99\x9e\x9e' '^\xd6\xbd\xaa\x01\xb3\xd2\xe6L\xc5\x10\xa8\x97Dy\xd7Z\x96E__\x1f\x1f\x7f\xfcq\xe8XCC\x03c' 'cc\x15\xe3\xb4\x1e\xdd\xa9\x8a\x1d\xa1zI\x945t\x9c\xae\xae\xcb~\xcc\xef\xda\xb5\x8b]\xbbv\xf9\xe7\xa4' "R)R\xa9\x94\x9f(_|\xf1E\xe6\xe6\xe6x\xec\xb1\xc7\xfcUj\xadq\xb50G\xae'\xcf\x99/\xc7" "\xb929\x17:\xb6q}\x13\x7f\xf7W\xbbk3 \x18\x93\xf5\x90\xa8\xcf\xfb'B\xd7}\xf8\xe1\x87\xa4\xd3" 'i\xffx&\x93\xc1I\xa7\xf9\xa1R\xa05\xf6K/qR\x84\xff>\x91\xe6\xae{\xf6\xb0\xfe\xb6\xefq\xd7' '\x8e=\xab\xf2\xf6\xcd\xeb\x1b\x97N\x82A\x06\xd4C\xa2\xfe\xf7l\x0f\xd3\xa3=\x1c:t\x08\xa5\x14\xe9t\x1a' '\xc9d\x88+\x93\xae\x1e\x06\xe2Z\x93P\xdad/\x84\xb4#dNw\x919\xd5\xc9\xd8\xfe\x9f\xae\n\x80\xe7' '\x9e\xba\x8f\xf6M\xcd\xa1\xb97>8\xcf\xb3\xfbvT\xcf\x01k-Q\x17\xcf\x9f\xa6\xf7\xc2i\xee\x1b\x1fF' '^y\x05\x01b\x9e\xc1~\x9c\x8b\x9b\xb7K9<\xa1 \xa15/\xd96\xbf\xe9\xc9q\xf1\xfc\xe9e\x81p' '\xcb\xa6f~\xf1\xd4}\x8b\x8a\xa4>\xba\xce\x0e\xd7\xee\n\xd7K\xa2z/ts\xdf\xf80\xff\x142\xb8\x92' 'hU\x9e\x8b\xa3\xc8\xf4\xe4\xe8\xbd\xd0]\x13\x80\x9f\xee\xdf\xc9\x9d\xb7\xde\x14*\x87_\xfe\x8fOq\x1cY\xba' '%VO\x89\x92\xfe\xdf\xf1?\xef\x11\xf0\xf4b\x83\x03\xde\x0f\x12\xc1\xdd&,M\xc6\x962\x16\xfc\xfc\x07\xf7' 'rG\xfb\xfa\xd0\xb3\xde\xfb\xe8"\xa7\xcf\xe7W\xdc\x14\xad\xabD%\x93I\xde|\xf3M^\xfe\xf2\x0bP\x9a' 'DD\x19C%\xe0\xe9\xd0\xe7\xc5\xb8\x08qeX\xd0w\xeam\xfe\xf6\x89\x9d!e\xf8\xdd\xc7\xbd|rn' 'd\xd5\x92\x18Y\xfc\x82q\xb1Dy\xe3\xe8\xd1\xa3|\xf6\xd9g+~\xc0\xee={9\xf0\x8b\x7f\xe4\xd7\xff' '|\x88\xcc\x95\x01\x12\xeeRU\x94B9b\xec\x16\xd7V\x87\x12\x18N \x1fhC\x87\x97\xd3i^\xfe\xd5' '\x7f\x92\xfe\xa2y\xcdj\x81\xc8\xe2\xcc^\xa9}\x15\x9c\xf7;\xbdg\x87\xb9xy\x92\x0b\x97&\x96|\xd8\xc5' "\xa1Y\xd6mhC\xae\x0e \r%\xef\x8ax\xc6\xbb-v\x01qAP\x02\xd8% \x12Z\x91q\x14'" 'F\xce\xb0\xf3\xe6K\xec\xdd\xbbwII\xbe\xfd\xf6\xdb\xab\xd7\x0ene[\xb1\x10Z\x8a\xe2Zk>\xe8\xec' '[6\xda\xbd\x17\xba\xf9\xee\x979\x12\xeb54\x04,\x07\x94\x03\x82\xf8^W\x96\x01A\x04\x946 \x88\x03' '\xcaQ\xc4\xb5"\xd3\xd9\xc9\x91#G\x10\x91%_\x82>\xf9\xe4\x93\xb4\xb4\xb4\x94\xbd\xa4\x1d\x19\x19\xf1\xeb\x90' '\x8a\x1d\xa1\xe5\x8e\xe6\xa6\x083s\xc5\xda\xde?\x7f\x9a\xbe\x9e\x1c?\x8e*\xe2\xcd\xdau\xad\x1b\xf7\x881\x94' '\x92\xe7E\x00\xc7\x05\xc9V\x88\x16\x94\xcb\x84\x84\xad\xc9\x88Cft\x94\xcd\x9b7\x93H$j>\xbb\xa1\xa1' '\xc1o\xf9\xcf\xcd\xcd1>>\xce\xe5\xcb\x97\x19\x1a\x1ab``\xa0r\x1d\xe0\x95\xc0\xcb\x89\x9f\xa5\x8c\xf7\xbd' '\xdf\x9b#\xb6\xd12\xde\xf7\xb3\x9d\x80\x18CE\x14X \x8e\x18\x16h\xf7\xb0\x16\xc3\x00\xe52EA\\4' '\x99l\x96\x8e\x8e\x0e\x1ey\xe4\x91%\x9f?55EOO\x0f333LMMq\xed\xda\xb5P\xd3G' '\x07\xa9Q\xa9\xa9\xb1\x9c\xee\xf0\xe2\xf1\xf3\x1f\xdc\xcb/\x7f\xf6 \x89\xed3\xa8\xc2\x17\xc4Z\x14\x89\xf5\xda\xbc' '\x85\x88(\x88\x08D\x14\x12\x11\xf3F\xb2A\xcc~\x04h0\xf3b\x81\xb2\x14D@E\x00\xcbl\xe3\x8d\x9a' '\xb86\xd5d*\x95Z\xf2\xbbNOO355\xc5\xd4\xd4\x14333\xcc\xcf\xcfs\xee\xdc9r\xb9\x1c' '\xc0\xa1\x08P8~\xfc8[\xb7n\xadip>\x9f\xaf\n\xc0S\xc9\xed\xec\xda\xdeVv\xdd\xebo\xfc\x17' 'NW\x9ax\x9b\x854\x94D^\xdc\x10P\xbe\x04\nJL"\xc4\xf6\x18\xa0\x10[\x8c\xe7\x1d\x05\xca\r\x05' ' \xde\xa0\xc9d\x0c\x00\xb5\x16K"\xc2\xe0\xe0 \x85B\x81\xd9\xd9Yl\xdb&\x9f\xcfs\xf8\xf0a\xa6\xa6' '\xa62\xc0+\x11\xe0\x8d\xd9\xd9\xd9b\x7f\x7f\xff\xe6\x1a@\xce\x03?\x01\xee\xa9V\t^-\xccq\xf8\xe8g' '\xcc-\xd8~\xec\x9f\xfc(\xcb\x8f\xd6)\x127kC\xe3\x90\xdc\x8b\xfb\xa7P\x8e2\xb9\xc0Q\x86\x93\x8e\x02' 'G@)\x13\x16\xb6\xa0\x80\xd4\x82Cv\xde\xe4\r\x96 \xaa\x97\xec\xfa\xfa\xfa|\xe3\xbb\xbb\xbby\xf5\xd5W' '\x01F\x80\xe7\x80b\x04\xb8\x02\xfc\xdb2r\xde\xc3\x1e\x00\x00\xc5b\x91\xc1\xc1\xc1\xe0\xdbX\x9e\xfd\x8b-\xfe' '\xc3_={\x84;\x07r\xc4o\xb3\x0c\xb5\xbd\xbc\xe7\xa3\xe0\xee8n\xac{\x0cp\x14\xcaq\xe5\xcf1\x89' '\x10\x84\xd4\x9c\xf0\xca\x8c\x83\xf3\xe7\xdf\x07\xa0\xa3\xa3\x83\xef\xcf\xcf\xfb\xfd\xc9\xe0p\x1c\x87\x91\x91\x11:::' "\x18\x1a\x1abbb\x82\x13'N\x90\xcb\xe5l\xe0$\xf0C`\x18V\xf9\x0b\x11\xcb\xb2\xc8\xe7\xf3ttt" '\x10\x89DX\\L\x9d;w\x8ec\xc7\x8e\xf1\x977)\xe2\x1b\x8d\xf7+\x96\xfc\xe2\x85\x80r\x8b \xb3\x15' '\x07\x94mh\x8f2\xd7e\x8a\x0e\xb9\xa6\xf5lqCq\xcb\x96-\xbc\xf3\xce;\x1c?~\xbc"\x00W\xae' '\\aaa\x81B\xa1\xc0\xe4\xe4$\xc01\xe0_\x81\xe3.\xa3Y5\x00"B{{;\xcf<\xf3L\xc5' '&\xc3\x993gh\xed\xff?\xe2wX\xa5\xdf\xa0x\x1e\x0f\xc4\x81_\x0e\x04\xab?\xd7\xeb\xa2\x0c\x08A\xf0' '\n\x85B\xaaP(\xf8\x99ohhh\xc9\x060p\xc65~\xb2f)\xbc\x9a\xb6\xb2eYe\x85S*' '\x95\xa2\xa3\xa3\x83\xd8\x06El\xb36_A\xe1\xea\xff\xa2Z?\xb8\x06\xb0\xbd\xf3\xdcd\xe1\xca\x1e\x8e\x9bt' '\xcd#\xd2\xc0\xa1\xb5l\x8f\xad\xfaGR\x8b_{\x05\x01\xb0\xbb\xd3$\xb6FJ\xb1\xaf\x8d]\x12r\x7f ' '\x0c\x1c1\xc7\x05\xbf\xe8\xc1q\x01)\xd6X1\x7f]\x00\x04\xe9\x1e,\x9cR\xa9\x14\xe9t\x9a\xf8FE\xac' 'M\xa3\x94\x80V(%\xc6\x9b\xca\x95;\xaf\xf4\x05\xb3 \xd2n8x,p\xdc}w\xa4\xff\xe0\x90\x99\x91' '\x1b\x0b\x80J\xfb\xa9T\n\xfb\xf7i\xe2\x7f\x141\x95\x9dv\xa9[\xa20\xe23@\xb9\xa5\xaf\xd1z\xc4\x8d' "wGB\xc6\x03d\xa6\x1d\xb2\xd3\x92\x02R_'\x00>\xd7+IO6\x9b\xe5\xc4\x89\x13\xc47)bm" '\ne\t\xe2\x19\xae\xbd5o\xa9\x18\x10\x7f\t\xac\\\xef\xbb\xbao\x97\x18oj\x07\xe5\xd1?\xfdu\x030' '\x00\xb0w\xef^\xff\xd5Yp\x0c\x0e\x0er\xf7\xec0\xf1\xbbM\xe6\xf7\xbd\xaf\xdd\x04\xa8}\xca\x98\xca\x0f\xd7' '\xdb:\xd0\xfcp\xa4\xd4\x1cR\nU\x14\xc4\n\\\xfb5\x87\xc0s\xc0\xd9\xce\xce\xce\xcd\x15\xfa7\xdb\x80\xe4' 'O\xb6[\xdb\xe2\xed\x1a,ejym\x8a\x1c\xa5]z\xab\xf2.\x90)z\\\xcd\xf7\xbd\xaf\x90\xa2\x98\xfa' '\xbf\x8e\tp\xa5\x00\xd8\xc0\xaf\xab\x1c{1\xb6Im\x8b\xb7\xa9\x92\xee[\x02\x96\x82 \x13\x82\xfdNw\xd9' '+n\xc6Wv\xb8\xceP\x16\x88(2Wl2\x93rC\x00P\xb5\xed\x07$b\xad\x8ax\xbbe$\xcf' '[\xc9Yf\x15W\n\x05\xd7~O\x04\x1c\xfc\xb2W0jQj\x15\x99J);\xe9\x90\x9d\xacO\x02\\' '3\x00b\x9bT2\xde\xa6\re\x83\xde\xb7\xcc\xbeX*P\x10\x95\x9a\x1fh\x10\xdb\xa4\x05\xb3\xc0P>\x80' '"\n\xb4x\x95`\xfaF\x06\x80x\xab"\xde\xa6}O\x8bv\x8d\x8fP\x02"\x10\x06B\xa0\xe0Q E' '\x0f\x10C}\x9c\xd2\xf9\xaa\x9e\t`\r\x00H\x02\t\xc15\xd4\xab\xfa,\x10\xcfh7\x0c\xbcy?\x05\xb8' '\xfd>Ut\xcf\x11L\xbe\xf0:A\xaejJ}\xed\xff\xca\x02\x93\x04\x92\xd9\t!=f\x1b#\x03T\xf7' '\xbdny\x9d LW\xd8R\x06\x8c`\x8e\xf0@SF5\x94\xbbpRJ\xea\n\x80\xb5\x06\xf7\xe8\xbb4' '\x03\x97fd[\xdf\x94\x90\x19s@\t[7j\xd7@U\x02\xc0R>S\x14\xca\xac\x8f\xfc\xff\xf0\x92X' '\tdF\x1d~3\xe8pi\xe6\xc6\xcd\x01^vNf\xc7$\x9d\x1d\xb3\x01\x12\xd9Q\x95\x8c\x8d\x8a\xef\xd9' '\xd8w5\xb1\xbb,?9*\xdbM\xf4\xca\xf54\xc1\xb7#\xa5\x85Sf\\\xc8^\xa9\x9f\x02\xacY\x12\x0c' '\x00\x01\x90\xcc\x8eH:;\xe2\x0b{"vY\x92\x0f_vLa\xa4 \xf6\x1dmz\x05\x9e\xb5e+\xc4' "\x10\xf5\xd3\xdf\x04\x00\xaa\x81a\x00\x19t\xd2\xd9A\xffs\xa2\xe3N'\x99\xbd\xc3A9\x10\xbbM\x13\xbfU" '\x97*C\x11S\x13\x88\xaaPp~3\x00X\x1a\x90~Ig\xfbM\xb8\xc4\xb68\xc9\xecm\xda\xef\x01\xc4' '\xda4\x89\xcd\xc1\x06\xe27\x1f\x80\xda\xe12$\xe9\xecP \\\xda\x9dd\xb6U\x83#d\xc7\xeb\xcf\x00\xc5' '\x8d5\x92\xee\x7f-\x06};\xbe\x1d\xdf\x8e\xb5\x1b\xff\x0f\xa5%Vc}\xbc\xc9\x16\x00\x00\x00\x00IEN' 'D\xaeB`\x82' ) images_by_filename["toolbar_images/delete.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq' '\xde\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\r\xbfIDATx\x9c\xed\x9b{lT' '\xd7\x9d\xc7?w\xee\x8c\xe7e{\xc6\xc6\x06\x1b3\x1e;\x98G\xd88&\x1b6)\x0b\xc2iQD !' '\xc1B]JSD\xa2 \xc1?\xdd\xb0\xf2n\xda\xd0$\xa5\x14\xa2\xae\x84\xb6I$\x92&a#6\x16\xd9' 'U\x104\x11qK\xd2.\x0b\xc6\x85%q\x8a\xc3\xd8`c\xf0z\xfc\xc0\x0f\x8cg\xeex\xdes\xef\xdc\xfd' 'c<\xe3\xb7\xb1\xc7\xe6\x11)_i\xe6\x9e\xdfy\xcd\xef\xfb=\xe7\xfc\xe6\xdc\xabs\x05f\x00\x0f\x18D^' '\xc91\x92\x9a\xa2A\x10\x05\x04Q`Vv:\xc5\x0f\x16`4\xeaG\xd5o\xe9\xec\xc1\xd9\xdd\x9b\xa8+\x88' '\x02\x1aQ\x83\xa0\x15\x86\xe5\x19t:\xe6\x98\xd2\x99cH\xbf\xa5\x0f\xcfU\xd6\xf1\x1f\x8e\xce)\xfb.N\xb9' '\xc5\x18\xe8\x91U\xaa\xbc\x11\x8aMZ2u\x1a\x04\x8d@0\x14\xa1\xb3\xab\x0f\xbdAGZ\x9aiX}\xb7' '\xd7\x87\xe4\xf3#h\x04\x04\x8d\x80F\x14\x12\xe9\xa1\x1fE\x00\x8f\x12\xc2\xab\x86\xb0h\x8dh\x04a\\\x1f\xdc' '!\x99O\x9bnL\xd9\xf7\x19\x11\x00\xc0\x17\x85\xaa\xfe\x086\xbd\x88\xcd \xa2\xd1\x08\xa8\xa8\xdc\xb8\xe9\xc1\xeb' '\x0b2+3\x1dQ\xa3\x01\xa0\xeb\xa6\x9bk.?\xbf\xb9\xd8\xc7\xa7N\x1f\x7fl\xf5\xd1$E\xa8\xef\x0b\xf1' '\xd7\x9e \x82\x06\xe6\xa6\xe9b3A#\x10F!\x8cB\x86\xd64\xa1\x0f\xef\xd6\xb6O\xd9\xef\xf1%\x9d\x06' '\x9e\x9dm`\xcb\\\xd3\xb0\xe9\xac\xd3kY\xb2\xd0Fv\xa6\x85\xda+-\x9cnw\xb1\xf3\xcb\xde\t\xfb)' '\xb5\xa7\xb2\xb9$\x83\xbf\xb3\x99\x11D\x01\xbb>\x93Lq|\x11\x84\xdf\xfci\xca\xbe\xce\xd8\x0c\x18\n\x87O' '\xe6Z@f\x99%\x05\xfd\xc0(\xaa@\x8f\xcb\x83\x1c\x8d\x12\x08\x85i\xf3\x868\xd1\xe1\x9f\xb0\x1f\xa7\x14\xe6' '\xb3F\x0f\xfda\x85\x15\x05\xa9\x04\x05\x99l1u\xdc\xfa\xbf\xaan\x9e\xb2\xaf\x9a)\xb7\x98$\xceI\x11\xfe' '\xa5Q\xe2\x9a_\x8e\xcd\xb3\x81\xb9\xd6\xde}\x13o 8\xa5\xbe\xfe\xf3\x1b\x17\x1f\xd5\xf6\x11F!\xa0Ff' '\xd4\xcf\xdb&\x00@s@\xe1\x9f/K\x9c\xed\x0b\x0f\xcb\x17\x12_\x93\xc7\xef\xce\xc7\x02\x9cb\x98\x19\xdf\xe2' '\xb8\xad\x02\x00\xf8\x14\x95_6H|\xe8\xf4\x02\x83\xbcs\x8d\xda)\xf5\xe3\rE\xa9i\xf7\xc1\x06\x0b\x18F' "\xab\xe7\x0e&73n\xbb\x00q|\xd8\xea\xe3\xb5:\x17^Y\x05\x01rLS\x13\x00\xa0\xb1'HJ\xae" '\x01\x96\x1aG\x95\x9dju%\xe5\xd7\x1d\x13\x00\xe0/\xbd!v~\xdd\xcb\xd5\xfe\xe4Fk\xfeb\x0b)\x19' 'z(\x18\xbd\xb9\xfaV\x08\x00p\xcd+\xb3\xf3\xcb^\xaa\xbb\x03Sn[\xb26/\x96\x18c\t|\xd2\xd4' '\x93\x94?w\\\x00\x00\xaf\xac\xf2\x8b\xafoN\xa9\x8d\xddnf\xf9\x13\xf3\xc6,;\xe4\xb8\x8eS\x9a\xda?' 'K\x1cwE\x80d\xb0\xfb\x95\x92A\xa3a8\xd9\xdd\xd5\xd7\x92\xee\xf7[!@Iq\x06[\x7f2\x7f0' '\xa3!\x94H\xbe\xf1\x953\xe9\xd1\x87o\x81\x00\x96t\x1d\xff\xfe\xf6\xf7\x063.\xf8\xc1\xad\x00P\xdb\xdd\xcf' "\xee$v\x7fCq\xcf\x0b\xb0\xff\xd7\x0fQ\xf2@\x06\xa8@ \n'<@\xec\x7f\x7f\xc3\xb1Z\xa4\x90<" '\xad\xfe\xefi\x01\xde\xdd\xbf\x8c-\xffP8\x98\xf1\x89\x1b\x82*\xee`\x84\xc7>\xaa\x99\xd6\xd4\x8fc\xea\xbb' '\x91;\x00K\x9a\x96\x7f\xddU\xc2O6\x16\x0cf~\xea\x86\x86\x10\xb5\xdd\xfdl8V;#\xe4\xe1\x1e\x14' '\xc0\x96k\xe4\xa3\xdf>\xc2\xd2\x92\xccD\x9e\xa6\xd2\x83P\x17\xe2\x90\xe3:;\xff\xdc8\xedi?\x14\xf7\x94' "\x00kW\xce\xe6\xc0/\x97\x92\x99\x15\xbb\xe3\x11\x00\xed\x17^Z\xffW\xe2\xb9\xcazN'\xb9\xdb\x9b\x08\xf7" '\x84\x00\xe9f-\xbf\xde\xb1\x88\x1f\xaf\xb7\xa11\x881\xe6\x11\x95\xce\xc37\xd8\xf3\xe9\x95\xa4\x9e\xf5M\x16w' "]\x80\x1f>\x96\xc3\x9e\xed\x8b\xc8\x98\xa5\x07A@\x10\x04\xfe\xf0E''\xfe\xab\x83\x0f\xffz\xfd\xb6\xff\xfe" 'my$6\x19<\xba(\x9d\xdd[\x8b\xc8\xcf3q\xb9\xc3\xcf\xe56\x1fg\x1d.\xfeR\xdb\x87\xc7;s' 'k\xfc;|\x87\xef\xf0\x1d&B\xd2A0++\xeb\x19\xe09\xc0:c\xde$\x87S\xbd\xbd\xbd\xbfJ\xb6' 'qR\x02dee\x95\x00\xb5\xc9\xfe\xe8m\xc0\xeedEHv\x1f`\x05\xf8QI\x01?*\xb1CT\x05' 'U\x8d]\xa3\xd1\xc1\xab\x12\xb7\x07\xf2\x94\x81\xb42\x96=p\x8d\xb7Q\x86|F\xdaJ\x14\x14\x85\xba\x90\xcc' '+\xfda\x80\xc7\x80;*\x00\x006\x8b\x89\x15\xb6\xac!\x84\xc7sX\x01yHZ\x89\x0e\xd8\xca\x04\xd7x' '\xbd\x91i@VA\xd0@d\xfa\xdb\x98\xe9\xdd\x0e\x0f\x1d\xf5;B>\xfe\x89\x0e\xe6\xdf}\x01\xee\x12yY' '\x89\xd5\xbf\xbb\x02p\xf7\xc8\xdf\x133`d \xbb\x93\xe4\xef\t\x01F.\x81;I\xfe\x9e\x11\xe0n\x91\x9f' '!\x01\xa6\xf7< \xaa\xde=\xf2\xb2\x02Q\xa6}\xc4cz\x02\xa8w\x91\xbc\xac\x80:\xfd}\xc0\xa4\x05\x98' '7o\x1e6\x9b\r\x80\xa6\xa6\xa6XfT\xbd{\xe4e\x85\xa1+x\xe5\xca\x95\x89\xb4\xc3\xe1@\x92\xa4I' '\xf1\x9a\xb4\x84\xe5\xe5\xe5\x94\x97\x97#\x08\x02\xc5\xc5\xc5\xa5\xc0\xa9\xc9\xb6\xbd\x038\xd4\xdc\xdc\xfc|\xdcX\xb7' 'n\x1d\xd5\xd5\xd5\x93j8\xa5 (\x0c\x9c\xd3s8\x1c\xa7\x81CSi{\x1b\xd1\x02\xbc\x117\x04A\x98' '\xf4\xe8C\x121 .B]]\xdd\xf3\x82 H9\xe9\x18\x9a\x9f\x9f\x8f\xc7\xe3\xe1\xc4\x89' "\x13\xd4\xd7\xd7\x13\n\x85\x10\x04\x01\x8b\xc5\x820\xf047\xee\xc8HG\xc7*\x8b\xa7'*\xbbU\x1fCm" 'I\x92(++\xe3\x83\x0f>\x00\xe0\xe0\xf7\n([\x92\xcbR[\x06\x05\x16#\x8b\xcd)\xa4\x04#\x9cw' "\x0f?\x981\xed0j\xb3\xd9X\xbbv-\xdb\xb7o'???\xe1\xd4\xb0\x1f\x99\xe1\x991\x96\xbdc\xc7" '\x0e\x0e\x1f>\x8c\xddn\xa7l^\x16\x1b\x8d^\n\xe7Y\xc9\xb0\x1a\t\x06e\xda\xbb<44\xf7\xf2O\xf5' ']\\\x1f\xb2\x7f\x98\xb2\x00yy\xb1c*\x1d\x1d\x1d\xa3\xca\xb6o\xdf\xceK/\xbd\x84\xc5b\x991r\xb7' '"\x1e\xb7SSS\xb1X,\xb4\xb4\xb4p\xe4\x07+\x98\xa3\xf5\x92\x9fk!-5\x85pX\xa1\xab\xd7\xc7' 'U\xe7M\xf69]8\xa3\xea`\xfb[\x11.((`\xd9\xb2e,X\xb0\x80\xec\xeclt:\x1d:\x9d' '\x0e\xadV\x8b$I455QUU\xc5\xa5K\x97\x80\xd8\x8c\xa8\xa8\xa8\xa0\xb8\xb8\xf8\x8e\x10\x8f#55' "\x95\xd2\xd2RN\x9d:\xc5\xcf\xb5\x1a\xe6\xcdI'wv\x1af\x93\x8eH$J\xaf\xcb\x87\xf3\xba\xc4\xdb\xfe" '\x08C#\xc1\xb8A\xd0j\xb5RVVFQQQB\xdd\xb0\x98\x86*\xea\xd0\xe9RH3\xa5`\xb7+' ',\\\xb8\x905k\xd6\xd0\xd0\xd0\xc0\xfb\xef\xbfOss3O?\xfd4\xc7\x8f\x1fO\x88p;\x89;\x1c' '\x0e6m\xda\x94\x10A\x96e\xb4\xa9\xe9\xb4uy\xe8\xf3\x040\xa4h\x91\x95(>\x7f\x84N\x9d\x9e\x1b\x0c' '?\xa16f\x10\\\xbcx1[\xb6l\xa1\xa0\xa0\x00M\xf6b\xdc\xa6\xf9\\\x0e\xcd\xe5z\xc4Jg8\x8d' '\xf6\x80\x89f\x8f\x9eK.#\xaa!\x93\xd4t\x0b\xc5EsY\xb5j\x15n\xb7\x9b\xfa\xfazjjj\xd8' '\xb8q#\x06\x83aZ\xc1\xedV\xf6\xd6\xad[q8\x1c\x94\x94\x94\xb0c\xc7\x0e222\xf0vu\xd1u' '\xa1\x96pX\xc1\x1f\x88\x10\x0c\xc9(Q\x95\x92\x9f\xfd\x8c\xcf\xaa\xaa\x86q\x1d5\x03\n\n\n\xd8\xbcy3' ':K\x0e\x9di\x0f\xe2\x89\xe8\x88\x8b&j\x04R\x8dz\x10\xc0\x17\x08#+Q\x9anDh\xba\x01\x19&' '3\xa56-\xaf\xbe\xfa*\x81@\x80\xd3\xa7OS^^\xce\xb3\xcf>;J\xe08\x11\x9dN\xc7\xaaU\xab' 'F\x95\xb5\xb5\xb5\xd1\xd8\xd88\xd6\xd8\x0c\x13\xc1\xef\xf7s\xe6\xcc\x19\xecv;g\xcf\x9e\xe5\xc2\x85\x0b\xb4\xb4' '\xb4p\xdf\xb6m\xf8\x14\x05\xe7\xb1c\xc8>\x1f)\xd9\xd9\xdc\xf7\xc2\x0bt\x1bG\x1f\xb0\x1c&\x80\xc1``' '\xf3\xe6\xcd\x04\xcd\xf9t\x18\x8bA\x06\xadV\xc4\x9e7\x8b\x9c\xac4t\xa2\x06%"\x0f\xbc\xe4 \x12\x96\x15' '\xbaz\xfbi\xed\xb8\x89\xcb/\xf3I\xa3\xc8*\xbb\x86={\xf6\xf0\xd4SOq\xf4\xe8QV\xaf^Mn' 'n\xee\x98d\xc6#6\xd1\x92\x18\x8a\xe6\xe6f 6h\x8a\xa2\xe0\xf3\xf9\xc8\xcc\xcc\xa4\xbb\xbb\x1b\xcb\xda\xb5' ',y\xfcqdYFQ\x14\x1a\x1a\x1by{\xdf\xbe\x89\x05X\xbe|9)\x96\\:\xd3bk7+\xc3' '\xcc\xdf\x96\xccG\xaf\xd71K\x0f\xb3\x0c \xcb257#\x84\xd1\xa0\x8fF\x99o2Ph\xcb\xc6\xd1\xd8' 'NO\xaf\x87*\xa7\xc0\xa6\xe2t^~\xf9ev\xed\xdaEuu5\xef\xbc\xf3\xce\xb8\x84\xc6\xb2m6[' '\xe2\xbec\xa2\xfa\xa1P\xec\xb4\x98\xaa\xaa\xa8\xaa\x8a\xc1` \x12\x89\x90\x92\x92B^^\x1e\xed\xed\xedX\xad' 'VZ[[\xd9\xbf\x7f\xff\x98"\x0e\xdb\n\xaf\\\xb9\x12\xc9\\\x04\x80Y\xafe\xd9\x03v\x8ag\xebXc' '\x83\xe59\xb0\xd0\nK\xb2\xb4\x14\xcc6\x120\xe9\xf1\xa6\x19\xe9O3\xa0\xa4\xa6\xf0pI!\xf3rc\xa7' ':\xbel\x8f\xf2\xc4\x13O\x00PYY\x99p|\xb2k|\xe4&i\xbc\xfa\xfb\x06F\xf4\xc5\x1f/\xc3\xe5' "ra\xb5Z\x91$\t\x8b\xc5\x82\xdf\xef'77\x97\x8c\x8c\x0c\xfc\xfe\xf1\xdfKH\x08\x90\x9b\x9b\x8b)-" '\x03\x7f\xcal4\x02X\xb2-<\xb5\xc8\xc8B+\xe8F\xdc1\xe8\x87\xda\x1a\x01Y+\x124\x8a\x14\x97\x14' '\x90j6\xe0tG1\xa6ZX\xbe|9\x92$\xe1p8&El\xbc\xb2\xb1\xf2***\xb8x\xf1"' '\xa5\x8f.b\xfd\xf7\xef\'\xda]\x8d\xdb\xedF\x14E"\x91\x08\xa1P\x08\xbd^O__\x1fmmm\xe3' '\n\x90X\x02&\x93\tE\x1f\xdb\xc0\x98g[\x11\xee/@QA7F\xa3\x06\xdf\xb0w \xe2\xae!\xeb' '\xa0\xf0\xbe98\x1cN\xc2Q\r\x0b\x16,\xe0\xdc\xb9sx<\x9ei\xfd\xd5\x8de\xc7G\xff\xb5\x7f|\x06' "9\xd8\xcf\x9c\xe85$\x7f\x0fu\xbd\xd9\x89u\xdf\xd5\xd5\xc5\xc9\x93'9~\xfc\xf8\xad\x05\xc8\x9d;\x17Q" '\x8c\x99\x99K\xec\xa8\xc0\x9f\xfa`}\xf6`\xe5`\x14\xce\xb8\xa0_\x06\xad0(\xc2P\xff\xe6\xe6gr\xf9' "r;\xa2V;*\xf8\xcd\x04q\x80\x8a\x8a\n\x9cN'\xa5\x8f.b\xe5\xc3E\xc8\xfd\xd7Q\xa3Q\xe6\x9b" 'Z\xc8\x9bs\x95^\xaf\xc0\xd9\xcb=\x1c\xfbc-_T7\x8dK~\x98\x00\x1eIB\xaf\x05cF*Z' '\x9d\x96(P\xef\x83\xf6\x10\xe4\xa4@8\n7# \xab#\xc8\x0f\x11A\x00DQCv\xa6\t\xadV\x97' 'xp2\xd64NV\x08I\x92\xd8\xbbw/\x00\xff\xf6\x8b\xcd(\xa1~^\x7f\xf7$\xfb\x0eVQ\xe9\xda\x08Z!\x8a8@FU' '\xc1\xab\xc0\xb5@,X\x88Bl\xe7$\x00\x1a!\x967\x96\x08s\xd2S\xc8\x9be&\x18\x8c\x9d\xe5\xab\xa9' '\xa9\xa1\xb0\xb00\x11\xd9\x01\xda\xda\xdahhh\xb8\xa5\x83#\x858z\xf4(N\xa7\x93\xad\x1bW\xf0\xc0\x82' '\x1cn^o\xa6\xba!\xc4\x8b/\xbe\xc8\x9bo\xbe\tQ\x85:\xa7\x8b\xff>\xdfN0\xa4L^\x80+W' '\xae\xa0\xd3\xe9\xc8\xf4\xb6\xa2\xe1\xfea\xef\xf5\x0c%<\x8c\xb8\x10\xb3\x87\x8a\xa0*\n\x0f\xe5\x19\xf1\xf9|t' "ww'\x1c\x1fId*\xa4\xe3p\xbb\xdd\x1c9r\x04\x80\xd7~\xfa\x0crP\xe2\xc0\xd1\xcb<\xf7\xc2\x0e" '\x96.]J\xcf\xa5J\xaevz\xb8\xd6\xe6\xe2\xf4\xd7]\x93\xea?!\x80\xdf\xefG\x14E\n\r^\xbaz' ":\x11\xe7\x0c\xae_\x81\xe1\xe4\x13\xd7\x91\xe2\x00ro\x1f\x7fS\x94\xce\x17'\xfe@OO\xec%\x86\xd5\xab" 'Wc\xb3\xd9\x86\x91\xc9\xcf\xcf\xc7n\xb7O(\xccH{\xef\xde\xbd\x04\x02\x01\xb6n\\A\xde\xec4\xae5' '8\xc8Z\xf0\x03jkk9t\xe8\x10\xfb\xb7>H\x9d\xd3\xc5gg\x9c\x04\xc3\xb7\x1e}\x18\xf27\xd8\xde' '\xdeN__\x1f&\xbd\x96\xfc\xaeZ\xb4^7"\xb1)/\n\x83$\xc5qD\x10\x04\xf0u\xf4\xf0\xf7\xf3' "\x0c\xdc\xbc\xd1MMM\rmmmX,\x16JJJ\x92\xda\xe7\x0f\xb5\x9dN'o\xbd\xf5\x16\x964\x13" "\xaf\xfe\xf4id_/\xaf\xbf\x7f\x1a\xab\xd5\xca\x86\r\x1bX\xf7H\x16\x91\xa8\x8a\xf3\xba\x9b\xaf\xea'~!" "sL\x01\x00\xce\x9c9\x83\xd9l\xc6b\xd41\xbf\xab\x16\xf3\rgb\xdd'\x84`0\x16h\x06\x84\x89\x06" '\x82D\xdb\xae\xb3\xa6\xc8\x8c\xa8\x04\xa8\xac\xac\xe4\xfc\xf9\xf3\x00\xac_\xbf~Z\xc4\xe3\xf6\xde\xbd{\x91$\x89' '\x9d\xcf?N^\x96\x99\xa8\xbf\x17g\xa7\x9bS\xa7N\xf1\xdb\xfd\xafS\xf6\xb0\x86\x8667\x95g\x9c\x93&' '\x0f#\xee\x06[ZZX\xbdz5v\xbb\x1d\x83NKF\xd8\x8d\xde\xd5M\xd8\xebGQ\x05D\xad\x0eQ' '\xd4\xa0\x01T\xbf\x1f\xc5\xd3\x8f\xde\xdd\xc7b\x83\xcc\x8a\x05\x19\xf8\xbd\x1e>\xff\xfcsZ[[\xf9\xf8\xe3\x8f' '\x91e\x99\xf7\xde{\x8f\x9c\x9c\x9ci\xcd\x00\xa7\xd3\xc9\xb6m\xdb\xb0\xa4\x998\xfc\xc6v\x84@\x17\xd1\xb0\x97' '\xe2B+\x9f\x9f<\xcb\xa6\x15\xe9ht"\xe7\xea:9\xf2\xe7\x96\xe4\x05\x88D"\\\xbcx\x91\'\x9f|' '\x12\xbb\xdd\x8e\xd9lF\x8fL\x9eA!_\xf0\x93\xe9\xef\xc3\xe4\xbeAf\xc0M\x91Qfi\xb6\x8e%9' 'f\xac&\x91\x9e\x9e\x1e\x1a\x1a\x1aP\x14\x05\xbd^OGG\x07\x1d\x1d\x1d\x1c\x0e\xfe\xfe2.\xcf\xf0\x974o\x85\xff\x07\x9a\xf8\xa1(\x8f%\x1f\xd6\x00\x00\x00\x00' 'IEND\xaeB`\x82' ) images_by_filename["toolbar_images/diff.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq' '\xde\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00' '\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd6\x02\x04\x0b\x07$\x82\x06\x0cR\x00\x00\x00\x1dt' 'EXtComment\x00Created with The GIMP' '\xefd%n\x00\x00\x00\xaeIDATx\xda\xed\xda\xc1\n\x80 \x14D\xd1\xec\xc3]\xf4\xe3\xb6i\x15\x19' '\x1a\x89\xc5;wY\xa61\xc4\x1d\xa9\x96\x05@d\xd2\xc5\xb1\xd2xMy0\xf7\x1dS\xd6]\xa3?\x01\x02' '`\x01h\x81\xb3\x8aKe`\xba;_\x1b\xdfQ\x01]\xf3\xbeu\x1f$(\x00@\x0b\x0c\xda\x93\xf7\xee\xd5' '\xa7\xacK\x82\x02\x00\xb4\xc0 \x1b{#D\x82\x02\x00~\xd8\x02\xa5q\xaf\x9dR\xdf\xf8\xda\xf5\x1c \x00\x01' '\x00Z\x80\x03\x04 \x00\xe0\x1b-\xe0\xbb\x00\t\n\x00\x08\xdd\x02s\xd8*v\xcf\xc7=n\x8d\xf6\xcf\xbe\x0b' '\x90\xa0\x00\x00-\xc0\x01\x02\x10\x00\xf0\xb0\x05\xfc#D\x82\x02\x00\xa2\xb0\x03\xd3\xa1,Ga/<\n\x00\x00' '\x00\x00IEND\xaeB`\x82' ) images_by_filename["toolbar_images/edit.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq' '\xde\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x0beIDATx\x9c\xed\x9bilT' '\xd7\x15\xc7\x7f\xde\xb0\xf16v0n\x12;5\xa1\xb8I\xc3b\x0bhiHR[QZU\n\xc4\x94(' '\x89\xa2,&A\xaaT\x95-\x8a\x88\x8a\xb2\x00"\x91\xdb|\xa0\xb4\xa9\xd2J%\xb1\xd9\x12\tTj\xd1\xd0' '\x10\x89\x08\xaf\x10H(v\x0c\x06\x8c\x81\x99\xb1=\xe3e\xc6\xb3\xd8\xb3\xbd\xb5\x1f\xc6ojl\xcf\xf0f\x06' '\x1b\xb7\xe5/\x8d4g\xee=\xf7\xdd\xff\xb9\xe7\x9c{\xef\xbbw\xe0\x0e\xfe\xbf\x91p\xbb;0\x05(\x02\xe6' '\x8c|7\x02\xa6\xdb\xd6\x93)F\tp\x02P\xc7|N\x8c\x94\xfdO\xa3\x04p\x00\xea\xbd\xe93\xd45\x0f' '\xe4\xab/\x17\xcfV\xef\x99\x99\xa2\x19\xc11RGw\x08\x8cv\xa3\xe9\x8e\x1c`\x1bP\xba\xa20\x87?\x96' "\xcd#';\rQR\xe8\xb3yx\xff\x1b3\xfb\xbb\x1c\x10\x0c\x87\xfb\x93u4X\x06\xd4MVo'\x0b" '\xf33Sys\xc1=\xccLK!3}\x06\x01Q&-5\x89\x17\ns830D\x87_\x9a\x03\x94' '\xe91\xc0&\x80\xf9\xf3\xe7\x93\x9d\x9d=\xae0!!\xb6<:\x19zMMM\x00TWW\x93\xf8\xe9n' "|\xfe~<>\x81\xe4\xe4D$I\xc1\xeb\x97\xf0\x07$\xe6&'\xd1\x81\x04P\xae\xc7\x009\x00\xdb\xb7o" "g\xf9\xf2\xe5\xe3:0Z\x0e\xf7}*\xca\x9a\x9a\x9ax\xf2\xc9'\xa9\xa8\xa8\xe0\xa9\xf2r\xfe\xb2\xe9W\xa4" '~\xf7.\x92\x93\x12\x18\x1a\x16\x90d\x05\xbb\xd3\xc7\xc0\xa0\x17\xc5/\x86\xf4\xf4\x18 \xf40=d\xa7\x9a\xb8' "\xf6\xddl6\x03PZZ\x8a\xc3h\xc4\xe9\xf6s\xbd\xdb\x81\xd7'\x92>3\x05YQp\xba\xfd\xf4\x0e\x0c" "\xd3#)\x9a\xbaQ\xb7\x01n'\xb9\x9b\x95UUUQUU\x05@nn.\xf7.[\xc6\x8c\xacl\xec" '\x0e7\xee\xa1\x00)\xc9I(\xaa\x8a \xc8\xa4\xe6\xe7s\xc1\xdf\xa7\xa9\xd6&\x86#;\x16c\x1f\xae\xc9\x13' 'y\xc6T\x96\x99\xcdf\xaa\xaa\xaa0\x18\x0cl\xd9\xb2\x85\xc7\x1e{\x0c\x8b\xc5\xc2C/\xbd\x04\x80()x' '\xfd"\xfe\x80\x84\xa2\xaa,\xdc\xb8\x91y\xf3\xe6i\xea\xae\x98=`\xb2G5R\xbd\xd1\xf2g\x9f}\x06\xc0' '\x9a5kX\xb7n\x1d\xed\xed\xed\xf8|>\n\x9ey\x86@Z\x1a\xdd\xc7\x8e\xe1\xbep\x81\xac\x87\x1e\xe2\x9e' '\x15+\xf0\x15\x14`\xb5ZC\xedD\x95\x03\xa6\x82\x9c^\xe2\x1a\xdcn7\x00\xe5\xe5\xe5\xc8\xb2\x8c\xaa\xaa\xa4' '\xa6\xa6b\xb3\xd9H)-\xe5\xbe\x05\x0b\x90e\x19Y\x961wu\xb1\xe7\xad\xb7\xf0x<\x00\xbb \n\x03' '\xe8%4\x19\x9e\x11I6\x99\x82K\xfb\xc1\x81^$I";;\x1b\x8f\xc7Cvv6yyytu' 'u1k\xd6,\xacV+;w\xee\xc4\xeb\xf5\x02\xd4\x00\xaf\x01\xe8\xce\x01\xa3\x1f\x8e6' '\x9a\xf1\x0b2\x04W|\x11G\x1f\xe2\x08\x01\xf8\xcf|=\xb6\xc3z\xe4\x92\x92\x12\xdd\xf5w\xec\xd8\x81\xcf\xe7' '\xa3\xf2\xe9G(\xc8\xcf\xc4\xdf{\x1eU\x16P\x15\x99\xc2\xbcT\n\xee\x9a\xc59\xe3\x10&\xab\x8b\xaf\xdbm' 'z)\x011\x84@\xa4\x8eN\x86\xecr\xb9\xf8\xe0\x83\x0f\x00x{}\x05\x82\xb3\x07%0\x8c\xaa*\xa0*' '\xa8\x8a\x84\xd7/q\xb5w\x98\xa3\x8d7\x1d\xf0q\x88j!4\xd9Y~"\xf9\xf5\xd7_\xc7\xe5r\xb1u' 'C\x05\x05\xb33\x10]f\x14Y@\x95\x05\x14Y@\x91E.Y\xbd\\\xebr\xf0M\xbb\x9d\xa2\xa2\xa2(' '\xe8\xc70\rN\x16\xd1\x89d\x93\xc9\xc4\xbe}\xfb0d\xa5\xb3\xbe\xf2\t\x02\x83F\x14\xd1\x87*\x8b(\xb2' '\x88*\t\xb8=\x02W\xfb\xbc\xfc\xed\xcb\xeb\x00l\xdd\xba5*.\xd3\xda\x03v\xec\xd8\x01\x04\xb7\xbb\x19\xa9' '\t\x88\x83\xd7F\xc8k\x1e r\xde"p\xa1\xd3\xc6\xd5\xee!\x8a\x8a\x8aX\xb3f\x8d^J\xc04\xce\x01' 'F\xa3\x91\xbd{\xf7b\xc8Jg]\xe5\x13x\xadm\xc1\xd1WG\x8e\xf7T\x05\xbbG\xc5\xe2\xf0\xf3ys' '\x17\x00\xdb\xb6m\x8b\x9aK\\\xaf\xc5\xf5\xcc\xe5\xa5\xa5\xa5\xdc}\xf7\xdd\x00tuu\xd1\xde\xde\x1e\xb1~q' 'q1\xc5\xc5\xc5\xac]\xbb\x16\x80\xdf\xbf\xfd<\xe9\xc9"\xee\xc1k\xc1\nj\x90\xbc\xaa\xaa\\\xeeO\xe0\xec' '\x85>\xaev\x0fQRR\x12\xf5\xe8C\x9c\x0b!=u\xb3\xb2\xb2\xa2\xd2IMM\xa5\xa1\xa1\x81\x86\x86\x86' '\xd0v\xd7y\xb5\tU\x1e9\xcdQUTU\xc1\xe1K\xc0\xeaT8|\xc2\x08\xc0\xae]\xbb\xa2\xa1\x11B' '\\K\xe1\xd2\xd2\xd2\x1b\xe4\xb1\xf5\xc7\xcaEEE\xa1,\x1d\xa9\xfe\xab\xaf\xbe\n\x04\xb7\xbb~w?\x82\xed' '\xcaH\x89\x1a\x0c\x01U\xa5\xb5w&M\xe7\xbaq\xb8\x05\xca\xca\xca(//\xd7K\xe5\x06\xdc\xd2\xcd\xd0\xad' '\x90\xeb\xeb\xeb\xa9\xaf\xaf\xa7\xa8 \x8f\x17\x7f\xf10\xc3\xa6\xafBY_\x91ETY\xc42\x94\x88\xc3#p' '\xecdp\xbb\x1bK\xeck\x98\x94\xedp<\xb26\xfa\xd5\xef\xaf\xc5g7#:\xbb \x81\xe0\xb5\x06\x82\xa3' '\xdf1\x98\xc1\xf1\xaf\xae\xe3p\x0bTTT\xc4<\xfa0\xcd<`\xcf\x9e=\x98L&\xca\x96=\xc0\xf2%' 's\xf1\x98\xbfBUDTYB\x1d\x19}\xf3P\x1a\x96A?\xc7N\xf5\x00\xb1\xc7\xbe\x86i\xe5\x01\xdb\xb7' "o\x07\xe0\xcd_\xaf\xc0\xddoDp\x98G\xd5P\x91\x94D\xae8\xd3\xf8\xf2t'\xfe\x80Lee%s" '\xe6\xcc\xd1KaBL\x1b\x0fhii\xc1d2\xb1\xe8\xc1B\x96.( \xd1o#u\xf6\xf7I\xfc\xee' "O\x103\x8bPe\t\xb3'\x83~\xa7O\xdb\xee\xc6\x15\xfb\x1a\xe2z+|\xee\xdc9z{{#hL" '\xac[VVFVV\x16g\xcf\x9e\r\x1dTj\xa76\xe9i\xc9(\x8aBF\xe1bDQ\xc4\xe5v!' '\r\x0f\xa1\xca\nV1\x87\xa3\rm\xf8\x05\x99\xd5\xabW\xd3\xd6\xd6\x86\xddng\xc9\x92%\xd1\xd0\xb8\x01\x93' 'z.\x10M\xdd\xabW\xaf\x02\x90\x91\x9e\x82\xc7\xebA\x92$dE\xc6\xe7\xf7\xe1I\xc8DL*\xc0+%' '\xd3p\xae\x0f\x83\xc1\xc0\xb3\xcf>\xab\xbb\xedH\x88\xcb\x03\x16/^\xbf\x0fo@D\xcd\xfe\x1e_\x1c9\x03\xc0\xa6M\x9bx\xee' '\xb9\xe7\xa2\xe9zX\xc4\xbd\x12\x8cW\xd6\xd0\xd2\x12<\xb2KMI\xc0\xe5v\x92\x98\x98\x88\xaa\xaa!\xc3\x9c' '\xbb\xd8\xcb\xb1\xc6N\x80\x98\x96\xbc\xe1p\xdbf\x81\xb10\x1a\x8d\x008\x87%\x14EAQB\xf7x\xf8\xbc' '\xa1\x93\xdf\xfe5x\xbcU]]\x1d1\xf3\x8f\xbe\xfc\xa0\x07S>\x0b\x84\x83f\x80\x93m.\xec\xae@\xe8' '\xf7\xea\xc3-7\x90\x8f4\xfa\x83\x83\x83\x1c?~\\/\x15`\n=\xe0f\xd0\x0cPPP\xc0\xef\xf6]' "\xe7\xbe\xd9\xc9\x0c\xf4\xf5P\xff\xb5\x11\x83\xc1@mmm\xc4\x15_OO\x0f\xcd\xcd\xcd\x08\x82\x10\xd5s'" '=\x07\xe8\x85\xc9d"))\t\x9f\xcfG \x10\xe0\xf3\x13\xd7\xf0x<\x18\x0c\x06\xea\xea\xean\xd8x\x8d' 'F__\x1f\x9d\x9d\x9dX,\x16\x02\x81\x00\x97.]\xd2\x8a\x9cz\x9e\x1b\xf3,0<tvv\xe6\tc$\xa0\x95r\xef\xdd\xb3\xbe' '\xben\xa9L!05\x80\x99@\xc5R\\\xef\x1dv\xc3\xd4\x00\x82 \xa8/\xbd@\xc52\x86~l7,' '=@/h\xa9\x14w\xca\x08L\x1e@\x13\xe63+\xae\x80\xd9\x03\x00\xe0\xe1\xe1\x01\xa1P\x88K(A\x10' '\xd0\xde\xde\x8e\xaa\xaa*u\xee\xf2\xf2\x12\x91H\xc4\xf4,\x9f\xcf\x87\xe6\xe6\xe6\xbcy\xbb\xc1\x95\x04EQ\xb4' '\x14\xc6h\x9d\x16Ff\xbc\xaa\xaa\xaa>N\x08(\x87WVV\xe2\xeb\xd7\xafy\x02\xf1\xba\xba\xcf\xe7\x83\xcf' '\xe7\xfb\\!\xf0\xde\x12f\xd7\xd8n\xbc+\tj\xc7N+^\x92\x10\xd0\x1f,\xcb2\xa2\xd1(U A' '\x10puu\x85\xca\xcaJu\xfd\xfa\xfa\xda\x90\x17\xed\x0c\x05\x1e\x8f\x07\x92$\xa9\xef\xcf\xce\xd4\xee\xb7\xd5J' 'f\x1d\xfe\x06\x10\xa2-r%\xc1t:\x8d\x83\x83\x03*\xdd\xd4\xd4\x14B!\xeaYv\xe1{\x01{~\x05' '\xe5\x16\xc9\x95\x04\xddn7\x86\x87\x87\xdf\xack\xc7\xe3\xe3\xe3\x00\x80\x89\x89\tC^N\xcf\xe9\xe7\xcf\xcf\xcf' '\x95\x0bT+\n1\x80\x96!OlNLL\x94\xb4}V\xfe\xae\xad\xad)\x06\xa0\x829\t\xb2\x1cL\x13' '\xa4\xd8\x8a\x1b\xc9N\x03W\x0e\x00\x80\x9b\x9b\x1b\xc3y#\xfaRz\x01+\xb8B\xe0\xfa\xfa\x1a\xcb\xcb\xcb\x96' 'LK\xad8K\xa7\xa9\x80\xab\x0f\xf0x<\xe8\xe9\xe9\xb1\x14\xe0\xbdJ\xd8\xddg\x98\x81\xab\x0f\x90$\tu' 'uu\x96\x87\xe9\xe3\xef\xf8\xf8\x18\xb2,s\xf5\x01\r\r\rp\xbb\xdd\xea\x9a,\xcb\x86\x17(\xed~Q\x14' '\xd5\x0b\x14\x8d\xaf\x1e\xdc9\xa0\x10\x17\x0c\x87\xc3\x90e\x99\xe5(\x15\xe9t\x1a\x1d\x1d\x1d*\x9fT*\x85\xbd' '\xbd=\xd3=\x15\x15\x15hnn\xe6\xca\x03\xb6\xb5\xc2\xfa}\xda\xbf\x83\x83\x83\xefr\x7f\xe0\xb53\x1c\x1d\x1d' 'e\x96\xc5\xb6*\xa0er\x7f\x7f\x8f\xed\xed\xed\xbcy\xb3=\xefU\xdc\xa9\xd8W\xc0\xfd<@\x92$\x10B' '\x98\xf7\x94Bq\xc7\xaa\x80\xcb\xe5\x82\xdf\xefg\n\x01\xed|:\x9dF*\x952\xa4\x17\x04\x01\xa2(\xc2\xed' 'v\xab\xef\x1f\x1f\x1f\x91J\xa5L\xc3\xcb\x88\x97\xcb\xe5R\x9f<9R\x05X\xddN\xff\x1fXXX\xb0\x14' '\xa4\xaf\xafO5\xc2\xe6\xe6\xe6\x9b[$+DQT\xf3\x84\x91\\F(J\x15\xe8\xed\xed\x05!\x84j(' '\x00oJ^[[[\x1e\xbd\xd9^\x05\xca\xf5\xd9\xb1*\x10\x8b\xc5\x10\x8f\xc7-\x05\xd1\x0b\xaeuo\x1a\x8d' 'v\xec\xf1x\xb8\xe8ic\xdb\xab\xc0\xf1\xf11\x8e\x8e\x8e,\x99\xf2\x08\xe9\xe4\x98\x05\\!\xd0\xd5\xd5\x85\xee' '\xeen\xea\xc1\xca\xf3\x80bdx\x96\xb1\xedU\xc0\xea`\xda\xfa\xea\xea\xaaa\x0e\x00\x80\xda\xdaZ\xb4\xb6\xb6' '\x82\x10\x82\x95\x95\x15*\x9f\xa6\xa6&H\x92d\xfaDJ\x92$\xf4\xf4\xf4\xd8\x97\x03\xf4B\xf0\xb8\x9a\xd6p' '\xa2(R[a\x97\xcb\x05A\x10 I\x92i\x8f\xe1r\xb9 \x8a"U6-/e\xcd\xf1*`F\xaf' '\xa53\xfa\x88\xddh\xec\xf7\xfb-i\xfa\xfb\xfb-ixPP\x08X\xa1\xd49\x80\x07\\!\xc0\x8a\xcf\xa0' '\xb8\x02\xa6\x10(\x04Z\xc1\xc2\xe1\xb0\x1a\xdb4\x81\x95\xf9\xba\xba:x\xbd^\xec\xef\xefS\x13\xa7\x19\x9f\xc6' '\xc6F\xd4\xd4\xd40\xcb\xe9\x88\x01\xf4!\x10\n\x85\x98.P\x00\xd0\xd2\xd2\x02\xaf\xd7\x8b\xdd\xdd]\xe6=Z' '\x88\xa2h\x9f\x01\xa6\xa7\xa7\x11\x08\x04\xb8\x85\xd0\xbb\xe8\xc8\xc8\x88\xe1\xbc\xd9xll\xccVW\xa7\x81f\x80' ' \x80\xe0\xd9\xd9Y\xab\xe6#).|\x94\x18\xb7\x02\xcd\x00\x0f\x00~\x01\xd0MY\xa7!\x00\xd8_\x05\x9c' '\x84U\x0eX-\x84\xa9^\x91X,\x96\xa7\x90U\x07i\x06\xaf\xd7\xcbDw{{kI\xe3x\x15\x88\xc7' '\xe3X\\\\\xb4\x95\xf7\xc0\xc0\x80\xa5\x11NNN\xb0\xb5\xb5e\xc9\xcb\xf1*P]]\xadvoF4\x85' '\xc0L\xf9t:\x8d\xc3\xc3CD"\x11\xb0\xfc\x1a\xc6Q\x03\x00\xaf\x17\x94\xfa\xfa\xfa\xbcy;b<\x97\xcb' '!\x93\xc9\x80\x10\x82d2\x89X,\x86D"\x81\xa7\xa7\'\xe4r9\xed\xe7\x08\x7f\xd1x\x14\xa5\x11r*' '\xb9\xdd\xdf\xdf\xe3\xe2\xe2\x02\xd9l\x16\x99L\x06\x99L\x06\xb2,\xe3\xf9\xf9\x19\xcf\xcf\xcf\xd8\xd8\xd8PH\x03' '4\x1eEi\x84\xf4c\xbb\x90\xcdf\xf1\xf4\xf4\x04B\x08\xb2\xd9,\xb2\xd9,\x08! \x84`iiII' '\x82\x7f\xc2\xe4\xcb\xd5\x8e\x87\x80\x93\xe5,\x1e\x8f\xab\x06P\x8c@\x08\xc1\xe9\xe9)\xe6\xe6\xe6\x142\xd3\xdf<' '\x14%\x04\x9c\xc0\xdd\xdd\x1d\x12\x89\x04dY\x86,\xcb\xaa\x11NNN099\xa9\x90}\x83\xc5W\xeb\x1d' "\xf7\x00'@\x08A0\x18D*\x95R\xe3]q\xfb\xd9\xd9Y\x85l\x1a\xc0\xefV\xbc\x1c\xf3\x00\xa7@\x08" '\xc1\xe6\xe6&b\xb1\x18r\xb9\x1cr\xb9\x1cvvv0??\x8f\xcb\xcbK\x85\xec\x1b\x18\x94\x07\x1c2@' '!\x17(\x16$\x93I\x04\x83A$\x93ID"\x11$\x12\tD\xa3Qm\xc7\x17\xc0k\xcc;\xfeU5' '\x1aVP\x9a\xdf\x11\xfd\x01\xfe{\x0b\x00\x98\xfej\xac\x10\xfc\x88W\xf7\xfbb3_\x1a\x02x\xbd\xb9>\x14' '\xe9\xbc2\xca\xf8\xaf\xe1\x1f \x1f\rF\x15\x18v\xbc\x00\x00\x00\x00IEND\xaeB`\x82' ) images_by_filename["toolbar_images/editcut.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq' '\xde\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x0eGIDATx\x9c\xed\x9b\x7flT' 'Wv\xc7?3\x83\r\x98\xb1g\x0cU\x0c\x82\xe2\x81\xc0BD\xa9\rA5\x01\xe4q %,\xbfL\xcb' '\x16QJ\xc0\xa5"\xd9"5\xf1j\xc5\x92n\xd4\xc5\x96\xd0*J\xab\x02\x01\xd4D%`\xec$\xcaf\x93' '2\xb6\xc3\x8f\xfc0\x1e~\x85\xee\x06\x9c\xb1\x13\x15\x02\x08\x8f\xc1`\x9b\x18f\xc6\x9e\x19\xcf\xef\xdb?\xee\xbc' '7?,\x84\x10"\x10\x08\x88\x9e\x9e\x1eq' '\xfb\xf6mq\xfd\xfauQWW\x17?\xce<\x04\xba\xa3\x01\x1a\x81\x92\xca\xcaJf\xcc\x98\x81F\x13\x0b\x0c' '\xdf\xf78\xfe<\xd5\x983g\xce\xf0\xea\xab\xaf\xb2q\xe3F\xaa\xaa\xaa\x08\x87\xc3\xf8\xfd~\xd5\n|>\x1f' '\xef\xbd\xf7\x1e\xaf\xbc\xf2\n\xc8(Q\x08\xb8\x06H\xd7\x94P\t\xf8\xf0\xc3\x0f\x997o^Z\xe1\xef\xa5\xd8' '\xfd\xf6\xb9\\.&N\x9c\x88\xc1`\xc0\xe9t"\x84\xe8G\x80\xcf\xe7c\xcb\x96-466\x82\\2\x95' '\x03\xafv\x0c\xda\xf8\x13\x8dF\x93 p\xaa\xf6 }F\xa3\x91\x05\x0b\x16\xe0r\xb9\xb0\xd9l\x00\xe8t:' '\xb4ZmB\xdb\xb6m\x1bz\xbd\x1e\xa0\x1c\x99C\x0c>\x01\xe9\x04\x1fhR\x96/_\x0e@UU\x95z' 'M\xa7\xd3%\xb4\xf1\xe3\xc7\xb3n\xdd:\x00c\x94\x84\xa1!\xe0~\x88x\x90\x06\xb0b\xc5\n\x00\xacV\xab' '\x14 i\xf6\xb5Z-:\x9d\x8e\x95+W*\xa2\r*\x01\xc3\xe2O\xe2\x05u\xb9\\\x04\x83\xc1\x04r\xd2' '\x1d\xe7\xe6\xe6\x92\x99\x99\t\x80\xc7\xe3\xc1\xe3\xf1\xa4\xbd\xc7d21s\xe6L\x9a\x9b\x9b\xb1\xdb\xed\xe4\xe7\xe7' '\xa7$a\xc2\x84\t\x98\xcdfN\x9e\xfe<33\x93\xdc\xdc\xdc\x84k\xb7o\xdf\xee7\x0e@\xaf\xd7\xa3\xd7\xeb' 'iiia\xee\xdc\xb9\x94\x96\x96b\xb1XR\x86C\xa5\x99\xcdf\xdcn\xb7\x13\xc8\x1d\x0c\x02\xfa-\x01\xe5' 'off\xa6:\xab\xc9}\xc9\x8a%\x1f\xe7\xe5\xe5\xdd\xb3\xbf\xa0\xa0\x00\x83\xc1@mm-N\xa7\x13\x83\xc1' "\x90r\x19h\xb5Z\xccf3G\x8e\x1c\x19\xb4ep\xcf(\x90\xee\xfa\xfd8\xcc\xf8\xfeTc\x15'g\xb1" '\xc8]r\xaa%\xa0\xd5j\x993g\x8e"\xe2\xaa\x81V\x1eR\xf8\x00\xe5\xb8\xb5\xb5Uufo\xbe\xf9&' 'S\xa6L\xa1\xb0\xb0\x901c\xc6\xa0\xd7\xeb\x99 \xf9\x1a\xc8\xec\xad\xb5\xb5\x95\x9d;wRPP\xc0' '\xf5\xeb\xd7\xd9\xb7o\x1fO=\xf5\x14\xd3\xa7Og\xeb\xd6\xad\xd4\xd7\xd7\xdf\xb7\x8fP`\xb5Z\xd3Z\x80V' "\xab\xe5\xc9'\x9fT\x86\x96\x0c\xa4\xf2\t\x04\xdcK\xe0xAM&\x13\xe5\xe5\xe5\xd8l6\x95\x8c\x95+W" 'r\xfd\xfau\xf6\xee\xdd\xcb\x9a5k\xc8\xcb\xcbc\xf3\xe6\xcd\xd4\xd5\xd5\xdd\xd3!*P\x96A:\x02\xa6O' '\x9f>h\x04\xa4u\x82\xe7\xce\x9d\xc3\xedv\xa3\xd1h\xc8\xca\xca\x02\xe4R\x88\x87B\xc6\x8b/\xbeHww' '7\x16\x8b\x85\xba\xba:\x8e\x1c9BMM\r555\x18\x0c\x06\x16.\\Haa!\x85\x85\x85,[' '\xb6\xac\x9f\x05\xb4\xb5\xb5a\xb3\xd9(((H\x1b\r\xa2~\x00 \x9f\xd4e3\x03\x92\xa0\xc2\xa4\xebV\xe0' '\xe4\xbdHh\x04\xc4\xd1\xa3G\x85\xdb\xedV\xdb\xf9\xf3\xe7\x85\xc5bQ\x1b \x0c\x06\x83H\x85H$"\x82' '\xc1\xa0\xe8\xeb\xeb\x13\xbd\xbd\xbd\xc2n\xb7\x8b={\xf6$\x97\xba\xc4\xc8\x91#\xc5\x86\r\x1b\xc4\x81\x03\x07\xc4' '\xdd\xbbw\x13\xfav\xee\xdc\x99\xb2Hr\xf9\xf2e\xd1\xd2\xd2"\x96/_\x9e\xaePb\x00\x0er\xef"\xcc' '=\x0b\xaf\x8d\x808v\xec\x98p\xbb\xdd\xc2\xe3\xf1\x08\x8f\xc7#\xbc^\xaf\xda\xfa\xfa\xfa\xc4\xfa\xf5\xeb\x05 ' '\x0e\x1e<\x98\x92\x84p8,\xfc~\xbf\xf0x<\xc2\xe5r\x89;w\xee\x88\xae\xae.q\xf1\xe2E\xb1\x7f' '\xff~\xb1z\xf5j\x91\x9d\x9d\x9dJ8\x07 \n\n\n\x84\x10B\x84B!\xe1v\xbbEww\xb7ho' 'o\x17W\xaf^\x15_\x7f\xfd\xb5x\xe1\x85\x17\x94\xf1\xa5q\xb2\x17(\xf7gg \xfev\x12\xe2\xb7\x85\x88' '\x83\xb3\x10\xfb\x7f\x82\xf8\xe5\x18DqF\xc2\xb3\xbe$i{\xad\xa6\xc2\xc7\x8e\x1d\xa3\xb8\xb88\xad\xf7>}' '\xfa4\x8b\x17/\xc6`0`\xb7\xdb1\x1a\x8d\t,\n!\x08\x85B\x84\xc3aB\xa1PB\x8b\xbf\xf6\xf1' '\xc7\x1f\xf3\xe9\xa7\x9f\xd2\xd0\xd0\xa0\xd4\xffT\xb4\xb6\xb6\x92\x9f\x9f\x9f2-\xde\xbd{7o\xbc\xf1\x06\x80\x13' 'i\xd6\x16`\x17`\xfc\xd9\x0c\xf8M1d\xeb \xd2\x07\xa1\x1e\x089 x\x07\x82\xdf\xc2\xf5oa\x9b\x1f' '\xae\xca\xc7\xd8\x90K\xc5\x05\xb2,^\x06\x98\x9e{\xee9L&\x93\xaax\xb2\xc32\x99L\xb8\\.N\x9d' ':\xc5\xa5K\x97X\xbbvmJsR\xbe7P\n\x9fJS\xaeM\x9a4\x89\xa7\x9f~\x9aM\x9b61' 'u\xeaT222\xb8u\xeb\x16\x81@@\xf5\x13B\x08\xc2\xe1pB\xbby\xe9\x0fx\xed\xe7i\xefa\x04' '0\x1d\x19\x12G\xec\xfc\x1b\xd8\xba\x14F\x8d\x01\xed(\xd0\x0e\x07M\xd4\xb5\x8b0\x88\x10\x8c\n\xc2J\x1ft' "\x00Wal\xf4\xfe\xdfA\x1a'\x08P__\x8f\xc3\xe1HPn\xf2\xe4\xc9L\x980\x81\xda\xdaZ\xca\xca" '\xca\xa8\xaa\xaaJ\xb0\x16\xc5kG"\x91~\x9e<\xf9Z$\x12a\xd1\xa2E\x98\xcdfB\xa1\x10\x8d\x8d\x8d' "\xdc\xb9s'm8\\;G\xc3\x86a\x10\xf1\xc2\xfb6\xd8\xf5G\x98\x99\x0f\xeb\x16\x81.\x1b\xb4\x19\x10\xf1" 'Ck+\x9c\xbe\x01Y>\x98=\x02\x86\xebA\xd7\x0b\xe1\x1ex1(\xd7@\xa7$\xcf\x0c\x9cL\xc8\x04\x15' 'E\x00\x8a\x8a\x8a\xe8\xec\xec\xec\xd77k\xd6,^z\xe9%\x0e\x1d:\x84\xcdf\xc3b\xb1\xa8\x96\x03\xdc\x93' '\x04!DZR\x14"B\xa1\x90\x9a\x06\xc7\xb7\x8c\xe07\xe8\xb2\xe5\xec\xae\x99\x05\xab\xa7@\xe8q\xc8\xf83' '\xc8\x18\r\x9aL\xd8\xfa\x9f\xf0_u1y\xb3\x87\xc1\xafL\xb0t8h2 ;\x04\x9b\x04\xfcVv\x97' "'\x10\x90\x1c\xf7\xc7\x8e\x1d\xcb\xb8q\xe3\xfa\x11\xa3\xd1hhhh`\xd1\xa2E477SXXHy" 'y9\x15\x15\x15\xfdf/\xd5\xac\x7f\x97e\xb8\\.F\x8f\x1e\xddo\x1ca;:}\xd4\xb4#R!\xbd' '\x11tz\x186\x06~\xb5[U\xde\tT\x01\xc6\xde\x10e\xffv\x15\xb2\xc6\xc1\xbc\xe8;\xb0\xa5\x1ax]' '\x80;\xba\xb9\xea\x97\x08%/\x87TIQnn.MMMl\xdf\xbe\x1d\x97\xcbEee%&\x93\x89' '\x8a\x8a\n\x9cNg\x82\xe0\xc9\xa4\xa42\xf1\xf8kw\xef\xde\xed7\xc6\xeb\xf5\xb2\xbf\xa5\x98\xffm\x03]\x16' 'hG\xc8\xb5.\xd9\x00W\x0f\xec\xa9V\x957\x01\xbf@n\x9cV\x01\xbc\xd9\x9dh\xe5Sc\x87\x05)S' 'a\x80\xce\xce\xce\x84\xd6\xd1\xd1\x91p\xdc\xd1\xd1\xc1\xf3\xcf?\xcf\xd9\xb3gY\xbcx1mmmTVV' '2z\xf4h6m\xdaD}}\xfd\x0f"\xe1\xee\xdd\xbb\xeaRR\xcc\xff\xddw\xdf\xa5\xf2\xb5\x83\x94\xbe\x06' '\xffq\x1c\xb4\x99\xd2\n"}\x10\xee\x85\x0b\x7fT\x15\xb2\x90\xf8\x1e\xa1\x16\xb0_\x0eJ\x87\x98\x02\xc6\x94\x9b' '\xa1@ \xc0\xb1c\xc7R\xde\x91\n\xabW\xaf\xc6l6\xf3\xd1G\x1fa\xb3\xd9\xa8\xae\xae\xa6\xba\xba\x1a\x83' '\xc1\xc0\xf2\xe5\xcbY\xbat)\xcf>\xfb\xec}-\x87H$\x82\xcb\xe5"\'\'\x07\xadV\xcb\xf1\xe3\xc7\xd9' '\xb1c\x87\xf2(\xe7\xbf\x1f\xc1\x98\xad\x83\xb2\x19\x10r\x81F\x07Y}\xaa(\xc9Y\xa0\x010\xe95 \x82' "\xa9eW\xf3\x80\xcf>\xfb\x0c\xb3\xd9\xac\x12\xe1v\xbbq\xbb\xdd\x89\x83\xd3\xec\xf9\x93\xf1\xc9'\x9f`\xb1X" '\xa8\xad\x8d\x15prrr\x98?\x7f>s\xe7\xce\xa5\xa8\xa8\x88i\xd3\xa6\xa5\xcd\x1b\xc6=\x96\xc5\x9f\x9b\xfe' '\x82\x0b\x17.\xf0\xcc3\xcf(\xb5\xc92d\x0c\xb7\x02\xc6\xa3\xff\x00O\x8c\x86a\x06\xd0fA\xd1k\xd0\xee' '\x04b/S\x0cH_\xb0j\xa9\x0e~\x1d\x89\xc9W&\xd4\x9c\xc0\xa4\x12\xd0\xd0\xd0\x80\xd9l\xee\xa7h\xba' '\x9a\xe0\xfd\xc0\xe9tb\xb1X\xb0Z\xadX,\x96~E\xd6\xa2\xa2"5O\x981^\xb0\xe3\xef\x05\xc3G' '\xdc"kL&\x97GY())Q\xeeQ\x94\x02\x99\xd2\xee*\x1a\x075\xc5\xd1\x108\x1c\xfe\xef\x0e\xac' '\xfd\x1f\xe8\r$\xca0U\x07{\xb50*$\xcf;\x04\xfc\x9d<\xb4\x03\x93\x12\x08())IK\xc0\x0f' 'E0\x18\xc4\xef\xf7\xe3\xf5z\xf9\xe0\x83\x0f8}\xfa\xb4Z\x12\xef\xeb\x8b\xd9\xee\xbc\xc7\xc1\xb2\x15\x86\xe5H' "\xa5\xce^\x86\xa5\xbfT\xbb\x8d$\xaem\x07`|\xfd/\xe1\xaf'\x80&\x9a\xfc\xb4\xf7\xc0\xeb_\xc1\x89o" 'A\xaf\x81e\xc3aM\x18\xb2\xfc\xb1\x1bw\x0b\xf8\xbd<\xac\x00*U\x02N\x9c8\xa1\x120\x10\x8a+\xb8' 'q\xe3\x06]]]x\xbd^\xb5y<\x1e\x0e\x1d:DCC\x83"H\x05@\xe7.\x19\xd6t\xa3@;' '\x12\xb6\xbc\x0e\xef\x9e\x00\xe4l\xc5\x17Cv\x02\xe5\xd9:8:\x03rFH\x02D\x18D\x00\xc2^\xe9\x1c' 'C.\x88\xf8b\xb2|)\xe0_\xe4\xa1\x12-\\\taPQ|\xa0\x94\x07\xe9K\xbc^/>\x9f\x0f\xbf' "\xdf\x8f\xdf\xef\xa7\xa3\xa3\x83\xcf?\xff\\\x11d\x17\xd1\xef\x87\x8e^\x80\xb0'*\xbc\x13\xf6l\x82\xb5\x0b " '*\xac\r8\x8c\xdc\x0cU\x01\xf4\x86\xe1\x9f.A\xdb\r\xf0\xdf\x8c\xb6[\x10\xe8\x80@w\x7f\xe5\xff5v' 'ZF\xd4\xa2\xd2\xa6\xc2\x03\x81`0Hgg\xa7\xaa\xb8B\xc2\xde\xbd{\x15\xf3\xdf\x15\x15\xc4\n\xac\xfa\xef' 'S\xf0\xec49sa\x97\x9c\xc9\x1d?\x81\xd9\x1ai\xda\xedNV!-\xc1\xa9<\xe3r\x00\xd6u\xc0\x9a' 'a\xb0\x14\x18\x1bA\xee\xfb\xa2\xb8"\xe0} .\xa6\x95\x11W^\xef\x97\n\x0f$\xda\xdb\xdbq\xbb\xdd\xaa' '\x1fp8\x1c\xbc\xfd\xf6\xdb\xdc\xb8q\x03\xe4\x8cV"\xb7\xb4\x15 S\xd7\xee6\xd0\x8b\xa8\x19Gwu\xcf' 't\xc3\x9c,x\xc3\x05\xefK\xe5\x94\xad\xa8\x1d\xb0\xba\xa1\xec@\x08\x0e\x00S\x80\xechg\x07\x10\x97\xcc;' 'I\xf1\x86i\xd0\x08p8\x1c477\xe3\xf3\xf9\x08\x04\x02\\\xbbv\x8d\xb7\xdez\x8b\xf6\xf6v\x88mI' "\xf3\x89\x86\xb5\x8a%\xb0y>\xa0\x91\x9b\x9a\xb0\x1bB\x99\xf2\\\x84!'\x00\xe5#a\x8aW\xcd\xe5A\xe6" '\xf3\xb5H\x02\xcb\x81UW\xfb\x7f\xeacC.\x99*R|l1(\x04\\\xb9r\x85/\xbe\xf8\x02\x87\xc3' '\xc1\xc5\x8b\x17ijj\xe2\xdc\xb9sJ\xb7\x15i\xc6.\xa2\xfb\xf9\xcd\x0b\xe0\x9f\x97K\xc7\xa7\xd1\xc8\xb5\x1b' 'rBo\x10F\x84%!:\x8f\xdc\xd5-\xf5\x01\x11\x95\x84]H\x02\xda\x90\xe9\xef/\xa2\xcf(@\xcex' '\xaa\xd2Y?\xecdh\xbe\rr\x90X\x96\xca\x07\xc4\x84\\D\xd7>\x84\xe3w\x88\x9ec\x88\x9e\xe3\x88m' '?C\xe4\x8c\x8c\xdd\xbbj\x02\xe2\xccl\xc4\x17\x13\x10g\x87!\xceh\x10\xb3b\xbf\x1b_!\xfa\xde\x18\x86' '4\x1f\x13\xd2$\xe3\xd7\x96\xfdA~8\x0ev\xa4\x97O~\xaf\xb7\n\xe0\xa73e\xd8\xd3\x19!\xf31\xd8' "\xfc\x1b\xa8\xa9W\xc7X\x81BK;\xc6\x8bw\xe0\x9d\xfe*' '\xb03\xb3m\x00\x00\x00\x00IEND\xaeB`\x82' ) images_by_filename["toolbar_images/editpaste.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq' '\xde\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\t\xddIDATx\x9c\xed\x9b]\x8c\x13' '\xd7\x19\x86\x9f\x19\xff\xc2z\xcd\x8a%\xe1\xa7]JR\xd6TD\x84\xbf.\xca\x96vY\x94VUA\x81r' '\x97\xaaRH\xaa\xb6j\x15UI\xafz\x11Em\xae{\xd1\xf4\x8eJIH\x84\xc4E\xb7R\x03B\x026' '\x17\xb0Z\x10ZD\xb6lR\xa8\xd9\x90,e\xc1u`Y\x9b\xb5\xbd\xe3\x99\xb1gz1\x1e{<\xf6\x8c' '\xc7\xacwM\xd5\xbc\xd2\xd8\xc7g\xce\x9c\xf9\xbew\xbe\xef=\xc73g\x04]\xd7\xf9\n_\xe1\xff\x17\xc2\x12' '\x9dg+\xd0e\xf9=\xb2D\xe7m;\x0e\x02S\x80n\xdbR\xc0\xef\xdbh\xd7\x92\xe00\xb5\x8e\xdb\xb7\xa3' 'm\xb3\xae\x84\xc5J\x81o\x00W)\x85\xfd3\xcfX' '$;\xda\x86\xa3\x80\x1e\x8dF\xf5\xb3g\xcf\xeaw\xef\xde\xd5\x13\x89\x84\x9eH$\xf4\xa1\xa1!\xfd\xe0\xc1\x83' 'z8\x1c6\xa3`\xaa\x9d\x86.F\x04\xac\x00\xd2\x00\xa7N\x9d"\x16\x8b122B2\x99\xacj45' '5\xc5{\xef\xbdg\xfe|\x996E\x81\xb8\x08}\xbe\x0e\xf0\xea\xab\xaf\xb2q\xe3F\xce\x9d;\xc7\xe7\x9f\x7f' '\xce\x993g8s\xe6\x0cO?\xfd4\x83\x83\x83l\xda\xb4\x89\x9d;w\x9a\xc7\xfca\x11\xech\x0b\xb6\x02' 'z$\x12\xd1/\\\xb8\xa0\x0f\x0f\x0f\xebo\xbe\xf9\xa65\xdc\xf5\x15+V\xe8\x9f~\xfa\xa9>44\xa4\xbf' '\xf1\xc6\x1bVA|\xad\x1d\x067\x8a\x80\xad\xc0\xdf1\x86\xadF\x8a\xaec\x08\x1f\x83\x83\x83\x84\xc3aTU' 'e||\x9c|>_\xee\xf0\xe1\xc3\x87\x1c?~\x9ch4J \x10\xa0\xb7\xb7\xd7\xdc\xf5v\x83\xbe\xff\x81' "1\xb2\xb4\x14n\x04\x1c,9\xf4c\xaa'1\r\xf1\xca+\xaf\x00\xd0\xd1\xd1\xc1\x86\r\x1bj\xf6o\xdf\xbe" '\x1dI\x92H\xa5R\xbc\xf0\xc2\x0bu\xfb\x88=Q\xd9\xbe\xbe\x02\x80m\xc0\xfb\xc09\x0c\x9di\t\xdcD0' '\x05t\xfd\xee\x87\xf0R?\x08\xa5\x96z\xe9C\x07t\xbdv\x93\x9e:\x8c\xda\xfb3"\x91\x08\x9a\xa6\x11\x8f' '\xc7\tN\x9f\xe0_\x9f\\!\x18\n\xb1\xe7\xc0\xcf\xe9\xfd\xdea\x8e\x1c9\xc2\xcc\xcc\x0ck\xd6\xac\xc17}' '\x96\xe9\xa9I4\rV\x06\xe7Y\xbf\xeca\x8d1\xd3i8z\x19\xee\x18\xbb>\x04\x0e\xb5\x82\x00\x9fC\xfd' '\x1e\xe0W\x07\xb6\xc2o\x9f\xa7L\x93 T\x18+3g\xa1p\xfe\xc9\xef\x93\xfd\xe6\xaf\x01\xe3\xea\xcb\xb2L' '*\x95"\xbcn\'?}\xed\x8f\xec\xda\xf7K\x84\xce\xf5\x0c\r\r133\x83\xae\xebd\xb3Y6~\xfb' 'G\x8c^{\xc0\xe4\x8c\xc8\xcd\xecJ\xb6u\xddC\x14\xaa\xff\xa5\xae\x08C_\x0f\xfc3\ts2\xdf*U' '/xJ\xedD\xc0\x06\xe0\xe5\xbd\x9b\xa0oC\xb5\xa3\x82P\x89\x06\xc1\xc2Fz\xddOH\xad\xff\x05\xba\xae' '\xb3l\xd92\xfc~?sss\xe4\xf3y\xe6\xe6\xe6\xb8|\xf92\x17/^\xe4\xe3\x8f?&\x9b\xcd\xa2\xeb' ':\x9a\xa6Q,\x16\xe9\xee\xee\xa6\xaf\xaf\x8fb\xb1\xc8\xdd\xbbw\x914?Ou\xd4FA\xc0\x07\xd10\\' '\x99\x06\x8c\xb4\xfc\xcbB\t\xf0\xbb\xed\xd4u\xd04\x10\xc5\x92\xff\x96\xab-\x08 una\xbe\xab\x9fL\xf7' '\xf3\x14\xc5\xe5\x86\x91\x81\x00\xd1h\x14I\x92(\x14\n\x04\x02\x81\xaaMUU4MC\x10\x04DQD\x14' 'E>\xfb\xec3v\xef\xde\xcd\xfe\xfd\xfb\xd9\xbd{7\xf1x\x9c+_\x8c\x13\x99\x1dc]8Cg@)' '\x9fwy\xa0\\\xdc\xb6P\xe7\x1b\x13\x00h:\x08:h\x18\x8a\xf9P_\x8d\x1e{\ti\xe5w\xd0|\x1d' '\x00D\x97/\xa7\xa3\xa3\x83p8\x0c\x80$I\xe8\xba^\xe3\xbc\xdf\xef\xc7\xe7\xf3Q(\x14\xca\xce\x8b\xa2\x88' ',\xcb\x8c\x8e\x8e\xd2\xd3\xd3\xc3\xaaU\xab\x18\x18\x18\x80\x81\x01\xf2\xf9<\xd7\xaf_\xe7\xd4\xf0_Y\xef\xbb\xc3' 'S\xcb\xd2\x80\xe2d\xee"\x10P\x126M7\x9c\xff\xe2?\x90\xdf\xf1\x1b\x9e\xfc\xdaw\xf1\x03\xd1h\x94U' '\xabV\xe1\xf3\xf9PU\x15Y\x96\xc9\xe7\xf3(\x8a\x82\xa6i\xf8|>B\xa1\x10\xba\xae#\x8a"~\xbf\x9f' "p8\x8c,\xcb\x14\x8bE\n\x85\x02\xc5b\xb1\xbc\xcd\xce\xcer\xff\xfe}&''\xe9\xe9\xe9!\x16\x8b\xf1" "\xdcs\xcf\xa1(\n'N\x9c\xe0\x12=l\xf4\xdd\x02\x1e,\x1d\x01\x9a\x0eh \x88p+\xbb\x86\x90\x12 " '\x7f\xfb\xb6\xe31\xa9T\xaa\xe1I\x05A\xc0\xef\xf7\xe3\xf7;\x9f^\x92$&&&\x00\x10\x8by\xd6\x852' '\x00\x14\xc5.\x96\x96\x80R\xec\x0b\xba!\x01\x89\xf1\xd3\x08X\x84\xb0\xf4a\xca\x83\x98K"\x14\xf3\xe56\xd6' 'o\x9c~S-\xac\xf6\xdfO(i\x0e\xadN\x97\xeb\xff\xd6"\xe7\xc1\x83\x06\x14\xb5\x92\xa1:\x90K"&' "\x92\xd5\x8e\x95\xca\xa2@\xdd\xfa\x9ar\x0b\xeaZ\tO) h%'\xad\x8e\xda\x8c\x14\x81D\x16N\xde\xb0" '\xcd\xae\x04\xdbo\xea\x8c(\xf6\xfd\xb6\n{\x7f\x16\x9cs\xb3\xbf\x84[\x18\x7f\xd0j\xc7U\x1a\x10\xa0\x95R' '\xa0\xcaQ\xb1\xfe\xd5\x11\x058r\x05\xf2\x05\x0f&\xb5\x0e\x83\x1e\xdb\xdd\x02\xde\xaa\xb7\xa3a\x04\x94S\xc0\x8c' '\x02\x87\xb0\x14\x85\x8a\xf3\x89D\x020\xc4\xce\x84Y\xaeW\xd7h\xff\xa3\xf63::\xca\xbe}\xfb\xdc\\l' "\x1c\x01E\xcdp\xb0hq\xd4I\x03Z\xe1\xd8b\x1c\xe3\x06\xf7\x08\xd0*\x04\x94U\xbe\x9e\x06P'o\x1f" "s\xc7Mx\x8b\x00,\xa1.\xd6:n%\xc84\xa0\x9dQ`/\xbb\xa1\xb1\x06\x14\xab\x1d\x16]\x86('" 'C\xdb\xe5\xf8\x82S\xc0\xaa\x01\xe5Q\xc0!\xfc\xad\xe7z\\\x1co\t\x01\x85b\xad\xd89MT\x9c\x8c9' 'y\xf2$\xaa\xaa64\xc6\x8aX,f\xbdiJ:\x9d\xe6\xf4\xe9\xd3\xae\xc7\x04\x02\x01^|\xf1\xc5\x1a\x1b' '\xdc\xe0I\x04\xc5b\x1d\x11\xc4\x99\x00\xab\x01\x82 \xb0y\xf3\xe62\x01N\x86\xd9\xebW\xaf^]U\x1f\x89' 'D\xd8\xb2e\x8bk\x1f\xc1`p\x11R\xc0)\x02\\F\x01\xbb\x08\xc6b\xb1\x1a\x83\x9a\r\xf5`0\xc8\xb3' '\xcf>\xdbR\x01\x84fS\xa0\x89Q\xc0\xabc^\x1dy\x94\xf2\xc2#@\x83\x82\x06B\xb1r\x85\xabF\x01' '*\x04X\xd1n\xc7\x9b\x81\xe7\x080u\xc0$\x80:i\xe0d\xd0\xd8\xd8\x18\xb9\\\xce5w\xfb\xfb\xfb\t' '\x06\x83\x00\xc4\xe3q\xee\xdc\xb9\xd3\x94#\x82 \xb0r\xe5Jv\xed\xda\xd5\xc2Q@3\x08\x10\x05(\x08\xe6' '\x89\x1c\x86\xc1:\x06\x99\xdf\xaa\xaa\xba\x1a\xa3(\n\xaa\xaa\x96E,\x97\xcb54\xbc^\x7f\xd9l\xb6)\xe7' '\xc1c\x04\x98\xc2W\xf5w\x18\xf7\x08\xb0\x1a000P\xd7p\xa7r___S\xed\x17m"T\xd4\xaa' '\t\x08\x95H\xa8K\x80m\x14h\xa5\xf1\x8b\xa9\x03\x9eR\xc0t0\xe4\xab\x8d\x00p&\xa0\xdd\xe5\x96\xcc\x03' '\xd4B)\xfc\x05\x10J\x04T\xdd\xdb\xb3\x91Q\xcf\x98T*\xe58\x11\x8aD"D"\x11\x04A \x99L' '\xd6\xec\xb7N\x84\x82\xc1`\xcdMWk\xfbP(DwwwC\xa7\xad\xf0,\x82\xa2\xedj\xbb\x11`O' '\x01\xb7)lOO\x0f{\xf7\xeeEUU\x86\x87\x87\x1d\xdbm\xdb\xb6\x8dP(\xc4\xd8\xd8\x98c\x9b`0' '\xc8\xe1\xc3\x95\x07\xc8-\xd1\x003\x02\x84\xd2s\xe4\x9a\x14\xf0@\xc0\xa1C\x87\xc8f\xb35F\x99C\x979' '\xd3;p\xe0\x00\x8aR\xfd\xe0\xc3l\xdf\xdd\xddM0\x18\xac\xba\xc2v\x07;;;=;n\xc2\xdb( ' 'V"\xc0$\xc08\x91{\x04\x98ek\x98;\xb5\x01\xcad\xb8\xb5Y\xbbvm\xcb&A\xe0!\x05T\x8b' "\x08\x1a'\xa9?\x17\xb0O\x04\x1eW\xd5\xb7\xa39\x11\xd4]R\xc0r\\;\x86\xc1G\x85+\x01P\xd1\x01" 'k\n\xd8\x9f\xf0\xd4\x9b\x08Y\xcb\xc7\x8f\x1f\xf7|?\xa0\xb7\xb7\x97\x81\x81\x01\x8e\x1d;V\xa3\x07^\xd0\xdf' '\xdf_\xfe\xdb\xec\x05\r\t\x80\x8a\x0e@\xfd\xbc\xb7\x13`\xd4Uj\xb6o\xdf\xee(nv\x989\xbec\xc7' '\x8e\xaac\xbc^\xed\xb5k\xd7zjg\xc2\x13\x01`D\x01\xd4\x11A\x9c#\xc0\xfc\xde\xbcys\xd3!m' "\xbd\x8a\xad\x08u'x&\xc0|\xaf\xc2K\x04\xbe\xf1\xa5k\xdc\xb3\x7f_\x00\xb0\xa4\xcb\xfc" '\xfb\xdb\xdf\xba\xfa\xe2l\x10\xbc\x1a\x005\xdd\x03\xbc6\x8e\xe8\xef\xbe\x02\xb0\xfb\xe7\x0fS\xb2\xc0\x06\t \x14' '\x87\xa3\xbe\xe4\xbe\xbf\xe6P\rJD\xfd\xf3\x05\xf0\xee\xeeEl\xf8\x87\xc2a\xae\xde\x0b\xe1\x04\xdep\x8c\xc7' '?\xaa\xbe\xa3\xa5?$\xd2\xa4\\\xf6i\x12\xff\xba\xbd\x84\xef\xad-\xb8\xfa\xf2\x13/\xd4G\xa8\xe9\x1e`\xcd' '\xa1\x9a\tQ~R\x02\xb0\xe7\x18\xf9\xe8\x97\x8f\xb2\xb0$\xe3\xea2\xad\xf0!\xd4F\xd8\xe7\xec`\xeb\x1f\x1a' '\xeex\xd9OZ\x00+\x97Ng\xcfO\x17\x92\x91iH\xe6\r\xd2\xe7~Z\xfeW\xe1\xf9\x8a:N\x8c3' "\xda\x9b\xf4\x00\xd2\xcd\x12?\xdf2\x97\xef\xae\xb6\xa33\x88\x83\x9a\xc7\x12t\xee\xefe\xc7'\x17\xc7u\xd6w" '\xdf\x00\xf8\xfb\xc7\xb3\xd9\xb1y.\xb6iz\x10\x04\x04A\xe0\xc8\xe7\x9d\x1c\xfdM;\x1f\xfe\xa9\xe3\xae\xff\x7f' '\xe1^)\xfe\xd8\xdct^\xdbXD~\xae\x89\x0b\xedA.\xb4\x068\xe5\xf4\xf0\xc7\x1a7>\xbf\xca\x94L' '\xc9\x94L\xc97!\xe3v\x82\x99\x99\x99\xcf\x02\xcf\x03\xd6{\xac\xc3\xf1\xbe\xbe\xbe\x9f}\xa3\x00233K' '\x80\x9a\xc9t\\0^\x08\xe3\x8d\x03\xac\x00\x0b\xfes\xc1=\xd7\xbc\xf6\xefj\x01\x1e\x07\xc6\x05@\xf7\x97\xee' '\x03\xa6\x00L\x01\x98\x020\x05`\n\xc0\x14\x80)\x00\x7f\xb92\xeaH0//\x0f\xbb\xdd\x0e@cc\xe3' '\xa4Sd\xe9\xd2\xa5\xc9g\xa7\xd3\x89\xa2(\x13\x0b`\xdd\xbaul\xdb\xb6\rA\x10(..\x1e\x1e\x86N' '\x06i>r\xe4H\xb2\xb0j\xd5*\xaa\xaa\xaa&\xde\x04\x84+\xf7\xf4\x9cN\xe7\t`\xdfdQ\x1exc' '\xf8\x18G;\xfb\xe3J\x86\x86 \xd4\xd6\xd6\xbe \x08\xc2\x0b7\xaa\xbb\xf6y\xbcu\xe3\xe9c\xcf\x9e=8' '\x9dNR%\x1d\xdb\x1f\xce\xa3(\xc3LL\xd5\xf8\x1f\x97\x87\xbd\x97\xae\xbfH9\xeakr\xf9\xf9\xf9\xf8|' '>\x8e\x1e=J]]\x1d\x91H\x04A\x10\xb0X,\x08WNs\x87\x06r\xed@oT7\xf4|\xab\xba' '\xdb\xf51\xbc\xac(\neee|\xf0\xc1\x07\x00\xec\xfdV\x01e\xf3sXh\xb7Q`12\xcf\x9cB' 'J8\xc6\x19ohb\x0eD\x86\xc4n\xb7\xb3r\xe5J6o\xdeL~~\xfe\x84\xcd\xfeX\xea\x00\xb6l' '\xd9\xc2\xfe\xfd\xfbq8\x1c\x94\xe5e\xb2\xd6\xe8\xa70\xcf\x8a\xcdj$\x1cVi\xeb\xf2Q\xdf\xd4\xc7?\xd5' 'u\xd1\xa1\xc5\xc7\x0f 7w\xf0\x9aJ{{\xfbuu\x9b7o\xe6\xe5\x97_\xc6b\xb1|c\x8a\x0f\x95' 'SSS\xb1X,477s\xe0;K\x98!\xf9\xc9\xcf\xb1\x90\x96\x9aB4\xaa\xd1\xd5\x17\xe0\x92\xab\x9f' '].\x0f\xaexb\xf4\x00\n\n\nX\xb4h\x11\xb3g\xcf&++\x0bY\x96\x91e\x19I\x92P\x14\x85' "\xc6\xc6F*++9\x7f\xfe|rE\x94\x97\x97'w\x8a\xbb\xad\xf8\x90\xa4\xa6\xa6RZZ\xca\xf1\xe3\xc7" '\xf9\xa1\xa4#oF:9\xd3\xd30\x9bdb\xb18}\x9e\x00\xae\x0e\x85\xb7\x831zG\xe3\x04\xadV+' 'eee\x14\x15\x15%\xe9F\xc54\x12\xa2\x8c,\xa7\x90fJ\xc1\xe1\xd0\x983g\x0e+V\xac\xa0\xbe\xbe' '\x9e\xf7\xdf\x7f\x9f\xa6\xa6&\x9ey\xe6\x19\x0e\x1f>\x9c\x84p7\x15w:\x9d\xac[\xb7.\tAUU\xa4' "\xd4tZ\xbb|\xb8}!\x0c)\x12\xaa\x16'\x10\x8c\xd1)\xeb\xe9%v{'8o\xde<6l\xd8@" 'AA\x01\xba\xacyxM\xb3\xb8\x10\x99IG\xccJg4\x8d\xb6\x90\x89&\x9f\x9e\xf3\x1e#\tC\x06\xa9' '\xe9\x16\x8a\x8bf\xb2l\xd92\xbc^/uuuTWW\xb3v\xedZ\x0c\x06\xc3\x1d9\xb7\xdb\x957n' '\xdc\x88\xd3\xe9\xa4\xa4\xa4\x84-[\xb6`\xb3\xd9\xf0wu\xd1u\xb6\x86hT#\x18\x8a\x11\x8e\xa8h\xf1\x04' '%?\xf8\x01\x9fVV\xdez\x1b,((`\xfd\xfa\xf5\xc8\x96l:\xd3\x1e\xc2\x17\x93\x19\x82&\xea\x04R' '\x8dz\x10 \x10\x8a\xa2jq\x1a{c4\xf6\x82\xcdd\xa6\xd4.\xf1\xea\xab\xaf\x12\n\x858q\xe2\x04\xdb' '\xb6m\xe3\xb9\xe7\x9e\xbb\xe9V*\xcb2\xcb\x96-\xbb\xae\xae\xb5\xb5\x95\x86\x86\x86[n\xc3\x00\xc1`\x90\x93' '\'O\xe2p88u\xea\x14g\xcf\x9e\xa5\xb9\xb9\x99\x076m"\xa0i\xb8\x0e\x1dB\r\x04H\xc9\xca\xe2' '\x81\x17_\xa4\xdbh\xbcu\x1c`0\x18X\xbf~=as>\xed\xc6bPA\x92D\x1c\xb9\xd3\xc8\xceL' 'C\x16uh1\xf5\xcaG\x0e"QU\xa3\xabo\x80\x96\xf6~\t@EE\xc5\x98m\xfc\xda \xe9f\xedw]\x99\xd1\x97' '\xbe\xbb\x08\x8f\xc7\x83\xd5jEQ\x14,\x16\x0b\xc1`\x90\x9c\x9c\x1cl6\x1b\xc1`\xf0\xf6\xb9@NN\x0e' '\xa64\x1b\xc1\x94\xe9\xe8\x04\xb0dYxz\xae\x919V\x90\xaf\xc9\x18\xf4\xc3\xcb:\x01U\x12\t\x1bE\x8a' 'K\nH5\x1bpy\xe3\x18S-,^\xbc\x18EQp:\x9d\xa3R\xecfu7zW^^\xce\xb9' 's\xe7(}l.\xab\xbf\xfd \xf1\xee*\xbc^/\xa2(\x12\x8b\xc5\x88D"\xe8\xf5z\xdcn7\xad\xad' '\xad\xb7\xcf\x05L&\x13\x9a~0\x801O\xb7"0\x03\xa7\xd3E4\xaec\xf6\xec\xd9\x9c>}\x1a\x9f\xcfwG[\xdd\x8d\xcaC\xb3\xff\x93\x7f|' '\x165<\xc0\x8c\xf8e\x94`\x0f\xb5}YI\xbb\xef\xea\xea\xe2\xd8\xb1c\x1c>|\xf8\xf6\x00rf\xceD' '\x14\x07\x8b\x19\xf3\x1d$\x80\xdf\xbbau\xd6\xd5\xc6\xe18\x9c\xf4\xc0\x80\n\x92p\x15\xc2\xf0\xf1\xcd\xcc\xcf\xe0' '\xc2\x856DI\xba\xce\xf9M\x84\xe2\x00\xe5\xe5\xe5\xb8\\.J\x1f\x9b\xcb\xd2G\x8aP\x07:H\xc4\xe3\xcc' '25\x93;\xe3\x12}~\x81S\x17z8\xf4\xbb\x1a>\xafj\x1c]6\xe8S\x14\xf4\x12\x18m\xa9H\xb2' 'D\x1c\xa8\x0b@[\x04\xb2S \x1a\x87\xfe\x18\xa8\x89k\x94\x1f\x06A\x00DQGV\x86\tI\x92\x93\x07' "'7Z\xc6\xe3\x05\xa1(\n;w\xee\x04\xe0\xdf~\xb4\x1e-2\xc0\xeb\xef\x1ec\xd7\xdeJ\x8ageR" '\xf9\xab5D\x83\n\xee\xde\x16*\xbf\xbc\xfd-Rix\xb6\x97.\xc5\x90\x848\xe2\x15e\x12\t\xf0kp' '94\xe8,Da0r\x12\x06M\x1f\xddM \xccHO!w\x9a\x99px\xf0._uu5\x85\x85' '\x85I\xcf\x0e\xd0\xda\xdaJ}}\xfd\x98S\xf1\x83\x07\x0f\xe2r\xb9\xd8\xb8v\t\x0bfg\xd3\xdf\xd1DU' '}\x84\x97^z\x897\xdf|\x13\xe2\x1a\xb5.\x0f\xff}\xa6\x8dpD\x1b=\x80\x8b\x17/"\xcb2\x19\xfe' '\x16t<8\xe2\xbb\x9e\xe1\n\x8fP\\\x18,\x0f\x87\x90\xd04\x1e\xce5\x12\x08\x04\xe8\xee\xee\x1e\xd5\x0c\x8f' '6>\xf0z\xbd\x1c8p`\xd0\xf6\xbf\xff,jXa\xcf\xc1\x0b<\xff\xe2\x16\x16.\\H\xcf\xf9\n.' "u\xfa\xb8\xdc\xea\xe1\xc4W]c;\x10\t\x06\x83\x88\xa2H\xa1\xc1OWO'\xe2\x8c\x9c\x11\x19\x93\xee\x06" '\x10t\xd7\xc2\x01\xd4>7\x7fU\x94\xce\xe7G\x8f\xd0\xd33\xf8\x11\xc3\xf2\xe5\xcb\xb1\xdb\xed#\x94\xc9\xcf\xcf' '\xc7\xe1p\x8c\xc9\x14v\xee\xdcI(\x14b\xe3\xda%\xe4NO\xe3r\xbd\x93\xcc\xd9\xdf\xa1\xa6\xa6\x86}\xfb' '\xf6\xb1{\xe3C\xd4\xba<|z\xd2E8\xaa\x8d\n@rCkkk\xc3\xedvc\xd2K\xe4w\xd5 ' '\xf9\xbd\x88W\x92\x05Q\xb8\xaa\xa4x\x13\x08\x82\x00\x81\xf6\x1e\xfe6\xcf@\x7fo7\xd5\xd5\xd5\xb4\xb6\xb6b' '\xb1X())\x19W\x9c?\xbc\xecr\xb9x\xeb\xad\xb7\xb0\xa4\x99x\xf5\xfb\xcf\xa0\x06\xfax\xfd\xfd\x13X' "\xadV\xd6\xacY\xc3\xaaG3\x89\xc5\x13\xb8:\xbc|Y\xd77\xbec\xf1\x93'Ob6\x9b\xb1\x18ef" 'u\xd5`\xeeu%\xed^\x1c\xa6\xfc\xd0;\xdd\x150\xf1P\x98xk\x07+\x8a\xcc\x88Z\x88\x8a\x8a\n\xce' '\x9c9\x03\xc0\xea\xd5\xab\xefH\xf1\xa1\xf2\xce\x9d;Q\x14\x85\xad/J\xd7\xfa5\xe0L\xb1\xde%8\xc0q\xeb\xbd\x05X]\x0f^\xf0\xb1#{=\x10'\x96\x18\x01\xdc" 'c\x80}\xd3\xcf\x165\xd3\xc6\xbb\x1e\xdd\xbf\xd33\x7f \x8e\x03\x94I0\x99L\xe2M\x81\xb0\xa4%|\xd9' '\xa8\x99\xa4\x15L\x80\x92\xbe\xe0O5\x07P++\xd3\x9e\xf7m\xf1\xaa}!\x98\x03\xa4\\\x06\x93I\xe7\x05' '\xae\xbdc3\xe5\xdcd\x05\xb0\xae\x17YH\xcf\xa0\xe5\xb3\xd8\xb6\x15G\xff\x8a\xd3\xba\xb5\x95jS\x11\x10X' ':\x84]M4z\xa9H\xf2\xee\x8de\x01n\xe8:F\xa9T\xc3\xf7GRp\xdf\x11\xbc\x0fD\x9c\xa2m' '\xf5*0Lz\xa8\x8d\x93\xcb8;\xd2\xb3\x1eC9\x05\xbc\xb5\xc7\x99\xd76\x00\xb3\x0bS\x15\xf0\xc5|\x9e' '\xe9\xf1;h\xa9T\x93.P\xf1\x0c\x00va:\xda`\x1f\xcbZ\x9c\n\x7f\x96\x91\xc7.\xe7\xab\x1d\x02\xaa' 'D\x90\xa5h\x8b\xd3\xfe\xf1\xc7v\xb2o\xd7V\xae\xbe5\xd1\xe7\xf1"\x1cP]r\n\xc5<\xa6i2\x99' 'L\xb2y\xc7\xc3\x1c>\xf1\x0c\xb6\x9e\xa2<\xf1"\xc2\xc8\x07\x8d\x14\xb2\x8e\xa0-\xc2o\x9d\x90\xcc\x172\x08' 'Q\x03JH\x0e\xf2\xb6\xddR7\x02N\xf3)\xe9^_\xcd\x92u\x1cP]_\xb3\xd9\x14\xa6i\x92\x9e[' '\xe0\xe0\x93\xdf\x00\x04\xc6\xc4K\xd8\xba\x16\x03\x9c\xb4\xbc\xf9\x0cV\x82\r\xa8\x10\xd2\xbd\x04.\xca\xd9T\x93\xa5' '\xd79\x9b/r\xe1\xf5\x9b\xe0|\xab\x04b:\xa0d\x94\xd0Ri\x0c\xa3\xcc\x03\x03\x1f\xc1\xcaOa\x16\x16' '\xea\x83\x8b\x1a5\x19\x9c\x84^\x04\xbd\x133\x92\x84t\x1f\xb4\xe7\xd7\x7f\xb8\xe8q\x87\xbdJ\xe4\x14\xf0\xbc\xaa' '\xe52d\xd3Y\xb6>x\x905\xeb\xba(\xde\x19\xc5\xb6\xecpp\r\x84\xa4p\r\x86d\xe8\xa8-{>\x80\xf1\xe9\x14\xff\xba9\tN\xf8W\xf6;\x91\x0e\x00' '\xd0\xf5\x02f\xd9d1_`\xfb\xee\xc3X\xa5\x1c\xa6\x9e]\x96\x90\xac\x0b\xaef\xfeGDV\x1d\xe7\xff\xf9' '\xefW\xbd\xea\xb0\x9f_\x7f\n\xd8\x02\xbdP\xc40L6\xf7\xee\xc1,\xccc\xb9\xe1\xbf\xd4\x90T\x81\x93e' '\xe3\x80\x8bC/_~\xdb\xab\x9e\xf1\xf3e\x07\xf4\x02\x0c\r=\x0e\x80\x91\x9b#\xa3\xa5Y\xcc\x17X\xbd\xb6' '\x93\xb5\x1d\x9d\xe4S\xd7\xb1-A\xe8\xa8\x05\xee\t\x195\x9ft \x116\x81,\x06e\xf3E\xfer\xe1\x1a' '8\xef:\x81\xed\xbe\xec\x80>\x9f9\xd8\xa6\x8ea\x94X\xcc\x17\xd8\xb4}7\x00\xa5\xccD\xe0SYU:' '\xf0\xb3,\xa3\xd6,\xc9\x8e\xbcX\x1d\xfdaY6\xfc]\xc0\xfd\xb5-\x8b\xc2\xa2\xce#{\x1f\x05\xa0\xa4\xcd' 'VV\x00Ov\xe5\xc05\x1b\xfe\xefx\xd5\x11\xb9-:\t\nA*=\x8fa\x98\xbcw]\x17\xb6\xa9c' '\x95\xf5\xc6-\x88EKqdt\xdf\x8bcM9\xc0\x99\xa3\xba\xae\xb3\x98\xd3\xd9\xbac\x0fF~\xae9\xf3' '\x96\x14"q\x1c\xa3\x9av\x0ei\xf9"\xd7\xde\x9e\x02\xe7U\xb8\xe6\x9fj\xa5\x03\xfaz\xb7\xe3iL\xa7S' 'X\xb6\xcd\x86M\xf7c\xe9\xb9&\x8ck\xdchY&\\.\xaa\xdda\xbc\\\x1d}\xe5\x9f1j\x07\xf4\xf5' 'RL\x8f#\x10\xcc\xcf\xcecY\x82\x8d\x9b\xb7\x91\xbd\xf5J\xcc\xd1\\\xda\xa8\xd5o\x0fk\xabu\xc8\xc5\x88' '\xf9\x0f\x91\xab\x00\xe4r\x1a\x86a\xb2e\xe7\x1e\x00\x8c\xfc<\xffmp\xf2\xeaR\xa9\xc9k\xb2\xcbs\xc3\x1f' '\x1as\x80\xb3\xcekZ\x96b\xd1`\xc3\x96N\x00\xac\xb2.}\x00U\x1b\x19\x05 \x1e\x88*?Z\xd6\x7f' '\x1f|\xa0w\x7f\xe9\x8d\xa4\xc7R\x9eTQN\x81\xa1\xa1\xc7\xc9M^e~n\x9a\x92n\xb2\xed\x81\xbd ' '\x04zz|\xc9!\x19\x8f\xa7\x06\xa3\xba\x0f\xb6\x89\x80\xce\x89\x99\xcaq\x84\x11\x95\xd5\x10\xb6\n\x08A\xb9\xa8' '\x91\xc9\xa4)\x19&k;\xba\x00\xe7\x9b\x9d\xffaA\x10* *^\xe3\xa3\xe8\xe9\xf1\xeb\x8cS\x1f\x9f\xae' '|\xb3L\xca\x10=\x92\x1dP\xfd\x94,\x04ss3\xe8\xba\xcd\xee\x0f\x1e\xa5\x98\x1aw\xfe\xd8XbHF' '\x1b\x1d\rH\xf9\xbf\x83\xaf\x1e\xe4\t^\xb9\x92\xf4:W*2\xc9\x0e\xe8\x19\x1c\xfc\x10\x00\xa5\xdc,\x13\x13' 'N\xc8\xaf\xeb\xecB *_\x88\x9b\r\xc9\xb0z<@Apa\xcf\xad\xf2\x04Z\xbe\xe85\x8f(\xd1\x13' '\xb1\x15\xbe\x93|\x8bR\xc9\x01\xbc\xe3\xfd\x0f1\x7f\xe3\x02\x96e\x05d\x1a\r\xc9z\x80\xc2\x80x\xd5\xf06' 'uy\xfd\xd6\x8c\xa7\xb6z\xee\xa1\xbe\x03\x1c\xe5\xd7\xaf_\xc54\x05}\xbb\xf6\xb9\x0f\xb5\xb1mkI!Y' '\x1fd-X58u$\xc8\xe5\xd4|e\xe3\x16zV t+\x9c\xd34\xcaeA\xdf\xae\x87\x00An' '.\x89iZ!\x80\x9b\x01Y[\xd6\x07\xa5\xee\xabj\xcb\x17JL;\x0e\x88<\x8eS\xe3\x80L&\x0b\x08' 'z\xee\xdb\x86\xae\xdb\xec9p\x04\x80\xa2\xb6\x80e\x99\xbe\x075\x07R\x1e\xc5 \x888\x80\xab\xed*Y\xaf' "\xee\x0b\xffd#\x0e\xb8<6\xf6\xc6P2y\x9b'>\xfbM&\xef\xa684\xf4\x14\xa5\xc54\x05m>" '\x02d\xbc\x90\x8c\x02\x15\xa5\xab\xb6\x1e\xde\xd7\x93\xfd\xc7he\xdf3\x12\xe5\x00\xf9\x94\xd8q`\xf8\xf8\xf1\xa7' '\x18~\xfe7xa\xf9\xe6\xdf~I\xea\xee\xb5\x98`\x1a\x1f\xbd\xa8\x91\x0c\x03(;C\x96\xff\xca\x0f^`' '.]\x04gw\x1bz^YuXz\x14\x18\x18\x1c<\xca\xd0\xe0Q^{\xf5\x12\x13\xb7\xae\x07\xe6w-' '\x85\xbd\x1f\xf8\xf3\x85C\x9b6t\xb0icG\xdd\x04\x19^W\xaf,~^rJ\xe3\x9f\xff\x9e\x05\xe7\x0b' "\xd0'#\x0cW\xd2~\x1c'\x88\xff\xf1k\x14\xe8\xae\x076\xec\xb8>\x02\x1d\x1d\x90\xa6\xa6&\x1a\x1b\x1bu' '@F\xbf\xde\x01lg\x18\x94\x8b\xb6!\x17\x03\xc0\x8c\xa1\x0eM\x99|>\x1f\xfb\xf7\xef\x1f\xc3\xf8x\xf9\xee' '\xeen\x1a\x1b\x1b\x8d\x94\x07\x90\x1c\t\xe1\x02\x00\xb9\x18\x00\xae\x05\xea\xeb\xea\xea\xd8\xb6m\x9b\xd1\xe9|\xd77' '\xdex\x835k\xd6\xb0z\xf5j~\xfb\xdb\xdf\xe6<\x1b/\x9f\xaf\xac\xbb\xbb\x9b={\xf6\xd0\xd8\xd8\xc8\x9e' '={>\x16@.\xc6\x06T\x8e\xd7\xe9\xd1\x9dojj\x02`\xd1\xa2E\x1328Y\xbe\xa2\xa2\x82\xbb\xee' '\xba\x8b\xaf\x7f\xfd\xeb\x00tuu\x19`\xec\xd9\xb3\x87\xae\xae\xae\x1c\x1b\xc2\x14\x009\x1f\t\x98\x87f\xc8\xf4' "\xe4\xd4\x99z\xf9\xe5\x97'\x04\xe0\xba\xeb\xae\xa3\xb5\xb5\x95\x03\x07\x0ePQQq\xde\x8cO5\xdf\xd5\xd5\xc5" '\x9e={hhh\xd0\x01\x19\xcd\xc3H@\xfe\x04\x13\x03\x90\x97a\x9d\xae\xb8\xe2\n\xda\xda\xdaX\xb4h\x11' '\xaf\xbc\xf2\xca\xb8\x9d\n\x87\xc3TUU\xe1\xf3\xf9x\xef\xbd\xf7>\x11\xc6\xc7\xcbwuu\xd1\xd0\xd0`\xa4' 'Q\x80l\x07\xbe<\x9e\n<\x8af\xdd\r\xaa\xab\xabc\xd1\xa2EFjnn\xe6\xf6\xdbo\x1f\xf7\xe3\xfa' '\xb5\xb1\xb1\x11\x80\xc5\x8b\x17\xe7\xd4\xeb\xeb\xeb\xc3b\xb1\xe0p8>1\x10f\xcc\x98\xc1\x8c\x193\xb8\xfb\xee' "\xbb\x11\x04\x81\xce\xceN\x1a\x1a\x1ax\xf0\xc1\x07\t\x85B+a|\x1b\xb0\x14\xe0\xbe\xfb\xee3\x18\xcf'\xde" "#I\x10\x04\x92\xc9$\xc9d2\xa7\xce\xee\xdd\xbb\r\x00\xf4z\xa1P\x88\x93'O\x1au\xecv;\x0e\x87" '\xc3H\x1f\x17\x08\xa3\xef+++q:\x9d\xac]\xbb\x16\x86f\xb0\t\x8d\xe0\xb2e\xcb\xa8\xa9\xa9\xc9ih' '<=\x17\x04\x81\xc3\x87\x0f\x93\xcdfs\xda\xd8\xb9s\xa7\x01\x80\xfeNqq1\xd3\xa6M#\x1c\x0e\x93L' '&\t\x87\xc3\x84\xc3a\xc3\xaa\x8f\x04\xc3\xe9t^4\xe3#\xf3\r\r\rz\xb6~"\x00\xea\x81\xa5\xcd\xcd' '\xcd\xcc\x9d;wB\x00t\xe6\x05A\xa0\xb4\xb4\x94h4j<\x0b\x85B\xb4\xb7\xb7\xe3\xf3\xf9\xa8\xa8\xa80' '\xcaeYf\xf6\xec\xd9\x00$\x93IB\xa1\x90\x91F\xde\xeb\xa4\x03\xa1\xa7\x0ba\\\xcf\x9f\x0f\x00455' '\xe9\xe22\xa9\x04tww\xb3w\xef^Z[[9r\xe4\x080\xac\xff\xa1P\x88\xdbn\xbb\r\x80%K' '\x96PSS\xc3\x92%Kp:\x9dX\xadV\xacV+^\xaf\xd7\x00$\x18\x0c\x12\n\x85\x08\x06\x839\x80' "tvv\x02\xe4\x80\xe1r\xb9\xa6\xc4\xb8N\xbaJ\xea\x1e\x7f\xee\x05.\x9f;\xd7\x18\xe1TZ\xa1\xf9x\x1f\x87:\x02DS\x19' 'J\xecV-9,\x94\xd8\xad\x14YddQ@\x125ip\x16\x98(2I\x98$\x91\x8c\xa2\x03\xa1\xb0' '\xb7\xb1\x81\x9f\xdd\xfb}z{\xba\xa9\xae\xae\xe6\xf9\xe7\x9f\xc7n\xb7\x13\x0c\x06\x19\x1c\x1c\x1c\xd3\xb9\x91`\xb8' '\xddn\xa3<\x9f\xea\x04\x83AJKKa\x84\xfeO\x05\x80]\xc0R\x9f\xcfGww7W~~\x11\x1b' '\x9e\xdd\x84\xcb\xe94\x18\xdf\xdc\xd0\xce\xde\xe3}xl\x16J\x1cVf\xb8\xadT\x97\x98\xf0\x16K\xb8\xac ' '\x8b"\x82\xa0}(+\x9aP$3\x98-\x88\x92\tY\x12\xb0\x0e-\xaaj\xaa\xa1\xe2\x0f\x04\xf8\xf9}\xeb' '\xd8\xf3\xd77p8\x1c\xbc\xf9\xe6\x9b\xcc\x9b7\x8fL&c\x8cl \x10\x18\x17\x10\xb7\xdb\x8d\xcb\xe5\xc2\xe3' '\xf1\xe4<\xdb\xb1c\x07w\xdcq\x07\x8c\xd0\xff\xa9\x00`\xb8\xc47~y5\x0f\xfe\xf2\x19L\x92\x80I\x14' '\xd9w\xbc\x8f\xad\r\xedd\x14\x15\x8f\xcd\xc2\xb5\x97\x16\xb3\xb8\xc2Ji\x91\xc6P(\x14"\x1a\x8db6\x9b' '\x19\x1c\x1c\xc4j\xb5RVV\x86\xaa\xaa\xa8\xaa\x8a"[\xc0Z\x8cThC\x1a\xb2\x11\x89\x8c&\t\xa9\xac' '\xca\xe3\x0f\xfc3o\xbd\xbc\x95\x8a\x8a\n\xf6\xed\xdb\x87\xd39\x1c\x8a\x08\x82@:\x9d\xce\x01$\x12\x89\xe4t' '\\\x96e\xae\xbd\xf6ZL&\x13\xa0y\xb5O?\xfd4\x8c\xd0\x7f\x98<\x1c\x9e\x0fp\xf5\xf5\xcb\xf8\xf1\xe3' 'O\x1b\x85\xff\xfd\xb7\x13\xec=\xd6\x87Y\x16\xb9\xa6\xca\xc6\x9dW:p\x17J\x86C\xa4(\n\xa1P\x88L' '&\x83\xc3\xe1\xc0n\xb7\xd3\xd3\xd3\x83\xaa\xaaH\x92\x06\x90\xaafP\xa2~\x88\x87\x11\x9d\xd3\xb0X\x0b0I' '"\xfeX\n\x80\xfb\x9fx\x1aEUy\xfb\x95?p\xc3\r7\xb0y\xf3fctM&\x13&\x93\x89\xb2' '\xb22\xca\xca\xca\x00\xc8d2\xf8\xfd~\x02\x81\x00~\xbf\x9fL&\x93\x03\xd8;\xef\xbc\xa3\xdf\xd6\x8fdp' '"\t\xf8\x11\xf0T\xf9\xa5s\xf8\x97\xff\xdaFY\xa9\x07\x93(\xb0u\xf7)\xf6\x1d\xef\xc7Q \xb3nq' '\t3\x8bSH\x92\x84\xc5b!\x1a\x8d\xe2v\xbb\r&?\xf8\xe0\x03\xa6O\x9f\x8e\xdf\xef\xcfqv2\x99' '\x0c\x03\x03\x03\xa8\xaa\x8a\xc7\xe3AUULEv\x8a\xdc\xd3Hf\x14\xce\x84\x13\x864\xac[y\x03\x1d\xc7' '\xdaX\xb1b\x05\xdf\xfe\xf6\xb7\x01\xb0\xd9l\x86\xb8\xbb\xddnc\x94\xc7\xf3\n\x83\xc1 %%%0J\xff' 'a|\tp0$\xfa_}\xe0\t\nm\xda4\xb2\xf9\x9dS\xbc\xfb\xa1\xc6\xfc#7{\xb1\xa4\x02\xf4\xf7' 'G\xb0X,\xa4\xd3i\x14E\xc1f\xb3a6\x9b\x8d\x86t\x7f?\x99L\xe2r\xb9(,,$\x1a\x8d\x12' '\x8dF\x91e\x19Y\x96Q\x14\x85L,BB\x00\x9b{\x1a\x978\n81\x10\x05\xe0\xa7\xcf\xfe\x81\x1f~' '\xa9\x8e\x1d;v\xb0d\xc9\x12\xe6\xcc\x99C$\x12!\x12\x89\x18\xe1\xad\xcdf3\xc0\xf0x<\x06 :\x08' '\xe3\x8d>\x804\x0e\x00\xff\x01,\\|\xe7w\xf9\xdc\x8d\xcb\xb1\x17\x9a\xd9\x7f\xac\x8f]Gz5\xe6o\x99' 'N\xa5\xc7Jqq1\xa2(\x92\xcdf\xa9\xaa\xaa\xa2\xbf\xbf\x1f\xb7\xdb\x8d\xd5jE\x14E\xbc^/n\xb7' '\xdb\x10U\x8b\xc5\x82 \x08\x86\xfb\x1b\x89D\xf0x<\x08\x82\xa0\xb5\x93N\xa1*\x19' "JJJ\x10E\x91d2I4\x16\xe3\xb2R\x1b\xcd'\xb4\xcd\xce\x19W\x7f\x91`\xcf\x87\xec\xdc\xb9\x93Y" "\xb3f\x19\xc6P\x0f\xd2\xecv;eee\\v\xd9e\xc6w'\x12\x7f\x18k\x03\x96\x02\xb8\xe7,2\n" 'b\xa9\x0c\xe5n+W\xfeS\xa1!f\x8a\xa2`\xb1X((( \x9dN\x1bz?R\xff%I2\xe6' 'X\xe5\xd4\xa91\xcfF\xa7\xae\xae.\x12\x89\x04\xf1x\xdc(\x0b\x05\x83XM"\x97\xb8\n\x01(\xbdB[' 'V\xeb\xec\xec\xe4\xe6\x9bo\xa6\xb6\xb6\x96\xca\xcaJ#\xd8\t\x87\xc3|\xf8\xe1\x879\x0cM\x06\xc0h\tX' '\n`\x9bq%\x00\x89T\x16g\x91\x99\xab.)\xa2\xc0$"Y\xb5\x0fI\x92dD]\xb2,\xe7\xd8\x04' ']\x12\x002C\xc6H\x8dF\t\x87\xc3\x86\xbf>R\nti\xf1\xf9|x\xbd\xde\x1cg)\x19\x8f\x91Q' 'T\xca\x1d\x9a\xea\x14\xb8\xbc\x14yg\xd1\xd2\xd2B4\x1a\xc5\xeb\xf5\xe2\xf5z\r\xcfP7~:M\xa6\xff' '\xf9\x00p\x02XK5_!\xab\xaa\x94\xd8\xadTy\xccFG\xcb\xca\xcap:\x9d\x84\xc3aDQ\xa4\xb4' '\xb4t\x0c\x00z\n\x0f\xad\x0c\x11\xd5\x0c\xd9\xc8\xd5$\xdd\x18\xea\x80\xd9l6\x14E1\x12\x80\xd9l&:' '8H\x99\xbd\xc0\xe8`\xa1\xb7\x8ah\xef)^{\xed5n\xba\xe9\xa6\x1cc8\xda\xff\x9fl\xf4\xf3\x01\xb0' '\x14@\xb4ha\xa8\xd5$Qb\xd7\xfc{\x9d)\xaf\xd7ktX\x17\xfb|\xcc\x8f$\xa5\xbd\x9dl6\x9b' 'wEY\x14\xc5\x1c0b\xb1\x18\xa9T\n\xab\xd5\x8a$I\xc4b1\x8a\x9c\xc3\x8cY\x1c\x9a\xeb\xbb{\xf7' 'n#$\xb6\xdb\xedx<\x1e\xbc^o\x0e\x08S\x01`\x8c\x1f Z\x8a\x8c\xbc\xad\xd0D\x89\xc3\x82\xc3\xac' "u\xee\xdc\xb9stww\x1b\x86+\x9f\xe1\xd3Sf\x948\x0e\x0e\x0e\xe6\xad'\x86\xc3\xc8\x8d\x8d\x98\xda\xda" '\x90$\x89\xce\xceN\xda\xdb\xdb\xe9\xe8\xe8@\x14Ed\xb3\x89\xac\xa2\xe2\xb5[\xb4A)\x9b\xa5]\xadV\x83' '\xd9p8L{{;\xcd\xcd\xcd9\xdf\xbc\x10\t\xc0Z:\xcb\xc8\xbb\x8a\xcd\x94\xd8\xad\x08B\x8cX,F' '\x7f\x7f?\x85\x85\x85\xf4\xf4\xf4PUU\x95\x97!c\x068r\x84[\x00\x97\xde\xd85\xd7\x8c\xd7\x07\x002' 'K\x96\xa0\xbc\xfe:\xe5\xe5\xe5\x9c\xd2\x0e\x97\x94\x11\x8c\xa6\x90E\x81\x0c2\x90\xc9\xf1\xf3\xa7' 'B\xbe\x1f\xfc\x80@ @\xd5\x90\x95\x1eI#\xe3\x82|\xa4\xcf\x0e)d\xb2\x8a\x82?6t\x124\xa1M' '\xa9\xabV\xad\xa2\xae\xae\xce\xd0\x7f\xdd\x19\x82\xa9\xeb?\xe4\x97\x80\x95\x91\xce\xc3p\xd5B\x06\xe3iT\x15\xd2' '\x92\t\xd4\xa1\x0ed\xb3\xb4\xb7\xb7\xa3(\n\xb3f\xcd\xca\xf90\xe4\xaa\x83\xbe\xe99\x91\xa4\x8c\xa4\xb6\xb66' 'dY\xe6\xd2K/\x05 \xad\xa8\xa4T\x81`4i\xd4\x89t\x1e\x06\xb4\x8d\xce|\xba\x0fS\x1f}\x18;' '\r\xd6\x03\xf8\xdfo2\n\x02\xd1$Y\xc9b\x8cX,\x1636$F\xee\xdf\xe94\xd1\xa8\x8e7c\x00' '\x9c9s\x86\xbe\xbe>"\x91\x08\x82 \x90\xcdf\x89d\xb5U\xe3\xc0\xe00\x00\x83\xbd\'\x8dHr<\xba' '\x18\x00Z\x80\x8e\xc1\xde\x93\xc4\xfd\xbd\x00\x9c\r%\x10E\x91\xb4d5*\xe9j\x90H$\xf22<\x19\x08' '\xf9\xca\x8e\x1d;f\x04Y\x81@\x80x2E8+\x91UT\xce\x865\x00\xce\xb55\x92MDY\xbat' '\xe9\xb8\xedG"\x11\xe3\xc4\n\x17\x00\x00h\'\'\xe8~\xf7\r\x00\xe2\xa9,\x89t\x96\x8cy8\x92\x1b\xb9' '\x05>\x9e.\x8f4l\xa3\xcb\xf3\xd1\xfc\xf9\xf3)--%\x91H I\x12\xc1\xb4@Z\x81D2M4' '\xa5}\xaf\xb7U\xdb\xd9]\xb9r\xe5\xb8\x0c\xed\xda\xb5K\xdff\xef`\x12\xfd\x87\xfc\x00<\x05p\xeco/' '\x91\x8aiK\xcd\xfd\x83)DY&k.\xa4\xa8\xa8\x88\xcf|\xe63TWW3c\xc6\x8c1V|2' '0\xf4400\x80\xdf\xef7\x02\x18\xb7\xdbMEE\x05\xd7]w\x1d\x19\x04\x02\x19M\xfc\xc3\x894\x00\xa9' '\xd8 \x1d{\xb5\xbd\x82\xf1\x00hlld\xfb\xf6\xed\xfam\xfdd\xccC~?\xa0\x13\xd8\x98\x8e\x0f\xae9' '\xfc\x97-T}\xe7\xc7$\xb3\n\x83\xc9\x0c\x9e\xc2b\xd4x\x16\xabU\x0bm\x15EA\x14E>\xf8\xe0\x03' '\xce\x9d\xd3\x8e\xab.X\xb0 g\x97f<\xda\xb7o\x1f\xe5\xe5\xe5\xb8\xddnc)KUU"\xd1\x18\x1f' '\xa5d\xd2\n\xa8J\x96xF\x03\xf4\xc8[[\x00X\xbf~=N\xa7\x93\x8e\x8e\x0e\xb2\xd9,\xd9l\x96x' 'MIII\x8e?\x1e\x08\x048p\xe0\x00\x1d\x1d\x1d\xc4b1\x14E' "\xc9\x01\xc0f\xb3\x11\x8f\xc7\xc9d2\x04CaN'\x04\x82\x19\x11EUH+\xc3\x9d\xf9\xe3o\xb4>\xe8" '\xa3\x7f\xea\xd4)\xe2\xf18\x89D\x82D"A2\x99\xe4\x85\x17^\xd0\x83\xa1 \xc3\xe7\x04\'\xa5\x89\xbc\x9a' 'w\x80\x95\xfe\xde\xd3\xde\x81\xde\x1e\x16\xddt+\x92lb0\x91\xc4n\x16\xb1\xd9\xed\xa0*(\xd9\xacq\x8a' '\xa3\xaa\xaa*\xc7\x1d\xfd\xe8\xa3\x8f\xf0\xfb\xfd\xa4R)JKK\xb1Z\x87g\x12\xbf\xdf\x8f\xc9d\xa2\xbc\xbc' '\x9ch*\xcbGi\x99hVc\xde"BJ\x01EU\xd9\xfa\xbb\xdf\xb0\xe7\xcf\x7fd\xde\xbcyl\xdd\xba' "\x95\x8e\x8e\x0e\x0e\x1e\x07\xf76\xc2\xb01s\x0e\xa5\xa5h\xceM\x07Cg~kkksF\xfc\x93\x06' '\x00\xb4M\xd3\xedC\x9da\xf9\x9dw\xb1\xee\x81\x87\x98QY\x99sDFDAI%\x11u_A\x10@' '\x141\x8b\x02\x8a(\xa1\x08b\xce\x11\x99\x81@\x80\x8d\xcf\xfe;/\xfe\xfew\x0c\x86\xb5\x9fB\xd0D\xda\xd0' '\xe7\xda\xda\xdaI;\xf7\x8f\x00@\xa7o\xa29\x1aN\x80\x1b\xbe\xf8%\xee\xf8\xda\xdd\xdc\xba|\xc5y\x1f\x92' 'zi\xf3&^\xde\xf2"\x91\xb01\x98\x8f\x01?\x1b\xfd\xc1O\x1b\x00\xa0I\xc3z\x86\xc5\x14\x80\xba%\xd7' '\x18G\xe5f\xce\x9c\xa9\x9d\r\x12\x04\xb2\xaa\xa6\x0e\xf5\xf5\xf5\x1cn9\xc4\x91\xc3-\xc61\xb9!\xda\x88\xc6' "|\xde)\xec\xd3\x08\xc0H\xfa&\xb0\x92<\xc7\xea'\xa1z4\x95\xdaX[[\x1b\x9a\x88\x81O;\x00F" '\x07\xdf{\xef\xbdyhGk*\xd1\xc0\xd0\x03\xf7 \x9an\xeb\xd7C\xa3\x8d\xd8\xd0\xfb\x13\xb6?\x11]\x08' '\x00\x1f\xfb\x7f\x83\xb5\xb5\xb5-\x9c\xa732\xea\xfd\x0bb\xe4B\xe9S\xf9\xf3\xf4TF\xfb\xe3\xa2O%\x00' '\xffH\t\xf8?\xd3Z0sp\x96\xdaJ\x00\x00\x00\x00IEND\xaeB`\x82' ) images_by_filename["toolbar_images/include.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq' '\xde\x00\x00\x00\x06bKGD\x00\x83\x00\x84\x00\x84\x03\xec\x85X\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00' '\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd6\x02\x05\x12\x07\x1b\x9f+\xde\xf5\x00\x00\x0c$I' 'DATx\xda\xed\x9b{lT\xd7\x9d\xc7?\xf71\x0f\xcf\xc3\x1e\x8f\x19\xdb\x19{\xc0\xc1^H0)\x10' ' \xc4&8\xee\x06m\x17\x91T[5\x1bh\x826\x06\tEy(u7J\x938\xab&-\xd2\xaeU' '\xd5y\xa1T\x96B\x02\xb4\x81(\x0fE\xbb\x1bE\x8d\xd2*\xdd\xb0\x80\t\x81\xe0\xb8\x06\xc7@\x0c\xd8\x8c!' '\x0e~\xdc;x\x1e\x9e\xb9s\xfb\xc7\xb5\xc7\xe3\x17\x8c\x1fc\xd2]\xae|u\xcf\xb9\xf7\xe8\xdc\xfb\xfd\x9e\xef' '\xef\xfc~\xbf3\xc7\x02\xb3t8\x1c\x0e\xea\xeb\xeb\xc9\xca\xca"\x14\n\x11\x0e\x87\xf9\xe4\x93O\xd8\xb5k\x17' 'YN\xa8{\x122\xcd\xb0u\x1b(\xfd\xf0\xfe\xfb\xefc2\x99\xb8\xff\xfe\xfb\t\x04\x02i\xfb.i\xb6\x08' "\x18\x18\x18\xa0\xb1\xb1\x91\xd5\xabW'\xea\x99\x99\x99D.\xb7\xb2\xe77\n\xffp\x1b,\xf0@\x9e\x1b>8" "\x00===\xbc\xfc\xf2\xcbi\x05?k\x04,_\xbe\x1c\xb7\xdb\xcd\xc9\x93'\xe9\xe8\xe8\xe0\xb6\xdbn\xa3\xbb" '\xbb\x9b\x90r\x88W~~\x84\x02\xbb\x86\x1e4\xda~q\x1c><\x04mmm\x0c\x0c\x0c\xf07\x7f\xdcw' '\xdf}\x1c\x10%\xe88\r\x0b\xf2\xa1>M' '$\x08\xb3\x01^\x96eZZZx\xe6\x99g\x00Xr3|\xd9\x02\x99v\xf8\xe2w\xe06\x83=\x0b:' "\xce\xc1\xd1C \x88\xb0d9\\\xfa\xc6\xb8'J\xb0h\x19\xfc\xfb^\xd8\xfb\xa7\xef\x00\x01v\xbb\x9d\x8a\x8a" '\n\xd6\xae]\xcb\x9c9s\xb0Z\xadH\x92\xc4\xaaU\xab\xc6\x05?t655Q[[KOO\x0f\x00' '\xa5E\xd0\xb0\x072\xcc\xa0G\xe0\xc0\x9f\xe1\xd2\xb7\x83\xd2\x94`\xd9\n8s\n\xba\xbb\xc1b\x83[W\xc0' 'O~\t\xfb\x9b\xafa\x1cP^^\xce\xf6\xed\xdb\xa9\xac\xac\xc4\xedvc6\x9b\xb1X,X,\x16zz' 'z\x88D"\xa8\xaa:\x06\xbc\xd3\xe9\xa4\xac\xac\x8c\x9c\x9c\x1cb\xb1\x18g\xce\x9c\xe1\xe1Mp\xe7\xedp\xb6' '\x19\xbe\xbd\x009\x1e\xe8\xfa\xc6P\x00\x18\xc0\xbf\xb7\x04\x02\n\x84\x83\xd0\xa3\xc0\xe6\x7f\x82?\x1f\x83\xae\xbek' '0\x07l\xda\xb4\x89m\xdb\xb6\xa1i\x1a\x8a\xa2000\x80(\x8aH\x92\x84,\xcbH\x92\x84\xa6i\xe3\x82' '_\xb8p!\x92$\xe1\xf7\xfb\x13\x1eA\x07\x02\x01hi\x86S\xa7@\xed\x05\xdf\x9f\x05\x02\xd6\xae]Kee%\xe1p\x18' 'Q\x14\t\x06\x83\xa8\xaa\x8a\xcb\xe5\x1a\x01>\x99\x04\xb7\xdb\x8d\xc3\xe1 \x16\x8bq\xf4\xe8\xd1\t\xb3\xba\xd63' '\x10\xe8\x87M\x1b\xe1b;\xc4\xe3\xd0\xd3\x07\x0b\xff\x0e\xba{\x8diZ\x14@U\xc1\xe3\x05\xb3\x08\xfd!\xc3' '\\\xd6\xdc\x01\x17:\xa1\xf9\\\x9a\tx\xf8\xe1\x87\xf1x\xfblJ\x9d\xed\xdb\xb7\x8f\x8a\x8a\x8a\x84\x87\x10E\x11A\x10hhh\x18\xd1\xee\xe8' '\x7f\xbf\x91R\x7f\xae\x1b\xef$p)\x00\xe0*\xc8\x87\xa8\x1f\xc2Q\xe3Y\xc7)X\xb8\x0c\n\xc2\x10\x14@' 'P\xa6f\x023\x9a\x0c\xf9\xfd~L&\xd3\x08\xf0o\xbf\xfd\xf6\x94\xfb\x1b\x14\x07\x00\xdf\x0e\xc0\xdc<0\x99' '\x06\x03%\x19zz\xc0\x95\x03\x1e\t~\xbd\x0b\x1aZ\xaf!\x01\xa5\xa5\xa5\xbc\xf4\xd2K\x08\x82\x90\x00\xaf(' '\nuuu\xd3\xe8u\x98\x01\xb5\x1b"v\x98\x9b\x03\xa6\xc1 )g\x0e\xf4\\\x82\xea\x1dP\xf7_\xd7p' 'A\xa4\xb4\xb4\x94\xf7\xde{\x0f\xa7\xd3\x89 \x08\x89I\xef\xb9\xe7\x9e\xe3\xfc\xf9\xf3\xd3P\xc00\x01\xa2\x04\x17' ';\xa0\xf8f(\x8c\x81d\x81\xbe\x13\xf0\xe3Z8\xdeq\r\xd7\x03\xc6\x03\xaf(\n\xd5\xd5\xd5\xbc\xf3\xce;' '\xd3\xea;\xc9\x02\xc8+\x18\x0c\x85U\xb0:\xe1x+\xac\xfd\xc5\xf4\xc0O[\x01\x8b\x16-\xe2\xddw\xdfM' '\x80WU\x95\x8f>\xfa\x88\xba\xba\xbai\x8d\xfcx\x93\x80,\x81\xd7\r\xa6\x10|\xb0\xdf\x90\xbd\x1a\xba\xc6k' '\x82\x8a\xa2\xb0e\xcb\x96\x84\xe4G\xcf\xf6\xd3\xc6\x9f\xa4\x81\xf5?\x85\xf5w\xc0\x993\xf0\xf6\xa7\xdf\x91EQ' '\xbf\xdf\x7f\xc5\x10w\x06\x05\xc0_N\x1b\xe7wrU\xd8\xeb\xf5f\x01Kg\x9c\x80a\x05,\xf5z\xbdW' 'k\xde\xd7\xd9\xd9\xf9\xe5\xac\x13\xe0\xf5z\x97\x00\xff\x03\xb8f^\x01\t\x02^J\xf1[\x1a\x81\xca\xce\xce\xc1' '\xdca\x96\x14\xf0<\xe0\xf2\xcc\xcb\xc5b\xb3\x0c\x8e\x99\x9e\x90\xaf\x8e\xce\xe0\x1fC\x05%\xc5\xf4\xd5\xe9\xcd"' '\xae\xeb\xa0\xebI\xfd\x0e\x96\x07\xafC\xf5\xa8\x12&\xaaD\x96\x0e*\xf1\xd3\xd9$\xc0\x05p\xf9\xe2A.\xcf' '\xb0\x02\xba\x0e\x7f\x94r\xdb\xcc\x15\x95t\x1f\x98\xbc\xe7\x11\xf9?r\xc4\xf5\xf8\xb5\t\x84\xbe3\x04\xc4\xff\xbf' '\x13\x90\xec3\xaf\x9b\xc0u\x02\xae\xcf\x01\xd7\xe7\x80\xeb\n\x98\x81@\xa8\xb0\xb0\x10\x9f\xcf7\xe6\xfeP\xf6\x07' '$\xb6\xbf\\s\x02\x02\xd1\x99\'`\xc3\x86\r<\xf1\xc4\x13#\x00\x8f&\xe0\xd6[o\xdd\x05T\x86"\xa9' '\xefn\xca\xb0\xa4\x96\xd6\xe9\x9e\xa5\x84\xcf\xa7\x18_~m$D\xc0\xd9\x19#`4\xd8\xf1\xca\xc7\x8e\x1d\xdb' '\xbdl\xd92\x80\xa2\x14\xf3\x86\xa9$C\xbbR\x04\xf6\x9f\x9d\x9d\x9d\xe7f\x94\x80+\x910Toll\xdc' '=\xd1\xb3\xe4\xf2-\xb7\xdc29\x02\x86\x8b\xbbZZZ>\xbdR\xff\xeb\xd6\xadc\xff\xfe\xfd3k\x02\xc9' '\x0b\x9cW#!\x95g\xd3H\x87\xaf\xfa\x8e\xa9\xbeG\x9e\xee\xe8O\xe5\xd9\x14\x16Df\x9c\xdcI\xb9\xc1\xd1' '\x0c\x8fVF*\xcf:::\xa6\xa5\x80\xc5\x8b\x17\xf3\xea\xab\xafN\xf8\xce\xb4\x100\x13\xc0\x01\xea\xeb\xebY' '\xb3f\xcd\xb4\x08hoo\xe7\xe9\xa7\x9f\xa6\xb4\xb4\x94\xa6\xa6\xa6iK\x7f\xca\n\x98\x0cpUUy\xf4\xd1' "G\xa9\xa9\xa9Il\x93\x9d\xaa\tl\xd8\xb0\x01\xbb\xddN{{;\xe5\xe5\xe5\xec\xd9\xb3'\xfd\nH\xee|" '\xb2jPU\x95\xbb\xef\xbe\x9b\xb7\xdez\x0b0~b\x9f\x8e\x02\x1ex\xe0\x01v\xee\xdcIYY\x19\x00\x0f' '=\xf4\x10o\xbe\xf9f\xfa\x150U3\xa8\xa9\xa9\xa1\xb9\xd9\xd8\xd0\x97\x9f\x9fOuu\xf5\x14\x08\x18.\xfb' '|>\xdcn7\xdb\xb6m\xe3\xa9\xa7\x9e\x9a1\x12\xc4TF\xffJ\xc0\x87\xca\xc9\xf5\xfa\xfaz\xf6\xee\xdd\x9b' "\x00_UU\x85\xddn\x9f\xfc\xd7E\x87\xe3\xfbV\xd5A\xc9\xc2E\xd8l6\xd6\xaf_\x9f\xf8\xc9\xfe\xc9'" '\x9f\xa4\xa9\xa9i\xf6\xdd\xe0D.IQ\x14jkk\x13\xb2\xaf\xaa\xaa\xc2\xe9tR8o>\x83\xd1\\Q' '\xaaa\xb3\x10I\x84\xb7\x8d\x9f|\xa5p\xac\xbd\x9fu\x8b\xe6\x90!\xf5r\xcf=\xf7\xd0\xd5\xd5\xc5k\xaf\xbd' "\xc6\xd6\xad[q\xb9\\\xe9!`\xb2\xfe\xbe\xb6\xb6\x16E1\x96\xe57n\xdc\x88\xddn'\xcf\xeb\xa3)<" '\x0f\xa0r0\x1c.J\xf1\xfb\xfa\x80\xe7\x9f\xde{T\xf9\xfa\\\x17\xbd\xc1({\x8f\xa8T\xcc\x15)\xc9\xd6' 'x\xe4\x91G8v\xec\x18G\x8e\x1c\x99\xb2\tL)\x12\xbc\x12)C\xd2/**\xa2\xb8\xb8\x98\xccl\x0f' "'b\xc5\\\x8e\x9b\xf9\xe1\xf3\x1f\x9c+\xf1fm.*.\xc0\x921<)\xca\x028Mq\xd4@\x98o" '\xc2q$\x01L\xba\x9e\xd8\x8f(\x88"y\x1e\x17_\x9d\xee\xc4\x7f\xb1\x97}\xed\x02\x97\xfb#\xdc^b\xe5' '\xb1\xc7\x1e\xa3\xaa\xaa*=\x04L6\xc2\xfb\xf0\xc3\x0f\x13\xa3\x7f\xd7]wa\xb1X8k^L\x7f\xdc\x82' 'I\x12(t\xdb\xb9\xb1\xa4\x10\xb3\xd5B^\x06\xe4\xdb\x8c\xd3$\x1a\xd3Q$\xcf\xc6[\x17@\x8d\x1b\xfb\xf7' "\xcc\xf18\xd6\xa8\x869\x1e\xc7$I,)\x9dKV\xa6\x8d\x13'\xfd|\xd1m\xc7j\x8dPVV\xc6\xaa" 'U\xab\xf8\xec\xb3\xcf\xd2\xbb \x92\xca\xec?\x94\x8c\xb8\\.\x8a\x8b\x8b\xe9\xb5\xce\xa7_\xb7"\x00\xae\x0c3' '\xee\x9c,|\xd9\x16\xfe\xbe\x00V\xe6\x82\xcf1\x04\xde8,"x\xcc\xc3uM\x12\tg\x98\xe8\xb7[\x88' '\x98%t\x04n\x9c\x97K\xc1\rn\x00>\xef\x14\xd0\x11\xb9\xf7\xde{\xd3\x1b\t\xa6\x1a\x0b\x0cEh\xf3\xe7' '\xcfG\x96e\x14S\x9e\x01L\x16\x91M2\xcb\x8b3Y\x99\x0b\xb6+\xe8\xce"\x0eo^\x14\x92N\xcd$' '\x12\xce\x10\x89\x8b\x02\x0b\x8ao\xc0l\x96\x89\xea"\xfe\xbe\x18+W\xaeL\x9f\t\x8c\x96\xba\xdf\xef\xa7\xb5u' '\xfc\xddH\xbd\xbd\xc6f\xbe\x9c\x9c\x1c\xe2\xb2\x8d\x98\x98\x01\xba\x8e\xd9$!\xdc<\x8f\xa8#\xe3\xaa\xef\xf2G' '\x86\xc1\x8f\xf3!D\xcd \xc5$\xf2\xad\xff/\x9e\x92\x1bL\xc5#\xc8\xb2L\x8e\x14\x18\x06.\x18\xf2' '\x96\x84\xb1\x84\x08Im\x84\xa4\xebDd\x0c\x9d\x99f\x81\xa2\\\xc7\x94\x16[\xa6\xe4\x06S!"!QI' '\xc2g\xe9c \x10\x1c\x01v\xc4)\x18\xc0\xc4d\x80\x13]G\xbb\xc5H\x94\\q\x80\x0c\x8b)\xe1~\xa7' 'r\xfc\x15\xf3\xfd\xa6K,\xa2\x00"\x00\x00\x00\x00IEND\xaeB`\x82' ) images_by_filename["toolbar_images/info.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq' '\xde\x00\x00\x00\x06bKGD\x00\x1b\x00j\x00\x1b!V\x9a\xf6\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00' '\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd6\x02\x0b\r\x16,\x83\xea\xcb\x94\x00\x00\x07#I' 'DATx\xda\xed[Ml\x13G\x14\xfe6k\x83cG\xb6Ylg\xed\xd8\xe6\xc7\x04\x08\x8d\x80\x86Z' '=4r\x16\xa8P\xab\x80\xa0p(\x07$RU\xbd\xf4\xd0K\xa5\xfe\x1c\xaaB\xcf=\xf4VU\xadT*' '\xf5\x10\x95\x03\xa8\x10\x15\xa1\x92n\xd2\x1c\xaa\xd2"\x85\x82\x83@\x08\'v\xec\x8dcl\'\xe0\x9f\xc6v\xdc' '\xc3\xae[\xc0^gm\xef\xd8\x14\xf2\xa4\x95-\x8f53\xdf7\xef\xbdyo\xe6-\xb0*\xab\xf2\\\x0b\xd5' '\x8418\xe9\xa9Gx\xe9\xf9\xdf\x11\xf0(\xe8\x01\x8a\xd2j\x8c\xc6\xadS\x06\x83\xcb\xde\xde\xde\xe9\xd0j\xcd' '6\x8dF\xbf\xae\xadM\xd3\x0e\x00\xcb\xcb\xf9L>\x9fN\xe4r\xc9h&3\x17N\xa5\x82\x91\xc5\xc5\xdb=' '\xc5b.\x0f`\x8c$\x19\x14)\xd04\xad\xd7\xd9\xed\xfb\x17\xcc\xe6\xde~\x87\xe3\x80\x81ekS\x02A\xe0' '\x11\x0e_N%\x937&"\x91+\xa6B!\x9d%A\x06\xa5"\xf8O\x01\x8a\xb2X\xbcA\x9b\xcd\xf7\x9a' '\xd39h\xa9\x15t52B\xa1\x91X4:~)\x16\xbb\xea\x02\x8aE\x00\xa7\xd5 \x81Ri\xd5\x07t' ':+\xe3\xf3\r\xef`\xd9}\x1a\x926+\x08\xa3\xf9\xf1\xf1\xe3\xfelv>.iDC\xda@7\xba\xea' '4\xad\xdf\xbem\xdb\xbb\x1bw\xee\xfc\xa4\x9be\xf7\xb6\x91\xf6\xa8\x1d\x1d\x9b\xda\xd6\xaf\xf7vj\xb5fM"' '\xf1\x97\xadX\xcc\xf5\x03\x98\x06\x10h\x96\x06\xfc\xbb\xeaz}Wg\x7f\xff\xf7=j\xa9z=\xa611q' 'b*\x9d\x9e\x9d\xabW\x1b\xe8:m}\x93\xc7s\xd2\xb8{\xf7g;[\x05^\xd4\x86\x8d`\x98>+\x80' 'x"q\xdd\x01`\xa0Vm\xa0j\x05OQ4\xcdq\xe7\xd6\xbb\\\x87v\xbc\x17O$&\xedJ\xb4\xa0M\x89\xea\xeb\xf5]\x9d\x1e\xcfP\x1f\t\xd5=z\x140\x1a+\xb7' '\x1dY\x96C\x7f\xff\xf7=4\xad\xd75B\x00\x07`\xa0\xbb\xfb\x1d'\xc9\xad\xce`\x90oK\xa7\xeb" '\xef\x97e9tw\xbf\xe3\x94\xb6F\xaeV\x028\x00\x9f\xeatV\xc6\xed>\xe2$\x1b\xcc\xc8\xb7MM5' '\xd6\xb7\xdb}\xc4\xa9\xd3Y\x99j\xa6P\x85\x00\x8a\x12c{\xb2\x81\xce\x8f?V\xfe\xfd\xef\xbf\x81s\xe7\x1a' '\xeb\x9be9\xf8|\xc3;\x00\x8a\x92#@6q\xb1X\xbcA\xd2\x89\r\x00\xfc\xf6\x1bP(\x00\x83\x83\x80' '\xcb%~\xbfu\x0b8{\x16\x98\x9dm\xbc\x7f\x96\xdd\xa7\xb1X\xbc\xc1X\xecw(%\x80\x030`\xb3\xf9' 'z\x9b\x15\xc4\\\xbd*>\xa4\xc4f\xf3\xbd\x16\x8b\xfd~C\xc2\xc6\xafd\x02\x1cM\xebuN\xe7\xa0\x05\xcf' '\x888\x9d\x83\x16\xb9\x1d\xa1\xa2\x0f\xb0\xdb\xf7/\xb42\xc9Q[X\x96\x83\xdd\xbe\x7fA\x89\x13\xe4\x00\x0c\x98' '\xcd\xbd\xfdx\xc6D\xc2T\xb6%>\xe9\x038\x8a\xd2j\x1c\x8e\x03\x06\xd2\x13\xd2\xe9\xc4\x1c\xc0b\xa9\xfc\xf9' '\xd1G@2\xa9\xdex\x0e\xc7\x01\xc3\xcd\x9b\x9fk\x8a\xc5\xdcc~@S\x9ePl\x9dbYNu\rx' '\xe5\x15`\xf7n\x11\x9c\xd5\ntt\xc8\xff7\x99T\x17|\xc9\x0c\x8c\xc6\xadS\x0b\x0b7\xab\xef\x02\x06\x83' '\xcbNb\xc5\x0f\x1e\x04\x1c\x0ee\xffm4\x00\x92\x8f:]\xf6\x85\x85\x9b\xe1\xaa\x04\xb4\xb7w:H\x0c\xfe' '\xe5\x97b\x96g6\x03\x1b6\x00\xaf\xbe\xda|\x02$l\x7fV%@\xab5\xdbH\x0c>3\xf3xj\xbb' 'k\x97h\n\xcd$\xa0\x12\xb6\xb2mP\xa3\xd1\xaf#\xed\x00i\x1a0\x99*\xb7\xc5\xe3@4Jf\xdcJ' '\xd8\xca\x08(]W\x91\x94\xd2\xe1G3W_\x0e\x1b\xf1s\xfcJ\xf2\xc2\x0b\xcd\xb7\x7f\xc5\xe9\xf0\xf2r>' 'Cz\xd0\xde*Y\xc6\xad[\xe4\xc6\xad\x84\xad\x8c\x80|>\x9d \t\xbe\xbd\x1d\xd8\xbc\xb9r\xdb\xfd\xfb\xc0' '\xfc<\xb9\xb1+a+# \x97KFI\x12\xb0}\xbbx\xf0\xd9\n\xf5\xaf\x84\xadl*\x99\xcc\\\xb8U' '\xeaO\x9a\x80J\xd8\xca\x08H\xa5\x82\x91V9@\x92\xf6/\x87\xad\x8c\x80\xc5\xc5\xdb=\x82\xc0\x13\x99\x00\xc3' '\x00v\x99@{~^\xbc\x07 %\x82\xc0cq\xf1v\xcfJ\x04\xf0\xc5b.\x1f\x0e_N=k\xdb_' '8|9%\x95\xdcT=\x11\xe2\x01\x8c%\x937&\x9e5\x02$LcPp$\x86H\xe4\x8aIm3' '\xa0\xa8\xd6\xd9\xbf \xf0\x88D\xae\x98\x14\x05B\x00\xf8B!\x9d\r\x85FT\xb5H\xa7S\xfe\nlnN' '\xcc\x01J\xe2\xf5\x02o\xbf]\xfd\xd2\xa4\x16\t\x85FbR\x91\x15\xaf\x88\x00\x00c\xd1\xe8\xf8\xa5Vx\x7f' '\xad\x16x\xf3M\xe0\xe5\x97\xe5\xe3\x85ZE\xc22\xa6\x94\x00\x00@,v\xd5%\x08\xa3\xf9f\x87\xbf\x87\x0f' '\x8bi\xf2\xa5K\xc0\x83\x07j\xa8\xffh^\xac,S\x98\x0b\xfc\xa7\x05\xc5\xe2\xf8\xf8q\xbf\x1a\xbe\x80\xa2\x80' '-U\xaa\n\xa6\xa7\xc5\xcf\x9e\x1e\xf1\x82$\x16\x03.^T\xc7\xf6\xc7\xc7\x8f\xfb\xa5\xb2:\xbeF\x02p:' '\x9b\x9d\x8f\xcf\xcc\x9c\x0f5:\x11\xabU\xcc\x01\xe4\xc4l\x16M\xe4\xbd\xf7D\xb2\xbe\xf9\x06XZR\xe3\x10' '\xe6|H*\xa7\x93-\x99\xa9v\xf5\xc5\x03\xe0\xee\xdc\xf9Z\xe7v\x1fi\xe8\x86X\xa7\xab\xde\xfe\xc1\x07\xff' '}\xff\xe1\x07u\xb6DA\xe0q\xe7\xce\xd7!9\xdbWz\x1e\xc0\x17\n\xe9\xec\xc4\xc4\x89\xa9FL!\xa1' '0\xbf\xbcx\x11\x18\x19Q\x07\xfc\xc4\xc4\x89)9\xcf\xff\xd8\xe9\xd4\n}\x05\x00L\xe7r\x0f^\x02\x10w' '\xbb\x8f\xd4ub\xbc\xb4\x04tu\x89O%I\xa7\x81o\xbf\x15\x1d\x9f\x1a29y\xeaZ4\xfakU\xd5' 'WJ@\x89\x84M\x89\xc4u\x07\xc3\xf4\xa5M\xa6m\xd6z&u\xfd:\xa0\xd1\x00\xeb\xd6\x01k\xd7\x8a\xa4' 'LO\x03\xa3\xa3\xc0W_\x01w\xef\xaa\x03>\x18\xbc\xe0\xbfv\xed\xe3\x84\xa4\xfagVt\xd0\n\xfb\xe5\xf0' 'H\x99\\W\xd7\xebn<\x852;\xfb\xd3\xcc\xe8\xe8\xa1\xe9Zj\x05\x95\x96\xc9\x05\x00L\x03E_ 0' "\xbc\xc80/\xa6\xea\xd5\x04R\x12\x0c^\xf0\xff\xf2\xcb\xe1\x08\xa9B\xc9'IH?|x/\xbef\x8d\xd9" '\xde\xd1\xb1\xb1\xa5\xc0\x05\x81\xc7\xe4\xe4\xa9k\xa2\xda//\xa3\xc62\xfaZk\x85%\x12\x80Db\xd2\x1e\x89' '\xfc\x9ca\x98>k\xabH(y{\xc9\xe1\x8d\x01\xf8\x0e\x84\x8b\xa5K$\xf0\xa5\xdd!\x108+d\xb3\xf3' '\xa0i\x9d\xb1YD\x08\x02\x0f\xbf\xff\x8b\xd0\x1f\x7f\xbc\xef_Z\x8a/H\xab~\x06u\x94\xcc?\xf7/L' '\xac\xbe2\xa3\xe2\xe2\x94\xb4\x01x\x0e_\x9a\xaaJ\xc6\xf3\xf2\xda\x9c\x122j\x15"\xa0WeUV\xe5_' '\xf9\x07+;\x05!\xc0\x1a@\xe3\x00\x00\x00\x00IEND\xaeB`\x82' ) images_by_filename["toolbar_images/lock.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz' '\xf4\x00\x00\x00\x06bKGD\x00\xff\x00\xf8\x00Ki\xfe\xc6\x83\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00' '\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd6\x02\x10\x0b\x13,}\x08\x93\xfd\x00\x00\x03\x91I' 'DATX\xc3\xedVMl\x1bE\x14\xfefw\xe3\xd8.q\x1cL\xfe\xdc\xd0\xa4H5v%\x1a*\\' '\x01\x91h.HET\xa8j\x0f\x9cr\xe3\xc0\x01U\x95Z$\xaa^\x90z\x80\x13\x7f\x17\xc2\xcf\x81CU' '\xa4V\x1c8\xa0\x08\xa4F\x88H\x10\xa8\x9b&\x08h\xd3\xd2\x98&\x90\xd8N\x9a\xd8\xeb\xf5\xae\xf7o\xe6q' 'X\x07[\x11jWN8\xe1O\x1a\xadf\xe6\xcd\xf7\xbe}\xef\xcd\xd3\x00-\xb4\xf0\x7f\x07k\xe6\xd0\xf8\xf8' "W\xfd\x86\xa1\xbf*IxN\x92\xe4\xa0m[\xd74\xad\xf4\xe9\xf9\xf3\xaf\xfd\xfa\x9f\n8{\xf6\xc3'\x06" '\x06z\xdfN&\xf7\x1c\x1d\x1c\xec\xb3\xbb\xbb\xa3\x9c1Fkk%%\x9b]\t\xdc\xb8\x91\xbd:==\xfd' '\xce\xa5K\x1f\\\xf6\xcb)\xfb5\xf0\xe3[\xa9/\xa3\xbb{\x8f\xf5?\xf988\x84\xa7' 'X\x91A$A\x10 D\x9d\x81\x01PdA\x9c\x0b\x06\x06\xc8 \x14~\xb9\x8d\x95\xdf\xf3S\xa3o\xde\x1a' 'm*\x02\xeb*\x1d;\xf0\xca\x08B]\xf3\x80\xb0\x01\x90\xbf\xc6\xc6\xbcO\x9b\x00\xfe\xf8M:\xdct\nr' "\xf7\x08\\\xbd\x03\xf0E\xbf\xf5Z\xc7R\x11|^Ea\xbd\x8b\x9a\x16P\xd8 \xb8\xba\x0b\x90\x05\x10\xab'" "\xec\xdf(Ym\xdfr\x81\xbc\x06\x14*\xe0\x16\xc3\xea\x065_\x84E\x8d \x0c\x07\xe0\xb6'\xe0~\x95C" '\x00\x8a\x06P\xaa\x02\x16\x07\\\x01n3\x944\xd1\xbc\x00"\x06\x18\x0e`\x99\xff\xe4\x15\x00\xc0\xc9\xf3X\xbb' '\x02p\x04P\xb5\x00K\x00\xbca\xd8\x12\x88\xb6q\r\x19\x03`\xd8@P\x00\x8a\xe49vk\xe4\xae\xf0\xe6' '\x9c{\x02Dm\xafq\xdfG+x`\x1f`\x12\x03d\xe69\xe1[\x1c\xb85\x01\x8ds\xb7\x1e\x01\xe6l' "S\x00\x11a\xa5\x18@\x98d0\xc7\xac\xfdq\xa3cj\x88\xc4\xe6\xba'\x86\xb8\xc0\xaa\x1a\x80+\x18kZ" '\x80\xe9\xf0\xd7?\xf9,{rhw8\xa2\x10\x18\x045\xdc\x82Z\rl&\x99\xc8[\xaf\xcd\xb9`\xb8\xab' '\xb6\x1bk\x15\xfb\xf2\xb6^D\xcf\xef\x8b\xed\xdb\xd0\x10uI\x91i\xf3\x08k8\xc9\x1a\xa8X\xbd-J\x0c' 'hc\x96umi}\xb6\xf5\xf2m\xa1\x85\xfb\xe1o\\.\xdb\xd0\x87m\xd2\x10\x00\x00\x00\x00IEND' '\xaeB`\x82' ) images_by_filename["toolbar_images/open.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq' '\xde\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\r\x88IDATx\x9c\xed\x9akLT' '\xe7\x9f\xc7?s\x05F`@\xac\xdc\x04\x072(x\x81\x81\x1aPjt\xd4j\xad\xb5-K\x83\x12\xd3x' 'M\x1acb\xdaM\x9b\xb4/\x9af\xdb4\xa9\xfb\xa2imb\xfa\xb7/\xbanj\xcd\xee\x9b\xf6\xbfI\xaf' '\xd6\xd8ic\x83\x91\x06GH*\xadu`*\x82\x16\xb13\x9c\xb90\xcc\x9c\x99}q\xe6\x1c\xe7\xc6\xc0\x80' 'e_,\xbf\xe4\xc99\x9c\xe1<\xe7\xf7\xfb>\xbf\xfb\xf3\xc0\x02-\xd0\x02-\xd0\x02-\xd0\x02\xfd\x7f%\xd5' '<|\xc3\x08\x1c\x04L\x80%\xc5\xef.\xc0\x0e\x9c\x01\x9c\xf3\xc0\xcf\xbc\xd3\xf7@d\x06\xe3/\xa0a\xbe\x99' '\x9b\x0f\r\x88\x14\x15\x15\xb1g\xcf\x1erssY\xbf~=F\xa3\x91\xec\xecl\xfa\xfa\xfa\xb8s\xe7\x0e?' '\xfd\xf4\x13\x17.\\\x00I\x1bL\x80{\x1e\xf8\x02@;\x1f\x1f)--e\xc3\x86\r\x08\x82\x80\xdf\xefG' '\xa3\xd1\x10\x0c\x06Y\xb6l\x19\x05\x05\x05\x94\x97\x97\xe3v\xbb\xe9\xee\xee.\x00l\x80\x95y\x02A=\xc7\xf7' '\x8d\xc0\xe7\xcc@\x85\xf3\xf3\xf3\xc9\xce\xceF\x10\x04\xdcn7.\x97\x0bA\x10\x98\x98\x98`rr\x92\xf6\xf6' 'v\x8a\x8b\x8bA\xf2\x13\xef\xc7\xcc\x7f5:\xff@\xba\xf9gKs\x01\xa0\x01\xc9y\xb5E\x19\x97WoJ' '&\x8b\x8a\x8a\x88D"\x8c\x8e\x8e244\xc4\xad[\xb7p8\x1c\xdc\xbe}\x9b\xfb\xf7\xef\xf3\xdcs\xcfQ' 'TT\x04\x92\xd3|/:\x9f\xa5\xb0\xb0\x10$\xd3\xb0\x01\x9b\xe7\xc0s\x12\xcd\xd6\x04\x1a\x90\x98)\xd8\xb1c' '\x07/\xbe\xf8"\x17/^\xe4\xddw\xdf\x8dU\xe1k\xc0\xf2\xc4\x17\xb3\xb2\xb2\x10\x04\x81\xcd\x9b7\x93\x93\x93' 'C$\x12QF8\x1cf\xcd\x9a5\x1c;v\x0c\xe0%\x80\x96\x96\x16:;;\xf9\xf9\xe7\x9f\xf9\xf4\xd3O' '\xe5\xf9\x0f\x02\xff9K\xde\xe3h6NP\x11~\xdf\xbe}\xbc\xf0\xc2\x0bD"\x11&\'\'\xf9\xea\xab\xaf' '8y\xf2$<\x08mV\x80\xda\xdaZN\x9c8\x01\xc0\xd8\xd8\x18\x05\x05\x05<\xf9\xe4\x93D"\x11\x00E' 'x\xf9\x1a]q\x1a\x1b\x1b\xd9\xb7o\x1f\x91H\x04Q\x14\x19\x19\x19\xe1\x83\x0f>\x90\xf9h\x03\xfeg\x96r' '+\x94\xa9\t\x18\x89\n\x7f\xe4\xc8\x11\xf6\xef\xdf\xcf\xe4\xe4$\x93\x93\x93\x88\xa2\xc8\x96-[8~\xfc8H' '\xe6`]\xbcx1O?\xfd4\x8f?\xfe8N\xe7\x83\x10\xafR\xa9\x92\xaej\xb5\x1a\x95J\xa5<[\xb3' 'f\rO=\xf5\x14^\xaf\x17\xbf\xdf\x8f\xdf\xefg\xc9\x92%\xbc\xf6\xdak\xf24gx\x08>A\x93\xe1\xff' '_\x06LV\xab\x95\xe7\x9f\x7f^Y1Q\x14\t\x85B\x84B!4\x1a\r\xa1P\x88m\xdb\xb6q\xe0\xc0' '\x01\xca\xcb\xcb\xd1j\xb5\xdc\xbbw\x0f\xadV\x8bN\xa7#++\x8b\x9a\x9a\x9a8\x10dR\xa9T|\xf7\xdd' 'wl\xde\xbc\x99p8L8\x1c&\x14\n\x11\x0c\x06\xf1\xfb\xfd\xa8\xd5j\x0c\x06\x03\x0e\x87#\x1b\xe8\x04\xbe' '\x01\xee\xce\x07\x00\x07\x80\xa3\x15\x15\x15\xbc\xf2\xca+I\xc2\x8b\xa2H__\x1f\xeb\xd6\xad\xa3\xa3\xa3\x03\x87\xc3' 'A~~>\xa2(\x12\x0c\x06\t\x06\x83\x0c\r\r\xb1h\xd1"\xf2\xf3\xf31\x9b\xcdI\x9a \xd3\xd8\xd8\x18' '~\xbf\x9fP(\x14\xf7\x8d@ @kk+\xdb\xb7o\xc7\xe9t\xe2t:e\x10\xfe\x01\x04f\x03@&' '&P\x00\xb0x\xf1b\x02\x81\x00\x13\x13\x13\xca\xd5\xeb\xf5\xd2\xdf\xdf\xcf\xde\xbd{ijj\xa2\xb8\xb8\x98=' '{\xf6000@0\x18T\x9c\x1c@__\x9f\x02^\xac\xf0\xb2\xfa\xabT*\xf4z=\xabV\xadbr' 'r\x92@ \xa0|g\xfb\xf6\xed\xac]\xbb\x96\xa5K\x97r\xe2\xc4\t\xd6\xaf_/\xf3eC2\xcf\x8c)' "\x13\r\xe8\x07v\xde\xbd{\xb7\xc4\xeb\xf5RWW\x87(\x8a\x84\xc3a\x9cN'{\xf7\xee\xa5\xa4\xa4DY" '\xb1\x9c\x9c\x1c***\xb8|\xf92\x00^\xaf\x97\xa2\xa2"\xacV+\x16\x8b\x05\x83\xc1\x10\x07@,\xe5\xe4' '\xe4\xe0r\xb9\xb8{\xf7\xae\xa2\x05\xcf<\xf3\x0c\xf5\xf5\xf5\x8a\xb3T\xab\xd5l\xd9\xb2\x05\xbb\xdd\xce\xc8\xc8H' '\tP\x0b\xfc\xf7\xdf\t@\x00\xc9\x07t\x0e\x0c\x0cd\x17\x15\x15QQQ\x81\xc3\xe1`\xff\xfe\xfd\x94\x95\x95' ')6+\x83\x90\x9d\x9dMEE\x05\xa3\xa3\xa3\xec\xd8\xb1\x83\xed\xdb\xb7SYY\x89\xc1`\x88[q\xb5Z' '\x1d\x07\xc6\xd2\xa5K\xa9\xab\xabc\xc3\x86\rh4\x1a,\x16\x0b\x16\x8b%.R\xc8 l\xdc\xb8\x91\xae\xae' '.\xee\xdf\xbf_\x1b\xe5\xf3\x87L\x00\x98m\x18\xb4\x03\x1c>|\x98\x8e\x8e\x0e6m\xda\x84(\x8aq\xceP' '\xb6\xfbP(\x04\xa08@\xf9\xaa\xd1h\xd0j\xb5h4\x9a8-\x88\xcd\td \x13\xe7\x9d\x9c\x9cT\xe6' '\x0f\x06\x83\xdc\xbau\x8bC\x87\x0e\xe1\xf1x@J\x98f\\U\xce&\x13\xbc\x86\x94\x88p\xf6\xecY\xccf' '3\x10o\xc3\xf2\xaa\xaa\xd5\xea8\x01c\xe3~\xac\xb0\xa9(q\xbet#//\x8f\xdc\xdc\\\xf9US&' '\xc2\xcc\xa9\x16\xa8\xad\xad\xe5\xc3\x0f?dxx8\xa5\xe0Z\xad6n\xc5eU\xcf\x94\xd2\t\xef\xf5z9' '~\xfc8w\xee\xdc\x01I332\x81\xe9Ra#\xf0o$72L\x00\xdb\xb6m\xc3h4r\xea\xd4' ')\x8e\x1d;Fii)\x91H\x04\x8d\xe6\x81kQ\xab\xd5\x84\xc3\xe18pb\x05\x98\x8eR\xe5\t\xf2\xf0' 'x<\x1c=z\x94\x1b7n\xc8?\xbf4\xed\x84\t4\x9d\x13\xfc/\x1etsbG\xc1\x92%K\x94L' 'P\x10\x04\xba\xba\xbaX\xbdz5\xf9\xf9\xf9I\xcc\xca\x82\xcb\x9a\x11\xfb\xb7\x0cH*\x8a\xad\x13\x12}\x83\xcb' '\xe5\xe2\xf0\xe1\xc3\xfc\xf6\xdbo\xb1\xaf\xb4\x01w\x90\xcctF4\x9d\x06X\x01N\x9d:\xa5$"rl\xd6' 'h4\xb8\\.\xfc~?\xa2(r\xef\xde=\xbe\xf9\xe6\x1b\x8e\x1c9\xa2\x08/3+\xdby" }}' '}\x8c\x8f\x8f\xd3\xdb\xdbK}}=\x9b6m\x9a\x92\x91D_q\xfd\xfaujjj\x94\x8crll\x8c' '\xae\xae\xae\x02\xa4\x14\x19fX,M\x07@Auu5\xabV\xadB\xa5R)9\xf9\xc4\xc4\x04~\xbf\x1f' '\x9f\xcf\x87\xdb\xedF\x10\x04\xd4j5V\xab5)\xafW\xab\xd5q\xcc\xabT*\xce\x9d;\xc7;\xef\xbc\xc3' "\x1f\x7f\xfc\x11\xf71\xa3\xd1\xc8\xcb/\xbf\xcc\xb1c\xc7\xd0\xeb\xf5I\x82\xc7\xde777\xd3\xd7\xd7'{~" '\xca\xca\xca\xd0\xeb\xf5\xfc\xf0\xc3\x0fDAp1\x83b)\x9d\t,\x07^*//g\xeb\xd6\xad\xa8T\xaa' '\xa40$\x87\xb8\x81\x81\x01V\xae\\\xc9\xb3\xcf>\xab\x08\x99**\x08\x82\xc0\xe1\xc3\x87y\xef\xbd\xf7p\xbb' '\xdd 9\xad\x7f er\xae@ P\xfb\xfd\xf7\xdfS^^NUU\x15YYYI\x95\xa2\x1c\x16\xe5' 'gN\xa7S1\xa1%K\x96\x90\x9b\x9b+\x17^;\x99A\x8a\x9c\xce-\x9b\x00DQ\xc4\xeb\xf5\xe2\xf5z' '\t\x85B\x8c\x8d\x8d\xf1\xd7_\x7f)+\x1f\x0e\x87)++\xa3\xb5\xb55IMe\xc15\x1a\r\x1a\x8d\x86' '\xa3G\x8f\xf2\xc5\x17_\xc8\x82[\x81F\xe0\xcd\xe8\xf8\x97(\x10twws\xe6\xcc\x19\xfc~\x7fJ?\x00' '\x926TUU)\xf7\xf2X\xb1b\x05MMM \xa5\xc8\x07\xd3\t\x0f3h\x88\x84\xc3a<\x1e\x0f:' "\x9d\x8e@ \x80(\x8a\xb4\xb4\xb4(j._\x97-[\xa60\x93\xcas\x7f\xf2\xc9'\x89\xc2O\xd9\xf33" '\x18\x0c\x8c\x8f\x8f\xf3\xe5\x97_\xd2\xd6\xd6\x96\xd2\x11F"\x11rss\xd9\xbd{w\\A\x16\n\x85\xa8\xac' "\xac\xa4\xa7\xa7\x07$\xa7xr\xb6\x00\x0c\x82\xa4\x01\x1e\x8f\x07\xadV\x8b\xd7\xeb\xa5\xb6\xb6\x96\xe5\xcb\x97'y" 's\xd9\xe9\xc5\n\x1f\x9b\x00\xbd\xfd\xf6\xdb\xf2\xe3\x97\xd2\x08o\x01())abb\x82\x9e\x9e\x1e6m\xda' 'D~~~JM\x88D"\x94\x96\x96*\xe6(_\xe5\x86\n\xd1\x02.\x1d\xa53\x01\'HE\x8c \x08' '\x08\x82\x80\xcf\xe7#///\xce\x0ec=\xfdT\xc3\xe9t\xca\x0e/]\xa2r\x00(\xa8\xab\xab\x8b\xf3\x1b' '\xdd\xdd\xddq\xdfI\xf7=\x19\xec\x18SL\xb5\x11\x13G\xd3\x99\x80kdd\xa4@\xd6\x00\x8f\xc7\xa3\xa8\x99' "\xfc!\x8dF\xa3\xdc'&9\xb29\x0c\x0e\x0e\xca\xf3\xd9\xa6\xf8\x8e\x9cp\xd1\xda\xda\x1a\xf7\xc3\xf0\xf0pZ" '\xe1\xd3\x811\x13\x9a.7\xb5\x01|\xfe\xf9\xe7\x08\x82\xa0\xf8\x80`0\xa8hA\xe2Hd46\x0f@\n' 'M\x89$\xf7\x18M\x8f>\xfa(UUUq\x82\xf8\xfd\xfe$\xef?\x13\xcd\x9b)M\xa7\x01\x07\x01\xdb/' '\xbf\xfcb\x11E\x11\xab\xd5\xaaTe@\\Y\x1a\xab\t\x89!Pn~\xf0\xc0+\xdb\x90\xd4\xd3\x8a\xe4\xa8' 'X\xbdz5\x1d\x1d\x1dJ\x03E\x9e\xdbd2\xa5\x14|\xaa\xf1\xb0\x01pG\x99\x1c\xfc\xf5\xd7_\x0b\x16-' 'Z\xc4\xce\x9d;\x93\x98\x94\x85\x9f*\xc5mmme\xed\xda\xb5\xf4\xf5\xf5\x99\x88\xaa\xbaL999<\xf1' '\xc4\x13<\xf6\xd8c\x04\x83\xc1\xa4\x9e\xc2#\x8f<2\xad\x96\xa5\xd1\x80\xc1\xb9\x02\x10\x0b\x82\xad\xa7\xa7\xa7\xe0' '\xca\x95+\x98L\xa6\xa4\x04%\x1c\x0e\xa7\x8c\x0c\xf28}\xfa4\xe7\xcf\x9f\xa7\xbf\xbf\x1f\xaf\xd7K8\x1c\xc6' 'l6S]]\xadxpY8Y\xd0\xbc\xbcX\xb1b\x05\xabW\xaf\xa6\xb0\xb0\x90\xdd\xbbw\xd3\xd0\xd0\x10\xd7\x03H\x04\xc1' 'f\xb3\xe1r\xb9\xe2\x84Llv\x84B!%\xd5noog\xd9\xb2e\x8a\xf0\x89\xef\xb8\xddnv\xed\xda' '\x95\x8e\xe7\xf7\x81\x7fM\xf7\x0f\x99\xb4\xc4~E\x8a\xab\xeb\x01,\x16\x0b\xd5\xd5\xd5\x04\x02\x01\xa5\xd3SSS' '\x13\xd7\xf0LT\xd1\xd2\xd2R\x86\x87\x87\xf1z\xbdq\x82\xc4\xde\xcb\xa9\xf6\xd6\xad[\xa9\xaa\xaaJ\xebl/' '\\\xb8\xc0\xa5K\x97@Zi{\xf4*\x0f\x1b\xf0\x1a\xd3\xa4\xc2\x99l\x8d\x19\x89V\x87\xcd\xcd\xcd455' ')\xa9\xb0^\xaf\xc7\xe1pp\xff\xfe}\x8cF#\x1a\x8d\x06Q\x14\x93\xb2E\x95JEkk+v\xbb\x1d' '\x87\xc3\x91\xd4\xf2\n\x06\x83dee\xb1c\xc7\x0eJKK\xe3\xda\xe2\x89Z#\x8a"_\x7f\xfd\xb5\xcc\xdb' 'A2l\x84\xcc\x06\x80\x83\x80\xa5\xa1\xa1\x81]\xbbv)\xe9\xb1l\xfb:\x9d\x8e\xae\xae.\xb6m\xdb\xa6\x08' '\x9f\xca\x17\xa8T*,\x16\x0b555\x0c\r\r122B8\x1c\xc6h4b4\x1a\xa9\xac\xacT6' 'C\x12\x85\x8f\x05\xe1\xc6\x8d\x1b\\\xbb\xa6\x94\xfd\xa9\xc2\xebC\x07\xc0\x050::JNN\x0ej\xb5Z\xf1' "\xda*\x95\x8a@ @qq1\xa1P(\xe5\xca'v\x81t:\x1dUUU\x98L&\xe0A\x06'\x0b" '\x9e\xd8\x18M\xbc\x1a\x8dF\x1a\x1b\x1b\xb9z\xf5*H\xe5\xaf\x95Y\x9c)\xc8\xc4\x07\\\x03\xda\x04A(\x19' '\x1f\x1f\xa7\xa9\xa9\t\xbd^\x8fN\xa7cbb\x82\x95+Wb6\x9b\x932\xb4\x99\xc4\xeeT\xf6=\xddU' '\xa7\xd3\xd1\xd8\xd8H\x7f\x7f?\xa3\xa3\xa3%H\xe5\xef\xe9\xbf\x13\x00\x90ZdGo\xdf\xbe\x9d\xbdt\xe9R' 'L&\x13\x82 PQQ\xa1\x84\xc6\xd9\x000\x15(\x89\x19`b\xf8\xd3\xe9t\xac]\xbb\x96+W\xae\xe0' '\xf5zK\x90J\xf8\x8cv\x8c3\x05 \x80\xb4\x19\xd9\xd9\xdb\xdb\x9b\x9d\x9f\x9fO]]\x1d\x95\x95\x95\x00S' '\x02\x90\xd8\xcfKlp\xa4\x8b\xed\xa94#\xf6\xef\xac\xac,\xd6\xad[\x87\xcdf#\x18\x0cZ\x90B\xf5\x8c' '7Kgs@\xe2\x1aRI{\xe6\xb3\xcf>\xa3\xb9\xb9Y\xb1\xdbT\x99`\xac/\x80\xe4\xadq\x19\xb8D' '\x00\xd3iQ"hz\xbd\x1e\x83\xc1\x80\xcf\xe7\xcbX\x98L5 \x16\x84AQ\x14\xdb\xe4\x9a]\xab\xd5\xa6' '\x14@\x10\x04\xbe\xfd\xf6[\xccfs\xd2\xea\xa7\xf2\x03\x89\xcf\xecv;\xe3\xe3\xe3\xcaNs*\x9f\xf0\xf1\xc7' '\x1f\xd3\xdf\xdf\x0fR\xec\xff\xf7\xf9\x00@\x06\xc1\xe4\xf3\xf9,W\xaf^e\xc3\x86\rI \x08\x82\xc0\xab\xaf' '\xbeJoo/z\xbd\x9e@ @vv6\x1a\x8d&\xed\xaa\x8e\x8f\x8fc\xb3\xd98}\xfa4\xe7\xce\x9d' '\xe3\xc7\x1f\x7f\xa4\xa1\xa1!%\x08g\xcf\x9e\xe5\xe2\xc5\x8b %?V2\xdc&\x9f\x0b\x00 9\x1c\xab\xdb' '\xed6\xd9\xedvZZZ\x14\x10<\x1e\x0f\xaf\xbf\xfe:\x83\x83\x83\x14\x16\x16b6\x9b\xf9\xfd\xf7\xdf\xb9~' '\xfd:Z\xad\x96\xc2\xc2\xc2)Ahoo\xe7\xf2\xe5\xcb\xfc\xf9\xe7\x9f\x00\xae`0\x98}\xe9\xd2%\xea\xeb' "\xeb\xe3@8{\xf6,\xe7\xcf\x9f\x07)D\xefd\x16'M\xe7zL\x0e\xa4r\xd6\xeet:y\xeb\xad\xb7" '\xb8y\xf3&n\xb7\x9b7\xdexCi\x84LLL(L\xcbip\xec\xe6i\xe2}\x94\x06\x91V\xb4\x10' "8\xe3\xf3\xf9x\xf3\xcd7\xb9y\xf3&\x82 \xf0\xd1G\x1f\xc5\no%\x83\xcd\x90Xz\x98'E\xff\x03" '8h0\x180\x18\x0c\xdc\xbbw\x0f\xa4\xfc\xdc\x05X\xab\xab\xabY\xb3f\r\xa2(\xb2q\xe3F\xaa\xab\xab' "\xa7t\x84\x9d\x9d\x9d \xd9\xf3\x96T\xf3\xe7\xe4\xe4066\x06\x12Hm\xccRxx\xb8'E\x0f\x01v" '\x9f\xcf\xf7~\xd4\x1b+\xa7\xc4\x00\xbb\xc3\xe10\xc9mkY\x03\x12\xbb\xc7\xd342\x0e\x01\xf8|\xbe\x83\xd1' '\xf9mH\xc2\xcf\xe9D\xe9\xc3>*{\x12)\x0e\x9b\x88/N\xda\x00\xdb\xc0\xc0@A^^\x9e\x92\xeb\xa7' '\xa2\x19\x80 \x9f"\x9d\xf5\xaa\xc7\xd2\xdfqV\xd8I\xb23\xba\x86\xa4\r\xf6\xde\xde^\x96/_Nii' 'i\xca\x97o\xdd\xba5\xdd\xfc\x0fE\xf0\xff+:\xc0\xcc\x8e\xceG\x90l\xfeo\xa7\xb9\x86\xc1L\xe9\x1a\x92' '\xe3*\x88^M\t\xbf\xdb\x91\x0ec\xfd\x93\x1943\x16h\x81\x16h\x81\x16h\x81\xe6F\xff\x0b\xdbmQ' '\xbehc\x91\x9b\x00\x00\x00\x00IEND\xaeB`\x82' ) images_by_filename["toolbar_images/property.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq' '\xde\x00\x00\x00\x06bKGD\x00\x1b\x00j\x00\x1b!V\x9a\xf6\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00' '\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd6\x02\x0b\r\x17.t\xff\x9b\xf9\x00\x00\x08EI' "DATx\xda\xed[Ml\x1bu\x16\xff\xcdL\xc63\xe3\xcf\x99\xc4Nc;P'\xa5v\x9a\x04\x1a\x17" 'VE\xe5b5B\x01\x81z\x00i\xcf\xdc\xdaC/Hp\xa8V\xda\x96\x95\xca\x8dC%@\xa0\xd5\n\xed' '\x81\xcbr\x81riT\n9\xec\x01\x906\x86V\xf9h\x12l\xa7\xdd:\xc1\xf9\x18\xdb\x93\xf1\xd7x<{' 'h\x12\x95\xda3\x1e\x7f6\xab\xe6Is\x997_\xef\xf7\xff\xbd\xf7\x7f\xff\xf7\x7f\x03\x1c\xca\xa1<\xd5Bt' '\xf1]\x91\xdd\xc3\x8c\xcc\xec\x1e\xff\xd7\x00\xec\x1bLq\x14CP\xc4\xab\x94\x8dr\x08aa\x87\xb2Q\xfd\x14' 'K\t\x04Ep\x00\xa0\xa9Z^-\xa8\xa2*\xab)1*\xdaUY\x954U\xbb\xa9\xe6\xd5b\xa7\x01!' ':e4\xc9\x90S\x16\xc1\xe2\xee\x9f\xec\xf7\x12=\x84\r\x00\xf8\t\x1eBX0|\x80\x18\x15\x91\xfe%\r' '\x00\xd0\xca\x9a\x9c\xba\x95Z+\x89\xa5\xcdJ\xb12\xdd\t0\x88\xb6\x1aN B\xbbha`j\xc0G\xb2' '\xa4\xdb\x8c\xc1\xf5d\x0f\x90J\xa1\xb2\xb9>\xbd\x9eT2\x8a\x08m\x1f\x84\x99\x83\x00@\x04\xc0e\x9a\xa7{' '}\xe7|c|\x98\xa7Z5\xda\x10\x8chZM^O\xce)ie\x1b\xc0\x07\xad\x82@\xb4:\xea$C' 'N\xd9\x02\xb6\xd1\xe1\x0b\xc3\xceN\x19^\x0b\x88\xd8g\xb1\xac\x9c\x90\xe7w]\xa3i6P\xad\x8c\xba\xc5m' '9\xed\x7f\xcb\x1f\xf6\xbe\xe9e\xbae<\x00p^\x0e\xdc \xc70nf0\xff \x7fD\xcd\xa9/\x01X' '\x05\x90\xe8\x06\x00\x11\x10\xb8b\rXC\xa1\xf7CC\xde\xd7\xbd\xe0\xbc\\\xd7\xe7o\xce\xcbA\x08\x0b\xb0\x1f' '\xb7{\xa4\xbb\x12\xa3d\x94\xe7w\x01Ht\x12\x80\x08H\xfc\xcd\x11tL;o\x8a\x01\xfc\x04\x0f\xcaF9\xea\x01\x00!,\xec\xb4\xcd\xff\x1d\xad\xf9\xff" '+}\xaf \xe2\xd1M\xe2\xa0j*\xae-_3\xf5-BX\x80\x10\x16v\xea\xc7\x00\x1b\xd5\xdf.\xfa\x8f' '\xb9\x9aO\x80\x82\xf6 .\x8d\\2\xbc\xff\xe3\x95\x8f\xb1\xb4\xb3d\xbe\xfcU\xc3\xb6\x9e\xaa\x8bX\xaam\xa5' '\x1e#\x06\xa4\n\xa9\x9a\x0b \x008\xeb9\x8b\xf7B\xef\x81\xa3j\xd7\x1a+Z\x05\x9f\xfe\xf6)\xbeN~' '\xdd\xd0\xf7\xd4\xb2\xad\xa7F\xa0hK\x85\x93%Y\x0c\xdb\x87\xf5\xf5\x14\x8b\xa0=\x88\x98\x1c\x03\x01\x02\x1e\xc6' '\x83\x93\xfcI\xbc1\xf0\x86!s\xee\xe5\xee\xe1\xa3\xa5\x8fp;s\xbb\xf1Ui\r\xdbz:U\xa7\x0b9' 'B\xa0\x08\xfd\xa5\x84\x93v\xe2\xf3\x17?7\xfd\xbc\xdb\xe9\xdb\xf8v\xed[|\x9f\xfa\xbenj\xdc\x88T\x01' '\xa0\xa9Z\x1e@\xcby@\xbd\x04\xc8\xa8&(*"\x1e\xe4\x1f`eg\x05\xf3\xd9y\xcc\xa6gu\xdd\xa5' '\xa1g?\xb4\xcd\x18\x00\xb5\xa0\x8a\x9d\x06 \xabdq1z\xb1*\xa2\xef\x15B\xdb9\xc25l\xab\x03\x80' '\xac\xa6\x00\x0cv\x12\x80Ei\x11\xf7\xf3\xf7\xd1m\xd9\xb5\xcdx\x1a\x14\xa3\xa2]\x8c\x8a-\xbd\xe8\x08s\x04' '\xbd\x96^]\xfdBv\xa1\xeb\xc6\x8bQ\x11bT\xb4\xd7\x03`F\x95Uio\x7f\xbeS\xfe\xbf(-v' '\x1d\x80\xf4/i\xa8\xb2*\xe1\xb1\xbd\x82*\x004U\xbb\xd9\xe9\n\xd0\x82\xd4}\x06\xec\x06\xc1\x9b\xf5\x00\x80' '\x9aW\x8bZY\x93;\xc5\x80\xb5\xfc\x1a2J\xa6\xfb\xc6\x975\xf9\x91\x96\x1b\xe3z@\xeaVj\xad\xd98' '@\x13t\xcd*\xed\x93\x1c}1*"u+\xb5f\xb6$6S\x12K\x9b\xcd\xc6\x81\xa0#\x88\x1e\xb2\xe7' '\xc0\xf9\x7fI,m\xa2\xc6^aM\x00*\xc5\xcat\xa5P\xd9\xecD\x00|\x123@\xa5P\xd9|\xa4\x93' '\xa4\xbe\x0b\x00\xc0\xfa\xf4z\xb2\x1970\n\x80\x15\xadb\xaa\x02\xd4n\xfa\xafO\xaf\xeb\xd6\xcc\xf4\x00\x98Q' '2\x8a\x98\x8e\xa6\xd5F\xfd\xff\x14\x7fJW\xbfU\xdaB\xa1R\xe8.\xfd\xa3iU\xc9("t\xb6\xcau' '\x01\x80\x86\x99\xe4\xf5\xe4\\=\x16\xb0$\x0b\x96d\xc1\xd3<.\x1c\xbb\x00\x07\xed\xd0\xaf\xca\xd0<\x82\xf6 ' '8\x8a\xdb\xbf\xcfh\xc1\xd4\x8e\xd1O^O\xce=\xd2VW\xbdB4\xb8?\x02\xe0\xb2#\xe48\xa5\xb7?' '\x18\xb0\x06\xf0\xc5\x9f\xbeh\xe9#/\xdd\xb9\x84\x1f\xb7\x7f\xec\x88\xf1\xb1\xcfbY\xe9\xae4\x0b\x83v:\xa3' '\xcd\xd1\x19\x00\x1f\xc8\ty^oFhv\xc5\xf7\xa84R\xd1i4\xf2\xcb\ty\x1euz\t;\xde ' "\xf1$\xa4\x9d\r\x12\x0f\xb3\xc3\x9c\xfa\x92tWb\xacG\xad\x8e'\xd1\x12\xd7\xa8\xf1\xcb\xd7\x96\xd7\n\xc9\xc2" '*\x80\x7f\xa2N\x8b\x8c\x19\x00\x12\x00V\x95\x8c\xf2|.\x9e\xeb\xe5\x069\xe6\xa0\x82\xb0\xdfA\x1a\x93\xe7`' '\xb2\x8d\xd6l\x08N\x00H\x94\xc4\xd2h\xfe^\xde\xc3\xfaX\xfa\xa0\x81 FE\xc4\xff\x1e\xcf\xedv\x88]' "\x86\xc9\xd6\xd9F\xe6\xa0\x044\xc4\x8b[\xc5\x91\\<'\x1c$&\xfc\xa1=\xae\x82\xbf\xa2\x81\xbe\xe1F'" '\xe1\x044\xc4K\xdb\xa5\x11i\xf1`\xc4\x84=\x9f\x97c\xf2\\##\xdf,\x00\xfb\xee\xa0\xa4\x95q\xf1?' 'b\xbe,\x97=\x00\xba\xde/,FE\xac\xdfXG\xfc\x1f\xf1\x85\xdd\x80\xd7T\xeb|\xb3iX\x02\xc0\xaa' '\x9aSeiQb\xe5\x15\xd9\xd9M\x97\xd8\xa3\xfc\xe6\xbf7\xa3e\xa9|}7\xda\xcf4\xf3\xac\xc3\x1f&' '\xda\xf4m\x11<\xc5\xbf\xccT\x03\x81\xa7\xef\xa7)C0\x9e\xa6\xdf\xe6L\x01bB:f\xf0\xa1\x1c\xca\xa1' '\xfcA\xfe\x07\x01s\xe0E\x96J\xc3E\x00\x00\x00\x00IEND\xaeB`\x82' ) images_by_filename["toolbar_images/revert.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq' "\xde\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x08'IDATx\x9c\xed\x9aml[" 'W\x19\xc7\x7f\xe7\xda\x8e\x9d\x97\xc6v\xa2\x96d%\xad\xb7L\xb4\x1d\xb4N\xd1\x06\x94N40it\x82\xd1' 'L\xb0i\xb0\x96\x84\x0f\x08&\x81\x12\t\xf8\x00Ck\xba~)\xd2D\x836\xb1U\x03\x9a\x16\xadT \x8d' "D\xc0\x08\xeb\xa4\xba]7h\xd9V\xa7)4m\x976\xe9k\x92&\xf6ub'~\xbf|8\xbe\x89\xdd" '\xa4M\xae\xe3\xbcH\xf8/\x9d\xc4\xbe\xc7\xe7\x9c\xe7\xf9\x9f\xe7<\xe7<\xcf\xb9B\xd34\xf2\xc8#\x8f<\xf2' '\xc8#\x8f<\xf2\xc8\xe3\xff\x0f\xa6E\x1aw\x1b\xb0\x07X\x0bx\x81\xc8"\xc9\xb1(h\x04\xb4\xb4\xb2sq' "\xc5YX\xec\x074\xbb\xdd\xa25\xfe`\x9dN\xc0\xd1E\x96iA`'M\xf9\xd3\xa7\xbe\xaa\x1d}\xeb\xd1" '%A\x80y\x01\xc6\xb0\x03\x1e\xa0\xc6\xbd\xc1I\xdb\x9f\xbe\x88\xcbU\xb2\x00\xc3\xce\x0e\xf3M\x80\x1bh%\xa5' '\xbc\xe7\xc8\x97q8\n\xe6yHcP\xe6\xb1o7\xa9\x99\xaf\xdfQ\xbd$\x95\x87\xf9\xb3\x80m\xc8\x99w' '\xd4\xef\xa8\xa6\xf57\x9b3k\xc7\x93\xe0\x19\xd5\xbf9\xe6I\x86Ya>\x08\xa8G*\xcf\xfe\xd7>O\xc3' '\xb7\xef\xcf\xac\xf5\xc7\xe1\xb0\x1f\xfa\xe3\xfa\x93\x1a\xa6n\x85\xbd\xa9rl\x1e\xe4\xcb@\xae\t\xd8\x0b4\x01\xfc' 'n\xdf&\xea\xb7WK?/R\xb5\x97#R\xf9\xf0\x94\x01\x05mA\x94\xa1\xc4\xf4\xad\x9b\x96\x83sv\xabPU\xa3\xb4\xbct\x8e\x96\x97\xfe\xab\x13\xe1\x01\xea' '\x80\xc0\\\x14\x98\x0b\x01\xfa6\xe7\xd8\xfc\xe9r^\xff\xe5C8\xec\x93\xca\x9b\x02\x1a\x85\x1dc\x88\xe8]z' '\xa8)\x84pR\xfa\x84\xfe\xd8T\xdf`\x13Pa\x01\x87I\xfe_k\xa57\x10\xa6\xee\xc9\xa3t\x9e\xf1\x83' '\x0c\xa4j\x99\x03\t\xd9\x120\xa1\xfc\xd3[W\xf2\xf2s\x1b\x10\x8a\x80\x94\xf2\xd6\xabI\x8aN\xc5f\xe8"' 'KT\x98Q\xd7Yhx\xf94\xed\x7f\xb9\n\x92\x84\x8d\xd9v\x97M8\\\x0ft\x00\xb6\x1f=s\x1f\xbb' "\x9f]\x8b\x96\x04-\xa9AB\xa3\xe4t\x82\xc2sw0\xf9\\ \x98\xc4\xd6\x1bg\xeb\n'\x1d\x17n1" '\x10\x8aV \xcf\x12\xff\xc8\xa6;\xa3\x16\xb0\r\xe9\xf0\xd8\xfb\xc3u<\xf5H\xa5\x9cyE`\xd2\x04\xceN' '\xb0\x04\xb3\x11#;\xa8\xe1\x18\xaeWN\x10\x88\xc4A.\x05\xc3\xe7\x06\xa3\x16\xf0*\xe0z~G5\xdb\xbf' 'T\x89\x96\xd4\xd0\x12\x1a\x96 \x94\x9d\x15X\xc6s\xb9\xab\xce\x0c\x9b\xd9DE\x89\x95\xf6\x8b\xb7\x00\\\xc0\x01' "\xa3}\x18\x95x'\xd0\xbc\\\x81\x9f\xda\x0b\xb8\xd7jB\x98\x05\x056\x0b\xab\xaaW\xf0\xc9\x8d.\xa3\xe3\xe7" '\x04\xaeW\xde\xa1/\x10\x86,\xac\xc0\xa8\x05x\x81\x8a1\x8d\x9aw"\t\x9c&\x85j\xab\x82\xa6i\xa8\xfe' '\x10\x037\xfd8\xcbK\xb0\xda\x166\xe8q\xd8,\xba\x15\x00\xb4\x1bi\x9b\xad\xcd6\x02-\x00\x8f\x97\x98\xf9' 'n\xb9u\xf2\xd4g\x16<\xb0~5\xae\xfb>v\xc7\xc6\xd7\x06\x87\xe9\xb91\x90qX\x12&\x85B\xab\x05' '\x9b\xb5\x80b\xab\x95R\xab\r\xa7\xadx\xd6\x029\xf6\x1e\xd5}\x81\x03\x03\xdbb\xb6I\xd1\x93\xa4\x0e"\x17' '\xa2I[W$\xc9\xa6b3\x05f\x81P\x04C\xc3#\xf8\xfdA\xca\xca\x97a\xb1L=\xe8\xf8F\x82\x04' "Bc\x08EL\x14E\x11$\x84F4\x19'\x14\x8f\xe0\x8b\x8e1\x1c\r\x81\x80\x12\xb3uF\x81\xba}!" ':\x07\x83\x00\xff\x02\xce\xcfV\x91\xb9d\x85\xfb\x80\xc3@\xed`\\\xab8\x1e\x8c\xb1\xbe\xc8L\x99EA(' '\x82p$\xc6\xcd~\x1fV\x9b\x85e\xcb\x8a2\x1a\xaa\xc1P\x06\x01\x8aId\x90\xa1\x97\x84\x80\x91D\x84\xa0' '\x16\xc1n.D\x11w6X5\x12\xd7\x97A?\x06\xb6\xc4\xb9\xa6\xc5\x03H\x12*BIj\x8e\x8f\xc6\xa8' '\xb2\x9a\xa8\xb2\x99P\x14\x81\x86\xc6\xad\xe1\x11\x82\xa10\xe5e\xa5\x98\x14\x99\x7f\xe9\x1fV\xe9\xf1\x8f\xb1\xe7\x8c' '\x8f\xf6\xbe\x10\x7f\xbf\x12\xe2b \xc6\x7f|\x11>\x1c\x0c#\x14\xb8g\x99E.\rE\x10%A\x94\x04N' "s\xd1]\x85\xd9\xe7\xbd\x06\x10\xc6\xc0n\x90\xcb}k'\xa9\xb0\xf6\x99\x156v\xdcS\x94\xb1\xc6-V3" '\x0f|\xa2\x8a\xe5ev\xbc\x17z9v\xcdO\xd3\xa9\xa1\xbbv\xb8eu\t\xdft;y\xa8\xaa\x18a\x12' '\xac\xb6\x96Qf\xba3\tb\xcf\x110x2\xcc\xe5\xc5\xc8\xb1\xd4\xe0[\xbbBq[\xcfx\x9c\x07\xed\x05' 'XS\xb3\xa8\x01\x83\xfe\x11\xe2\xc9$\xe3\x91(W\x83\x11:\xae\x8f\xe9\x02?\x8d\x0cu\xdb\x80n@\x05*' '\xfa\x02Q\xdb_\xcf\x8f0\x1aM\xb0\xd9UBX\xc4Yn\xbasBu\xd7\x89K\x00\x15\x18\x88\x14s\x9d' '\x13lG\xee\xc5\xde\x7f\x06b\xfc\xe4|\x80\x9e\xb1\xb8\xb4\xb3\x94\xad]\x1b\x18&8\x9e\x11\x1a\xabH\xf2\x8e' '\xa5\xda\xef\x02\x9e\x00\x9c@\x03\xa0\xfe\xa1\xd3\xcf!\xaf\x8f(\t\xc6\xb5\xdc\xc6\x18\xf3\x91\x14\xedD\x92\xd0v' 'i<\xc1\x8f\xcf\x05x\xcf\x97\x19\x12\x8a\x89?3\xe2\x002e\xa6\xbezR\xee\xf3\t\xdb\x8cmT\x03\xb2' '\xce[V8\x80\x9c\xc5\xe6PBcgw\x80\x83}2H\xd0\xf5\xae,\x9cu6\xae\x0f\xf0\x04#I\xde' '\xbf\x16\x82:\xbb\x0c\x93o\x83\x1a\x9e\xb0\x0c\xaf\x11A\xe73-\x0e\xd2\x9c\xeb\x00\xf5\xe0\x95\x10\xcf\x9f\xf5\x13' '\x8ck \xa0\xa2\xc8P:\xd2\x0bp~0LA\xa5M\xe6\x11n\x83\xe7\x8a_\xff\xd8k\xa4\xe3\xf9&\x00' '\xd2\xfc\xc2\xbbC\x11\x9a>\x18\xe2\xa3\xd1\xec\xd6q\xf5Z;\x05N+\xb8\xa6\x1e\x8c\xd2\x08\xf0\x18\xe9s!' '\x08\x804\xbf\xd0\x13\x8c\xd3tj\x88\x13\x03\xe3F\xda\xd7\x01\xb8\x1f[)\xbfM\xb3\x04\xda.\x0e\xea\x1f=' 'F:^(\x02 \xcd/\x04\xe3\x1a\xcf}0\xac?\x9f\xc9im\x01jV\xaf.f\xd3\xd6\x8fO\xfb\x83' '\xd6\xae\x1bz4h8u\xbe\x10\x97\xa3\xb7c\x17rM7!\x95o\x9e\xe1\xf7\xcd\x00\xcd?wO>\xe9' '\xce\xcc07\x9f\xe8\xd1?\xb6\x18\x15f1\x08\x00\xe9\x17f\x13\xb66\x02\xb5\xee\xf5Ny\xc9\xa2\xa3{\xf2' '\x85\x92\x96\x7f\xf7\xe9\xb3\xef!\x8b\x8c\xd0B.\x01\xa3\xa8\x07Z\xec\xa5\x16~\xfb\xeb\xcfM>==\x06\xaa' '\xcc9z\x07Fi\x96\xa7?H\xddH\x19\xc5R%`\xe2~\xf1\xc5\xdd\x1bq\x7f\xca)\xaf\xd8\xc6\x93\x13' '\xb7Kj8F\xdd\x1b^=\x07\xd0\x84t\xb4\x86\xb1\x14\th$\xa5\xfc\xbe\x17\x1fd\xc7S\xf7N\xd6\xb4' "\xa9\x10\xd6P\xc31j\x0f\xbd\xaf\x9b~+\xf0\xabl\x07[,\x1f0\x1d\xecH'\xd6`_f\xe6\x17?" 's\xb3\xfd\xeb\xae\xc9\xdav\x15\xba#x\x07F\xa9{\xc3\x9b\xae\xfcw\xe62\xe8R!`\x0bR\x19WU' 'e!\x87\xf6~\x86\x1aw\xd9D\xa5\xf2\xb7\x11\xc4\xd9\x08\xad]7hz\xfb\xbcn\xf6\xad\xccQ\xf9\xa5\x00' ';\xf2J]\x03\xb4\xc7\x1e^\xa1]:\xf2\xa8\xa6\x9e\xfe\x9a6z\xee\tm\xec\xf27\xb4\xf8\x81\xafh\x97' '\x9f}X\xdb\xb2\xca\x99\xfej]c\xae\x04XL\x0b\xa8G\x9a\xbc\xa3\xb4\xd8\xcc\xee\xef\xaf\xe1[\x8fW\xa1' '\xd8L2b\x8ai\xdc|\xfd\x16/\xb4_\xe0@\xd7M\xbdM/\xf2T\x98\x95\xc3\x9b\x0e\x8bA\x80\x1by' 'bs\x01\xf0\xb0\x00/F\xe5\x91G\x1ey\xe4\x91G\x1e\x00\xfc\x0fT\xec\xe3\xdd' '>G/W\x00\x00\x00\x00IEND\xaeB`\x82' ) images_by_filename["toolbar_images/terminal.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq' '\xde\x00\x00\x00\x04gAMA\x00\x00\xaf\xc87\x05\x8a\xe9\x00\x00\nIIDATx\x9c\xed\x9bOl\x1c' '\xd5\x1d\xc7?\xf3fv\xd7\xf6f\xed\xa4\xc9\xc6\xae\xb1c;RU(\xca\x1f\xb2`\x9a\x14;F\x11AA' '\xaa\xd4HH\x80\xd4\xaaB \xd1S+\x0e\xa8\x9c\x90(\x97\xd2JH=\xd0J !\xf5\xc0\x01n \xe3' '*\xa5\x8e\x12)\x11!-\x11N\x82\x9786\xb5\x83\x9d\x18\xc7v\xece\xbd\xb3\xbb\xb33\xd3\x83\xf36\xcf' '\xb33\xe3\xb5\x89\xe3\x88\xfa+\x8df\xf6y\xe6\xcd\xfb}\x7f\x7f\xdfo\xd7\xb0\x81\rl`\x03\x1b\xf8\xff\x85' '\xe6\x1dH\xa5R\r\xad\xad\xad{\xa3\xd1h\xbdm\xdb\xeez,j\xb50\x0c\xa3b\xccq\x1c7\x93\xc9\x8c' '\x1e;v\xec\x0b\xdfg\x94k\xf1\xcc3\xcf\xfc\xa6\xad\xad\xed\xb7\xed\xed\xed?\xde\xb1c\x07\x91HdU\x0b' 'q\xdd\xb5\xe1\xcd;o\xd0gu|~~\x9e\xd1\xd1\xd1o\xdb\xda\xda\xfa\x87\x87\x87_;~\xfc\xf89\xf5' '\x19I\x80\xf6\xec\xb3\xcf\xfe\xb5\xa7\xa7\xe7\x85G\x1f}\x94\xe6\xe6ft]_\x03\x11\xd6\x06\xaa\xc0\xea\xb5\xe3' '8X\x96E&\x93I\\\xbe|\xf9\x17}}}]\xc0\xaf\x8e\x1f?\xfe\x0fy\x8f\x01\xf0\xf4\xd3O\xff\xee' '\xe0\xc1\x83/\x1c=z\x94D"q\x07\x97\xfe\xdd \x85\xd5\xb4[\x9e,\xaf]\xd7E\xd7u\x0c\xc3 \x16' '\x8bQ[[\xcb\xa6M\x9b\xb6\xe6r\xb9\xbf\r\x0f\x0f?q\xe5\xca\x95A\x00=\x95JmK\xa5Ro?' '\xf5\xd4S[\x92\xc9\xe4:\x88Q\x1d\xaau+\xd7u}-"\x12\x89PSS\x03\xb0yll\xcc\x1a\x19' '\x199\x01\xd8"\x99L\xde\xdf\xd6\xd6\xb6\xb3\xb1\xb1\xf1\xb6/\xfavB\xd34\xdf#\xe8>\xb8E\x86\xeb\xba' 'h\x9aF]]\x1d\xed\xed\xed455\x1d\x04\x9a\x00\x0c\xc7q\x12\xcd\xcd\xcd\x15\x11\xd4u]\n\x85By' 'RX\xf4)\xc7q|_|\xbb\x11\xa6\xf1\xa0\xbf\t!\xca\xd7\xd2\xfc\xe5\xfd\xae\xeb"\x84\xa0\xa1\xa1\x81\xfa' '\xfa\xfa\x06`+p\xc5\xb0,K\x18\x86Q!T6\x9b\xa5\xb7\xb7\x97\x99\x99\x194M\xc3\xb6m\x12\x89\x04' '\r\r\rk\x16\xe5\xbf\x0bl\xdbfjj\n\xd7u\xb1m\x9bT*EWW\x17\xa5R\tXJ\x02 ' '\x80\x18\xa0\x19\xba\xae\x07jt||\x9c7\xdex\x83L&CSS\x13\xcf?\xff<\xb6m\xe38\xce\x9d' '\x92\xabj\x08!8u\xea\x14\x1f}\xf4\x11\x96e\xf1\xe2\x8b/\xd2\xd5\xd5\xb5$5\xfa\x05M\xc3; !' '\xc7\xe6\xe6\xe6\xc8\xe7\xf3\x1c\xec\xee\xa6\xb6\xb6\x96\xb9\xb9\xb95\x17f5\x10B\x90J\xa5H\xa7\xd3\x9c?\x7f' '\x1e\xd34\x01|\tP]%\x94\x00!\x04\x8e\xe3p\xf8\xf0a~~\xf4(#\x97/\xb3\x90\xcd\xa2\tq' 'W\xd6\t\xba\xae\xf3\xf8\xe3\x8f\x93N\xa7+\x8a\xa2@\x0b\x08s\x01M\xd3H$\x12\x1cx\xe4\x11\x0e\x1d9' "\xc2O\xbb\xba\xb880@\xfa\xe2E\xa6&'q\x1c\x07\xbf\xf8\xb1\x9e\xa8\xaf\xafg\xef\xde\xbde7U\x05" '\x97\xd9\xa0*\x17\x90\x0f\xb4\xb6\xb6\x12\x8f\xc7)\x95Jl\xdd\xb2\x85\x9e\x9e\x1e\x1e\xda\xbf\x9f\xcb\x97.q\xfe' '\xdc9F\x86\x86\xc8\x9b&\xbaa,1\xad\xf5\x82eYttt\x10\x8dF\x81J\x02`\x051@\x08\xc1' '\xb6\xad[)\x16\n\xb8\x80\x0e\x14\x81X,\xc6\x9e\xdd\xbb\xb9\x7f\xf7n\xae\x8e\x8fs\xee\xecY.\x0e\x0c0' '}\xfd:\x9a\xa6\xf9nJ\xee\x144M#\x16\x8b\x95+Z\xd5\x12l\xdb.\xdf#a\x18!\x9a\xd3u\x9d' 'XM\r\xe3W\xae\xf0\xf7\xb7\xdf&\xd5\xd9\xc9Ov\xed\xa2!\x1eG&\xc2\x96\x96\x16v\xb4\xb4\xd0}\xe8' '\x10\x17\x06\x06\xf8\xec\xcc\x19\xfe;2R6\xb7;\r\x99\xb2c\xb1\x18\xc5b\x91l6[\xae_\xfc\xea\x98' 'P\x17\x10B`\x9a&\x0b\xb9\x1c\xff9s\x86\xcf>\xfd\x94\xc6\xa6&v\xef\xdbG\xaa\xb3\x93\xb6\x9d;\xd1' "u\x1d\x1b\xa8oh\xa0\xa7\xbb\x9bTg'\xaf\xbe\xfc2\xb3\xd3\xd3\x88u\x08\x94\x9a\xa6\x91\xcb\xe5\xb0,\x0b" '\xc7q\xc8\xe7\xf3\xc0R\xf3W\x03x\xa8\x0bh\x9a\xc6\xec\xec,\xc9d\xb2\xecKc\xa3\xa3\x0c}\xf9%}' '\x1f|\xc0C\xfb\xf7\xf3\xcb\xe7\x9e\xa3\xa1\xbe\x1e\x87E\xf7\xd0#\x11,\xcb\xc2\xcc\xe7\xd7%Sh\x9a\xc6\xc2' '\xc2BY\xf0\xe5`\xe8\xba\x1e\xe8\x02\x86a055Ecc#\x86aP\xb2,\xe2\x9b6\xb1k\xdf>' '\x0etw\xb3\xfb\x81\x07\xa8\x8b\xc7\x91eQ\x04pt\x9d|\xa1\x80\x99\xcfc\xac\x03\x01B\x08fff0' 'M\x13M\xd3|\x03`E\x1d\xe0G\x804\x95L&\xc3\xf8\xf88\x9d\x0f?\xccC\x07\x0e\xf0\xb3\x9e\x1e\xda' 'w\xee$\xa6i\x94n\xde\x1b\x01L\xc7\xe1\xb3\x0b\x178\xf1\xf1\xc7L^\xbbF\xc9\xb2\xb0\xd6VV_\xe8' '\xba\xce\xc4\xc4\xc4\x92\xe0\xa7\x9e+\xd2`\x98\x05\xc8\x80\x92/\x16y\xf9\xb5\xd7\xe8hn\xc6\x02l\xc0a1' '+L\xdd\xb8\xc1\xa7\xa7O\xd3\xdf\xd7\xc7\x97\x83\x83\xe4M\x93h,\xb6\x86"\x06C\x08A>\x9f\xe7\x9bo' "\xbeA\x08\x11X\x04\xad(\x08\xde|\x8ah]\x1d%\x16\x85v\x80\xf4\xa5K|\xdc\xd7\xc7\xc9\xfe~&'" '&p\xa1\\\x14U\xeb\x7f\xb7\x1b\x9a\xa6166V\xbe\x86\xa5[b\x89\xaaKa9.\x84 \x1a\x8bq' '\xc34\xf9\xe4\xc4\t\xfe\xd9\xdb\xcb\xb9\xb3g\xc9\xcc\xcf\x13\x89D\xca\xd1^\xe6Yu\x0e\xbfy%\xfcv\x95' '\xab\xddi\n!\x98\x9d\x9dejjj\xc9\\\xcb\x12\x10V\x07\x08!\x10B\xf0m&\xc3\x9f^}\x95c' '\xbd\xbd\x8c}\xf5\x15?\xd8\xb2\x85\xbax\x1c\x80\xa2e\x81eU\x08\xa7~\xf6\x9e\xbd\xf7\xf8Uk~\r\xce' ' h\x9aF6\x9berr\xb2\xe2o~%\xb1\x8ae]\xc00\x0c&\xaf^\xe5/\xaf\xbf^\x1e\x9f*' '\x95\xd8lY\xd4\xd4\xd4,\x11N\x1eB\x08\xa2\xd1(\xdaMb\xc3\x08(Y\x16\xb6mW\xf8\xab\xdf\x11\x84' "\x85\x85\x05\xe6\xe6\xe6B-*p7\x18\x94\xab\xbd\xe9B]\xbceYLOOSSSC<\x1e'\x12" "\x89\x94\x85w]\x97\x1f\xdes\x0f\x7f~\xf3M\xea7o\x0e\xec\x1dh\x9a\x86\xe38\xfc\xf1\x95W\xf8\xf7'" '\x9fTto\\\xd7\xc5q\x1c\xdf\xcf\x12\xb6mc\x9af`\xcc\xf1ZV\xe0^ ,\x0b\xf8A\xbaF\xa9' 'T"\x9b\xcd\x12\x8dF\x89D"\x18\x86Q.\x81[\xdb\xdb\xd9\xb2m\x1b\x8e\'.,!\xe0f\xc9Z\xb2' '\xac\x8a\x9c-\xcbV\xa0,\xb8Z\xce\x16\x8bE\x8a\xc5\xe2\xb2-:/\x01\xbeY\xc0\xcf\n\xbc\x16\xa0\x8e\xa9' '\x87\\x\xa9T*\xb7\x9c&\xaf^\xe5\xd7O>ID\xd9\x14\x95\xdd@]\x1cp\xf5\xeb\xaf\x11\xba\xbeD' 'H\x95\x04\xaf\xe0\xb6mS*\x95|76~\xc2W\xd5\x0f\x08\x82J\x80\x14X\xd6\r^\x12\xd4s\xb1X' 'dhp\xb0\xb2\x8b\xbb\xf8\xf6\x8aw\xc8\xf7H\r\xa9\x0b\xf5\x9a\xbd\xfa.IHX\x8b\xae*\x17\x08#@' '.^\xd7u_\xe1\xc3\x88\xf0\x06H\xef\xcb%\xa4\x00\xb2xQ5/\x05u\x1c\xa7\xdc\xa1\x92cr^I' '\x82\xdfWeU\x05\xc10_\x97\xedeI@\x10\t\xaa{\xac\x84\x00\xf9\r\x8e7\xe2K\xa1\xbd. \xe7' '\x92\xdaW\xe7U\xb3\x89:\xbf\x07\xb2\x80\xa3\x03\x85R' '\xa9\xf4m"\x91x\xe2\xde{\xef\x8d\xcaH\x7f\xa7\x16v;],L\xf3\x1d\x1d\x1d\x98\xa6\xc9[o\xbdu' "\xf1\xc3\x0f?|\x0f\xf8\x1c\xc8\x19\x80\xdb\xdf\xdf\xff/!\xc4\x1f\xb2\xd9\xec\xef\x1f{\xec\xb1\xad\xdb\xb7o'" '\x12\x89\xe0\xba\xe1A\xc9/p\x05\xdd_\xcd=a\xef\xa8v>\xf5Z\x08Qn\xd9\x0f\x0f\x0f\xf3\xee\xbb\xef' '\x0e\xbe\xf3\xce;\xef\x01\xe7\x80\x1bp\xabK-\x80\xed\xc9d\xf2\xf0\xa1C\x87\x9e\xdc\xb3g\xcf\xded2\x19' '\xd74\xed\xae\xfbI\xe8r[a\xef\xbd\xf9|\x9et:=}\xf2\xe4\xc9\x0b\x03\x03\x03\xa7\x81\xd3\xc0\x17@' '\x0e\x96\xb6\xe95`3\xd0\x01\xfc\x88\xc5\xdf\xd2\xae\xee?&\xee.\xb8,\xfa\xfb80\x02L\x00\xe5\xaf\x92' '\xfc\xe84\x80Z\xa0\x86E\xcb\xf8>\xc0bQ\xe3\x05\xe0\xae\xb3\xea\rl`\x03\x1b\xd8\xc0z\xe1\x7f8y' '\xac\x082"\x8f\xf8\x00\x00\x00\x00IEND\xaeB`\x82' ) images_by_filename["toolbar_images/unlock.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz' '\xf4\x00\x00\x00\x06bKGD\x00\xff\x00\xf8\x00Ki\xfe\xc6\x83\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00' '\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd6\x02\x10\x0b\x12\x00V\xcb\xce_\x00\x00\x05uI' 'DATX\xc3\xed\x97[LTG\x18\xc7\xff\xbbgo\x87\x8b\xb0,\xbb\xb0\x05A@D\xb4\x05#\x17o' '5\xb1\x94hc\x13\xb1/}h\xa2M\x9b&M\xdb\xa4\x0f}1US-\xd6&5\xed\x8b\xd2\xa4mL' '\x1a\xa3\xc6`\xad\xc1*^RZ\xadX\x84\xc8E\xdd\xc0\x82+\xcbM\x97]\x96\xdde\xf7\xec\xe5\xec9g' "f\xfa\xb0\xf0`\xd2*\xab\x18_:\xc9\xf70'3\xff\xff\xef|3\xf3M\x06\xf8\xbf\xbd\xe0\xa6Z(\xa1" '\xa6\xa6S\x0c\x00n\xde\xec\xae\xe7y\xde\x15\x08\x04|\xa3\xa3\x03\xc1\x9e\x9e\xab\xf1\xc7\xcd\xe3\x16\xc2\xb8\xa1a' 'GIII\x81K\xa7\xd3\xe9+*\x96\xbf\x9a\x92\xc2o\xd3j\xf5K\xa7\xa6\xa6\x9cccv\xefs\xcb@' 'S\xd3)VY\xb9\xc2\xb1ti^\x9e$I\xda\x9c\x9c,!\x1c\x8ej\x19c\xb4\xab\xab\x9f\r\x0c\x0c\xf7' '\xf4\xf4\xd8\x85\xe6\xe6Co-8@S\xd3)VW\xb7\xd6m\xb1d\x1aEQ\xd4\xcce\xd3\xe1\x18\r\xa5' '\xa5\xf1\x86\xec\xec\x0c\xc5\xef\x8fj\x8f\x1c\xf9\xe5\xfe\xccLX\xd5\xd2\xf2m\xf9\xbf\xe9h\x925\xde\xb3\xe7\x87' '\x14\xab5#RYY\xee\xb3X\x8ci\xa1P\x88tv\xda\x1e\x8c\x8dM\xf6\xc5b\x91\x8e@@\xac\xde\xb5' 'k\xe7;\x83\x83C,??W[UU\x96\xdbz\xf1\xa6\xe7\xbf\xf4\x92\x06\xb0Z3"\xf9\xf9y\xbf\xad' '^]V\x1b\x08\x04\r7n\xdc\x16\x1c\x8e\x91\xc3\xad\xadW\x8e\xf1<\xcf23s\x9a\x0f\x1c8\xdaa\xb5' 'f5\xeatC\xfa\xae\xae{\x0c\x0c\xdf,\x08\xc0\xee\xdd?r\x00`0\xe8\xaa\x01fp\xb9\xbc\xe8\xefw' '\xb4\xf5\xf6\xde\xba\x14\x8b\t\xa1\xde\xde\x8b\x0c@h\xcd\xda\x0fZ(\xa5\xaf\x85\xc3\xa2\x13\xc0\xd7\x97/\x1f\x9e' 'Y\x10\x00\xbb}\x88j\xb5\xe5\x1f\x96\x94\xd0\x83\x84\x10N\x92$n|\xfca\x18\xd0N\xdb\xed\xedln\x9c' "\x18'>\x9f?\xf2\xfe\xd9_\x0f\x85\x9e\xa4\x99\x14@ZZ\x1a79\xe9\xf5\x01\xd0RJ\xb5\xa2(\xeb\x8a" '\x8a\x16/\x19\x19q\xac\x02pun\xdc\x9d\xbe\x9f\xc5;}\x10\xe7\xa3\xa9N\x06\xe0\xf8\xf1F\xa5\xa2\xa2\xe8' '\xcc\xc4\x84\xdbM\x08\xd1/[\x96\xa7\xce\xca\xca\xdal1\x9bk?\xdd^\x99\xf3\xd7\x97eI\xd7\x15u\xb2' '\x13\xda\xdaz\r\xe7\xce\xb5;\x9c\xce\x87\x82F\xa3Fi\x1e\xa7T\xe5\xf8\xd6\xbe\xfb:\xfb\x84\x01?\xfd\xf1' 'E\x99!\x19\xbd\xa4\x89\x07\x07;HAAUL\x12<\x1b\x8b\x8cA\xa3%u\x92\xabX\x19-\xc9\xb6\xb0' '\xd7\xa8"-\x9f\xf6()\xa7:\xa6\xdb\x9e\x1b\xc0\x9f\xfbW\x16o.\x1e\xfeH\xa3GM\xc1\xe2\xa0&7' '\xf7\x81*\xbbX\xe0\xd2\xcd\x11\xe8S\x1d\xe8:\xba\x89U\x94\x04a\xa0\xd3\x00\x03 '\x00\xa8D@b\x04\xe3n\xb5\xdc}O'\x8ey" '\xd57\x87<\xf4\xe4\xadqv\x0f\x00l\xae\xc9\xce\xf9\x16\xb7\xc7\xd6\x81\xf6\xab\xd3(\xcd5C\xa7\xf1A\xad' "\xa2`\x84\x81H\x14~\xbf\x8a\xf4\x0c\x19D\xfb\x04\xe7|\xe8c'~\xb7+\xd7\xa1\x02l.wg\xb2\xa5" '\xfd\x89\x95\xf0Jc-\xdbP\xad@\xcf\xbc\x90#2z\x078\xb1g\x90\x13\xa6C\xecdK\xaf\xdc\x0c\x15' "`\x9bL\xdex\xdew\xc1\xf5\xae r\xf2\n\x11\x0fFE\xdb\x80\x14w\xfb\x94K\xb7\xee\xcb'\x9dSl" '\xfaY\xcd\xe7\x05p\xb0uH\xa5\xe6\xb8+\xb18\x95\xc3"9\xfb\xf7`|\x00\x00l\x1e\xcf3\x19\'\xdd' '\xeaKM\x05\xb5y\xa6U/\xec\x11Y_jJ}\x1e\xba\xff\x00.\x04\xdd\xd7\xe3\xb1Rd\x00\x00\x00\x00' 'IEND\xaeB`\x82' ) images_by_filename["toolbar_images/update.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq' '\xde\x00\x00\x00\x06bKGD\x00\x1b\x00j\x00\x1b!V\x9a\xf6\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00' '\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd6\x02\x0b\r\x12\x1b_;\xab\x9f\x00\x00\x0cmI' 'DATx\xda\xd5\x9aml\\\xe5\x95\xc7\x7f\xe7\xb9\xd73\xe3\xb1\x8d\xe3\xa6\xa1\x8b\x9d6Ai\x9b\xd4\xc1' '8%h\xbb\x01L\x0c(Q\x04\x01V@\x9a\xa6]\x16e\xb7\xfd@\xa5\xb2\xabUD\xf8\x02,R\x91\xb0' '\x90X\xad\xfa\x81\x08\xd4\xa6ZV\xadR\xa4\x8d\xd8\x02\xa5MY5\xd4MH\x9a\xda\x81\x90\x90\xb8-\x89=' '\x8e_&8\x8egl\xcf\x8bg\xe6\xd9\x0f\xf7e\xee\xf5\xbc\xd8\xe3\x97\xe0<\xd6hf\x9e\xfb2\xf7\xfc\xcf' '9\xff\xff9\xcfc\xa1\xb2\xd1n\xbf\x8a\x8d\xcd\xc1\xc6@{\xa01X\xf4`j Mz \xf5\x1c\xf0\xef' ',\xa1aV\n@\xb01\xf0l)#\x83MABMA4\x82\xa0\x01\xd0\x08\x00\xb1\xe3c\xa4\x07R\x9b' 'm\x00\x7fw\xad\x02@\xa01H\xfd\xdf^\x07\xb6a\xa0mS\x01\xdbl\xf1\xcdY\x10\x04\x9b\x82\xa4\x06R' '\xed\xe9\x81\xf4\xe1\xa5\x04\x80\x9a\xeb\x85\x96\x89\xda\x05B\xdcO\xda\x05\xc3yi \xd8\x14"X"r\xae9' "\x00,\xb3\xc5\x03\x81\xa0m\xbf\x8b'2\xf2\x11 \x9e\xefl.\xc3#\xd7V\x048\x86yS\xc0c\xa8\x0b" '\x8f\xb6\x8f\x05\x9b\x82\x04\x1a\x03\xed\xd7<\x00\xe2\x06\xbb.\x91\x18\xf98\xf0\x02\x12j\n.\xb94Ps\xf7' '>\xd3\xa8\xcfI\x06\xf1\x04\xbe?\x1d\x8bG\x0b\xf2\x12(\xee\x89b\xffi\x11BM!\x82\x8d\x81k\xb6\x10*\xc2' '\xef\xda\x15C\x11\xed\xe3\x08\x11qq\xd0"(\xad\xd1\x02"\xd78\x07\xe4eM\xa3E,C\x95 JY' '/S\x10S\xa1\x0cA\x99\n1\x14J\t\x18\ne(P\xb2dx`\xce\x1c X\x86\x8b\x12\xc4\xb0^' '\x18\x82\x98\x822\x14\xca\xb4\x8cwA0\x15\x86i\x1d\xaf\xfeR5\xc1\x95K\x83\x07\xcc9\x11\x80\xd2\x80B' '\x14\x16\x00V\xfc\x83\xb2\xc3\xdes\xae\x93$\xcaf\nA\x13^\x1d&y1E\xaa?u-r\x80\x13\xea' 'V2\x8b\xca\xe7:\x02J)\x8b\x13\xf2\xcc\x88B\x81h\xb4\x06\xb4\xc3\x0f\xae\x1c:\xdd\xe1\xef>\x8b\x1e\xa1' 'b\x00D\x812<\xc6{\x80\xc0\xe6\x03\xc7x\x8b\xf9A\x1cDt^0\xc37\x86AI;\x9a\xf6d_' '\x82d_r\xb3\x07\x88\xa5\x9c\x02\x16\xe1yA@\xd9E\x8f\xb2A\xf1h\xa57%\xdc\x9a@C\xcd\x9a0' "\xe15a\xc8i&\xcf'\xb8\xfc\xde\xe5\xf6d_\xf2/\xc0\xe4\x02\xf0\xdaE \xb2P\x00x\xc9j\xb3\x08" '(S\x10Qy\xe3%o\xbc\xe3yOq\xe0\x82\xa0\xb5-\x89Z\xdb\xa9\xa0A\t\xe155$\xac(\xf8' 'G\xe0[\xf3\x04@\x80\x18\xd0\x02\x8c,\x08\x00\xc1\x95\xc1gC+C P\xbd\xba\xda\xe6\x00O\xf8\x1b\xe2' '\xe3\x01\x17\x10\xa7B\x10O\x01\xa5\xedO\xdaBDr\x9a\x9cX\xf7\xda\xb9sg`\xd7\xae]\x01\xad\xb5\x9d' 'J\x82\xf7\xb3\x17L_d\xd9\xe7)\xa5\x18\x1a\x1a\xe2\xa9\xa7\x9e\xaa\x89F\xa3\xeb\x81\xf7\x16$\x05B+C' '4\xdc\xd1`\x19\xea\xc8\x9e\xb2_"``Kb\x9e\x07\x9c<\x10o>\xa0=Q\x00\xa2\x05T\xce\x12\x15' "\x11V\xaf^\xcd\xe6\xcd\x9bK\x1a\xeb\x05\xa2\xd4\xfc\x85\x0b\x17\x08\x85B\x0b\xcc\x01\n\xbf\xf1\x8e\xc7\xed9'" '\r\xdc\x14p\x80qjF\xf7\xa1\x05\xd1v3e\xf7O:\xa7\x10\xad\x11\x05\xb9\\\x8el6[\xd2\xb8\x99' '\xde\x01\xb2\xd9\xac\x95b\x0b\t\x80\xe0\xf7\xbaR\xca\x02\xc5\x06\xc4\xd1\x7fq\xdf\x1d\xfd\x93<\x1f\xd8\xde\x07\xdb' '\xf3\xda\xe2\x02Q\xc2x\xcf8\x89\xf3\t\xb8\xc5\x96QO\xd8{\xc3{\xba\xd1\xce\xfc\xf4\xf3\xa5\x82Z{V' '\x00$\xfb\x93$\xfb\x92\x84\xd7\x84m\x8fS\x10\rb\x80\x88\x05\x0c\x92/\x8e\xf2<`y\x1f\x9b\x04\xb5\x06' '\xc9\t\xe3=q.\x1d\xba\xc4M\x9f\xbf\x89\xd6\xd6VFFF|\x86._\xbe\x1c\xa5\x14"B<\x1e\'' '\x95J\x15x\xdd0\x0c\xea\xeb\xeb\xa9\xaa\xaa\xaa\xc8x\x07\x00\x054\x03\xe1\x12\xe7\x0c%\xfb\x92]\xa3\xbf\x1f' '\xbd\x05C\xa8\xfdr\rb8$8\xfd\xdd\xcb\x03\xb6\x97D\xf2-\x93]\x14H\xce.\t\x14L~2\xc9' "\xaa\xc0*\xb6n\xdd\x8aR\x8a\x13'N\xf8~|\xeb\xd6\xad\x18\x86\x01\xc0\xf9\xf3\xe7\xe9\xeb\xebs\xa3\xc1\x19" '555\xdcv\xdbms\x06\xe0!\xe0\x15\xa0\xaa\xccyF\xa27\x91\xe5\xf0e\xc30\x15\xb5_\xa9q\xf3' '_\x14(\x85\xf5]\xf2ibu\x19\x82\x12mG\x82]\x08i\x8b3\xb4\x06\x9d\xd3\xa0\xe0\x81\x07\x1e`\xcf' '\x9e=3\x86zkk+\x1b6l(\xca\x01\xdek+\x05`\xdd\xad\xb7\xde\xda\xb0w\xef^7\x9f\x9c\x1b' "ys\xec\xf4\xe9\xd3\x1c8p\x80\xbf\xfe\xdf'\x88)\xd4~\xb5._\x15\xda`(Q\x88a\xb7\xbe\x92/" '\x9a\xf2}\xb4\xb8\xf7\x15\r\xe3=\xe3L\xfcy\x12i\x11\xf77\xbdF\x14\x03\xa3\x98\x04\xfa\x8a\xa1Wi\xa2\xd1\xa8\xcf\xe0\xea\xeaj\xd2\xe9\xf4\xac\x00\xf8F\xf3\x17\xd8\xb6i\xb5o\xee\x9d\xf7/' 'p\xec\xf4p\x05\x0b[\xe2\xe7\x00\x07\x95`0\xc8c\x8f=\xe6;\xa9\x12\x89\xfa\xf8\xe3\x8fy\xe3\x8d7\x88' '\xfc,\x82\x18\xc2\xe764X"\xa8a\xec\xe4\x15Z\x97\xb5r\xfb\xed\xb7s\xe2\xc4\t\xa6\xa6\xa6\xdck\x83' '\xc1\xa0\xcb\xfc\xa5\x00\xf8\xb7]_\xa7.\xec\xdfU\xfa\x8f\x9fw\x13\x9bLW\xb6f\xe6Ie\xb3X\x85\xe5' '0\xa4\x17\x80\xd9J\xd4\x83\x0f>\xc8\x1dw\xdcAGG\x07\x1f\xfe\xf7)\xc6>\x1cs\xef3\xf6\xe1\x18m' "\xf7\xb7\xb1e\xcb\x96\x02r5M\x93W_}\xd5'U\x00\x0f\xb7\x7f\x99\x9b\xd6,/0\xe2\xb9\x1f\x1f\x9b" '\xdf\xba\xd9\xf4\x08\xf0\xe6\xc6\xf0\xf0\xb0/?+\x97\xa8\x15\xac\xf9\xbb]\x18\xe7\xd6B\x0cz{\xba\xa9\xca\xc5' '\xf9\xc1\xce\x1fp\xe7\x9dw\xfa\x0c,Uk\x88\x08\xcf\xfe\xf37\x8a\xde}6\xc6\x0bp\xcb\xba\xeb\xd9\xb8\xee' 'znX^\xe3;\x96\xcd\x96P\x01\x11arr\xd2\x95&oxV*Q7\xae\xdb\xc8\x8d\xeb6\x02p' '\xf8\xcd\x9fP\x93\x19\xe0\xe9\xa7\x9fFDH$\x12\\\xbat\xc9gl(\x14brrv\x0b\xc2\xa5\x80\xa9' '\xc4\xfb%U \x1c\x0es\xef\xbd\xf7\x16\xcc\xcdW\xa2\x9ck\r\xc3\xa0\xb7\xb7\x97\xa3G\x8f\xfa\x8eUUU' '\xf1\xe9\xa7\x9f\x16\xcd\xd3\x85\x1e\xbeFj:9h\xad\t\x04\x02\x0b*Q\xc6\xe0!\x8e\x1d\x1bps\xbe\xa5' '\xa5\x85\x96\x96\x96\x82s\x0f\x1e<\xc8\xa9S\xa7*6\xe8J\x19\xe1r\xcc\xbf\xdd\xb6' '\xac6\xc8\xbf\xec\xdcP>\x02\xbc9\xa9\xf5\xc2K\xd4\x9f#\xa3\xbe\xebr\xb9\\\xd1:\xdd\x99w\xe6\x8e\x9d' '\x1e\xe2\xfc@\x8cs}\xa3s\xf6\xf6u\xb5\x81\x99I\xd0\x1b\x01SSS\x0b.Q\x91\xe88\xb5\xd3\xa2l' '\xa6\x10WJ\xf1\xce\xfb\xbd\xf3\n\xf5\xef?t3+\x1a\xaa}s\xaf\xbds\x96G\xb7\xad+\xcd\x01\xa1P' '\x88\xed\xdb\xb7/\x8aD\x15[\xf5\x99iT\x07M\x12\xa9\xcc\xac\xcf\xbf\xbe\xa1\x9a\xc7\x1f\xbayZ\x91\xd4\xcb' '\xb1\xd3C\xe5W\x85\xbd\xb2\xb4X\x12\x95\xdf"\xd33\xf2\x88s|\xb6\xc6\xff\xd3\xf6f\xbe\xf8\x85:_9' '\xfc\xc3\x9f\xfe\x91\\N\xcf\xbc$\xe6\xcd\xc5\xc5\x90\xa8\x1f\x0e\x1e\xe2\xf8\xf1\xc1\x82m\xae\xd92u\xb1\xf1\xdd' '\x07\xd6\xd3\xb4\xa2\xd67\xf7\xe6\x1f\xce\xf3\xa7\xb3\xd1\xb9\xed\x0e/\xa6D\xd5\xd5\xd5q\xe8\xd0!V\xadZU' '\xd6\xe0h4Z\x12\x80\x87\xda\xd7\xd0\xb2\xe6\xf3\x05\xd7\xfd\xea\xe8\x05\x8e\x9f\x19\x9e3O\x98\xd37\x18\x17C' '\xa2V\xb7\xdc\xcd\xf6o\xff\xab\xdbA\xba\x1df\xc0dS\xcb\rn\xb9\xbd\x7f\xff~\xce\x9d;W2\xcd\xae' '\xc4S\xec;x\x8a\xd4Tv\xc1j\x01s:\xb3/\x96D\xddt\xdb\xdf\x17\xcc}\xe9o\xea\xd8}_\xb3' '\xfb\xbd\xb3\xb3\xd3\x05\x00 \x93\xc9p\xf1\xe2E\xefR7\x8f\xdes\x83\x8fG\xca\xa5Jccci\xc7\xd8' '\x95m\xd1B\xe8\xb3\x92\xa8\xfe\xe8\xb8\xaf\x05\x8fF\xa3tvvb\x9af\xc1V\xb8\xf3^n\xdc\x7f\xff\xfd' '\x84\xc3\xfe\r/\xad5\xc3\xc3\xc3\x1c>|\xb8\xf4\x8a\xd0g%Q\x93\xc9)\xdf\x83\xaeX\xb1\x82G\x1ey' '\xa4\xec\xaao\xb9QUU\xe5.\xf9\xa7R)FFF\x18\x18\x18`pp\x90\xfe\xfe\xfe\xe2u\xc0R\x91' '(\xc7\x11\x86a\xccy\xdb\x0b`bb\x82\x9e\x9e\x1e\x12\x89\x04\x13\x13\x13\x8c\x8f\x8f\xfb\x16}L\xefC-' '5\x89\xf2\xb6\xe9s\x1d\x93\x93\x93LLL\x90H$H$\x12\xa4\xd3i\xce\x9c9Cww7\xc0s&' '\x10_\x8a\x12\xe5u\xc6l\xa2\xb2\xe8\xb6\x8f\xd6\\\xbcx\x91xW\xe6^i`7\xb0\xf6jH\xd4L\x1b\xa0\xb3' '5~xx\x98\xde\xde^\xd7\xf8\xae\xae.^z\xe9%\x80a\xe0\xfb@\xa6\x92;\xff\x12\xd8\xae\xb5&\x9d' 'N\xa3\x94*\x90\xa8\xe9\x1e\xabT\xa2\xee\xbb\xef>\xde~\xfbm\xb4\xd6\xee\xba\xe3\\F.\x97cxx\x98' '\xce\xceN\x06\x07\x07\x19\x1d\x1d\xe5\xddw\xdf\xa5\xbb\xbb;\x0b\x1c\x01\xbe\t\x0c\xc1\x1c\xff[|\xb1$\xaa\xa9' '\xa9\t\x80M\x9b6\xb9\xeb\x92s\x05\xe0\xf2\xe5\xcbLMM\x11\x8f\xc7\x89\xc5b\x00\xbf\x06~\x04\x1c\xb2#' '\xda]:\xab8\x02\x9c\x87s\xfe\xa1i\xa1$*\x12\x89\xb0\x7f\xff~\xce\x9e=\xcb\x91#G4\xf0_\xc0' '\xf9yd\x92\x02>\xb2\x8d\x8f\x95-\x85\xe7\xb2\xac\xbc\x18\x12\xb5e\xcb\x16zzz\x9cS\xbe\x07L\xb1\x88' '\xc3\x9c/I-\xa6D-\xb6\xf1s\x06\xe0jI\x14Wa\x98\xf3\xf1\xfebK\xd4R\x03\xc0\x8d\xf5\xab%' 'QK\r\x80\xfe\xab-QWc\xfc?\xe6i\xb3;\xe5\xb6\xfd\xb9\x00\x00\x00\x00IEND\xaeB`\x82' ) images_by_filename["toolbar_images/flatview.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq' '\xde\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00' '\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd7\x08\x1a\x00\x025\xa8\xbe\xdc\xac\x00\x00\x00\x1dt' 'EXtComment\x00Created with The GIMP' '\xefd%n\x00\x00\x00\x83IDATx\xda\xed\xdaA\x0e\x00\x11\x10DQ\xed\xfew\x1e\x17\xb0d\xd2\xba' "\xdf_Z\x08?R\x15a\x0c\x00\x9d\x89\xcd\xd8wa\xce\xb4\xcc\xee'\x80\x00)\x00-p\xba\x05n\xacI" '\x06\x10@\x00\xd0\xa5\x05~\xdd\xaf\x10$\x00\xd0\x02\x8f\xb5@\xc8\x00\x02\x08\x00\x1en\x81\x90\x01\x04\x10\x00\x14' 'n\x01\xaf\xc3\x04\x10\x00Tn\x81\x90\x01\x04\x10\x00\xb8\x0b\xa4Y\xaf\x0c \x80\x00\xa0R\x0b\xf8)J\x00\x01' '@\xe5\x16\xf0:L\x00\x01@\x16\x16h3\nG\x92\x9d\xde\x16\x00\x00\x00\x00IEND\xaeB`\x82' ) images_by_filename["toolbar_images/onlychanges.png"] = ( '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq' '\xde\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00' '\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd7\x08\x1a\x03,+[\xf0V\xba\x00\x00\x00\x1dt' 'EXtComment\x00Created with The GIMP' '\xefd%n\x00\x00\x00\x9dIDATx\xda\xed\xda\xbd\r\x80 \x18\x06ap!+\x07pr\x07\xb0r' '"\xedm\x81/\xfc\xe1p8\xee\x01\xb6\x99H5C' '\x92\xa4\xb6\x1d;v\xa4\x02\x81\x80\xe8\xec\xec\x14\xf3\xe6\xcd{\x00,\x9f\xd4\xfcWN\xa7S\xe9\xe8\xe8\x10\xe7' '\xce\x9d\x13n\xb7[x<\x1e\xe1\xf5z\x85\xd7\xeb\x15>\x9fO\x0c\x0e\x0e\x8a\xb6\xb66a4\x1a\xcf\x01\xfa' '\xa7\x1d\xc1d(B\x88\xbf\\\xbdz\xb5*++\xab\xb8\xae\xae\x8e\xc2\xc2B\x83\xcb\xe5\xfab,\x16\xbb\x02' '4;\x9d\xce\r\x8d\x8d\x8dR0\x18DQ\x14\x0c\x06\x03&\x93\t\x9dN\x87\xc1`\xc0h4\xe2\xf7\xfb\xd9' '\xb9s\xe7\xf8\xe0\xe0\xe0w\x81\xde\x99\x10\x00x\xa4(\x8a\xbb\xa7\xa7\xe7\x85\x92\x92\x92\xdc\xaa\xaa*\x8cF\xe3' '\xac\x9e\x9e\x9e\x97\xd7\xad[\xb7d\xf3\xe6\xcdR0\x18\x04@\x96e\x1c\x0e\x07\x8a\xa2\x10\x0e\x87\t\x87\xc3\\' '\xbcx\x91}\xfb\xf6E{{{_\x13B\x9c\x9c|\x7fT3\x9c\x9c\xcb\x1c\x0e\xc7\xaf\x0f\x1c8`\xb7\xdb' '\xed\xb4\xb6\xb6\xf2\xe0\xc1\x03V\xaf^\xcd\xf0\xf00\x8b\x17/f\xe1\xc2\x85\xa4R)\xb6o\xdf\x1e\xef\xef\xef' '\x7fO\x08\x91\x18\x1b\x1b\xbb\x11\x8f\xc7[\x81\xcb\x80\xf2iF\xb7\nx\xa5\xac\xac,v\xe2\xc4\tq\xfc\xf8' 'qQSS#f\xcf1\x899\xf59\xe2\xf3U\x0b\x85\xcb\xe5\x12uuu1I\x92^\x9d\x18\\\xd9\x80' 'v\xa6f4\x15L\xc06\x9dN\xb7\xf0\xc6\x8d\x1b\xaa\xf2\xf2r\xb4\xf68\x1f\xae\x0c\xa1\xaa\xcc"\xf4\x8f\xdb' "\xfc\xee\xf8;i\x9f\xcf\xb7'\x95J\xfd\x18\x88\x02\xf1'H\xf6\x99\x08\x98\x80\x9f:\x9d\xce\xafn\xdc\xb8Q" ':u\xea\x14\x11\xc3\r\xfa\xe6\xdfC%C`\x14\xcc\x7f\x1b&\xa5\x8d\x10\x1aU\xe4T\x9aN`\xecY\xed' 'x\xaa\xe6\x1b\x1a\x1b\x1b%\xaf\xd7\xcb\xbb\xa1\xcbD_\xcaD\xa5\x12\xc4\xa3\x90\x0c\x80\xa8\xcd%\xf2\xbcEe' '-\xd4\xce\x19\xf7\x85+\x94\x14\xed@\xecY\t\x94\x02_\x9b\x18:\xdb\x9cN\xe7\x9a\xc7R\x0b\x06\x83x\xbc' ']\x18l\x12\xb1\x91\x14\xd1\x84\x06m&$\xd5\x90\x94 \xe9\xc8\xa0@$\x8bGo\xc6\xaci\xc1\x85O\xba' 'x\xaa),u\x9b\xddn\xff^}}\xbd\xbd\xa4\xa4D\x95\x9d\x9d\xcd\x92%Kx,5\x8dF\xc3\x85\x0b' '\xe7\xf9\xed[obX\xa4\xa0\xf9J>\xc1\x08\xa8\xb3 t\x1ftF \t\x96\xb7\x07\x92\xefvE_\x17' '\x82\x1fNE\xe2I;\xf0\xad\x8a\x8a\x8a\x96\xa6\xa6&sMM\x8d\xaa\xa8\xa8\x88\x82\x82\x02\x92\xc9$\x8a\xa2' ' \xcb2EEE\xcc\x9ae\xe6\xdc\xf9\x8b\xe8?\x9b\xe6~\xb6\x91X\x0cR\x02\xf4&\x88\x8e\x80\xc6\x00\xe3' '\xb3MR\xeeH\xb46x_y\x1f\xb8>\xe1\x88\x9fH\xc0\x9a\x93\x93\xd3\xb6{\xf7n\xab\xcdfC\xadV' '\xff\xcbXdYF\xaf\xd7\x93\x9f\x9f\x8f\xa2(455\xc5}>\xdf\xcf5\xd1\xe4\\\xf5\x82l=F5' '\xf1 \xa8$\xd0f@b\x14\xd4Yjt\xcf\xe95\x19\xb7Bu\xd1\x88\xe8\x06\x06\x9eF`Emm\xed' '+\xabV\xad\x92n\xdd\xba\xc5\x993g\x88\xc5b\x98\xcdf,\x16\x0bYYY\xc8\xb2\xcc\x96-[\xd2n' '\xb7{W:\x9d~=2\x96\n\xe7\xa6\x93+\x92s\xb34B\x03J\x08\xd42\x085(Q\x88ej\xc8' "\xfcL\xa6Q\xf2\x8e\xd5\x8e'xgr\x16x\x12\x81\xa5\r\r\r/\xcb\xb2\xcc\xfe\xfd\xfb\xc3\xdd\xdd\xdd-" '\x97.]\n\x8c\x8c\x8c\xccmhh\x90\xf4z=\x1a\x8d\x86\x96\x96\x16\x82\xc1\xe0&`\x04\xb8\x1a\xbc\x137' '\x17\xca\xc9\xaaH\xbeI\x9dRA\xea\x11\xa8u \xd2\xa0R\xe0Q\x86\x86\xfc\xe7$K\xa47R\xa6\xa4x' "\x1bH}:\xea\xf7\xfb7\x01'\x93\xc9\xa4\xd5\xe7\xf3]K$\x12" 'v\x9dNG"\x91@\x08\xf1p\x92\xbc\x14!h\xbay>PX\x9a\xa3\xfd\xf2\x07\xa5y*\xc1G$\xd0' '\x80J|D\xe2^\xa1\x19K\xd6\xd0\xd2{#\xe4\x00\x91\xa9\x12\xd1\x05\xaf\xd7\xbb\xdf\xef\xf7o\x00\x1e\x9b\xc6' '\xf3\x8a\xa2\x18\x1e\xdf\x87\xee\xeen\x02\x81\x80\x07\x08N\xaa\x8b$\x15\xb6\xdc~\xeb\xc1_\xf3G\xc3\xa4\x00I' "\x02\x91\x80x\x1223\xa0\xf4\x9a\x9f\x87A.O$\xe4i\xe3E\xab\xd5\xfa\xe1\xc9\x93'\xc5\xe0\xe0\xa0\xb8" 'r\xe5\x8a\xa8\xac\xac\x8c\x01\xeb\xa6\xf8\xbe\xd2l\x95n\xce\xdb=Wh_+\x17\xd6\x1f\x94\x8b\xbc\xef\x97\x8b' '\x92\x17rD\x86\x96S@\xd1L\x9a\x9beY~\xef\xe0\xc1\x83\xc2\xedv\x8b#G\x8e\x88\xf9\xf3\xe7G\xd4' 'j\xf5w\x9e\x12\xb1\xbf\x94_ \x8d\x16\xbeQ&\n\xde(\x13\xa5\x9f\xcbHi$~4\x8dD\xfdo\xd0' 'K\x92\xf4\x0b\x9b\xcdv\xd5f\xb3]\xd1\xe9t?\x03\x96M#\xdf\xab\x81\x8d\xb96i\xc0\x9a\xc3\x07j\x15' "\xdb\x80\x8c\x99L\xc2\xc9\x90'\xe2Sz\xe2\xcfh\xba^\xae\x06r'\x9e\x1f\xfe'\x7f\xed>5\xfe\t|" 'e\xfapjk\xd9&\x00\x00\x00\x00IEND\xaeB`\x82' ) WorkBench-1.8.2/Source/wb_source_control_providers.py000644 000765 000024 00000010417 11507143125 023327 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_source_control_providers.py ''' import wb_exceptions _source_code_providers = {} def hasProvider( name ): return _source_code_providers.has_key( name ) def getProvider( name ): return _source_code_providers[ name ] def registerProvider( provider ): _source_code_providers[ provider.name ] = provider def getProviderAboutStrings(): about_string = '' for provider in _source_code_providers.values(): about_string += provider.getAboutString() return about_string class Provider: def __init__( self, name ): self.name = name def getProjectInfo( self, app, parent ): raise wb_exceptions.InternalError( 'getProjectInfo not implemented' ) def getProjectTreeItem( self, app, project_info ): raise wb_exceptions.InternalError( 'getProjectTreeItem not implemented' ) def getListHandler( self, app, list_panel, project_info ): raise wb_exceptions.InternalError( 'getListHandler not implemented' ) def getAboutString( self ): raise wb_exceptions.InternalError( 'getAboutString not implemented' ) class ProjectInfo: def __init__( self, app, parent, provider_name ): self.app = app self.parent = parent self.provider_name = provider_name self.project_name = None self.new_file_template_dir = '' self.menu_name = None self.menu_folder = '' self.menu_folder2 = '' self.menu_folder3 = '' self.use_background_colour = False self.background_colour = (255,255,255) def init( self, project_name, **kws ): self.project_name = project_name def isChild( self, pi ): # return tree if pi is a child of this pi raise wb_exceptions.InternalError( 'isChild not implemented' ) def setBackgroundColour( self, use, colour ): self.use_background_colour = use self.background_colour = colour def readPreferences( self, get_option ): if get_option.has( 'new_file_template_dir' ): self.new_file_template_dir = get_option.getstr( 'new_file_template_dir' ) if get_option.has( 'menu_folder' ): self.menu_folder = get_option.getstr( 'menu_folder' ) if get_option.has( 'menu_folder2' ): self.menu_folder2 = get_option.getstr( 'menu_folder2' ) if get_option.has( 'menu_folder3' ): self.menu_folder3 = get_option.getstr( 'menu_folder3' ) if get_option.has( 'menu_name' ): self.menu_name = get_option.getstr( 'menu_name' ) if get_option.has( 'background_colour_red' ): self.background_colour = (get_option.getint( 'background_colour_red' ) ,get_option.getint( 'background_colour_green' ) ,get_option.getint( 'background_colour_blue' )) else: self.background_colour = (255,255,255) if get_option.has( 'use_background_colour' ): self.use_background_colour = get_option.getbool( 'use_background_colour' ) else: self.use_background_colour = False 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: pref_dict[ 'menu_name' ] = self.menu_name pref_dict[ 'menu_folder' ] = self.menu_folder pref_dict[ 'menu_folder2' ] = self.menu_folder2 pref_dict[ 'menu_folder3' ] = self.menu_folder3 if self.use_background_colour: pref_dict[ 'use_background_colour' ] = self.use_background_colour pref_dict[ 'background_colour_red' ] = self.background_colour[0] pref_dict[ 'background_colour_green' ] = self.background_colour[1] pref_dict[ 'background_colour_blue' ] = self.background_colour[2] WorkBench-1.8.2/Source/wb_show_diff_frame.py000644 000765 000024 00000005304 11507143125 021313 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_show_diff_frame.py ''' import wx import wx.stc import wb_config import wb_diff_frame class ShowDiffFrame(wx.Frame): def __init__( self, app, raw_text, title_left, title_right): self.app = app # fix up line endings CRLF to LF and CR to LF text = raw_text.replace( '\r\n', '\n' ).replace( '\r', '\n' ) try: text = text.decode( 'utf-8' ) except ValueError: pass diff_prefs = app.prefs.getDiffWindow() extra_style = 0 if diff_prefs.maximized: extra_style = wx.MAXIMIZE wx.Frame.__init__( self, None, -1, T_("Diff %(title1)s and %(title2)s") % {'title1': title_left ,'title2': title_right}, diff_prefs.frame_position, diff_prefs.getFrameSize(), wx.DEFAULT_FRAME_STYLE|extra_style ) # Reset the size after startup to workaround a potential # problem on OSX with incorrect first size event saving the # wrong size in the preferences wx.CallAfter( self.SetSize, diff_prefs.getFrameSize() ) 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 ) # Todo: should update the zoom value if the user changes it in this window... text_control.SetZoom( diff_prefs.zoom ) self.CreateStatusBar() wx.EVT_SIZE( self, self.OnFrameSize ) wx.EVT_MOVE( self, self.OnFrameMove ) self.Show( True ) def OnFrameSize( self, event ): pref = self.app.prefs.getDiffWindow() if not self.IsMaximized(): pref.setFrameSize( self.GetSize() ) event.Skip() def OnFrameMove( self, event ): pref = self.app.prefs.getDiffWindow() if not self.IsMaximized() and not self.IsIconized(): # don't use the event.GetPosition() as it # is off by the window frame thinkness pt = self.GetPosition() pref.frame_position = pt pref.maximized = self.IsMaximized() event.Skip() WorkBench-1.8.2/Source/wb_subversion_properties_dialog.py000644 000765 000024 00000022737 12717610335 024202 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_subversion_properties_dialog.py ''' import os import time import fnmatch import pysvn import wx import wb_source_control_providers import wb_subversion_history import wb_subversion_annotate import wb_tree_panel import wb_list_panel import wb_ids import wb_exceptions class SingleProperty: id_map = {} def __init__( self, dialog, name, present ): self.dialog = dialog self.name = name self.was_present = present self.starting_value = '' self.value_ctrl = None if not SingleProperty.id_map.has_key( name ): SingleProperty.id_map[ name ] = (wx.NewId(), wx.NewId()) self.ctrl_id, self.value_id = SingleProperty.id_map[ name ] self.checkbox = wx.CheckBox( dialog, self.ctrl_id, name ) self.checkbox.SetValue( present ) wx.EVT_CHECKBOX( self.dialog, self.ctrl_id, self.OnCheckBox ) self.dialog.g_sizer.Add( self.checkbox, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) def setValueCtrl( self, value_ctrl, value ): self.starting_value = value self.value_ctrl = value_ctrl self.value_ctrl.Enable( self.was_present ) self.dialog.g_sizer.Add( self.value_ctrl, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) def OnCheckBox( self, event ): self.value_ctrl.Enable( self.checkbox.IsChecked() ) def isValid( self ): return True def isModified( self ): if self.was_present ^ self.isPresent(): return True return self.checkbox.IsChecked() and self.starting_value != self.getValue() def isPresent( self ): if self.checkbox.IsChecked(): return True else: return False def getName( self ): return self.name def getValue( self ): return '' class SinglePropertyText(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=(300,-1) ), value ) def isValid( self ): if not self.isPresent(): return True text = self.value_ctrl.GetValue() if text.strip() == '': wx.MessageBox( T_('Enter a value for %s') % self.name, T_('Warning'), style=wx.OK|wx.ICON_EXCLAMATION ) return False return True 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( T_('Enter a value for %s') % self.name, T_('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 ) ctrl = wx.Choice( self.dialog, self.value_id, choices=choices, size=(150,-1) ) if self.was_present: ctrl.SetStringSelection( value ) else: ctrl.SetStringSelection( choices[0] ) self.setValueCtrl( ctrl, value ) def getValue( self ): return self.value_ctrl.GetStringSelection() class SinglePropertyNoValue(SingleProperty): def __init__( self, dialog, name, present ): SingleProperty.__init__( self, dialog, name, present ) self.setValueCtrl( wx.StaticText( self.dialog, -1, '' ), '' ) new_name_id = wx.NewId() new_value_id = wx.NewId() class PropertiesDialogBase(wx.Dialog): def __init__( self, app, parent, path, prop_dict ): wx.Dialog.__init__( self, parent, -1, path ) self.path = path self.prop_dict = prop_dict self.known_properties_names = [] self.ignore_properties_names = [] def initDialog( self ): self.g_sizer = wx.FlexGridSizer( 0, 2, 0, 0 ) self.g_sizer.AddGrowableCol( 1 ) self.property_ctrls = {} self.initKnownProperties() keys = self.prop_dict.keys() keys.sort() for prop in keys: if( prop not in self.known_properties_names and prop not in self.ignore_properties_names ): self.property_ctrls[ prop ] = SinglePropertyText( self, prop, True, self.prop_dict[ prop ] ) self.new_name_ctrl = wx.TextCtrl( self, new_name_id, '', size=(100,-1) ) self.new_value_ctrl = wx.TextCtrl( self, new_value_id, '', size=(300,-1) ) self.g_sizer.Add( self.new_name_ctrl, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( self.new_value_ctrl, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.button_ok = wx.Button( self, wx.ID_OK, T_(' OK ') ) self.button_ok.SetDefault() self.button_cancel = wx.Button( self, wx.ID_CANCEL, T_(' Cancel ') ) self.h_sizer_buttons = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer_buttons.Add( (300, 20), 1, wx.EXPAND ) self.h_sizer_buttons.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15 ) self.h_sizer_buttons.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 15 ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.v_sizer.Add( self.g_sizer, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer_buttons, 0, wx.EXPAND|wx.ALL, 5 ) wx.EVT_BUTTON( self, wx.ID_OK, self.OnOk ) wx.EVT_BUTTON( self, wx.ID_CANCEL, self.OnCancel ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() self.CentreOnParent() def OnOk( self, event ): for prop_ctrl in self.property_ctrls.values(): if not prop_ctrl.isValid(): return self.EndModal( wx.OK ) def OnCancel( self, event ): self.EndModal( wx.CANCEL ) def getModifiedProperties( self ): modified_properties = [] for prop_ctrl in self.property_ctrls.values(): if prop_ctrl.isModified(): modified_properties.append( (prop_ctrl.isPresent() ,prop_ctrl.getName() ,prop_ctrl.getValue()) ) new_name = self.new_name_ctrl.GetValue() new_value = self.new_value_ctrl.GetValue() if new_name != '': modified_properties.append( (True, new_name, new_value) ) return modified_properties class FilePropertiesDialog(PropertiesDialogBase): def __init__( self, app, parent, path, prop_dict ): 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:special'] self.ignore_properties_names = ['svn:mergeinfo'] self.initDialog() def initKnownProperties( self ): prop = 'svn:needs-lock' self.property_ctrls[ prop ] = SinglePropertyNoValue( self, prop, self.prop_dict.has_key( prop ) ) 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'] ) prop = 'svn:mime-type' self.property_ctrls[ prop ] = SinglePropertyText( self, prop, self.prop_dict.has_key( prop ), self.prop_dict.get( prop, '' ) ) prop = 'svn:keywords' self.property_ctrls[ prop ] = SinglePropertyText( self, prop, self.prop_dict.has_key( prop ), self.prop_dict.get( prop, '' ) ) 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:externals'] self.ignore_properties_names = ['svn:mergeinfo'] self.initDialog() def initKnownProperties( self ): prop = 'svn:ignore' 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, '' ) ) WorkBench-1.8.2/Source/wb_tree_panel.py000644 000765 000024 00000103357 12576040473 020327 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2011 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_tree_panel.py ''' import wx import cPickle import os import pysvn import wb_exceptions import wb_source_control_providers import wb_ids import wb_shell_commands import wb_dialogs import wb_project_dialogs import wb_config class TreeState: def __init__( self, project_info, place_holder=False ): self.debug_project_info = project_info # 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 self.unversioned = False self.need_checkin = False self.need_checkout = False self.need_upgrade = False self.conflict = False self.file_exists = False self.is_folder = True self.is_project_parent = False self.revertable = False def debugPrintState( self, print_fn, title='' ): print_fn( '-------- %s' % (title,) ) for item in self.__dict__.items(): print_fn( 'TreeState: %s -> %r' % item ) #-------------------------------------------------------------------------------- # # # WbTreePanel # # #-------------------------------------------------------------------------------- class WbTreePanel(wx.Panel): ''' WbTreePanel ''' last_position_bookmark_name = 'last position' def __init__( self, app, frame, parent ): self.app = app self.frame = frame self.list_panel = frame.list_panel try_wrapper = wb_exceptions.TryWrapperFactory( self.app.log ) wx.Panel.__init__( self, parent ) self.tree_ctrl = WbTreeCtrl( self, self.app ) 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 ) acc_init = [ (wx.ACCEL_CMD, ord('C'), wb_ids.id_SP_EditCopy), (wx.ACCEL_CMD, ord('X'), wb_ids.id_SP_EditCut), (wx.ACCEL_CMD, ord('V'), wb_ids.id_SP_EditPaste), (wx.ACCEL_CMD, ord('A'), wb_ids.id_SP_Add), (wx.ACCEL_CMD, ord('L'), wb_ids.id_SP_History), (wx.ACCEL_CMD, ord('I'), wb_ids.id_SP_Info), (wx.ACCEL_CMD, ord('P'), wb_ids.id_SP_Properties), (wx.ACCEL_CMD, ord('R'), wb_ids.id_SP_Revert), (wx.ACCEL_CMD, ord('T'), wb_ids.id_SP_UpdateTo), (wx.ACCEL_CMD, ord('U'), wb_ids.id_SP_Update), (wx.ACCEL_NORMAL, wx.WXK_DELETE, wb_ids.id_SP_Delete), (wx.ACCEL_CMD, ord('O'), wb_ids.id_Shell_Open), ] acc_tab = wx.AcceleratorTable( acc_init ) self.tree_ctrl.SetAcceleratorTable( acc_tab ) self.tree_drop_target = SvnDropTarget( self ) self.tree_ctrl.SetDropTarget( self.tree_drop_target ) wx.EVT_SIZE( self, try_wrapper( self.OnSize ) ) wx.EVT_TREE_ITEM_EXPANDING( self, self.tree_ctrl.getId(), try_wrapper( self.OnItemExpanding ) ) wx.EVT_TREE_SEL_CHANGED( self, self.tree_ctrl.GetId(), try_wrapper( self.OnTreeSelChanged ) ) wx.EVT_LEFT_DOWN( self.tree_ctrl, try_wrapper( self.OnLeftDown ) ) wx.EVT_RIGHT_DOWN( self.tree_ctrl, try_wrapper( self.OnRightDown ) ) wx.EVT_RIGHT_UP( self.tree_ctrl, try_wrapper( self.OnRightUp ) ) wx.EVT_MENU( self, wb_ids.id_Project_Add, try_wrapper( self.OnProjectAdd ) ) wx.EVT_MENU( self, wb_ids.id_Project_Delete, try_wrapper( self.OnProjectDelete ) ) wx.EVT_MENU( self, wb_ids.id_Project_Update, try_wrapper( self.OnProjectUpdate ) ) if wx.Platform in ['__WXMSW__', '__WXMAC__']: wx.EVT_TREE_BEGIN_DRAG( self.tree_ctrl, self.tree_ctrl.GetId(), self.app.eventWrapper( self.OnDragBegin ) ) wx.EVT_TREE_END_DRAG( self.tree_ctrl, self.tree_ctrl.GetId(), self.app.eventWrapper( self.OnDragEnd ) ) wx.EVT_SET_FOCUS( self.tree_ctrl, self.OnSetFocus ) wx.EVT_KILL_FOCUS( self.tree_ctrl, self.OnKillFocus ) # 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: 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 ) event.Skip() def initFrame( self ): bookmark_pi = None project_list = self.app.prefs.getProjects().getProjectList() if( self.app.auto_project_dir is not None and wb_source_control_providers.hasProvider( 'subversion' ) ): provider = wb_source_control_providers.getProvider( 'subversion' ) cmd_project_info = provider.getProjectInfo( self.app ) project_name = os.path.basename( self.app.auto_project_dir ) try: url = pysvn.Client().info( self.app.auto_project_dir ).url cmd_project_info.init( project_name, url = url, wc_path = self.app.auto_project_dir ) for project in project_list: if project.isChild( cmd_project_info ): bookmark_pi = cmd_project_info break else: self.app.prefs.getProjects().addProject( cmd_project_info ) project_list = self.app.prefs.getProjects().getProjectList() bookmark_pi = cmd_project_info except pysvn.ClientError: pass self.initTree( project_list ) if bookmark_pi is not None: self.gotoProjectInfo( bookmark_pi ) else: self.gotoBookmark( self.last_position_bookmark_name ) self.list_panel.firstTimeSelect() def OnSetFocus( self, event ): 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 'ZT: WbTreePanel OnKillFocus' #self.frame.clearEventHandler() if wb_config.focus_ring: self.Refresh() event.Skip() def OnDragBegin( self, event ): #print 'WbTreePanel.OnDragBegin' pass def OnDragEnd( self, event ): #print 'WbTreePanel.OnDragEnd' pass def getSelectionProjectHandler( self ): item = self.tree_ctrl.GetSelection() if not item: return None handler = self.tree_ctrl.GetPyData( item ) if not handler: return None return handler def getSelectionProjectInfo( self ): handler = self.getSelectionProjectHandler() if not handler: return None return handler.getProjectInfo() def getProjectTopProjectInfo( self ): item = self.tree_ctrl.GetSelection() while item: handler = self.tree_ctrl.GetPyData( item ) if handler.isProjectParent(): return handler.getProjectInfo() item = self.tree_ctrl.GetItemParent( item ) return None def savePreferences( self ): bm_prefs = self.app.prefs.getBookmarks() if bm_prefs.hasBookmark( self.last_position_bookmark_name ): bm_prefs.delBookmark( self.last_position_bookmark_name ) tree_pi = self.getSelectionProjectInfo() if tree_pi is not None: bm_prefs.addBookmark( tree_pi, name=self.last_position_bookmark_name ) def initTree( self, project_info_list ): self.__skip_update_ui = True self.root_item = self.tree_ctrl.AddRoot( T_("Projects:") ) self.tree_ctrl.SetPyData( self.root_item, RootTreeItem() ) first_item = None for project_info in project_info_list: provider = wb_source_control_providers.getProvider( project_info.provider_name ) item = self.tree_ctrl.AppendItem( self.root_item, project_info.project_name ) if first_item is None: first_item = item # mark all the project parents as such handler = provider.getProjectTreeItem( self.app, project_info ) handler.setIsProjectParent() self.tree_ctrl.SetPyData( item, handler ) self.tree_ctrl.SetItemHasChildren( item, handler.mayExpand() ) self.tree_ctrl.SetItemTextColour( item, handler.getTreeNodeColour() ) self.tree_ctrl.SortChildren( self.root_item ) #self.tree_ctrl.Expand( self.root_item ) self.__skip_update_ui = False if first_item: self.tree_ctrl.SelectItem( first_item ) # QQQ: EnsureVisible causes an assert in wxPython 2.8.12.1 on Mac OS X self.tree_ctrl.EnsureVisible( first_item ) def updateTreeSelectedItem( self ): self.updateTreeItem( self.tree_ctrl.GetSelection() ) def updateTreeItem( self, this_item ): if this_item is None: return handler = self.tree_ctrl.GetPyData( this_item ) self.tree_ctrl.SetItemTextColour( this_item, handler.getTreeNodeColour() ) pi = handler.getProjectInfo() if not pi: return project_info_list = handler.getExpansion() # remove any items that are no longer present del_items = [] found_pi = [] child_item, cookie = self.tree_ctrl.GetFirstChild( this_item ) while child_item: tree_pi = self.tree_ctrl.GetPyData( child_item ).getProjectInfo() found = False for index, pi in enumerate( project_info_list ): if tree_pi.isEqual( pi ): found = True found_pi.append( index ) break if not found: del_items.append( child_item ) child_item, cookie = self.tree_ctrl.GetNextChild( this_item, cookie ) for child_item in del_items: self.tree_ctrl.Delete( child_item ) for index, project_info in enumerate( project_info_list ): if index not in found_pi: provider = wb_source_control_providers.getProvider( project_info.provider_name ) 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, wb_config.colour_status_qqq ) self.tree_ctrl.SortChildren( this_item ) child_item, cookie = self.tree_ctrl.GetFirstChild( this_item ) while child_item: child_handler = self.tree_ctrl.GetPyData( child_item ) # Since WX 2.8.11.1 need to call updateStatus() here - guess that a callback sequence changed child_handler.updateStatus() self.tree_ctrl.SetItemHasChildren( child_item, child_handler.mayExpand() ) self.tree_ctrl.SetItemTextColour( child_item, child_handler.getTreeNodeColour() ) child_item, cookie = self.tree_ctrl.GetNextChild( this_item, cookie ) # set the has-children state self.tree_ctrl.SetItemHasChildren( this_item, len(project_info_list) > 0 ) def gotoBookmark( self, bookmark_name ): bm = self.app.prefs.getBookmarks() if not bm.hasBookmark( bookmark_name ): return pi = bm.getBookmark( bookmark_name ) self.gotoProjectInfo( pi ) def __findInTree( self, pi, parent_item ): child_item, cookie = self.tree_ctrl.GetFirstChild( parent_item ) while child_item: tree_pi = self.tree_ctrl.GetPyData( child_item ).getProjectInfo() if tree_pi.isEqual( pi ): # found it return child_item if tree_pi.isChild( pi ): # drill deeper self.tree_ctrl.Expand( child_item ) # child becomes parent at next level found_item = self.__findInTree( pi, child_item ) if found_item: return found_item # do not leave the tree expanded on nodes that did not hold the answer self.tree_ctrl.Collapse( child_item ) # keep looking at this level child_item, cookie = self.tree_ctrl.GetNextChild( parent_item, cookie ) return None def gotoProjectInfo( self, pi ): child_item = self.__findInTree( pi, self.tree_ctrl.GetRootItem() ) if child_item: self.tree_ctrl.SelectItem( child_item ) self.tree_ctrl.EnsureVisible( child_item ) def getItemHandler( self, event ): item = event.GetItem() if not item: return None handler = self.tree_ctrl.GetPyData( item ) return handler def updateTree( self, item=None ): if not item: item = self.tree_ctrl.GetRootItem() child_item, cookie = self.tree_ctrl.GetFirstChild( item ) while child_item: handler = self.tree_ctrl.GetPyData( child_item ) if handler.mayExpand(): self.tree_ctrl.SetItemHasChildren( child_item ) else: self.tree_ctrl.DeleteChildren( child_item ) self.tree_ctrl.SetItemHasChildren( child_item, False ) self.updateTree( child_item ) child_item, cookie = self.tree_ctrl.GetNextChild( child_item, cookie ) def OnActivateApp( self, is_active ): # pass to tree and let it pass to list 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 handler = self.tree_ctrl.GetPyData( item ) tree_pi = handler.getProjectInfo() if not tree_pi: return p = self.app.prefs.getView() if p.view_recursive: busy = wx.BusyInfo( T_("Refreshing view..."), self.frame ) 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() if item: self.tree_ctrl.Expand( item ) def selectTreeNodeInParent( self, filename ): item = self.tree_ctrl.GetSelection() if not item: return self.tree_ctrl.SelectItem( self.tree_ctrl.GetItemParent( item ) ) self.selectTreeNode( filename ) def selectTreeNode( self, filename ): item = self.tree_ctrl.GetSelection() if not item: return name = os.path.basename( filename ) sub_item = self.tree_ctrl.getItemByName( item, name ) if sub_item: self.tree_ctrl.SelectItem( sub_item ) #---------- Event handlers ---------------------------------------------- def OnSize( self, event ): w, h = self.GetClientSizeTuple() self.tree_ctrl.SetDimensions( 0, 0, w, h ) # Handler for when a new selection is made in the tree control child def OnTreeSelChanged( self, event ): if wb_config.debug_selection: print 'ZT: WbTreePanel OnTreeSelChanged __skip_update_ui', self.__skip_update_ui if self.__skip_update_ui: return tree_item = self.getItemHandler( event ) self.changedSelection( tree_item ) def changedSelection( self, tree_item ): if wb_config.debug_selection: print 'ZT: WbTreePanel changedSelection' self.frame.clearUpdateUiState() self.frame.setEventHandler( self ) # Update the contents of the list control to reflect the new selection if not tree_item: return pi = tree_item.getProjectInfo() # see if already handling this project if pi == self.list_panel.getProjectInfo(): return; p = self.app.prefs.getView() if p.view_recursive: busy = wx.BusyInfo( T_("Refreshing view..."), self.frame ) if pi is None: self.list_panel.clearHandler() else: provider = wb_source_control_providers.getProvider( pi.provider_name ) list_handler = provider.getListHandler( self.app, self.list_panel, tree_item.getProjectInfo() ) # if this tree item is a project parent then set the list panel as one if tree_item.isProjectParent(): list_handler.setIsProjectParent() # draw the list - its updates the status info self.list_panel.setHandler( list_handler ) # fix up the tree if required self.updateTreeItem( self.tree_ctrl.GetSelection() ) def OnItemExpanding( self, event ): this_item = event.GetItem() item = self.tree_ctrl.GetSelection() if this_item is item: return if not self.tree_ctrl.ItemHasChildren( this_item ): return if self.tree_ctrl.GetChildrenCount( this_item, False ) != 0: return handler = self.getItemHandler( event ) if not handler: return handler.getProjectInfo().updateStatus() self.updateTreeItem( this_item ) def isTreeHandler( self ): return True def isListHandler( self ): return False def getUpdateUiState( self ): item = self.tree_ctrl.GetSelection() if not item: return None handler = self.tree_ctrl.GetPyData( item ) if not handler: return None return handler.getState() def OnLeftDown( self, event ): if wb_config.debug_selection: print 'OnLeftDown', item = self.tree_ctrl.GetSelection() if not item: if wb_config.debug_selection: print 'no item selected' return handler = self.tree_ctrl.GetPyData( item ) if not handler: if wb_config.debug_selection: print 'no handler' return if wb_config.debug_selection: print 'handler %r' % handler event.Skip() def OnRightDown( self, event ): # move the selection to the clicked on node point = event.GetPosition(); item, flags = self.tree_ctrl.HitTest( point ) if (flags&wx.TREE_HITTEST_NOWHERE) != 0: return self.tree_ctrl.SelectItem( item ) self.changedSelection( self.tree_ctrl.GetPyData( item ) ) if wx.Platform == '__WXMAC__': self.popUpContextMenu( item, point ) def OnRightUp( self, event ): # 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 ) ) if wx.Platform != '__WXMAC__': self.popUpContextMenu( item, point ) def popUpContextMenu( self, item, point ): handler = self.tree_ctrl.GetPyData( item ) if not handler: return None self.frame.getUpdateUiState() menu = handler.getContextMenu( self.frame.ui_state_tree ) if handler.isProjectParent(): menu.AppendSeparator() menu.Append( wb_ids.id_Project_Update, T_('Project Settings') ) menu.Append( wb_ids.id_Project_Delete, T_('Delete Project') ) self.tree_ctrl.PopupMenu( menu, point ) menu.Destroy() def OnProjectAdd( self, event ): dialog = wb_project_dialogs.AddProjectDialog( self.app, self ) rc = dialog.ShowModal() if rc == wx.ID_OK: project_info = dialog.getProjectInfo() self.app.prefs.getProjects().addProject( project_info ) self.app.savePreferences() provider = wb_source_control_providers.getProvider( project_info.provider_name ) item = self.tree_ctrl.AppendItem( self.root_item, project_info.project_name ) # mark all the project parents as such handler = provider.getProjectTreeItem( self.app, project_info ) handler.setIsProjectParent() self.tree_ctrl.SetPyData( item, handler ) if handler.mayExpand(): self.tree_ctrl.SetItemHasChildren( item ) else: self.tree_ctrl.SetItemHasChildren( item, False ) self.tree_ctrl.SortChildren( item ) self.tree_ctrl.SelectItem( item ) self.tree_ctrl.EnsureVisible( item ) def OnProjectDelete( self, event ): item = self.tree_ctrl.GetSelection() if not item: return handler = self.tree_ctrl.GetPyData( item ) tree_pi = handler.getProjectInfo() if not tree_pi: return dialog = wb_dialogs.ConfirmAction( self, T_('Delete Project'), [('', tree_pi.project_name)] ) rc = dialog.ShowModal() if rc == wx.ID_OK: # get rid of the old self.app.prefs.getProjects().delProject( tree_pi ) self.app.savePreferences() self.tree_ctrl.Delete( item ) # back to the top self.tree_ctrl.SelectItem( self.root_item ) def OnProjectUpdate( self, event ): item = self.tree_ctrl.GetSelection() if not item: return handler = self.tree_ctrl.GetPyData( item ) tree_pi = handler.getProjectInfo() if not tree_pi: return dialog = wb_project_dialogs.UpdateProjectDialog( self.app, self, tree_pi ) rc = dialog.ShowModal() if rc == wx.ID_OK: # get rid of the old self.app.prefs.getProjects().delProject( tree_pi ) self.tree_ctrl.Delete( item ) # just like the add code project_info = dialog.getProjectInfo() self.app.prefs.getProjects().addProject( project_info ) self.app.savePreferences() provider = wb_source_control_providers.getProvider( project_info.provider_name ) item = self.tree_ctrl.AppendItem( self.root_item, project_info.project_name ) # mark all the project parents as such handler = provider.getProjectTreeItem( self.app, project_info ) handler.setIsProjectParent() self.tree_ctrl.SetPyData( item, handler ) if handler.mayExpand(): self.tree_ctrl.SetItemHasChildren( item ) else: self.tree_ctrl.SetItemHasChildren( item, False ) self.tree_ctrl.SortChildren( self.root_item ) self.tree_ctrl.SelectItem( item ) # command handlers def OnCommandShell( self ): item = self.tree_ctrl.GetSelection() if not item: return handler = self.tree_ctrl.GetPyData( item ) if not handler: return wb_shell_commands.CommandShell( self.app, handler.getProjectInfo() ) def OnFileBrowser( self ): item = self.tree_ctrl.GetSelection() if not item: return handler = self.tree_ctrl.GetPyData( item ) if not handler: return wb_shell_commands.FileBrowser( self.app, handler.getProjectInfo() ) def OnSpCreateBranch( self ): return self.Sp_Dispatch( 'Cmd_Dir_CreateBranch' ) def OnSpCreateTag( self ): return self.Sp_Dispatch( 'Cmd_Dir_CreateTag' ) def OnSpEditCopy( self ): return self.Sp_Dispatch( 'Cmd_Dir_EditCopy' ) def OnSpEditCut( self ): return self.Sp_Dispatch( 'Cmd_Dir_EditCut' ) def OnSpEditPaste( self ): return self.Sp_Dispatch( 'Cmd_Dir_EditPaste' ) def OnSpAdd( self ): return self.Sp_Dispatch( 'Cmd_Dir_Add' ) def OnSpCleanup( self ): return self.Sp_Dispatch( 'Cmd_Dir_Cleanup' ) def OnSpCheckin( self ): return self.Sp_Dispatch( 'Cmd_Dir_Checkin' ) def OnSpCheckout( self ): return self.Sp_Dispatch( 'Cmd_Dir_Checkout' ) def OnSpCheckoutTo( self ): return self.Sp_Dispatch( 'Cmd_Dir_CheckoutTo' ) def OnSpDelete( self ): return self.Sp_Dispatch( 'Cmd_Dir_Delete' ) def OnSpDiffWorkBase( self ): return self.Sp_Dispatch( 'Cmd_Dir_DiffWorkBase' ) def OnSpDiffWorkHead( self ): return self.Sp_Dispatch( 'Cmd_Dir_DiffWorkHead' ) def OnSpHistory( self ): return self.Sp_Dispatch( 'Cmd_Dir_History' ) def OnSpInfo( self ): return self.Sp_Dispatch( 'Cmd_Dir_Info' ) def OnSpMkdir( self ): return self.Sp_Dispatch( 'Cmd_Dir_Mkdir' ) def OnSpNewFile( self ): return self.Sp_Dispatch( 'Cmd_Dir_NewFile' ) def OnSpProperties( self ): return self.Sp_Dispatch( 'Cmd_Dir_Properties' ) def OnSpRename( self ): return self.Sp_Dispatch( 'Cmd_Dir_Rename' ) def OnReportUpdates( self ): return self.Sp_Dispatch( 'Cmd_Dir_ReportUpdates' ) def OnReportLocksWc( self ): return self.Sp_Dispatch( 'Cmd_Dir_ReportLocksWc' ) def OnReportLocksRepos( self ): return self.Sp_Dispatch( 'Cmd_Dir_ReportLocksRepos' ) def OnReportBranchChanges( self ): return self.Sp_Dispatch( 'Cmd_Dir_ReportBranchChanges' ) def OnSpRevert( self ): return self.Sp_Dispatch( 'Cmd_Dir_Revert' ) def OnSpUpdate( self ): return self.Sp_Dispatch( 'Cmd_Dir_Update' ) def OnSpUpdateTo( self ): return self.Sp_Dispatch( 'Cmd_Dir_UpdateTo' ) def OnSpUpgrade( self ): return self.Sp_Dispatch( 'Cmd_Dir_Upgrade' ) #---------------------------------------- def OnSpCopy( self, filename_list ): return self.Sp_DispatchDrop( 'Cmd_Dir_Copy', filename_list ) def OnSpMove( self, filename_list ): return self.Sp_DispatchDrop( 'Cmd_Dir_Move', filename_list ) #---------------------------------------- def Sp_Dispatch( self, sp_func_name ): self.app.trace.info( 'WbTreePanel.Sp_Dispatch( %s ) event' % sp_func_name ) item = self.tree_ctrl.GetSelection() if not item: return None handler = self.tree_ctrl.GetPyData( item ) if not handler: return None self.app.trace.info( 'WbTreePanel.Sp_Dispatch( %s ) calling' % sp_func_name ) fn = getattr( handler, sp_func_name, None ) if fn is None: print 'Not implemented', sp_func_name return None else: return fn() def Sp_DispatchDrop( self, sp_func_name, filename_list ): self.app.trace.info( 'WbTreePanel.Sp_DispatchDrop( %s )' % sp_func_name ) item = self.tree_ctrl.GetSelection() if not item: return None handler = self.tree_ctrl.GetPyData( item ) if not handler: return None self.app.trace.info( 'WbTreePanel.Sp_DispatchDrop( %s ) calling' % sp_func_name ) return getattr( handler, sp_func_name )( filename_list ) class SvnDropTarget(wx.PyDropTarget): def __init__( self, window ): wx.PyDropTarget.__init__( self ) self.tree = window # specify the type of data we will accept self.df = wx.CustomDataFormat("WorkBench.svn_wc_path") self.data = wx.CustomDataObject(self.df) self.SetDataObject(self.data) self.start_item = None # some virtual methods that track the progress of the drag def OnEnter( self, x, y, d ): #print 'OnEnter: %d, %d, %d' % (x, y, d) return d def OnLeave( self ): #print 'OnLeave' if self.start_item is not None: #print 'OnLeave: have start_item' if self.start_item != self.tree.tree_ctrl.GetSelection(): #print 'OnLeave: restoring start_item' self.tree.tree_ctrl.SelectItem( self.start_item ) self.tree.tree_ctrl.EnsureVisible( self.start_item ) self.start_item = None def OnDrop( self, x, y ): #print 'OnDrop: %d %d' % (x, y) item, flags = self.tree.tree_ctrl.HitTest( (x, y) ) if (flags & wx.TREE_HITTEST_ONITEMLABEL) == 0: return False return True def OnDragOver( self, x, y, d ): #print 'OnDragOver: %d, %d, %d' % (x, y, d) item, flags = self.tree.tree_ctrl.HitTest( (x, y) ) if self.start_item is None: self.start_item = self.tree.tree_ctrl.GetSelection() if flags & wx.TREE_HITTEST_ONITEMLABEL: if item != self.tree.tree_ctrl.GetSelection(): self.tree.tree_ctrl.SelectItem( item ) self.tree.tree_ctrl.EnsureVisible( item ) return d return wx.DragNone # Called when OnDrop returns True. We need to get the data and # do something with it. def OnData( self, x, y, d ): #print 'OnData: %d, %d, %d' % (x, y, d) # copy the data from the drag source to our data object if self.GetData(): # convert it back to a list of lines and give it to the viewer all_filenames = cPickle.loads( self.data.GetData() ) if d == wx.DragCopy: self.tree.OnSpCopy( all_filenames ) elif d == wx.DragMove: self.tree.OnSpMove( all_filenames ) # what is returned signals the source what to do # with the original data (move, copy, etc.) In this # case we just return the suggested value given to us. return d #-------------------------------------------------------------------------------- # # # WbTreeCtrl # # #-------------------------------------------------------------------------------- class WbTreeCtrl(wx.TreeCtrl): def __init__( self, parent, app ): self.app = app self.id_tree = wx.NewId() if wx.Platform == '__WXMSW__': style = wx.TR_HAS_BUTTONS else: style = wx.TR_HAS_BUTTONS|wx.TR_HIDE_ROOT wx.TreeCtrl.__init__( self, parent, self.id_tree, wx.DefaultPosition, wx.DefaultSize, style ) self.item_last_used = None try_wrapper = wb_exceptions.TryWrapperFactory( self.app.log ) # Returns the window identifier for this control def getId( self ): return self.id_tree # Searches the child items of a tree item for the child that matches the # specified name # Returns the tree item id of the found item or None def getItemByName(self, item_parent, name): #Iterate through the child items looking for one with the correct name child_item, ctx = self.GetFirstChild( item_parent ) if child_item and self.GetItemText( child_item ) == name: return child_item else: child_item, ctx = self.GetNextChild( item_parent, ctx ) while child_item: if self.GetItemText( child_item ) == name: return child_item next_child_item, ctx = self.GetNextChild( item_parent, ctx ) if next_child_item == child_item: self.app.log.debug("GetItemByName - Infinite loop detected... breaking") break child_item = next_child_item 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 return 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() ) #-------------------------------------------------------------------------------- # # # TreeProjectItem # # #-------------------------------------------------------------------------------- class TreeProjectItem: def __init__( self ): # true if this is the parent of a project tree self.is_project_parent = False def setIsProjectParent( self ): self.is_project_parent = True def isProjectParent( self ): return self.is_project_parent def getState( self ): raise wb_exceptions.InternalError( 'getState not implemented' ) def getProjectInfo( self ): raise wb_exceptions.InternalError( 'getProjectInfo not implemented' ) def getExpansion( self ): raise wb_exceptions.InternalError( 'getExpanding not implemented' ) def getContextMenu( self, state ): raise wb_exceptions.InternalError( 'getContextMenu not implemented' ) class RootTreeItem(TreeProjectItem): def __init__( self ): TreeProjectItem.__init__( self ) def getProjectInfo( self ): return None def getExpansion( self ): return [] def getContextMenu( self, state ): menu = wx.Menu() menu.Append( wb_ids.id_Project_Add, T_('&Add Project') ) return menu def getState( self ): return None def isProjectParent( self ): return False WorkBench-1.8.2/Source/wb_bookmarks_dialogs.py000644 000765 000024 00000032710 11507143125 021664 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2005-2010 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_bookmarks_dialogs.py ''' import wx import string import wb_exceptions # # all dialog changes are recorded in the BookmarkProperties # object when the user clicks OK the changes are set into the # preferences. Cancel leaves the preferences unchanged. # class BookmarkProperties: def __init__( self, pi ): self.pi = pi self.wc_path = pi.wc_path self.menu_name = pi.menu_name self.menu_folder = pi.menu_folder self.menu_folder2 = pi.menu_folder2 self.menu_folder3 = pi.menu_folder3 self.updated() def updated( self ): self.menu_list = _keyBookmarks( self ) while len( self.menu_list ) < 4: self.menu_list.append( '' ) def _keyBookmarks( a ): k = [] if a.menu_folder != '': k.append( a.menu_folder ) if a.menu_folder2 != '': k.append( a.menu_folder2 ) if a.menu_folder3 != '': k.append( a.menu_folder3 ) k.append( a.menu_name ) return k class BookmarkManageDialog(wx.Dialog): COL_MENU1 = 0 COL_MENU2 = 1 COL_MENU3 = 2 COL_MENU4 = 3 COL_WC_PATH = 4 def __init__( self, parent, app, bookmark_prefs ): wx.Dialog.__init__( self, parent, -1, T_("Manage Bookmarks") ) self.app = app self.bookmark_prefs = bookmark_prefs self.initControls() def initControls( self ): self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.h_sizer1 = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer2 = wx.BoxSizer( wx.HORIZONTAL ) self.g_sizer = wx.FlexGridSizer( 0, 2, 0, 0 ) self.g_sizer.AddGrowableCol( 1 ) self.bookmark_list_ctrl = BookmarkListCtrl( self ) self.bookmark_list_ctrl.InsertColumn( self.COL_MENU1, T_("Menu") ) self.bookmark_list_ctrl.InsertColumn( self.COL_MENU2, T_("Menu") ) self.bookmark_list_ctrl.InsertColumn( self.COL_MENU3, T_("Menu") ) self.bookmark_list_ctrl.InsertColumn( self.COL_MENU4, T_("Menu") ) self.bookmark_list_ctrl.InsertColumn( self.COL_WC_PATH, T_("WC Path") ) self.bookmark_list_ctrl.SetColumnWidth( self.COL_MENU1, 150 ) self.bookmark_list_ctrl.SetColumnWidth( self.COL_MENU2, 150 ) self.bookmark_list_ctrl.SetColumnWidth( self.COL_MENU3, 150 ) self.bookmark_list_ctrl.SetColumnWidth( self.COL_MENU4, 150 ) self.bookmark_list_ctrl.SetColumnWidth( self.COL_WC_PATH, 600 ) self.all_bookmark_props = [BookmarkProperties( self.bookmark_prefs.getBookmark( name ) ) for name in self.bookmark_prefs.getBookmarkNames() if name != 'last position'] self.sortBookmarks() self.bookmark_deleted_list = [] self.bookmark_list_ctrl.SetItemCount( len( self.all_bookmark_props ) ) self.button_delete = wx.Button( self, wx.NewId(), T_(" Delete ") ) self.button_delete.Enable( False ) self.button_props = wx.Button( self, wx.NewId(), T_(" Properties ") ) self.button_props.Enable( False ) self.button_ok = wx.Button( self, wx.ID_OK, T_(" OK ") ) self.button_cancel = wx.Button( self, wx.ID_CANCEL, T_(" Cancel ") ) self.button_ok.SetDefault() self.h_sizer1.Add( self.button_props, 0, wx.EXPAND|wx.WEST, 15) self.h_sizer1.Add( self.button_delete, 0, wx.EXPAND|wx.WEST, 15) self.h_sizer1.Add( (60, 20), 1, wx.EXPAND) self.h_sizer2.Add( (60, 20), 1, wx.EXPAND) self.h_sizer2.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15) self.h_sizer2.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 2 ) self.v_sizer.Add( self.g_sizer, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.bookmark_list_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer1, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer2, 0, wx.EXPAND|wx.ALL, 5 ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() self.CentreOnParent() try_wrapper = wb_exceptions.TryWrapperFactory( self.app.log ) wx.EVT_LIST_ITEM_ACTIVATED( self, self.bookmark_list_ctrl.GetId(), try_wrapper( self.OnActivateBookmark ) ) wx.EVT_LIST_ITEM_SELECTED( self, self.bookmark_list_ctrl.GetId(), try_wrapper( self.OnSelectBookmark ) ) wx.EVT_LIST_ITEM_DESELECTED( self, self.bookmark_list_ctrl.GetId(), try_wrapper( self.OnDeselectBookmark ) ) wx.EVT_BUTTON( self, self.button_delete.GetId(), try_wrapper( self.OnDeleteBookmark ) ) wx.EVT_BUTTON( self, self.button_props.GetId(), try_wrapper( self.OnPropertiesBookmark ) ) wx.EVT_BUTTON( self, wx.ID_OK, try_wrapper( self.OnOk ) ) wx.EVT_BUTTON( self, wx.ID_CANCEL, try_wrapper( self.OnCancel ) ) def sortBookmarks( self ): self.all_bookmark_props.sort( key=_keyBookmarks ) def OnGetItemText( self, item, col ): props = self.all_bookmark_props[ item ] if col in [self.COL_MENU1, self.COL_MENU2, self.COL_MENU3, self.COL_MENU4]: return props.menu_list[ col ] elif col == self.COL_WC_PATH: return props.wc_path else: return '' def OnGetItemImage( self, item ): return -1 def OnGetItemAttr( self, item ): return None def OnOk( self, event ): self.EndModal( wx.ID_OK ) def OnCancel( self, event ): self.EndModal( wx.ID_CANCEL ) def OnActivateBookmark( self, event ): self.OnSelectBookmark( event ) self.OnPropertiesBookmark( event ) def OnSelectBookmark( self, event ): self.button_delete.Enable( True ) self.button_props.Enable( True ) def OnDeselectBookmark( self, event ): if self.bookmark_list_ctrl.GetNextSelected(-1) < 0: self.button_delete.Enable( False ) self.button_props.Enable( False ) def OnDeleteBookmark( self, event ): # delete all selected bookmarks. bookmark_index = -1 selected_bookmarks = [] while True: bookmark_index = self.bookmark_list_ctrl.GetNextSelected( bookmark_index ) if bookmark_index < 0: break selected_bookmarks.append( bookmark_index ) for bookmark_index in reversed( selected_bookmarks ): self.bookmark_deleted_list.append( self.all_bookmark_props.pop( bookmark_index ) ) # if there is a bookmark left after the last deletion, # select it, so pressing delete multiple times works. if len( self.all_bookmark_props ) > 0: self.bookmark_list_ctrl.Select( bookmark_index ) self.bookmark_list_ctrl.SetItemCount( len( self.all_bookmark_props ) ) self.bookmark_list_ctrl.RefreshItems( 0, len( self.all_bookmark_props )-1 ) else: self.button_delete.Enable( False ) self.button_props.Enable( False ) self.bookmark_list_ctrl.SetItemCount( 0 ) def OnPropertiesBookmark( self, event ): refresh_list = False bookmark_index = -1 while True: bookmark_index = self.bookmark_list_ctrl.GetNextSelected( bookmark_index ) if bookmark_index < 0: break dialog = BookmarkPropertiesDialog( self, self.app, self.all_bookmark_props[ bookmark_index ] ) rc = dialog.ShowModal() if rc == wx.ID_OK: refresh_list = True if refresh_list: self.sortBookmarks() self.bookmark_list_ctrl.RefreshItems( 0, len( self.all_bookmark_props )-1 ) def setPreferences( self ): for props in self.getDeletedBookmarkList(): self.bookmark_prefs.delBookmark( props.wc_path ) for props in self.all_bookmark_props: props.pi.menu_name = props.menu_name props.pi.menu_folder = props.menu_folder props.pi.menu_folder2 = props.menu_folder2 props.pi.menu_folder3 = props.menu_folder3 def getDeletedBookmarkList( self ): return self.bookmark_deleted_list class BookmarkListCtrl(wx.ListCtrl): def __init__( self, parent ): wx.ListCtrl.__init__( self, parent, -1, size=(700,400), style=wx.LC_REPORT|wx.LC_VIRTUAL ) self.parent = parent def OnGetItemText( self, item, col ): return self.parent.OnGetItemText( item, col ) def OnGetItemImage( self, item ): return self.parent.OnGetItemImage( item ) def OnGetItemAttr( self, item ): return self.parent.OnGetItemAttr( item ) class BookmarkPropertiesDialog(wx.Dialog): def __init__( self, parent, app, props ): wx.Dialog.__init__( self, parent, -1, T_("Bookmark Properties") ) self.parent = parent self.app = app self.props = props self.initControls() def initControls( self ): all_menu_folders1 = set( [prop.menu_folder for prop in self.parent.all_bookmark_props] ) all_menu_folders2 = set( [prop.menu_folder2 for prop in self.parent.all_bookmark_props] ) all_menu_folders3 = set( [prop.menu_folder3 for prop in self.parent.all_bookmark_props] ) all_menu_folders = set() all_menu_folders |= all_menu_folders1 all_menu_folders |= all_menu_folders2 all_menu_folders |= all_menu_folders3 all_menu_folders = sorted( all_menu_folders, key=string.lower ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.h_sizer2 = wx.BoxSizer( wx.HORIZONTAL ) self.g_sizer = wx.FlexGridSizer( 0, 2, 0, 0 ) self.g_sizer.AddGrowableCol( 1 ) self.wc_path_ctrl = wx.TextCtrl( self, -1, self.props.wc_path, size=(500, -1), style=wx.TE_READONLY ) self.menu_folder1_ctrl = wx.ComboBox( self, -1, self.props.menu_folder, style=wx.CB_DROPDOWN, choices=all_menu_folders, size=(300, -1) ) self.menu_folder2_ctrl = wx.ComboBox( self, -1, self.props.menu_folder2, style=wx.CB_DROPDOWN, choices=all_menu_folders, size=(300, -1) ) self.menu_folder3_ctrl = wx.ComboBox( self, -1, self.props.menu_folder3, style=wx.CB_DROPDOWN, choices=all_menu_folders, size=(300, -1) ) self.menu_name_ctrl = wx.TextCtrl( self, -1, self.props.menu_name, size=(300, -1) ) self.menu_name_ctrl.SetFocus() self.menu_name_ctrl.SetSelection( -1, -1 ) self.static_text1 = wx.StaticText( self, -1, T_("WC Path: "), style=wx.ALIGN_RIGHT) self.g_sizer.Add( self.static_text1, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( self.wc_path_ctrl, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.static_text3 = wx.StaticText( self, -1, T_("Menu1: "), style=wx.ALIGN_RIGHT) self.g_sizer.Add( self.static_text3, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( self.menu_folder1_ctrl, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.static_text4 = wx.StaticText( self, -1, T_("Menu2: "), style=wx.ALIGN_RIGHT) self.g_sizer.Add( self.static_text4, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( self.menu_folder2_ctrl, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.static_text5 = wx.StaticText( self, -1, T_("Menu3: "), style=wx.ALIGN_RIGHT) self.g_sizer.Add( self.static_text5, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( self.menu_folder3_ctrl, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.static_text2 = wx.StaticText( self, -1, T_("Menu Name: "), style=wx.ALIGN_RIGHT) self.g_sizer.Add( self.static_text2, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( self.menu_name_ctrl, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.button_ok = wx.Button( self, wx.ID_OK, T_(" OK ") ) self.button_cancel = wx.Button( self, wx.ID_CANCEL, T_(" Cancel ") ) self.button_ok.SetDefault() self.h_sizer2.Add( (60, 20), 1, wx.EXPAND) self.h_sizer2.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15) self.h_sizer2.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 2 ) self.v_sizer.Add( self.g_sizer, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer2, 0, wx.EXPAND|wx.ALL, 5 ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() self.CentreOnParent() try_wrapper = wb_exceptions.TryWrapperFactory( self.app.log ) wx.EVT_BUTTON( self, wx.ID_OK, try_wrapper( self.OnOk ) ) wx.EVT_BUTTON( self, wx.ID_CANCEL, try_wrapper( self.OnCancel ) ) def updateProps( self ): self.props.menu_name = self.menu_name_ctrl.GetValue() self.props.menu_folder = self.menu_folder1_ctrl.GetValue() self.props.menu_folder2 = self.menu_folder2_ctrl.GetValue() self.props.menu_folder3 = self.menu_folder3_ctrl.GetValue() self.props.updated() def OnOk( self, event ): self.updateProps() self.EndModal( wx.ID_OK ) def OnCancel( self, event ): self.EndModal( wx.ID_CANCEL ) WorkBench-1.8.2/Source/wb_diff_processor.py000644 000765 000024 00000017023 12617361653 021214 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_diff_processor.py ''' import wx class DiffOneSideProcessor: def __init__( self, name, text_body ): self.name = name self.text_body = text_body self.diff_line_numbers = text_body.diff_line_numbers self.line_number = 0 self.last_line_number = -1 self.changed_lines = [] self.current_changed_block = -1 self.current_change_marker = -1 def _markChangeCurrentLine( self ): line_number = self.text_body.GetLineCount() - 1 if( self.last_line_number != line_number and self.last_line_number != (line_number - 1) ): self.changed_lines.append( line_number ) self.last_line_number = line_number def moveNextChange( self ): self.current_changed_block += 1 self._moveToChange() def movePrevChange( self ): self.current_changed_block -= 1 self._moveToChange() def _moveToChange( self ): if len(self.changed_lines) == 0: return if self.current_changed_block >= len(self.changed_lines): self.current_changed_block = 0 elif self.current_changed_block < 0: self.current_changed_block = len(self.changed_lines) - 1 line_number = self.changed_lines[self.current_changed_block] top_line = line_number - 3 if top_line < 1: top_line = 1 self.text_body.ScrollToLine( top_line ) self.text_body.GotoLine( line_number ) def getCurrentChangeLine( self ): return self.changed_lines[ self.current_changed_block ] def updateCurrentChangeMarker( self, line ): if self.current_change_marker != -1: self.diff_line_numbers.ChangeLineStyle( self.current_change_marker, self.diff_line_numbers.style_line_numbers ) self.current_change_marker = line self.diff_line_numbers.ChangeLineStyle( self.current_change_marker, self.diff_line_numbers.style_line_numbers_for_diff ) #-------------------------------------------------------------------------------- def _addLineNumber( self ): self.line_number = self.line_number + 1 self.diff_line_numbers.InsertStyledText( '%5d\n' % (self.line_number,), self.diff_line_numbers.style_line_numbers ) def _addBlankLineNumber( self, isChange=1 ): self.diff_line_numbers.InsertStyledText( '%5s\n' % '', self.diff_line_numbers.style_line_numbers ) def addNormalLine( self, line ): line_number = self.text_body.LineFromPosition( self.text_body.GetLength() ) self.text_body.SetFoldLine( line_number, True ) self._addLineNumber() self.text_body.InsertStyledText( line+'\n', self.text_body.style_line_normal ) def addGapLine( self ): self._markChangeCurrentLine() self._markChangeCurrentLine() self._addBlankLineNumber() line_number = self.text_body.LineFromPosition( self.text_body.GetLength() ) self.text_body.SetFoldLine( line_number, False ) self.text_body.InsertStyledText( '\n', self.text_body.style_line_normal ) def addInsertedLine( self, line ): self._markChangeCurrentLine() self._addLineNumber() line_number = self.text_body.LineFromPosition( self.text_body.GetLength() ) self.text_body.SetFoldLine(line_number, False) self.text_body.InsertStyledText( line+'\n', self.text_body.style_line_insert ) def addDeletedLine( self, line ): self._markChangeCurrentLine() self._addLineNumber() line_number = self.text_body.LineFromPosition( self.text_body.GetLength() ) self.text_body.SetFoldLine(line_number, False) self.text_body.InsertStyledText( line+'\n', self.text_body.style_line_delete ) def addChangedLineBegin( self ): self._markChangeCurrentLine() self._addLineNumber() line_number = self.text_body.LineFromPosition( self.text_body.GetLength() ) self.text_body.SetFoldLine( line_number, False ) def addChangedLineReplace( self, text ): self.text_body.InsertStyledText( text, self.text_body.style_replace_changed ) def addChangedLineDelete( self, old ): self.text_body.InsertStyledText( old, self.text_body.style_replace_delete ) def addChangedLineInsert( self, new ): self.text_body.InsertStyledText( new, self.text_body.style_replace_insert ) def addChangedLineEqual( self, text ): self.text_body.InsertStyledText( text, self.text_body.style_replace_equal ) def addChangedLineEnd( self ): self.text_body.InsertStyledText( '\n', self.text_body.style_line_normal ) #-------------------------------------------------------------------------------- def addEnd( self ): self.text_body.SetReadOnly( 1 ) #-------------------------------------------------------------------------------- class DiffProcessor: 'DiffProcessor' def __init__( self, text_body_left, text_body_right ): self.processor_left = DiffOneSideProcessor( 'Diff Left', text_body_left ) self.processor_right = DiffOneSideProcessor( 'Diff Right', text_body_right ) def addNormalLine( self, line ): self.processor_left.addNormalLine( line ) self.processor_right.addNormalLine( line ) def addInsertedLine( self, line ): self.processor_left.addGapLine() self.processor_right.addInsertedLine( line ) def addDeletedLine( self, line ): self.processor_left.addDeletedLine( line ) self.processor_right.addGapLine() def addChangedLineBegin( self ): self.processor_left.addChangedLineBegin() self.processor_right.addChangedLineBegin() def addChangedLineReplace( self, old, new ): self.processor_left.addChangedLineReplace( old ) self.processor_right.addChangedLineReplace( new ) def addChangedLineDelete( self, old ): self.processor_left.addChangedLineDelete( old ) def addChangedLineInsert( self, new ): self.processor_right.addChangedLineInsert( new ) def addChangedLineEqual( self, text ): self.processor_left.addChangedLineEqual( text ) self.processor_right.addChangedLineEqual( text ) def addChangedLineEnd( self ): self.processor_left.addChangedLineEnd() self.processor_right.addChangedLineEnd() def addEnd( self ): self.processor_left.addEnd() self.processor_right.addEnd() self.processor_left.text_body.SetFocus() def moveNextChange( self ): self.processor_left.moveNextChange() line = self.processor_left.getCurrentChangeLine() self.processor_left.updateCurrentChangeMarker( line ) self.processor_right.updateCurrentChangeMarker( line ) def movePrevChange( self ): self.processor_left.movePrevChange() line = self.processor_left.getCurrentChangeLine() self.processor_left.updateCurrentChangeMarker( line ) self.processor_right.updateCurrentChangeMarker( line ) def toggleViewWhiteSpace( self ): self.processor_left.text_body.ToggleViewWhiteSpace() def getChangeCount( self ): return len( self.processor_left.changed_lines ) def getCurrentChange( self ): return self.processor_left.current_changed_block + 1 WorkBench-1.8.2/Source/wb.icns000644 000765 000024 00000122436 10365523613 016430 0ustar00barrystaff000000 000000 icnsics#H88||888||8is32K&},D{kFuF@3 'dpƗJ5VNKR{'@Z9f1!! mZ!!]Υ 1 և ZF!=: &},D{kFuF@3 'dpƗJ5VNKR{'@ZJfZ{ mZkk ]Υ@[1 ևJ1ZFVS=:+&},D{kFuF@3 'dpƗJ5VNKJs'@J1 f!!)! mZ))!]Υ) 1 և11)ZF)=: s8mk)cBZRRc))91Jc))kZc{Jk!!BZBƌRJICN#?8?x?|>?|??8?x?|>?|?il32 R91Z 9R!s "B9 ֽsRRsRks1 J9 {ƵJ#1ƽ k# RsR {Kƽƌ# RJs9&!9J3 9s)) JB9)!)! RƄ!)!)! J!1)!)! Z{!!!ֵ!!!3!! #9RB ! 9!"1 )) ! #!! " !)!)!)Zc))  R91Z 9R!s "B9 ֽsRRsRks1 J9 {ƵJ#1ƽ k# RsR {Kƽƌ# RJs9&!9!9J3 9)JZ9) RƄ9{Z J1ssBZ{J1ֵR{{)3${{s #9RB 1{k 9!"1B{{{R! #J{{k" c{{!)ksZcsZ R91Z 9R!s "B9 ֽsRRsRks1 J9 {ƵJ#1ƽ k# RsR {Kƽƌ# RJs9&!9B3 9c))J)1))Rƌ))1) J!)1) Z{)1ֵ1))1)1!3)1)1! #9RB)1)! 9!"1)1))! #11)1")1)1!)))!Zc!l8mkcR9R9BRkRBZRJ!Z{k{B{kk1Z9{9)J1Μ1RZc!sZcBJ{1kR!)sk)sZ!9BZcR9ZJ99Bc9!ich#H@????????p@????????pih32rU9ZJބ$ k) !R{R !BB1cֵ)J)νZ!)URƵc{ sR sBB1{)U{J Ƶs)JƵրR9kJƵƀހJJJրޔZֵ9Bƀ ֵƽZsƔ!9k!))oƽ{199k c!)1k !9))11 9c)Z11!1J!B1!)19!Jk))1))!)!!)JZ11)! )))) Jƭ))1!1Bkk{s!!!1!)B{))!)Bƀ J)!1)ƽc)!)k co))!9!9c!!!!)9J)!)B1祁911)9 19 { 11 {ޔ)))1k֭k)))11J9!1$U9ZJބ$ k) !R{R !BB1cֵ)J)νZ!)URƵc{ sR sBB1{)U{J Ƶs)JƵրR9kJƵƀހJJJրޔZֵ9Bƀ ֵƽZsƔ!9k!))oƽ{199k c1c)1k !k1 9c)kJ!{k1J!Jk{{{9JZ! !JƭJcc{sBkk{s!!kB{9{RBƀJ9{{J)ƽcs{{1kco{{)9!9c 1{{s)9JB{{{kB1祁J{{{Bc{{{{99 ss{{s{Z { {{{Z {ޔ){{Rk֭k111J91R!$U9ZJބ$ k) !R{R !BB1cֵ)J)νZ!)URƵc{ sR sBB1{)U{J Ƶs)JƵրR9kJƵƀހJJJրޔZֵ9Bƀ ֵƽZsƔ!9k!))oƽ{199k c1k !1)1!99c1sB)J!!)1Jk)1) JZ)1))11)1) Jƭ)1))!Bkk{s!)1)1B {1)1)Bƀ J1))1))11))ƽc!1)1)k co)1)1)1)9!9c)1))1))1)!)9J)1B1祁 )1))1))1)1)))1)1)19!)1) {)1) {ޔ!)k֭k +1))1J9$h8mk BcR1B)11޽)!JZcJR)!19s!9B1!k!R!)cB)Z!R))R9cRZZRBsR!cRk9֌JscccJZRZR)RBR)!)Z!sZsZZ9Bޭ)1Z!RkZ猭B1kRR99BZJ)ZsJsZ{J)cJs9BZ{Z!91it327&LSZ0La 5bПnB j|+4ڮU*  *56842-Z֕T*  &?Tjloic7 -ܻl> $ Hxq8dh8;ټV .pq8 tȺX,Cve8VӻR"pq82dμ]8je8~ȼh  ^q8bx? 6wn8E^/ 0`J)RĽr9 DMsſC XPQþd% d] *N'.]½J% rʾj TN.TzնW 'xʾv06d`&\.5ʾH$dr.J_t칆F6ʿh46dr.lv8¤Ddπr 9\l62·IJb" 6do td+~ŹD( dπlEޔJ%$.]Ⱥĥyk`VJ>.@dк] ,ĕJHʼ˻}[9zπžO [z2&$Hľ·z= .]e2HξV+.vǤg*($H·؁+ >}S* Hξd>⹐R $H¶ŀެz= Lz( Hξׁ+&Lӥw>*$H¶޷R _i Hξ܀׏G$3f˒X/*$H¶ɥR$jкX  Hξɬl+?x¶|B!*:x¶ȲȺoV6 )zǟw<&:sξ۲sO+. Nưc-$Hȿ得nEMTUVH:( ("CZ-hݘS*("\¾R {aHݘS* >t}V0 /VjMm\.(O徘a*LQ^ 0\vݘS*(Qxf (O 01/.($ p"*38>6.Hξj5?L 021.+&  >DUfYNl\.$H¶J%(OD 021/,)$ 8**L\.Hξl)O,330330-*'# ,*'!\,$H¶` f23322/,+*'$!!'-FD! HξTL2320+&$"&-..!"$AD"$H¶` \"20.*&%#""  $(+.  $ o.0Hξl+^ 3.(" #*00/.. $" 0[.4$H¶S*3fH,4'?5+$n "',0 # ";<B&.56889DO¶e0 c &),)&  >Q  ,L[jloprξa0 W .01&   F-, $d+ڱb>>.1    7`0.p,լZ1 ?,    YNCvٜ`< /52  BJ%("p¸݌<=1  6J*5dļ徘[f  t,( XݘS*>M'   ,X,0q"徘a*>H   *^ &*TݘS*N.    9:&>|侘a*+V2     >P $R}gۗS*^   F+&_yOA4@LYfrր*:^!        MB  l̾O  $=]}hN  n  v̿yO*.>lI?        ,f3 5O Zp.?  _ 6̷yO*Lq Y0       ?@ 8˯O >}r&M)+    "@#jO*3fr&l     [RP (Os6l'   " "v)K(5a6L   n~žOL1!    etz=&L7   $z=kV+d   )" +$H؁+6k&  '%zH HdX   % l$HЀެz=DI    4d9Hׁ+-B ,V$H޷R  W2  DO+H׏G$&K,&  # ;H$HɡzN$&e%  ,sH"Hɬd -Z&% +V+ U·lV2 -V:^+ *csHDC&H9 2NRVROD:$ D^RofA &LSZ0La 5bПnB j|+4ڮU*  *56842-Z֕T*  &?Tjloic7 -ܻl> $ Hxq8dh8;ټV .pq8 tȺX,Cve8VӻR"pq82dμ]8je8~ȼh  ^q8bx? 6wn8E^/ 0`J)RĽr9 DMsſC XPQþd% d] *N'.]½J% rʾj TN.TzնW 'xʾv06d`&\.5ʾH$dr.J_t칆F6ʿh46dr.lv8¤Ddπr 9\l62·IJb" 6do td+~ŹD( dπlEޔJ%$.]Ⱥĥyk`VJ>.@dк] ,ĕJHʼ˻}[9zπžO [z2&$Hľ·z= .]e2HξV+.vǤg*($H·؁+ >}S* Hξd>⹐R $H¶ŀެz= Lz( Hξׁ+&Lӥw>*$H¶޷R _i Hξ܀׏G$3f˒X/*$H¶ɥR$jкX  Hξɬl+?x¶|B!*:x¶ȲȺoV6 )zǟw<&:sξ۲sO+. Nưc-$Hȿ得nEMTUVH:( ("CZ-hݘS*("\¾R {aHݘS* >t}V0 (JYXH$(O徘a*LQ Pz}HvݘS*(Qxe:LIZ* Lxc}/7+$.V{} $ +VH:\~} $HhpV2 6\u}|L HrF  X}|8$Hn> #F~}wJ'.4,#Hξj56}s+ "7+Wvy{x|-B&.56889DO¶e0 M~~uuyx= ,L[jloprξa0Dvz!2 $d+ڱb>4zwtvwy{w|yH$.p,լZ16}y|yuuuB:Cvٜ`< (z{vz}~w6("p¸݌<{xu|{t}6*5dļ徘[X{wyw}usw{|}X ( XݘS*6s}ustu@ 0q"徘a*6~{}xuu}yuuwG &*TݘS*B|ywt|wwu*&>|侘a* $Itsw}uzy<$R}gۗS* tuuvz~{u{&_yOA4@LYfrր*1wyzuw}uu~s1  l̾O  $=]}h vxywu}w~{}ytR   v̿yO*.>l$>w}}st|~s{uyvI$ 5O Zp'v|yzyy{wt~twD 6̷yO*LqJt{~vwsw}~u}uz-8˯O >}r @u}u{u}z{{{~~Z-#jO*3fr Y{zxwy{}s~vywvuCRP (Os,Y}y}}y{}v} uy}yyX)K(5a,|u~|u}{u {t~uxt}yP~žO@x}~wwy{} ywzzx~Htz= @}s}yu|ww~~ uzsw{V+kV+R|}|yv|} ~ysyX$H؁+,Xzs{s{tw{ xu}T1Hd {y~uyyzs{wJ $HЀެz=8~wtxx~sywyE' Hׁ+ %wwusz; $H޷R Guwus~yy6H׏G$=~xt|y2$HɡzN$RuwP2Hɬd $I}}; U·lV2 $w}}B *csH6xz' 2NRVROD:$6JTJ.&LSZ0La 5bПnB j|+4ڮU*  *56842-Z֕T*  &?Tjloic7 -ܻl> $ Hxq8dh8;ټV .pq8 tȺX,Cve8VӻR"pq82dμ]8je8~ȼh  ^q8bx? 6wn8E^/ 0`J)RĽr9 DMsſC XPQþd% d] *N'.]½J% rʾj TN.TzնW 'xʾv06d`&\.5ʾH$dr.J_t칆F6ʿh46dr.lv8¤Ddπr 9\l62·IJb" 6do td+~ŹD( dπlEޔJ%$.]Ⱥĥyk`VJ>.@dк] ,ĕJHʼ˻}[9zπžO [z2&$Hľ·z= .]e2HξV+.vǤg*($H·؁+ >}S* Hξd>⹐R $H¶ŀެz= Lz( Hξׁ+&Lӥw>*$H¶޷R _i Hξ܀׏G$3f˒X/*$H¶ɥR$jкX  Hξɬl+?x¶|B!*:x¶ȲȺoV6 )zǟw<&:sξ۲sO+. Nưc-$Hȿ得nEMTUVH:( ("CZ-hݘS*("\¾R {aHݘS*>t}V0 *(O徘a*LQ *vݘS* (Qx^-*IZ*  LkH*7+$.V{rL* +VH:\pU*$HhpV2 6\aeH* HrF  0?*$Hn> *Hξj5*** $H¶J%* Hξl)*$H¶` * HξT*$H¶` *Hξl+*$H¶S**Hξ{>*&.56889DO¶e0* ,L[jloprξa0*& $dڱb>*.pլZ1**Cvٜ`< *("p¸݌<**5dļ徘[*( XݘS**0q"徘a**&*TݘS**&>|侘a**$R}gۗS**&_yOA4@LYfrր**  l̾O  $=]}h*  v̿yO*.>l* 5O Zp* 6̷yO*Lq*8˯O >}r*#jO*3fr*RP (Os*)K(5a*~žO*tz=*kV+*$H؁+*Hd*$HЀެz=*Hׁ+*$H޷R *H׏G$*$HɡzN$*Hɬd* U·lV2* *csH* 2NRVROD:$*t8mk@ @```D( ?O(0gçwH$0_ǏH$H{Ǔ`4 $(4@0  H߿g ?GOg_?7/ o߫wG  0: count = self.app.getProgressValue( 'count' ) if count == 0: self.app.log.info( T_('Updated %(filename)s to revision %(rev)d, no new updates') % {'filename': filename ,'rev': rev.number} ) else: self.app.log.info( S_('Updated %(filename)s to revision %(rev)d, %(count)d new update', 'Updated %(filename)s to revision %(rev)d, %(count)d new updates', count) % {'filename': filename ,'rev': rev.number ,'count': count} ) else: self.app.log.warning( T_('Already up to date') ) if self.project_info.notification_of_files_in_conflict > 0: wx.MessageBox( S_("%d file is in conflict", "%d files are in conflict", self.project_info.notification_of_files_in_conflict) % self.project_info.notification_of_files_in_conflict, T_("Warning"), style=wx.OK|wx.ICON_EXCLAMATION ) self.app.clearProgress() self.app.setAction( T_('Ready') ) self.app.refreshFrame() def Cmd_Dir_Upgrade( self ): self.app.setAction( T_('Upgrade %s...') % self.project_info.wc_path ) try: self.app.log.info( T_('Upgrading working copy %s...') % (self.project_info.wc_path,) ) self.project_info.client_fg.upgrade( self.project_info.wc_path ) except pysvn.ClientError, e: print 'error upgrade %r' % (e,) self.app.log_client_error( e ) self.app.log.info( T_('Upgrade complete') ) self.app.clearProgress() self.app.setAction( T_('Ready') ) self.app.refreshFrame() def Cmd_Dir_Copy( self, all_filenames ): try: for src_filename in all_filenames: self.project_info.client_fg.copy( src_filename, self.project_info.wc_path ) self.app.log.info( T_('Copied %(from)s to %(to)s') % {'from': src_filename ,'to': self.project_info.wc_path} ) except pysvn.ClientError, e: self.app.log_client_error( e ) self.app.refreshFrame() def Cmd_Dir_Move( self, all_filenames ): try: for src_filename in all_filenames: self.project_info.client_fg.move( src_filename, self.project_info.wc_path ) self.app.log.info( T_('Moved %(from)s to %(to)s') % {'from': src_filename ,'to': self.project_info.wc_path} ) except pysvn.ClientError, e: self.app.log_client_error( e ) self.app.refreshFrame() WorkBench-1.8.2/Source/wb.ico000644 000765 000024 00000052366 10315471775 016260 0ustar00barrystaff000000 000000  &(L( @ʦf3f3̙f3fffff3ff333f3333f3ff3f3̙f3fffff3ff333f3333f3f3̙̙̙f̙3̙̙̙f3fffff3ff333f3333f3fffff3fffffff3fff̙ffff3fffffffffff3ffff3f3f3ff3f33f3ffffff3ff333f3333333f33333̙33f3333f3f3f3ff33f3f3333333f3333333333f3333f3f3̙f3ffffff3f333f3333f3wUD"wUD"UUUwwwwwwDDD"""wUD"]]2]]]]2]]]2]2]2]2Ĩ]]22]22]2]22]]2]V]]]]]]]]]]]]]#??x1???(@ʦ """)))UUUMMMBBB999|PP3f3333f333ff3fffff3f3f̙f3333f3333333333f3333333f3f33ff3f3f3f3333f3333333f3̙33333f333ff3ffffff3f33f3ff3f3f3ffff3fffffffff3fffffff3f̙ffff3ff333f3ff33fff33f3ff̙3f3f3333f333ff3fffff̙̙3̙f̙̙̙3f̙3f3f3333f333ff3fffff3f3f̙3ffffffffff!___wwwkkkkkkkllllkBBBBkklllCBBClkllCCCllllDDkkkkkkkkkkkkkklDDmnBBBkllllHHHHHHkkklnC BBklCBB!!!HHkkkmBkll!!!!!!HHkkklCBBkkkkkII""'!!!!!!HlkkllmBBBkllleBBHII(('''!!!!llkBCmBBkmCB!'''!!!됐kBC Bklm!!'---''!mlkkkkB DBlkkklJ("!'''''(IllkkkBkkkB mlllllkHHIIOO.'''''''(ClkkBBBkkB mlllmCB!'O.---'!!!!'--'!ClkkBBkkBmll!''--33--'!!!!''--'!! ClkklCCBkkBlkkkllmmJI(''----''!!''''!! lkklCBkkBBBkkkflIO.''''''''!!!! CkklBBkkBBCBBkkkfII.-3---'!!!!''---'!!! !! CkkkCBBkkBCBkkfC"'-333--'!!!!!'----'! !! CkkkBCDmmmBBkkkkk BkklC"'-----''!!!!'''!! BekkkBDBBBklllkHB BkkHI('--'''''''''''!!! BBkkB BBlllBB BkkHH---'!!!!'---''!! !! BBkkBB쒒CCBkpHN--3-'!!!!'-33--'! !!!! BkkB mCBBkkHH-33-!!!!'----'!! !!!!! BekB mmCBBklHH'-333-''''!!! !!!!!!BBekkB mmBBklmmkHB!'-333-'''!!!!!!!!!!!!!!BHkkB mmmmmlllmmmlkBB!!-33333---'!!!!!!!!!!''!!!HHkkBmmlkkklIlqkkkkkBBB!'-3333-''!!!!!!!!''''!HHkllDCm푐kkeBBIIOOONHHHBBBB!'-333333-''!!!!!!!"CIIHHkC CmmkeBB!(O.NNHHBBB!!-3333333--''!!!!!""Ikk m풒mmke"'-33-'!!!!!'-333333333333--''!!!!llC m쑐kf(-3333-'!!'-33333----3333--''!'IllmCmmmllklJO-3333--''-3333---333-'!"IlllmD mmCBekrO.333333----3333--''''-33-'!"Jmmll CmBBksPO-3333333333333-'!!!''---'!m BCmBkrO--333333333-(IBBBHOOO'BlmllBBBBBBBCmmCClllmII-3333333--(IICBHHIOOOIHHkllkkkkkkkllmllIII--333333--IIlkkklrrmllkklmmCCmmmIO-3333333--IImmlllmmmI-3333333-(ImllmI'-333333-(IlllmCC''-33333--IlklmCmC'--3334..OOpkllmCCmlIII---./POqplCmmlllmrO(''(.PPrqkB Cm쑐llrI!!"IQs푐kBll!kBBmCCkkBkBBBCmBkklBBBlkkBBCCBBklkkkkkBBCBBklB mCBBklB BemC k mmllkkmmllkkkkkklmCmBBkkkkHlCmBBBkllmBkn mBkk lk kkmCmmlllmBBBCCmllkkkllmlkkkkkBB CmllkkkllmmnlkkkBB mkkllmmCCmkB lkkkkkkllmCBkB llkkkkBBBlllllllmmmmlBklkBBCBBkkkBCClllmnlkkllCBBBkklmmmCCmllmmBBkllmnnmCllmmBBkkklmmnmlkklllmCBkkkkklmmllkllmmCBBkllmmlkkmBBkmmmm됐kkCmD BBkmmmlkBCC BBkknnmmlkBCC BBBk lkBBCm BkqllkBBCmm Bkll쒒lkBCCmmmm BlmmkkCmmmmC kllmmkBBCmCm BklmnmCkBBCCl둑mBBBklkllkB mCCBkllmmnmBBkk쒓lkkkB mCBkknmDDkkkkB CBBkmClkkBB풒BBkmB BkkklCBBkllBBBBBkllDDCCBkkkkkkkCBkm쒒m BklC BkllCmm BkllCCCBBklllBCBklllBBBBCDDCBkkkkkklDDDClkllDlllllllllllllllll@@@@ @  @ WorkBench-1.8.2/Source/win32.mak000644 000765 000024 00000005127 12701531545 016572 0ustar00barrystaff000000 000000 # # win32.mak WorkBench # all: run build_app APPNAME=wb # run_w for production image # run_w_d for debug meinc image APPTYPE=run_w PYTHONPATH=$(PYSVN_PYTHONPATH) SOURCES= \ wb_app.py \ wb_dialogs.py \ wb_diff_difflib.py \ wb_diff_frame.py \ wb_diff_images.py \ wb_diff_main.py \ wb_diff_processor.py \ wb_exceptions.py \ wb_frame.py \ wb_list_panel.py \ wb_ids.py \ wb_images.py \ wb_main.py \ wb_platform_specific.py \ wb_platform_win32_specific.py \ wb_preferences.py \ wb_shell_commands.py \ wb_shell_win32_commands.py \ wb_source_control_providers.py \ wb_subversion_utils.py \ wb_subversion_provider.py \ wb_subversion_project_info.py \ wb_subversion_tree_handler.py \ wb_subversion_list_handler.py \ wb_subversion_info_dialog.py \ wb_subversion_properties_dialog.py \ wb_tree_panel.py \ wb_version.py \ I18N\pysvn_workbench.current.pot wb.rc: wb.rc.template ..\Builder\version.info $(PYTHON) -u ..\Builder\brand_version.py ..\Builder\version.info wb.rc.template wb_version.py: wb_version.py.template ..\Builder\version.info $(PYTHON) -u ..\Builder\brand_version.py ..\Builder\version.info wb_version.py.template I18N\pysvn_workbench.current.pot: make-pot-file.cmd make-po-file.cmd en make-mo-files.cmd locale IMAGES = \ toolbar_images/add.png \ toolbar_images/checkin.png \ toolbar_images/delete.png \ toolbar_images/diff.png \ toolbar_images/edit.png \ toolbar_images/editcopy.png \ toolbar_images/editcut.png \ toolbar_images/editpaste.png \ toolbar_images/exclude.png \ toolbar_images/file_browser.png \ toolbar_images/history.png \ toolbar_images/include.png \ toolbar_images/info.png \ toolbar_images/lock.png \ toolbar_images/open.png \ toolbar_images/property.png \ toolbar_images/revert.png \ toolbar_images/terminal.png \ toolbar_images/unlock.png \ toolbar_images/update.png \ wb.png wb_images.py: make_wb_images.py $(IMAGES) $(PYTHON) -u make_wb_images.py wb_images.py $(IMAGES) # # Make the run script # run: run_$(APPNAME).cmd SCRIPT_NAME=run_$(APPNAME).cmd $(SCRIPT_NAME): win32.mak echo setlocal > $(SCRIPT_NAME) echo set PYTHONPATH=$(PYTHONPATH) >> $(SCRIPT_NAME) echo $(PYTHON) %CD%\$(APPNAME)_main.py %* >> $(SCRIPT_NAME) echo endlocal >> $(SCRIPT_NAME) clean:: if exist *.pyc del *.pyc if exist bin rmdir bin /s /q if exist wb_version.py del wb_version.py if exist wb_images.py del wb_images.py if exist locale rmdir /s /q locale if exist I18N\pysvn_workbench.current.pot del I18N\pysvn_workbench.current.pot !include WorkBench-1.8.2/Source/make-mo-files.sh000755 000765 000024 00000000124 12617124047 020114 0ustar00barrystaff000000 000000 #!/bin/bash set -e ${PYTHON} make_mo_files.py ${1:?missing arg 1 output locale dir} WorkBench-1.8.2/Source/wb_diff_frame.py000644 000765 000024 00000056120 13005660055 020256 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_diff_frame.py ''' import wx import wx.stc import wb_diff_images import wb_diff_difflib import wb_diff_processor id_expand_folds_command = wx.NewId() id_collapse_folds_command = wx.NewId() id_whitespace_command = wx.NewId() id_previous_command = wx.NewId() id_next_command = wx.NewId() # 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 DiffFrame(wx.Frame): def __init__( self, app, parent, file_left, title_left, file_right, title_right ): self.app = app diff_prefs = self.app.prefs.getDiffWindow() extra_style = 0 if diff_prefs.maximized: extra_style = wx.MAXIMIZE wx.Frame.__init__( self, None, -1, T_("Diff %(title1)s and %(title2)s") % {'title1': title_left ,'title2': title_right}, diff_prefs.frame_position, diff_prefs.getFrameSize(), wx.DEFAULT_FRAME_STYLE|extra_style ) # Reset the size after startup to workaround a potential # problem on OSX with incorrect first size event saving the # wrong size in the preferences wx.CallAfter( self.SetSize, diff_prefs.getFrameSize() ) # Set up the toolbar self.toolbar = self.CreateToolBar( wx.TB_HORIZONTAL|wx.NO_BORDER|wx.TB_FLAT ) self.toolbar.AddSimpleTool( id_expand_folds_command, wb_diff_images.getExpandBitmap(), T_("Expand folds"), T_("Expand all folds") ) self.toolbar.AddSimpleTool( id_collapse_folds_command, wb_diff_images.getCollapseBitmap(), T_("Collapse folds"), T_("Collapse all folds") ) self.toolbar.AddSeparator() self.toolbar.AddSimpleTool( id_whitespace_command, wb_diff_images.getWhiteSpaceBitmap(), T_("Toggle whitespace"), T_("Show/hide whitespace") ) self.toolbar.AddSeparator() self.toolbar.AddSimpleTool( id_previous_command, wb_diff_images.getUpArrowBitmap(), T_("Previous difference"), T_("Positions the cursor at the previous difference between the files") ) self.toolbar.AddSimpleTool( id_next_command, wb_diff_images.getDownArrowBitmap(), T_("Next difference"), T_("Positions the cursor at the next difference between the files") ) self.toolbar.Realize() # Set the application icon self.SetIcon( wb_diff_images.getAppIconIcon() ) # Add the status bar self.status_bar_size_changed = False s = self.CreateStatusBar() s.SetFieldsCount( 3 ) s.SetStatusWidths( [-1, -1, 480] ) s.SetStatusText( 'Ready', 0 ) s.SetStatusText( 'Steady', 1 ) s.SetStatusText( 'Go!', 2 ) self.total_change_number = 0 self.current_change_number = 0 self.setChangeCounts( 0, 0 ) self.status_bar_key_field = DiffBodyText( s, line_numbers=False ) self.status_bar_key_field.InsertStyledText( T_('Key: '), self.status_bar_key_field.style_line_normal ) self.status_bar_key_field.InsertStyledText( T_('Inserted text '), self.status_bar_key_field.style_line_insert ) self.status_bar_key_field.InsertStyledText( T_('Deleted text '), self.status_bar_key_field.style_line_delete ) self.status_bar_key_field.InsertStyledText( T_('Changed text'), self.status_bar_key_field.style_line_changed ) self.status_bar_key_field.SetReadOnly( True ) self.status_bar_key_field.Enable( False ) self._repositionStatusBar() wx.EVT_SIZE( s, self.OnStatusBarSize ) wx.EVT_IDLE( s, self.OnStatusBarIdle ) # Set up the splitter window with the editor panels if 'wxMac' in wx.PlatformInfo: style = wx.SP_LIVE_UPDATE | wx.SP_3DSASH else: style = wx.SP_LIVE_UPDATE self.splitter = wx.SplitterWindow( self, -1, style=style ) self.sash_ratio = 0.5 self.panel_left = DiffPanel( self.splitter, title_left ) self.panel_right = DiffPanel( self.splitter, title_right ) self.panel_left.ed.SetMirrorEditor( self.panel_right.ed ) self.panel_right.ed.SetMirrorEditor( self.panel_left.ed ) self.splitter.SetMinimumPaneSize( 150 ) self.splitter.SplitVertically( self.panel_left, self.panel_right ) self.splitter.SetSashPosition( 150 ) # Create the editor and calculate all the differences self.processor = wb_diff_processor.DiffProcessor( self.panel_left.ed, self.panel_right.ed ) self.diff = wb_diff_difflib.Difference( self.processor ) self.files_ok = self.diff.filecompare( file_left, file_right ) if not self.files_ok: return self.setChangeCounts( 0, self.processor.getChangeCount() ) self.SetZoom( diff_prefs.zoom ) # Move to the first change in the editor. event = wx.CommandEvent( wx.wxEVT_COMMAND_TOOL_CLICKED, id_next_command ) self.GetEventHandler().AddPendingEvent( event ) # Set up the keyboard shortcuts accelerator_table = wx.AcceleratorTable( [(wx.ACCEL_NORMAL, ord('p'), id_previous_command ) ,(wx.ACCEL_SHIFT, wx.WXK_F7, id_previous_command ) ,(wx.ACCEL_NORMAL, ord('n'), id_next_command ) ,(wx.ACCEL_NORMAL, wx.WXK_F7, id_next_command ) ,(wx.ACCEL_NORMAL, ord(' '), id_whitespace_command ) ,(wx.ACCEL_NORMAL, ord('e'), id_expand_folds_command ) ,(wx.ACCEL_NORMAL, ord('c'), id_collapse_folds_command ) ]) self.SetAcceleratorTable( accelerator_table ) wx.EVT_CLOSE( self, self.OnCloseWindow ) wx.EVT_SIZE( self.splitter, self.OnSize ) wx.EVT_SPLITTER_SASH_POS_CHANGED( self.splitter, -1, self.OnSashPositionChanged ) wx.EVT_TOOL( self, id_previous_command, self.OnToolUpArrow ) wx.EVT_TOOL( self, id_next_command, self.OnToolDownArrow ) wx.EVT_TOOL( self, id_whitespace_command, self.OnToolWhitespace ) wx.EVT_TOOL( self, id_expand_folds_command, self.OnToolExpandFolds ) wx.EVT_TOOL( self, id_collapse_folds_command, self.OnToolCollapseFolds ) wx.EVT_SIZE( self, self.OnFrameSize ) wx.EVT_MOVE( self, self.OnFrameMove ) wx.stc.EVT_STC_ZOOM( self, self.panel_left.ed.GetId(), self.OnZoomChange ) wx.stc.EVT_STC_ZOOM( self, self.panel_right.ed.GetId(), self.OnZoomChange ) def isOk( self ): return self.files_ok #------------------------------------------------------------ def OnStatusBarSize( self, event ): self._repositionStatusBar() # tell idle to fix up status bar self.status_bar_size_changed = True def OnStatusBarIdle( self, event ): if self.status_bar_size_changed: self._repositionStatusBar() self.status_bar_size_changed = False def setChangeCounts( self, current_change_number=None, total_change_number=None ): if current_change_number is not None: self.current_change_number = current_change_number if total_change_number is not None: self.total_change_number = total_change_number self.GetStatusBar().SetStatusText( T_('Diff %(change1)d of %(change2)d') % {'change1': self.current_change_number ,'change2': self.total_change_number}, 1 ) def _repositionStatusBar(self): rect = self.GetStatusBar().GetFieldRect( 2 ) rect.x += 1 rect.y += 1 rect.width += 4 rect.height += 4 self.status_bar_key_field.SetRect( rect ) #------------------------------------------------------------ def OnCloseWindow( self, event ): diff_prefs = self.app.prefs.getDiffWindow() # Size and Position are already saved diff_prefs.maximized = self.IsMaximized() self.Destroy() def OnSashPositionChanged( self, event ): w, h = self.splitter.GetClientSizeTuple() self.sash_ratio = float( event.GetSashPosition() ) / float( w ) event.Skip() def OnSize( self, event ): w, h = self.splitter.GetClientSizeTuple() self.splitter.SetSashPosition( int(w * self.sash_ratio) ) event.Skip() def OnFrameSize( self, event ): pref = self.app.prefs.getDiffWindow() if not self.IsMaximized(): pref.setFrameSize( self.GetSize() ) event.Skip() def OnFrameMove( self, event ): pref = self.app.prefs.getDiffWindow() if not self.IsMaximized() and not self.IsIconized(): # don't use the event.GetPosition() as it # is off by the window frame thinkness pt = self.GetPosition() pref.frame_position = pt pref.maximized = self.IsMaximized() event.Skip() def OnToolDownArrow( self, event ): if self.total_change_number == 0: return self.processor.moveNextChange() self.setChangeCounts( self.processor.getCurrentChange() ) def OnToolUpArrow( self, event ): if self.total_change_number == 0: return self.processor.movePrevChange() self.setChangeCounts( self.processor.getCurrentChange() ) def OnToolWhitespace( self, event ): self.panel_left.ed.ToggleViewWhiteSpace() self.panel_right.ed.ToggleViewWhiteSpace() def OnToolExpandFolds( self, event ): self.showAllFolds( True ) def OnToolCollapseFolds( self, event ): self.showAllFolds( False ) def showAllFolds( self, show ): self.panel_left.ed.ShowAllFolds( show ) self.panel_right.ed.ShowAllFolds( show ) def OnZoomChange( self, evt ): zoom = evt.GetEventObject().GetZoom() self.SetZoom( zoom ) diff_prefs = self.app.prefs.getDiffWindow() diff_prefs.zoom = zoom def SetZoom( self, zoom ): if zoom != self.panel_left.ed.GetZoom(): self.panel_left.ed.SetZoom( zoom ) if zoom != self.panel_right.ed.GetZoom(): self.panel_right.ed.SetZoom( zoom ) self.panel_left.ed.diff_line_numbers.SetZoom( zoom ) self.panel_right.ed.diff_line_numbers.SetZoom( zoom ) #---------------------------------------------------------------------- WB_EVT_SYNC_SCROLL_type = wx.NewEventType() def WB_EVT_SYNC_SCROLL( window, id, handler ): window.Connect( id, -1, WB_EVT_SYNC_SCROLL_type, handler ) class SyncScrollEvent(wx.PyCommandEvent): 'SyncScrollEvent' def __init__(self, id, text_body_this, text_body_other): wx.PyCommandEvent.__init__(self, WB_EVT_SYNC_SCROLL_type, id) self.text_body_this = text_body_this self.text_body_other = text_body_other #---------------------------------------------------------------------- class DiffPanel(wx.Panel): ''' DiffPanel ''' def __init__( self, parent_win, title ): wx.Panel.__init__( self, parent_win, -1 ) self.SetSize( wx.Size(200, 200) ) self.text_file_name = wx.TextCtrl(self, -1, title, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY ) self.ed = DiffBodyText( self ) box_line_numbers = wx.BoxSizer( wx.HORIZONTAL ) box_line_numbers.Add( self.ed.diff_line_numbers, 0, wx.EXPAND ) box_line_numbers.Add( self.ed, 1, wx.EXPAND ) box_file_name = wx.BoxSizer( wx.VERTICAL ) box_file_name.Add( self.text_file_name, 0, wx.EXPAND ) box_file_name.Add( box_line_numbers, 1, wx.EXPAND ) box_file_name.Fit( self ) self.SetAutoLayout( True ) self.SetSizer( box_file_name ) #------------------------------------------------------------------------------------------ class DiffBodyText(wx.stc.StyledTextCtrl): def __init__( self, parent, line_numbers=True ): self.text_body_other = None wx.stc.StyledTextCtrl.__init__( self, parent, -1, wx.DefaultPosition, wx.DefaultSize, wx.NO_BORDER ) if line_numbers: self.diff_line_numbers = DiffLineNumbers( parent ) else: self.diff_line_numbers = None self.fold_margin = -1 self.fold_start = -1 self.fold_context_border = 1 self.fold_minimum_length = self.fold_context_border * 2 + 1 self.style_line_normal = 0 self.style_line_insert = 1 self.style_line_delete = 2 self.style_line_changed = 3 self.style_replace_insert = self.style_line_insert | wx.stc.STC_INDIC1_MASK self.style_replace_delete = self.style_line_delete | wx.stc.STC_INDIC1_MASK self.style_replace_changed = self.style_line_changed | wx.stc.STC_INDIC1_MASK self.style_replace_equal = self.style_line_normal | wx.stc.STC_INDIC1_MASK self.EmptyUndoBuffer() self.SetMarginWidth(0, 0) self.SetMarginWidth(1, 0) self.SetMarginWidth(2, 0) self.SetScrollWidth(10000) # make some styles self.StyleSetSpec( wx.stc.STC_STYLE_DEFAULT, "size:%d,face:%s,fore:#000000" % (point_size, face) ) self.StyleSetSpec( self.style_line_insert, "fore:#008200" ) self.StyleSetSpec( self.style_line_delete, "fore:#0000FF" ) self.StyleSetSpec( self.style_line_changed, "fore:#FF0000" ) # and finally, an indicator or two self.IndicatorSetStyle( self.style_line_insert, wx.stc.STC_INDIC_SQUIGGLE ) self.IndicatorSetForeground( self.style_line_insert, wx.Colour(0xff, 0xb0, 0xb0) ) self.IndicatorSetStyle( self.style_line_delete, wx.stc.STC_INDIC_SQUIGGLE) self.IndicatorSetForeground( self.style_line_delete, wx.Colour(0xff, 0x00, 0x00) ) self.IndicatorSetStyle( self.style_line_changed, wx.stc.STC_INDIC_STRIKE ) self.IndicatorSetForeground( self.style_line_changed, wx.BLACK ) wx.stc.EVT_STC_MARGINCLICK( self, -1, self.OnMarginClick ) if line_numbers: self.SetupFolding( 1 ) wx.EVT_MOUSEWHEEL( self, self.OnMouseWheel ) wx.EVT_SCROLLWIN( self, self.OnNeedToSyncScroll ) WB_EVT_SYNC_SCROLL( self, -1, self.OnSyncScroll ) DiffBodyText.body_count += 1 self.body_count = DiffBodyText.body_count body_count = 0 def __str__( self ): return '' % self.body_count def OnMouseWheel( self, event ): assert( self.text_body_other ) self.GetEventHandler().AddPendingEvent( SyncScrollEvent( self.GetId(), self, self.text_body_other ) ) event.Skip() def OnNeedToSyncScroll( self, event ): if self.text_body_other is not None: self.GetEventHandler().AddPendingEvent( SyncScrollEvent( self.GetId(), self, self.text_body_other ) ) event.Skip() def OnSyncScroll( self, event ): line_number = event.text_body_this.GetFirstVisibleLine() event.text_body_other.ScrollToLine( line_number ) event.text_body_other.diff_line_numbers.ScrollToLine( line_number ) event.text_body_this.diff_line_numbers.ScrollToLine( line_number ) xpos = event.text_body_this.GetXOffset() event.text_body_other.SetXOffset( xpos ) sb_horizontal_postion = event.text_body_this.GetScrollPos( wx.SB_HORIZONTAL ) if event.text_body_other.GetScrollPos( wx.SB_HORIZONTAL ) != sb_horizontal_postion: event.text_body_other.SetScrollPos( wx.SB_HORIZONTAL, sb_horizontal_postion, True ) def SetMirrorEditor( self, text_body_other ): self.text_body_other = text_body_other def ToggleViewWhiteSpace( self ): if self.GetViewWhiteSpace(): self.SetViewWhiteSpace( False ) else: self.SetViewWhiteSpace( True ) #-------------------------------------------------------------------------------- def OnMarginClick( self, event ): if event.GetMargin() == self.fold_margin: self.ToggleFoldAtLine( self.LineFromPosition( event.GetPosition() ) ) #-------------------------------------------------------------------------------- def InsertStyledText( self, text, style ): pos = self.GetLength() self.InsertText( pos, text ) self.StartStyling( pos, 0xff ) self.SetStyling( len(text), style ) def ChangeLineStyle( self, line, style ): pos_start = self.PositionFromLine( line ) pos_end = self.GetLineEndPosition( line ) self.StartStyling( pos_start, 0xff ) self.SetSelection( pos_start, pos_end ) text = self.GetSelectedText() self.ReplaceSelection(text) self.SetSelection( -1, -1 ) self.SetStyling( pos_end - pos_start, style ) #-------------------------------------------------------------------------------- def SetupFolding( self, margin ): self.fold_margin = margin self.SetProperty( "fold", "1" ) self.diff_line_numbers.SetProperty( "fold", "1" ) self.SetMarginType( self.fold_margin, wx.stc.STC_MARGIN_SYMBOL ) self.SetMarginMask( self.fold_margin, wx.stc.STC_MASK_FOLDERS ) self.SetMarginSensitive( self.fold_margin, True ) self.SetMarginWidth( self.fold_margin, 15 ) self.MarkerDefine( wx.stc.STC_MARKNUM_FOLDEREND, wx.stc.STC_MARK_BOXPLUSCONNECTED, "white", "black" ) self.MarkerDefine( wx.stc.STC_MARKNUM_FOLDEROPENMID, wx.stc.STC_MARK_BOXMINUSCONNECTED, "white", "black" ) self.MarkerDefine( wx.stc.STC_MARKNUM_FOLDERMIDTAIL, wx.stc.STC_MARK_TCORNER, "white", "black" ) self.MarkerDefine( wx.stc.STC_MARKNUM_FOLDERTAIL, wx.stc.STC_MARK_LCORNER, "white", "grey" ) self.MarkerDefine( wx.stc.STC_MARKNUM_FOLDERSUB, wx.stc.STC_MARK_VLINE, "white", "grey" ) self.MarkerDefine( wx.stc.STC_MARKNUM_FOLDER, wx.stc.STC_MARK_BOXPLUS, "white", "black" ) self.MarkerDefine( wx.stc.STC_MARKNUM_FOLDEROPEN, wx.stc.STC_MARK_BOXMINUS, "white", "black" ) def ToggleFoldAtLine( self, line ): if self.GetFoldLevel( line ) & wx.stc.STC_FOLDLEVELHEADERFLAG: if self.GetFoldExpanded( line ): self.SetFoldExpanded( line, False ) self.diff_line_numbers.SetFoldExpanded( line, False ) self._ShowFoldLines( line, self.GetFoldEnd( line ), False ) else: self.SetFoldExpanded( line, True) self.diff_line_numbers.SetFoldExpanded( line, True ) self._ShowFoldLines( line, self.GetFoldEnd( line ), True ) def GetFoldEnd( self, fold_start_line ): current_fold_line = fold_start_line fold_level = self.GetFoldLevel( current_fold_line ) & wx.stc.STC_FOLDLEVELNUMBERMASK while (self.GetFoldLevel( current_fold_line ) & wx.stc.STC_FOLDLEVELNUMBERMASK) >= fold_level: current_fold_line = current_fold_line + 1 return current_fold_line - 1 def _ShowFoldLines( self, start_line, end_line, show_lines ): fold_start = start_line + self.fold_context_border fold_end = end_line - self.fold_context_border self.ShowFoldLines( fold_start, fold_end, show_lines ) def ShowFoldLines( self, start_line, end_line, show_lines ): if show_lines: self.ShowLines( start_line, end_line ) self.diff_line_numbers.ShowLines( start_line, end_line ) else: self.HideLines( start_line, end_line ) self.diff_line_numbers.HideLines( start_line, end_line ) def SetFoldLine(self, line_number, is_fold_line): if is_fold_line: if self.fold_start == -1: self.fold_start = line_number elif line_number - self.fold_start == self.fold_minimum_length: self.SetFoldLevel( self.fold_start, (wx.stc.STC_FOLDLEVELBASE+1) | wx.stc.STC_FOLDLEVELHEADERFLAG ) self.diff_line_numbers.SetFoldLevel( self.fold_start, (wx.stc.STC_FOLDLEVELBASE+1) | wx.stc.STC_FOLDLEVELHEADERFLAG ) self.SetFoldLevel( line_number, wx.stc.STC_FOLDLEVELBASE+1 ) self.diff_line_numbers.SetFoldLevel( line_number, wx.stc.STC_FOLDLEVELBASE+1 ) else: self.SetFoldLevel( line_number, wx.stc.STC_FOLDLEVELBASE ) self.diff_line_numbers.SetFoldLevel( line_number, wx.stc.STC_FOLDLEVELBASE ) if self.fold_start != -1: self.fold_start = -1 def ShowAllFolds(self, show_folds): for line in range(self.GetLineCount()): if( self.GetFoldLevel( line ) & wx.stc.STC_FOLDLEVELHEADERFLAG and ((self.GetFoldExpanded( line ) and not show_folds) or (not self.GetFoldExpanded( line ) and show_folds)) ): self.ToggleFoldAtLine( line ) #------------------------------------------------------------------------------------------ class DiffLineNumbers(wx.stc.StyledTextCtrl): def __init__( self, parent ): wx.stc.StyledTextCtrl.__init__( self, parent, -1, wx.DefaultPosition, wx.DefaultSize, wx.NO_BORDER ) self.style_line_numbers = 0 self.style_line_numbers_for_diff = 1 self.EmptyUndoBuffer() self.SetMarginWidth( 0, 0 ) self.SetMarginWidth( 1, 0 ) self.SetMarginWidth( 2, 0 ) self.SetScrollWidth( 10000 ) # make some styles self.StyleSetSpec( wx.stc.STC_STYLE_DEFAULT, "size:%d,face:%s,fore:#000000,back:#e0e0e0" % (point_size, face) ) self.StyleSetSpec( self.style_line_numbers, "size:%d,face:%s,fore:#000000,back:#f0f0f0" % (point_size, face) ) self.StyleSetSpec( self.style_line_numbers_for_diff, "size:%d,face:%s,fore:#000000,back:#d0d0d0" % (point_size, face) ) # Calculate space for 5 digits font = wx.Font( point_size, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False, face ) width, _, _, _ = self.GetFullTextExtent( '12345', font ) width = width + 10 self.SetSize( wx.Size( width, 200 ) ) # starting with 2.5.2.? we have to set the hint self.SetSizeHints( minW=width, maxW=width, maxH=-1, minH=-1 ) self.Enable( False ) # Hide the scrollbars for the line number edit control self.scrollbar = wx.ScrollBar( self, -1, style = wx.SB_VERTICAL ) self.SetVScrollBar( self.scrollbar ) self.scrollbar.Show( False ) self.SetUseHorizontalScrollBar( False ) #-------------------------------------------------------------------------------- def InsertStyledText( self, text, style ): pos = self.GetLength() self.InsertText( pos, text ) self.StartStyling( pos, 0xff ) self.SetStyling( len(text), style ) def ChangeLineStyle( self, line, style ): pos_start = self.PositionFromLine( line ) pos_end = self.GetLineEndPosition( line ) self.StartStyling( pos_start, 0xff ) self.SetSelection( pos_start, pos_end ) text = self.GetSelectedText() self.ReplaceSelection(text) self.SetSelection( -1, -1 ) self.SetStyling( pos_end - pos_start, style ) WorkBench-1.8.2/Source/wb_shell_unix_commands.py000644 000765 000024 00000014774 12575764146 022261 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_shell_unix_commands.py ''' import os import signal import subprocess import types import shlex import tempfile import wb_platform_specific __sigchld_handler_installed = False gui_terminals = ['gnome-terminal', 'konsole', 'xterm', 'xfce4-terminal'] gui_file_browsers = ['nautilus', 'konqueror', 'thunar', 'dolphin'] def setupCommands(): # install the sig child handler to get rid of the zombie processes global __sigchld_handler_installed if not __sigchld_handler_installed: signal.signal( signal.SIGCHLD, __sigchld_handler ) __sigchld_handler_installed = True def __sigchld_handler( signum, frame ): try: while True: pid, status = os.waitpid( -1, os.WNOHANG ) if pid == 0: break except OSError, e: pass def getTerminalProgramList(): return gui_terminals[:] def getFileBrowserProgramList(): return gui_file_browsers[:] def EditFile( app, project_info, filename ): p = app.prefs.getEditor() if p.editor_image: if p.editor_options: editor_image = p.editor_image editor_args = shlex.split( p.editor_options ) + [filename] else: editor_image = p.editor_image editor_args = [filename] else: editor_image = 'kedit' editor_args = [filename] cur_dir = os.getcwd() try: wb_platform_specific.uChdir( project_info.getWorkingDir() ) __run_command( app, editor_image, editor_args ) finally: wb_platform_specific.uChdir( cur_dir ) def ShellOpen( app, project_info, filename ): app.log.info( T_('Open %s') % filename ) cur_dir = os.getcwd() try: wb_platform_specific.uChdir( project_info.getWorkingDir() ) subprocess.call( ['xdg-open', filename] ) finally: wb_platform_specific.uChdir( cur_dir ) def GuiDiffFiles( app, args ): __run_command( app, app.prefs.getDiffTool().gui_diff_tool, args ) def ShellDiffFiles( app, args ): return __run_command_with_output( app, app.prefs.getDiffTool().shell_diff_tool, args ) def CommandShell( app, project_info ): p = app.prefs.getShell() working_dir = project_info.getWorkingDir() # calc a title that is leaf to root so that the leaf shows up in a task bar first title = [] pi = project_info while pi: title.append( pi.project_name ) pi = pi.parent f = tempfile.NamedTemporaryFile( mode='w', delete=False, prefix='tmp-wb-shell', suffix='.sh' ) app.all_temp_files.append( f.name ) if len( p.shell_init_command ) > 0: f.write( ". '%s'\n" % (p.shell_init_command,) ) f.write( 'exec "$SHELL" -i\n' ) f.close() # chmod +x os.chmod( f.name, 0700 ) path = os.environ.get( 'PATH' ) found = False for terminal_program in gui_terminals: if p.shell_terminal in ['',terminal_program]: for folder in path.split( os.pathsep ): exe = os.path.join( folder, terminal_program ) if os.path.isfile( exe ): found = True break if found: break if not found: return cur_dir = os.getcwd() os.environ['WB_WD'] = asUtf8( project_info.getWorkingDir() ) try: wb_platform_specific.uChdir( project_info.getWorkingDir() ) if terminal_program == 'konsole': __run_command( app, terminal_program, ['--title', ' '.join( title ), '--workdir', working_dir, '-e', '/bin/bash', f.name] ) elif terminal_program in ('gnome-terminal', 'xfce4-terminal'): __run_command( app, terminal_program, ['--title', ' '.join( title ), '--working-directory', working_dir, '-x', f.name] ) elif terminal_program == 'xterm': __run_command( app, terminal_program, ['-T', ' '.join( title ), '-e', f.name] ) finally: wb_platform_specific.uChdir( cur_dir ) del os.environ['WB_WD'] def FileBrowser( app, project_info ): p = app.prefs.getShell() path = os.environ.get("PATH") found = False for browser_program in gui_file_browsers: if p.shell_file_browser in ['',browser_program]: for folder in path.split( os.pathsep ): exe = os.path.join( folder, browser_program ) if os.path.isfile(exe): found = True break if found: break if not found: return if browser_program == 'konqueror': __run_command( app, browser_program, ['--mimetype', 'inode/directory', project_info.getWorkingDir()] ) elif browser_program in ('nautilus', 'thunar', 'dolphin'): __run_command( app, browser_program, [project_info.getWorkingDir()] ) def __run_command( app, cmd, args ): app.log.info( '%s %s' % (cmd, ' '.join( args ) ) ) env = os.environ.copy() # if this is a frozen with the McMillian Installer fix up the environment if '_MEIPASS2' in os.environ: for bad_env in ['_MEIPASS2', 'PYTHONPATH']: if bad_env in env: del env[ bad_env ] old_lib_path_parts = env.get('LD_LIBRARY_PATH','del_me').split(':') del old_lib_path_parts[0] if len(old_lib_path_parts) > 0: env[ 'LD_LIBRARY_PATH' ] = ':'.join( old_lib_path_parts ) else: del env[ 'LD_LIBRARY_PATH' ] cmd = asUtf8( cmd ) args = [asUtf8( arg ) for arg in args] os.spawnvpe( os.P_NOWAIT, cmd, [cmd]+args, env ) def asUtf8( s ): if type( s ) == types.UnicodeType: return s.encode( 'utf-8' ) else: return s def __run_command_with_output( app, cmd, args ): app.log.info( '%s %s' % (cmd, ' '.join( args )) ) try: cmd = asUtf8( cmd ) args = [asUtf8( arg ) for arg in args] proc = subprocess.Popen( [cmd]+args, close_fds=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) output = proc.stdout.read() rc = proc.wait() except EnvironmentError as e: return 'error running %s %s: %s' % (cmd, ' '.join( args ), str(e)) return output WorkBench-1.8.2/Source/make-po-file.sh000755 000765 000024 00000000101 12617124047 017727 0ustar00barrystaff000000 000000 #!/bin/bash set -e ${PYTHON} make_po_file.py ${1?locale missing} WorkBench-1.8.2/Source/wb_background_thread.py000644 000765 000024 00000003112 12271176117 021640 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_background_thread.py ''' import threading import locale class BackgroundThread(threading.Thread): def __init__( self, app ): threading.Thread.__init__( self ) self.setDaemon( 1 ) self.running = 1 self.app = app self.work_queue = [] self.queue_lock = threading.Lock() self.queued_work_semaphore = threading.Semaphore( 0 ) def run( self ): self.app.log.info( 'BackgroundThread thread %r' % (threading.currentThread(),) ) self.app.log.info( 'BackgroundThread locale %r' % (locale.getlocale(),) ) while self.running: # wait for work self.queued_work_semaphore.acquire() # dequeue self.queue_lock.acquire() function = self.work_queue.pop( 0 ) self.queue_lock.release() # run the function function() def addWork( self, function ): # queue the function self.queue_lock.acquire() self.work_queue.append( function ) self.queue_lock.release() # count one more piece of work self.queued_work_semaphore.release() def shutdown( self ): self.addWork( self.__shutdown ) def __shutdown( self ): self.running = 0 WorkBench-1.8.2/Source/wb_toolbars.py000644 000765 000024 00000016031 11507143125 020015 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2009-2010 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_toolbars.py ''' import wb_ids import wb_images class ToolBarResource: def __init__( self ): self.__all_groups = [] self.__all_group_contents = {} self.__all_buttons = {} self.__last_group = None def addButton( self, button_id, bitmap, short_help, long_help=None, is_toggle=False ): self.__all_buttons[ button_id ] = ToolBarButtonInfo( button_id, bitmap, short_help, long_help, is_toggle ) def getButtonById( self, button_id ): return self.__all_buttons[ button_id ] def addGroup( self, group_name ): self.__all_groups.append( group_name ) self.__all_group_contents[ group_name ] = [] self.__last_group = group_name return self def addToGroup( self, button_id ): self.__all_group_contents[ self.__last_group ].append( button_id ) return self def getAllGroupNames( self ): return self.__all_groups[:] def populateToolBar( self, toolbar_prefs, toolbar ): may_add_separator = False for group_name in toolbar_prefs.group_order: all_group_contents = self.__all_group_contents[ group_name ] if( len( all_group_contents ) > 0 and may_add_separator ): toolbar.AddSeparator() for button_id in all_group_contents: button = self.__all_buttons[ button_id ] toolbar.AddSimpleTool( button.button_id, wb_images.getBitmap( button.bitmap, (toolbar_prefs.bitmap_size,toolbar_prefs.bitmap_size) ), T_(button.short_help), T_(button.long_help), isToggle=button.is_toggle ) may_add_separator = True class ToolBarButtonInfo: def __init__( self, button_id, bitmap, short_help, long_help, is_toggle ): self.button_id = button_id self.bitmap = bitmap self.short_help = short_help self.long_help = long_help self.is_toggle = is_toggle if self.long_help is None: self.long_help = self.short_help toolbar_main = ToolBarResource() toolbar_main.addButton( wb_ids.id_SP_EditCut, 'toolbar_images/editcut.png', U_('Cut Files and Folders') ) toolbar_main.addButton( wb_ids.id_SP_EditCopy, 'toolbar_images/editcopy.png', U_('Copy Files and Folders') ) toolbar_main.addButton( wb_ids.id_SP_EditPaste, 'toolbar_images/editpaste.png', U_('Paste Files and Folders') ) toolbar_main.addButton( wb_ids.id_Command_Shell, 'toolbar_images/terminal.png', U_('Command Shell'), U_('Start new command shell') ) toolbar_main.addButton( wb_ids.id_File_Browser, 'toolbar_images/file_browser.png', U_('File Browser') ) toolbar_main.addButton( wb_ids.id_File_Edit, 'toolbar_images/edit.png', U_('Edit File') ) toolbar_main.addButton( wb_ids.id_Shell_Open, 'toolbar_images/open.png', U_('Open File') ) toolbar_main.addButton( wb_ids.id_SP_DiffWorkBase, 'toolbar_images/diff.png', U_('Diff changes against base') ) toolbar_main.addButton( wb_ids.id_SP_History, 'toolbar_images/history.png', U_('Show History log') ) toolbar_main.addButton( wb_ids.id_SP_Info, 'toolbar_images/info.png', U_('File Information') ) toolbar_main.addButton( wb_ids.id_SP_Properties, 'toolbar_images/property.png', U_('File Properties') ) toolbar_main.addButton( wb_ids.id_SP_Add, 'toolbar_images/add.png', U_('Add Files and Folders') ) toolbar_main.addButton( wb_ids.id_SP_Delete, 'toolbar_images/delete.png', U_('Delete selected Files and Folders') ) toolbar_main.addButton( wb_ids.id_SP_Revert, 'toolbar_images/revert.png', U_('Revert selected Files and Folders') ) toolbar_main.addButton( wb_ids.id_SP_Lock, 'toolbar_images/lock.png', U_('Lock File') ) toolbar_main.addButton( wb_ids.id_SP_Unlock, 'toolbar_images/unlock.png', U_('Unlock File') ) toolbar_main.addButton( wb_ids.id_SP_Checkin, 'toolbar_images/checkin.png', U_('Checkin changes') ) toolbar_main.addButton( wb_ids.id_SP_Update, 'toolbar_images/update.png', U_('Update working copy') ) toolbar_main.addButton( wb_ids.id_View_Recursive, 'toolbar_images/flatview.png', U_('Use recursive (flat) view'), is_toggle=True), toolbar_main.addButton( wb_ids.id_View_OnlyChanges, 'toolbar_images/onlychanges.png', U_('Show only changed files'), is_toggle=True), (toolbar_main.addGroup( 'edit' ) .addToGroup( wb_ids.id_SP_EditCut ) .addToGroup( wb_ids.id_SP_EditCopy ) .addToGroup( wb_ids.id_SP_EditPaste )) (toolbar_main.addGroup( 'shell' ) .addToGroup( wb_ids.id_Command_Shell ) .addToGroup( wb_ids.id_File_Browser )) (toolbar_main.addGroup( 'file' ) .addToGroup( wb_ids.id_File_Edit ) .addToGroup( wb_ids.id_Shell_Open )) (toolbar_main.addGroup( 'diff' ) .addToGroup( wb_ids.id_SP_DiffWorkBase ) .addToGroup( wb_ids.id_SP_History ) .addToGroup( wb_ids.id_SP_Info ) .addToGroup( wb_ids.id_SP_Properties )) (toolbar_main.addGroup( 'add' ) .addToGroup( wb_ids.id_SP_Add ) .addToGroup( wb_ids.id_SP_Delete ) .addToGroup( wb_ids.id_SP_Revert )) (toolbar_main.addGroup( 'lock' ) .addToGroup( wb_ids.id_SP_Lock ) .addToGroup( wb_ids.id_SP_Unlock )) (toolbar_main.addGroup( 'checkin' ) .addToGroup( wb_ids.id_SP_Checkin ) .addToGroup( wb_ids.id_SP_Update )) (toolbar_main.addGroup( 'view' ) .addToGroup( wb_ids.id_View_Recursive ) .addToGroup( wb_ids.id_View_OnlyChanges )) WorkBench-1.8.2/Source/wb_subversion_provider.py000644 000765 000024 00000003257 12717610335 022315 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_subversion_provider.py ''' import os import time import pysvn import wx import wb_source_control_providers import wb_subversion_history import wb_subversion_annotate import wb_ids import wb_exceptions import wb_subversion_tree_handler import wb_subversion_list_handler import wb_subversion_utils import wb_subversion_project_info def registerProvider(): wb_source_control_providers.registerProvider( SubversionProvider() ) class SubversionProvider(wb_source_control_providers.Provider): def __init__( self ): wb_source_control_providers.Provider.__init__( self, 'subversion' ) def getProjectInfo( self, app, parent=None ): return wb_subversion_project_info.ProjectInfo( app, parent ) def getProjectTreeItem( self, app, project_info ): return wb_subversion_tree_handler.SubversionProject( app, project_info ) def getListHandler( self, app, list_panel, project_info ): return wb_subversion_list_handler.SubversionListHandler( app, list_panel, project_info ) def getAboutString( self ): return ('pysvn version: %d.%d.%d-%d\n' 'svn version: %d.%d.%d-%s\n' % (pysvn.version[0], pysvn.version[1], pysvn.version[2], pysvn.version[3], pysvn.svn_version[0], pysvn.svn_version[1], pysvn.svn_version[2], pysvn.svn_version[3]) ) WorkBench-1.8.2/Source/wb_subversion_checkin.py000644 000765 000024 00000043663 11662230071 022065 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2006-2011 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_subversion_checkin.py ''' import wx import sys import wb_images import time import pysvn import wb_ids import wb_images import wb_exceptions import wb_list_panel_common import wb_subversion_utils import wb_subversion_list_handler_common import wb_platform_specific import wb_dialogs id_exclude = wx.NewId() id_include = wx.NewId() class CheckinFrame(wx.Frame): def __init__( self, app, project_info, all_files ): wx.Frame.__init__( self, None, -1, T_('Check in for %s') % project_info.wc_path, size=(1000,500) ) self.app = app self.menu_actions = wx.Menu() self.menu_actions.Append( wb_ids.id_File_Edit, T_('Edit'), T_('Edit') ) if wx.Platform in ['__WXMSW__','__WXMAC__']: self.menu_actions.Append( wb_ids.id_Shell_Open, T_('Open'), T_('Open') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SP_DiffWorkBase, T_('Diff WC vs. BASE...'), T_('Diff WC vs. BASE...') ) self.menu_actions.Append( wb_ids.id_SP_DiffWorkHead, T_('Diff WC vs. HEAD...'), T_('Diff WC vs. HEAD...') ) self.menu_actions.Append( wb_ids.id_SP_DiffWorkBranchOriginBase, T_('Diff WC vs. branch origin BASE...'), T_('Diff WC vs. branch origin BASE...') ) self.menu_actions.Append( wb_ids.id_SP_DiffWorkBranchOriginHead, T_('Diff WC vs. branch origin HEAD...'), T_('Diff WC vs. branch origin HEAD...') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SP_Annotate, T_('Annotate...'), T_('Annotate...') ) self.menu_actions.Append( wb_ids.id_SP_History, T_('Log history...'), T_('Log history...') ) self.menu_actions.Append( wb_ids.id_SP_Info, T_('Information...'), T_('Information...') ) self.menu_actions.Append( wb_ids.id_SP_Properties, T_('Properties...'), T_('Properties...') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SelectAll, T_('Select All'), T_('Select All') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( id_exclude, T_('Exclude...'), T_('Exclude from check in') ) self.menu_actions.Append( id_include, T_('Include...'), T_('Include from check in') ) self.menu_bar = wx.MenuBar() self.menu_bar.Append( self.menu_actions, T_('&Actions') ) self.SetMenuBar( self.menu_bar ) # Add tool bar t = self.CreateToolBar( name='main', style=wx.TB_HORIZONTAL ) # | wx.NO_BORDER | wx.TB_TEXT ) bitmap_size = (32,32) t.SetToolBitmapSize( bitmap_size ) t.AddSimpleTool( wb_ids.id_File_Edit, wb_images.getBitmap( 'toolbar_images/edit.png', bitmap_size ), T_('Edit File'), T_('Edit File') ) if wx.Platform in ['__WXMSW__','__WXMAC__']: t.AddSimpleTool( wb_ids.id_Shell_Open, wb_images.getBitmap( 'toolbar_images/open.png', bitmap_size ), T_('Open File'), T_('Open File') ) t.AddSeparator() t.AddSimpleTool( wb_ids.id_SP_DiffWorkBase, wb_images.getBitmap( 'toolbar_images/diff.png', bitmap_size ), T_('Diff changes against base'), T_('Diff changes against base') ) t.AddSimpleTool( wb_ids.id_SP_History, wb_images.getBitmap( 'toolbar_images/history.png', bitmap_size ), T_('Show History log'), T_('Show History log') ) t.AddSimpleTool( wb_ids.id_SP_Info, wb_images.getBitmap( 'toolbar_images/info.png', bitmap_size ), T_('File Information'), T_('File Information') ) t.AddSimpleTool( wb_ids.id_SP_Properties, wb_images.getBitmap( 'toolbar_images/property.png', bitmap_size ), T_('File Properties'), T_('File Properties') ) t.AddSeparator() t.AddSimpleTool( wb_ids.id_SP_Revert, wb_images.getBitmap( 'toolbar_images/revert.png', bitmap_size ), T_('Revert'), T_('Revert selected Files and Folders') ) t.AddSeparator() t.AddSimpleTool( id_exclude, wb_images.getBitmap( 'toolbar_images/exclude.png', bitmap_size ), T_('Exclude from check in'), T_('Exclude from check in') ) t.AddSimpleTool( id_include, wb_images.getBitmap( 'toolbar_images/include.png', bitmap_size ), T_('Include in check in'), T_('Include in check in') ) t.Realize() try_wrapper = wb_exceptions.TryWrapperFactory( self.app.log ) # Set the application icon self.SetIcon( wb_images.getIcon( 'wb.png' ) ) self.message_filename = wb_platform_specific.getLastCheckinMessageFilename() self.last_log_message_text = None try: f = wb_platform_specific.uOpen( self.message_filename, 'r' ) self.last_log_message_text = f.read().decode('utf-8').strip() f.close() except EnvironmentError: self.last_log_message_text = '' # Create the splitter windows self.splitter = wx.SplitterWindow( self, -1 ) # Make sure the splitters can't be removed by setting a minimum size self.splitter.SetMinimumPaneSize( 100 ) # list, log # create the individule panels self.panel_list = CheckinListPanel( app, self, self.splitter ) # Create the log message panel self.panel_log = wx.Panel( self.splitter, -1 ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.h_sizer = wx.BoxSizer( wx.HORIZONTAL ) self.label_ctrl = wx.StaticText( self.panel_log, -1, T_('Log message'), style=wx.ALIGN_LEFT ) self.log_message_ctrl = wx.TextCtrl( self.panel_log, -1, style=wx.TE_MULTILINE ) self.button_ok = wx.Button( self.panel_log, wx.ID_OK, T_(' Check In ') ) self.button_cancel = wx.Button( self.panel_log, wx.ID_CANCEL, T_(' Cancel ') ) self.button_ok.SetDefault() self.button_last_log_message = wx.Button( self.panel_log, -1, T_('Insert Last Message') ) self.button_last_log_message.Enable( len(self.last_log_message_text) > 0 ) self.h_sizer.Add( self.button_last_log_message ) self.button_ok.Enable( False ) wx.EVT_BUTTON( self.panel_log, self.button_last_log_message.GetId(), try_wrapper( self.OnInsertLastLogMessage ) ) wx.EVT_TEXT( self.panel_log, self.log_message_ctrl.GetId(), try_wrapper( self.OnLogMessageChanged ) ) self.h_sizer.Add( (60, 20), 1, wx.EXPAND) self.h_sizer.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15) self.h_sizer.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 2 ) self.v_sizer.Add( self.label_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.log_message_ctrl, 1, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer, 0, wx.EXPAND|wx.ALL, 5 ) self.splitter.SplitHorizontally( self.panel_list, self.panel_log, -150 ) self.panel_log.SetAutoLayout( True ) self.panel_log.SetSizer( self.v_sizer ) self.v_sizer.Fit( self.panel_log ) self.panel_log.Layout() wx.EVT_CLOSE( self, self.OnCloseWindow ) wx.EVT_BUTTON( self.panel_log, wx.ID_OK, self.app.eventWrapper( self.OnOk ) ) wx.EVT_BUTTON( self.panel_log, wx.ID_CANCEL, try_wrapper( self.OnCancel ) ) self.project_info = CheckinProjectInfo( project_info, all_files ) self.list_handler = CheckinListHandler( self.app, self.panel_list, self.project_info ) # draw the list - its updates the status info self.panel_list.setHandler( self.list_handler ) wx.EVT_MENU( self, wb_ids.id_File_Edit, try_wrapper( self.OnFileEdit ) ) wx.EVT_MENU( self, wb_ids.id_Shell_Open, try_wrapper( self.OnShellOpen ) ) wx.EVT_MENU( self, wb_ids.id_SP_Annotate, self.app.eventWrapper( self.OnSpAnnotate ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkBase, self.app.eventWrapper( self.OnSpDiffWorkBase ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkHead, self.app.eventWrapper( self.OnSpDiffWorkHead ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkBranchOriginBase, self.app.eventWrapper( self.OnSpDiffWorkBranchOriginBase ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkBranchOriginHead, self.app.eventWrapper( self.OnSpDiffWorkBranchOriginHead ) ) wx.EVT_MENU( self, wb_ids.id_SP_History, self.app.eventWrapper( self.OnSpHistory ) ) wx.EVT_MENU( self, wb_ids.id_SP_Info, self.app.eventWrapper( self.OnSpInfo ) ) wx.EVT_MENU( self, wb_ids.id_SP_Properties, self.app.eventWrapper( self.OnSpProperties ) ) wx.EVT_MENU( self, wb_ids.id_SP_Revert, self.app.eventWrapper( self.OnSpRevert) ) wx.EVT_MENU( self, wb_ids.id_SelectAll, self.app.eventWrapper( self.OnSelectAll ) ) 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 OnSelectAll( self, event ): self.panel_list.selectAll() print self.panel_list.getSelectedRows() def OnExcludeItem( self, event ): self.list_handler.Cmd_Checkin_ExcludeItem( self.panel_list.getSelectedRows() ) self.panel_list.drawList() def OnIncludeItem( self, event ): self.list_handler.Cmd_Checkin_IncludeItem( self.panel_list.getSelectedRows() ) self.panel_list.drawList() def clearUpdateUiState( self ): pass def getUpdateUiState( self ): pass def setEventHandler( self, handler ): self.handler = handler def OnInsertLastLogMessage( self, event ): self.log_message_ctrl.WriteText( self.last_log_message_text ) self.button_ok.Enable( True ) def OnLogMessageChanged( self, event ): self.button_ok.Enable( len( self.log_message_ctrl.GetValue().strip() ) > 0 ) def OnCloseWindow( self, event ): self.Destroy() def OnCancel( self, event ): self.Destroy() def OnOk( self, event ): self.Hide() message = self.log_message_ctrl.GetValue().encode('utf-8') try: f = wb_platform_specific.uOpen( self.message_filename, 'w' ) f.write( message ) f.close() except EnvironmentError: pass all_filenames = self.list_handler.getCheckinFiles() self.app.setAction( T_('Check in %s...') % self.project_info.wc_path ) self.app.setProgress( T_('Sent %(count)d of %(total)d'), len( all_filenames ) ) yield self.app.backgroundProcess ok = False try: commit_info = self.project_info.client_bg.checkin( all_filenames, message ) ok = True except pysvn.ClientError, e: self.app.log_client_error( e ) yield self.app.foregroundProcess self.app.refreshFrame() self.app.setAction( T_('Ready') ) self.app.clearProgress() self.Destroy() if ok: rev = commit_info['revision'] if rev: self.app.log.info( T_('Checkin created revision %d') % rev.number ) else: self.app.log.warning( T_('No changes to checkin ') ) post_commit_err = commit_info['post_commit_err'] if post_commit_err is not None: self.app.log.error( post_commit_err ) wx.MessageBox( post_commit_err, (T_('Post commit error')), style=wx.OK|wx.ICON_ERROR ) # command events def OnFileEdit( self, event ): return self.panel_list.OnFileEdit() def OnShellOpen( self, event ): return self.panel_list.OnShellOpen() def OnSpAnnotate( self, event ): return self.panel_list.OnSpAnnotate() def OnSpDiffWorkBase( self, event ): return self.panel_list.OnSpDiffWorkBase() def OnSpDiffWorkHead( self, event ): return self.panel_list.OnSpDiffWorkHead() def OnSpDiffWorkBranchOriginBase( self, event ): return self.panel_list.OnSpDiffWorkBranchOriginBase() def OnSpDiffWorkBranchOriginHead( self, event ): return self.panel_list.OnSpDiffWorkBranchOriginHead() def OnSpHistory( self, event ): return self.panel_list.OnSpHistory() def OnSpInfo( self, event ): return self.panel_list.OnSpInfo() def OnSpProperties( self, event ): return self.panel_list.OnSpProperties() def OnSpRevert( self, event ): self.Hide() # Invoke the revert handler result = self.panel_list.OnSpRevert() # If all files have been reverted, don't show the checkin box. if len(self.project_info.all_files) == 0: wx.MessageBox( T_('There are no changes to check in'), T_('Warning'), style=wx.OK|wx.ICON_EXCLAMATION ) self.Destroy() else: # Redraw the panel self.panel_list.drawList() self.Show() # update the main frame self.app.refreshFrame() return result class CheckinListHandler(wb_subversion_list_handler_common.SubversionListHandlerCommon): def __init__( self, app, parent, project_info ): wb_subversion_list_handler_common.SubversionListHandlerCommon.__init__( self, app, parent, project_info ) self.all_excluded_files = {} self.__parent = parent def overrideColumnInfo( self ): self.column_info.setColumnWidth( 'Name', self.column_info.getColumnWidth( 'Name' ) + 20 ) def getContextMenu( self ): menu_template = [ ('', wb_ids.id_File_Edit, T_('Edit') ) ] if wx.Platform in ['__WXMSW__','__WXMAC__']: menu_template += [ ('', wb_ids.id_Shell_Open, T_('Open') ) ] menu_template += [ ('-', 0, 0 ), ('', wb_ids.id_SP_DiffWorkBase, T_('Diff WC vs. BASE...') ), ('', wb_ids.id_SP_DiffWorkHead, T_('Diff WC vs. HEAD...') ), ('', wb_ids.id_SP_DiffWorkBranchOriginBase, T_('Diff WC vs. branch origin BASE...') ), ('', wb_ids.id_SP_DiffWorkBranchOriginHead, T_('Diff WC vs. branch origin HEAD...') ), ('-', 0, 0 ), ('', wb_ids.id_SP_Annotate, T_('Annotate...') ), ('', wb_ids.id_SP_History, T_('Log history...') ), ('', wb_ids.id_SP_Info, T_('Information...') ), ('', wb_ids.id_SP_Properties, T_('Properties...') ), ('', wb_ids.id_SP_Revert, T_('Revert...') ), ] return wb_subversion_utils.populateMenu( wx.Menu(), menu_template ) def Cmd_Checkin_ExcludeItem( self, all_rows ): #print 'Cmd_Checkin_ExcludeItem',all_rows for row in all_rows: self.all_excluded_files[ self.getFilename( row ) ] = None def Cmd_Checkin_IncludeItem( self, all_rows ): #print 'Cmd_Checkin_IncludeItem',all_rows for row in all_rows: if self.getFilename( row ) in self.all_excluded_files: del self.all_excluded_files[ self.getFilename( row ) ] def getAllGreyFilenames( self ): # show all excluded files in grey return self.all_excluded_files def getCheckinFiles( self ): return [entry.path for entry in self.project_info.all_files if entry.path not in self.all_excluded_files] def Cmd_File_Revert( self, all_rows ): # Pop up a confirm dialog box. dialog = wb_dialogs.ConfirmAction( self.__parent, T_('Revert'), self.getStatusAndFilenames( all_rows ) ) result = dialog.ShowModal() if result != wx.ID_OK: return try: #Revert each file for filename in [self.getFilename( row ) for row in all_rows]: self.project_info.client_fg.revert( filename ) except pysvn.ClientError, e: self.app.log_client_error( e ) # Build up a list of files that are still dirty. all_files = self.project_info.client_bg.status( self.project_info.wc_path, recurse=True, get_all=False, ignore=True, update=False ) all_files = [entry for entry in all_files if wb_subversion_utils.wc_status_checkin_map[ entry.text_status ] or wb_subversion_utils.wc_status_checkin_map[ entry.prop_status ]] # Set the project files to be this new list self.project_info.all_files = all_files class CheckinProjectInfo: def __init__( self, project_info, all_files ): self.all_files = all_files self.need_properties = False self.project_name = project_info.project_name self.url = project_info.url self.wc_path = project_info.wc_path self.need_checkout = False self.need_upgrade = False self.client_fg = project_info.client_fg self.client_bg = project_info.client_bg def getTagsUrl( self, rel_url ): return None def setNeedProperties( self, need_properties ): self.need_properties = need_properties def updateStatus( self ): pass def getFilesStatus( self ): return self.all_files def getProperty( self, filename, prop_name ): return '' def getWorkingDir( self ): return self.wc_path class CheckinListPanel(wb_list_panel_common.WbListPanelCommon): def __init__( self, app, frame, parent ): wb_list_panel_common.WbListPanelCommon.__init__( self, app, frame, parent ) def getAcceleratorTableInit( self ): acc_init =[ (wx.ACCEL_CMD, ord('D'), wb_ids.id_SP_DiffWorkBase), (wx.ACCEL_CMD, ord('E'), wb_ids.id_File_Edit), (wx.ACCEL_CMD, ord('L'), wb_ids.id_SP_History), (wx.ACCEL_CMD, ord('I'), wb_ids.id_SP_Info), (wx.ACCEL_CMD, ord('P'), wb_ids.id_SP_Properties), (wx.ACCEL_CMD, ord('O'), wb_ids.id_Shell_Open), ] return acc_init WorkBench-1.8.2/Source/wb_exceptions.py000644 000765 000024 00000002626 11507143125 020356 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_exceptions.py ''' class WorkBenchError(Exception): def __init__( self, msg ): Exception.__init__( self, msg ) class InternalError(WorkBenchError): def __init__( self, msg ): WorkBenchError.__init__( self, msg ) # # Helper class to cut down code bloat. # # in __init__ add: # self.try_wrapper = wb_exceptions.TryWrapperFactory( log ) # # where binding an EVT code as: # # wxPython.wx.EVT_SIZE( self, self.try_wrapper( self.OnSize ) ) # class TryWrapperFactory: def __init__( self, log ): self.log = log def __call__( self, function ): return TryWrapper( self.log, function ) class TryWrapper: def __init__( self, log, function ): self.log = log self.function = function def __call__( self, *params, **keywords ): try: result = self.function( *params, **keywords ) return result except Exception: self.log.exception( 'TryWrapper<%s.%s>\n' % (self.function.__module__, self.function.__name__ ) ) return None WorkBench-1.8.2/Source/wb_project_dialogs.py000644 000765 000024 00000073104 12264262327 021353 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_project_dialogs.py ''' import wx import wx.wizard import wb_source_control_providers import wb_platform_specific import pysvn import os # # Later need to make these dialogs work with the # registered providers to put up provider specific # dialog. # wc_path_browse_id = wx.NewId() wc_path_text_ctrl_id = wx.NewId() name_text_ctrl_id = wx.NewId() url_trunk_path_text_ctrl_id = wx.NewId() url_tags_path_text_ctrl_id = wx.NewId() wizard_id = wx.NewId() class ProjectDialog(wx.Dialog): def __init__( self, app, parent, title ): wx.Dialog.__init__( self, parent, -1, title ) self.app = app self.client = pysvn.Client() self.client.exception_style = 1 self.list_background_colour = None # get the list of names to use in the validation pi_list = self.app.prefs.getProjects().getProjectList() self.pi_names = [pi.project_name for pi in pi_list] self.g_sizer = wx.GridBagSizer( 5, 20 ) self.border = wx.StaticBox( self, -1, T_('Project') ) self.box = wx.StaticBoxSizer( self.border, wx.VERTICAL ) self.box.Add( self.g_sizer, 0, wx.EXPAND ) self.name_label = wx.StaticText( self, -1, T_('Project Name:'), style=wx.ALIGN_RIGHT ) self.name_ctrl = wx.TextCtrl( self, -1, '', size=(400,-1) ) self.g_sizer.Add( self.name_label, (0,0), (1,1), wx.ALIGN_RIGHT ) self.g_sizer.Add( self.name_ctrl, (0,1), (1,2), wx.EXPAND ) self.wc_path_browse = wx.Button( self, wc_path_browse_id, T_(" Browse... ") ) self.wc_path_label = wx.StaticText(self, -1, T_('Working copy Path:'), style=wx.ALIGN_RIGHT ) self.wc_path_ctrl = wx.TextCtrl(self, wc_path_text_ctrl_id, '' ) self.g_sizer.Add( self.wc_path_label, (1,0), (1,1), wx.ALIGN_RIGHT ) self.g_sizer.Add( self.wc_path_ctrl, (1,1), (1,2), wx.EXPAND ) self.g_sizer.Add( self.wc_path_browse, (1,3), (1,1) ) self.url_trunk_label = wx.StaticText( self, -1, T_('Subversion Trunk URL:'), style=wx.ALIGN_RIGHT ) self.url_trunk_ctrl = wx.TextCtrl( self, url_trunk_path_text_ctrl_id, '' ) self.g_sizer.Add( self.url_trunk_label, (2,0), (1,1), wx.ALIGN_RIGHT ) self.g_sizer.Add( self.url_trunk_ctrl, (2,1), (1,2), wx.EXPAND ) self.url_tags_label = wx.StaticText( self, -1, T_('Subversion Tags URL:'), style=wx.ALIGN_RIGHT ) self.url_tags_ctrl = wx.TextCtrl( self, url_tags_path_text_ctrl_id, '' ) self.g_sizer.Add( self.url_tags_label, (3,0), (1,1), wx.ALIGN_RIGHT ) self.g_sizer.Add( self.url_tags_ctrl, (3,1), (1,2), wx.EXPAND ) self.url_branches_label = wx.StaticText( self, -1, T_('Subversion Branches URL:'), style=wx.ALIGN_RIGHT ) self.url_branches_ctrl = wx.TextCtrl( self, url_tags_path_text_ctrl_id, '' ) self.g_sizer.Add( self.url_branches_label, (4,0), (1,1), wx.ALIGN_RIGHT ) self.g_sizer.Add( self.url_branches_ctrl, (4,1), (1,2), wx.EXPAND ) self.newfile_dir_label = wx.StaticText( self, -1, T_("New File Template Folder: "), style=wx.ALIGN_RIGHT) self.newfile_dir_ctrl = wx.TextCtrl( self, -1, '' ) self.newfile_dir_browse = wx.Button( self, -1, T_(" Browse... ") ) self.g_sizer.Add( self.newfile_dir_label, (5,0), (1,1), wx.ALIGN_RIGHT ) self.g_sizer.Add( self.newfile_dir_ctrl, (5,1), (1,2), wx.EXPAND ) self.g_sizer.Add( self.newfile_dir_browse, (5,3), (1,1) ) self.list_background_colour_label = wx.StaticText( self, -1, T_("Background Colour: ") , style=wx.ALIGN_RIGHT) self.list_background_colour_ctrl = wx.CheckBox( self, -1, T_("Use custom background colour") ) self.list_background_colour_example = wx.StaticText( self, -1, T_("Example") , style=wx.ALIGN_CENTER ) self.list_background_colour_picker = wx.Button( self, -1, T_(" Pick Colour... ") ) self.g_sizer.Add( self.list_background_colour_label, (6,0), (1,1), wx.ALIGN_RIGHT ) self.g_sizer.Add( self.list_background_colour_ctrl, (6,1), (1,1), wx.EXPAND ) self.g_sizer.Add( self.list_background_colour_example, (6,2), (1,1), wx.ALIGN_CENTER|wx.EXPAND ) self.g_sizer.Add( self.list_background_colour_picker, (6,3), (1,1) ) self.button_ok = wx.Button( self, wx.ID_OK, T_(' OK ') ) self.button_ok.SetDefault() self.button_cancel = wx.Button( self, wx.ID_CANCEL, T_(' Cancel ') ) self.h_sizer_buttons = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer_buttons.Add( (250, 20), 1, wx.EXPAND) self.h_sizer_buttons.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15 ) self.h_sizer_buttons.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 2 ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.v_sizer.Add( self.box, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer_buttons, 0, wx.EXPAND|wx.ALL, 5 ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() self.CentreOnParent() wx.EVT_BUTTON( self, wx.ID_OK, self.OnOk ) wx.EVT_BUTTON( self, wx.ID_CANCEL, self.OnCancel ) wx.EVT_BUTTON( self, wc_path_browse_id, self.OnBrowseWorkingCopyDir ) wx.EVT_TEXT( self, wc_path_text_ctrl_id, self.updateControls ) wx.EVT_TEXT( self, url_trunk_path_text_ctrl_id, self.updateControls ) wx.EVT_TEXT( self, url_tags_path_text_ctrl_id, self.updateControls ) wx.EVT_BUTTON( self, self.newfile_dir_browse.GetId(), self.OnBrowseNewFileTemplateDir ) wx.EVT_BUTTON( self, self.list_background_colour_picker.GetId(), self.OnPickColour ) wx.EVT_CHECKBOX( self, self.list_background_colour_ctrl.GetId(), self.OnUseColourClicked ) self._update_controls_lock = False def updateControls( self, event=None ): # on some platforms we will be reentered when SetValue is called if self._update_controls_lock: return self._update_controls_lock = True # If the wc_path exists and is a svn wc then disable the url field wc_path = os.path.expanduser( self.wc_path_ctrl.GetValue().strip() ) url = None if wb_platform_specific.uPathExists( wc_path ): try: info = self.client.info( wc_path ) url = info.url except pysvn.ClientError: pass if url is None: self.url_trunk_ctrl.Enable( True ) else: self.url_trunk_ctrl.Enable( False ) if len( self.name_ctrl.GetValue() ) == 0: self.name_ctrl.SetValue( os.path.basename( wc_path ) ) self.url_trunk_ctrl.SetValue( url ) if( url is not None and self.url_tags_ctrl.GetValue().strip() == '' ): url_parts = url.split('/') if 'trunk' in url_parts: trunk_index = url_parts.index('trunk') url_parts[ trunk_index ] = 'tags' self.url_tags_ctrl.SetValue( '/'.join( url_parts[:trunk_index+1] ) ) if( url is not None and self.url_branches_ctrl.GetValue().strip() == '' ): url_parts = url.split('/') if 'trunk' in url_parts: trunk_index = url_parts.index('trunk') url_parts[ trunk_index ] = 'branches' self.url_branches_ctrl.SetValue( '/'.join( url_parts[:trunk_index+1] ) ) self._update_controls_lock = False def OnOk( self, event ): is_url = self.client.is_url name = self.name_ctrl.GetValue().strip() if name == '': wx.MessageBox( T_('Enter a project name'), style=wx.OK|wx.ICON_ERROR ); return if name in self.pi_names: wx.MessageBox( T_('Project %s already exist. Choose another name') % name, style=wx.OK|wx.ICON_ERROR ); return url = self.url_trunk_ctrl.GetValue().strip() if url == '': wx.MessageBox( T_('Enter a Subversion trunk URL'), style=wx.OK|wx.ICON_ERROR ); return if not is_url( url ): wx.MessageBox( T_('%s is not a valid Subversion trunk URL') % url, style=wx.OK|wx.ICON_ERROR ); return url = self.url_tags_ctrl.GetValue().strip() if url and not is_url( url ): wx.MessageBox( T_('%s is not a valid Subversion tags URL') % url, style=wx.OK|wx.ICON_ERROR ); return url = self.url_branches_ctrl.GetValue().strip() if url and not is_url( url ): wx.MessageBox( T_('%s is not a valid Subversion tags URL') % url, style=wx.OK|wx.ICON_ERROR ); return if self.wc_path_ctrl.GetValue().strip() == '': wx.MessageBox( T_('Enter a Working copy path'), style=wx.OK|wx.ICON_ERROR ); return self.EndModal( wx.ID_OK ) def OnCancel( self, event ): self.EndModal( wx.ID_CANCEL ) def OnBrowseWorkingCopyDir( self, event ): filename = os.path.expanduser( self.wc_path_ctrl.GetValue() ) dir_dialog = wx.DirDialog( self, T_("Select Working Copy directory"), filename, style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON ) if dir_dialog.ShowModal() == wx.ID_OK: self.wc_path_ctrl.SetValue( dir_dialog.GetPath() ) dir_dialog.Destroy() self.updateControls() def OnBrowseNewFileTemplateDir( self, event ): filename = self.newfile_dir_ctrl.GetValue() if filename == '': filename = self.wc_path_ctrl.GetValue() filename = os.path.expanduser( filename ) dir_dialog = wx.DirDialog( self, T_("Select New File Template directory"), filename, style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON ) if dir_dialog.ShowModal() == wx.ID_OK: self.newfile_dir_ctrl.SetValue( dir_dialog.GetPath() ) dir_dialog.Destroy() self.updateControls() def OnUseColourClicked( self, event ): if self.list_background_colour_ctrl.GetValue(): self.list_background_colour_picker.Enable( True ) else: self.list_background_colour_picker.Enable( False ) def OnPickColour( self, event ): colour_data = wx.ColourData() colour_data.SetChooseFull( True ) colour_data.SetColour( self.list_background_colour ) dlg = wx.ColourDialog( self, colour_data ) if dlg.ShowModal() == wx.ID_OK: # If the user selected OK, then the dialog's wx.ColourData will # contain valid information. Fetch the data ... data = dlg.GetColourData() self.list_background_colour = data.GetColour().Get() # ... then do something with it. The actual colour data will be # returned as a three-tuple (r, g, b) in this particular case. dlg.Destroy() self.list_background_colour_example.SetBackgroundColour( self.list_background_colour ) self.list_background_colour_example.Refresh( True ) def getProjectInfo( self ): provider = wb_source_control_providers.getProvider( 'subversion' ) pi = provider.getProjectInfo( self.app ) pi.new_file_template_dir = self.newfile_dir_ctrl.GetValue().strip() pi.init( self.name_ctrl.GetValue().strip(), wc_path=os.path.expanduser( self.wc_path_ctrl.GetValue().strip() ), url=self.url_trunk_ctrl.GetValue().strip(), tags_url=self.url_tags_ctrl.GetValue().strip(), branches_url=self.url_branches_ctrl.GetValue().strip() ) pi.setBackgroundColour( self.list_background_colour_ctrl.GetValue(), self.list_background_colour ) return pi class UpdateProjectDialog(ProjectDialog): def __init__( self, app, parent, project_info ): ProjectDialog.__init__( self, app, parent, T_('Project Settings') ) self.project_info = project_info del self.pi_names[ self.pi_names.index( self.project_info.project_name ) ] self.name_ctrl.SetValue( self.project_info.project_name ) self.url_trunk_ctrl.SetValue( self.project_info.url ) self.wc_path_ctrl.SetValue( self.project_info.wc_path ) self.url_tags_ctrl.SetValue( self.project_info.tags_url ) self.newfile_dir_ctrl.SetValue( self.project_info.new_file_template_dir ) if self.project_info.use_background_colour: self.list_background_colour = self.project_info.background_colour else: self.list_background_colour = (255,255,255) self.list_background_colour_ctrl.SetValue( self.project_info.use_background_colour ) self.list_background_colour_example.SetBackgroundColour( self.list_background_colour ) self.list_background_colour_picker.Enable( self.project_info.use_background_colour ) self.updateControls() class AddProjectState: def __init__( self ): self.use_existing = False self.wc_path = '' self.url_path = '' self.project_name = '' class AddProjectDialog: def __init__( self, app, parent ): self.app = app provider = wb_source_control_providers.getProvider( 'subversion' ) pi = provider.getProjectInfo( self.app ) pi.init( '', wc_path='', url='' ) self.client_pi = pi # get the list of names to use in the validation pi_list = self.app.prefs.getProjects().getProjectList() self.pi_names = [pi.project_name for pi in pi_list] self.wizard = wx.wizard.Wizard( parent, wizard_id, T_("Add Project") ) self.page_wc_choice = WorkingCopyChoicePage( self ) self.page_wc_exists = WorkingCopyExistsPage( self ) self.page_url = SubversionUrlPage( self ) self.page_wc_create = WorkingCopyCreatePage( self ) self.page_project_name = ProjectNamePage( self ) self.page_wc_choice.SetNext( self.page_project_name ) self.page_wc_exists.SetPrev( self.page_wc_choice ) self.page_wc_exists.SetNext( self.page_project_name ) self.page_url.SetPrev( self.page_wc_choice ) self.page_url.SetNext( self.page_wc_create ) self.page_wc_create.SetPrev( self.page_url ) self.page_wc_create.SetNext( self.page_project_name ) self.page_project_name.SetPrev( self.page_wc_create ) self.wizard.FitToPage( self.page_wc_exists ) self.wizard.Bind( wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged ) self.wizard.Bind( wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging ) self.state = AddProjectState() def ShowModal( self ): self.page_wc_choice.loadState( self.state ) if self.wizard.RunWizard( self.page_wc_choice ): return wx.ID_OK return wx.ID_CANCEL def getProjectInfo( self ): provider = wb_source_control_providers.getProvider( 'subversion' ) pi = provider.getProjectInfo( self.app ) pi.init( self.state.project_name, wc_path=self.state.wc_path, url=self.state.url_path ) return pi def OnPageChanged( self, event ): page = event.GetPage() page.loadState( self.state ) def OnPageChanging( self, event ): page = event.GetPage() if event.GetDirection(): if not page.saveState( self.state ): event.Veto() #---------------------------------------------------------------------- def makePageTitle(wizPg, title): sizer = wx.BoxSizer(wx.VERTICAL) wizPg.SetSizer(sizer) title = wx.StaticText(wizPg, -1, title) title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)) sizer.Add(title, 0, wx.ALIGN_CENTRE|wx.ALL, 5) sizer.Add(wx.StaticLine(wizPg, -1), 0, wx.EXPAND|wx.ALL, 5) return sizer #---------------------------------------------------------------------- class TitledPage(wx.wizard.PyWizardPage): def __init__(self, parent, title): wx.wizard.PyWizardPage.__init__(self, parent.wizard) self.parent = parent self.sizer = makePageTitle(self, title) self.next = None self.prev = None def SetNext( self, next ): self.next = next def SetPrev( self, prev ): self.prev = prev def GetNext( self ): return self.next def GetPrev( self ): return self.prev def loadState( self, state ): pass def saveState( self, state ): return True class WorkingCopyChoicePage(TitledPage): def __init__( self, parent ): TitledPage.__init__( self, parent, T_("Working Copy") ) self.radio_new = wx.RadioButton( self, -1, T_(" Use new working copy directory "), style = wx.RB_GROUP ) self.radio_existing = wx.RadioButton( self, -1, T_(" Use existing Working copy directory ") ) self.sizer.Add( (10, 50) ) self.sizer.Add( self.radio_new, 0, wx.ALL, 5 ) self.sizer.Add( (5, 5) ) self.sizer.Add( self.radio_existing, 0, wx.ALL, 5 ) def GetNext( self ): if self.radio_existing.GetValue(): return self.parent.page_wc_exists if self.radio_new.GetValue(): return self.parent.page_url return None def loadState( self, state ): self.radio_existing.SetValue( state.use_existing ) self.radio_new.SetValue( not state.use_existing ) def saveState( self, state ): state.use_existing = self.radio_existing.GetValue() return True class WorkingCopyExistsPage(TitledPage): def __init__( self, parent ): TitledPage.__init__( self, parent, T_("Select Working Copy") ) self.g_sizer = wx.FlexGridSizer( 0, 3, 0, 0 ) self.g_sizer.AddGrowableCol( 1 ) self.wc_path_label = wx.StaticText(self, -1, T_('Working copy Path:'), style=wx.ALIGN_RIGHT ) self.wc_path_ctrl = wx.TextCtrl(self, wc_path_text_ctrl_id, '' ) self.wc_path_browse = wx.Button( self, wc_path_browse_id, T_(" Browse... ") ) self.g_sizer.Add( self.wc_path_label, 0, wx.EXPAND|wx.ALL|wx.ALIGN_BOTTOM, 5 ) self.g_sizer.Add( self.wc_path_ctrl, 1, wx.EXPAND|wx.ALL|wx.ALIGN_BOTTOM, 3 ) self.g_sizer.Add( self.wc_path_browse, 0, wx.EXPAND ) self.g_sizer.Add( (10,10) ) self.g_sizer.Add( (400,10) ) self.g_sizer.Add( (10,10) ) self.url_label = wx.StaticText( self, -1, T_('Subversion URL:'), style=wx.ALIGN_RIGHT ) self.url_ctrl = wx.StaticText( self, -1, '' ) self.g_sizer.Add( self.url_label, 0, wx.EXPAND|wx.ALL|wx.ALIGN_BOTTOM, 5 ) self.g_sizer.Add( self.url_ctrl, 0, wx.EXPAND|wx.ALL|wx.ALIGN_BOTTOM, 5 ) self.g_sizer.Add( (1, 1), 0, wx.EXPAND ) self.sizer.Add( self.g_sizer, 0, wx.ALL, 5 ) wx.EVT_BUTTON( self, wc_path_browse_id, self.OnBrowseWorkingCopyDir ) wx.EVT_TEXT( self, wc_path_text_ctrl_id, self.updateControls ) self._update_controls_lock = False def loadState( self, state ): self.wc_path_ctrl.SetValue( state.wc_path ) self.updateControls() def saveState( self, state ): # must get the abspath to convert c:/dir1/dir2 to c:\dir1\dir2 # otherwise all sorts of things break inside workbench state.wc_path = os.path.abspath( os.path.expanduser( self.wc_path_ctrl.GetValue().strip() ) ) state.url_path = self.url_ctrl.GetLabel().strip() if not wb_platform_specific.uPathExists( state.wc_path ): wx.MessageBox( T_('Path %s\n' 'Does not exist\n' 'Choose an existing subversion working copy directory') % state.wc_path, style=wx.OK|wx.ICON_ERROR ); return False if not wb_platform_specific.uPathIsdir( state.wc_path ): wx.MessageBox( T_('Path %s\n' 'Is not a directory\n' 'Choose an existing subversion working copy directory') % state.wc_path, style=wx.OK|wx.ICON_ERROR ); return False if state.url_path == '': wx.MessageBox( T_('Path %s\n' 'Is not a subversion working copy\n' 'Choose an existing subversion working copy directory') % state.wc_path, style=wx.OK|wx.ICON_ERROR ); return False return True def OnBrowseWorkingCopyDir( self, event ): filename = os.path.expanduser( self.wc_path_ctrl.GetValue() ) dir_dialog = wx.DirDialog( self, T_("Select Working Copy directory"), filename, style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON ) if dir_dialog.ShowModal() == wx.ID_OK: self.wc_path_ctrl.SetValue( dir_dialog.GetPath() ) dir_dialog.Destroy() self.updateControls() def updateControls( self, event=None ): # on some platforms we will be reentered when SetValue is called if self._update_controls_lock: return self._update_controls_lock = True # If the wc_path exists and is a svn wc then disable the url field wc_path = self.wc_path_ctrl.GetValue() wc_path = os.path.expanduser( wc_path ) url = None if wb_platform_specific.uPathExists( wc_path ): try: info = self.parent.client_pi.client_fg.info2( wc_path, recurse=False )[0][1] url = info.URL except pysvn.ClientError: pass if url is None: self.url_ctrl.SetLabel( '' ) else: self.url_ctrl.SetLabel( url ) self._update_controls_lock = False class SubversionUrlPage(TitledPage): def __init__( self, parent ): TitledPage.__init__( self, parent, T_("Select Subversion URL") ) self.g_sizer = wx.FlexGridSizer( 0, 3, 0, 0 ) self.g_sizer.AddGrowableCol( 1 ) self.url_path_label = wx.StaticText(self, -1, T_('Subversion URL:'), style=wx.ALIGN_RIGHT ) self.url_path_ctrl = wx.TextCtrl(self, url_trunk_path_text_ctrl_id, '' ) self.g_sizer.Add( self.url_path_label, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 5 ) self.g_sizer.Add( self.url_path_ctrl, 0, wx.EXPAND|wx.ALL, 3 ) self.g_sizer.Add( (1, 1), 0, wx.EXPAND ) self.g_sizer.Add( (10,10) ) self.g_sizer.Add( (400,10) ) self.g_sizer.Add( (10,10) ) self.sizer.Add( self.g_sizer, 0, wx.ALL, 5 ) def loadState( self, state ): self.url_path_ctrl.SetValue( state.wc_path ) def saveState( self, state ): state.url_path = self.url_path_ctrl.GetValue().strip() if state.url_path == '': wx.MessageBox( T_('Enter a Subversion URL'), style=wx.OK|wx.ICON_ERROR ); return False if not self.parent.client_pi.client_fg.is_url( state.url_path ): wx.MessageBox( T_('%s is not a valid Subversion URL') % state.url_path, style=wx.OK|wx.ICON_ERROR ); return False try: head = pysvn.Revision( pysvn.opt_revision_kind.head ) self.parent.client_pi.client_fg.log( state.url_path, revision_start=head, revision_end=head ) except pysvn.ClientError, e: wx.MessageBox( T_('%(url)s is not a accessable Subversion URL\n' '%(error)s') % {'url': state.url_path ,'error': e.args[0]}, style=wx.OK|wx.ICON_ERROR ) return False return True class WorkingCopyCreatePage(TitledPage): def __init__( self, parent ): TitledPage.__init__( self, parent, T_("Create new Working Copy") ) self.g_sizer = wx.FlexGridSizer( 0, 3, 0, 0 ) self.g_sizer.AddGrowableCol( 1 ) self.wc_path_label = wx.StaticText(self, -1, T_('Working copy Path:'), style=wx.ALIGN_RIGHT ) self.wc_path_ctrl = wx.TextCtrl(self, wc_path_text_ctrl_id, '' ) self.wc_path_browse = wx.Button( self, wc_path_browse_id, T_(" Browse... ") ) self.g_sizer.Add( self.wc_path_label, 0, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 5 ) self.g_sizer.Add( self.wc_path_ctrl, 0, wx.EXPAND|wx.ALL, 3 ) self.g_sizer.Add( self.wc_path_browse, 0, wx.EXPAND ) self.g_sizer.Add( (10,10) ) self.g_sizer.Add( (400,10) ) self.g_sizer.Add( (10,10) ) self.url_label = wx.StaticText( self, -1, T_('Subversion URL:'), style=wx.ALIGN_RIGHT ) self.url_ctrl = wx.StaticText( self, -1, '' ) self.g_sizer.Add( self.url_label, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 5 ) self.g_sizer.Add( self.url_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) self.g_sizer.Add( (1, 1), 0, wx.EXPAND ) self.sizer.Add( self.g_sizer, 0, wx.ALL, 5 ) wx.EVT_BUTTON( self, wc_path_browse_id, self.OnBrowseWorkingCopyDir ) def loadState( self, state ): self.wc_path_ctrl.SetValue( state.wc_path ) self.url_ctrl.SetLabel( state.url_path ) def saveState( self, state ): state.wc_path = os.path.expanduser( self.wc_path_ctrl.GetValue().strip() ) if state.wc_path == '': wx.MessageBox( T_('Choose a directory that is empty and not in used by subversion'), style=wx.OK|wx.ICON_ERROR ); return False if wb_platform_specific.uPathExists( state.wc_path ): if not wb_platform_specific.uPathIsdir( state.wc_path ): wx.MessageBox( T_('Path %s\n' 'Is not a directory\n' 'Choose a directory that is empty and not in use by subversion') % state.wc_path, style=wx.OK|wx.ICON_ERROR ); return False try: info = self.parent.client_pi.client_fg.info2( state.wc_path, recurse=False )[0][1] wx.MessageBox( T_('Path %s\n' 'Is a subversion working copy\n' 'Choose a directory that is empty and not in use by subversion') % state.wc_path, style=wx.OK|wx.ICON_ERROR ); return False except pysvn.ClientError: pass try: dir_list = os.listdir( state.wc_path ) if len(dir_list) > 0: wx.MessageBox( T_('Path %s\n' 'Is not an empty directory\n' 'Choose a directory that is empty and not in use by subversion') % state.wc_path, style=wx.OK|wx.ICON_ERROR ); return False except (OSError,IOError), e: wx.MessageBox( T_('Path %(path)s\n' '%(error)s\n' 'Choose a directory that is empty and not in use by subversion') % {'path': state.wc_path ,'error': str(e)}, style=wx.OK|wx.ICON_ERROR ); return False return True def OnBrowseWorkingCopyDir( self, event ): filename = os.path.expanduser( self.wc_path_ctrl.GetValue() ) dir_dialog = wx.DirDialog( self, T_("Select Working Copy directory"), filename, style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON ) if dir_dialog.ShowModal() == wx.ID_OK: self.wc_path_ctrl.SetValue( dir_dialog.GetPath() ) dir_dialog.Destroy() class ProjectNamePage(TitledPage): def __init__( self, parent ): TitledPage.__init__( self, parent, T_("Project Name") ) self.g_sizer = wx.FlexGridSizer( 0, 3, 0, 0 ) self.g_sizer.AddGrowableCol( 1 ) self.name_label = wx.StaticText(self, -1, T_('Project name:'), style=wx.ALIGN_RIGHT ) self.name_ctrl = wx.TextCtrl(self, -1, '' ) self.g_sizer.Add( self.name_label, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 5 ) self.g_sizer.Add( self.name_ctrl, 0, wx.EXPAND|wx.ALL, 3 ) self.g_sizer.Add( (1,1), 0, wx.EXPAND ) self.g_sizer.Add( (10,10) ) self.g_sizer.Add( (400,10) ) self.g_sizer.Add( (10,10) ) self.wc_path_label = wx.StaticText(self, -1, T_('Working copy Path:'), style=wx.ALIGN_RIGHT ) self.wc_path_ctrl = wx.StaticText(self, -1, '' ) self.g_sizer.Add( self.wc_path_label, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 5 ) self.g_sizer.Add( self.wc_path_ctrl, 0, wx.EXPAND|wx.ALL|wx.ALIGN_CENTER_HORIZONTAL , 5 ) self.g_sizer.Add( (1,1), 0, wx.EXPAND ) self.url_label = wx.StaticText( self, -1, T_('Subversion URL:'), style=wx.ALIGN_RIGHT ) self.url_ctrl = wx.StaticText( self, -1, '' ) self.g_sizer.Add( self.url_label, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 5 ) self.g_sizer.Add( self.url_ctrl, 0, wx.EXPAND|wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 5 ) self.g_sizer.Add( (1, 1), 0, wx.EXPAND ) self.sizer.Add( self.g_sizer, 0, wx.ALL, 5 ) def loadState( self, state ): self.wc_path_ctrl.SetLabel( state.wc_path ) self.url_ctrl.SetLabel( state.url_path ) def saveState( self, state ): state.project_name = self.name_ctrl.GetValue().strip() if state.project_name == '': wx.MessageBox( T_('Project name is blank.\n' 'Enter a project name'), style=wx.OK|wx.ICON_ERROR ); return False if state.project_name in self.parent.pi_names: wx.MessageBox( T_('Project %s already exist.\n' 'Choose another name') % state.project_name, style=wx.OK|wx.ICON_ERROR ); return False return True def GetNext( self ): return None WorkBench-1.8.2/Source/make-mo-files.cmd000644 000765 000024 00000000036 11261605162 020240 0ustar00barrystaff000000 000000 %PYTHON% make_mo_files.py %1 WorkBench-1.8.2/Source/wb_subversion_info_dialog.py000644 000765 000024 00000013433 12505525705 022733 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_subversion_list_handler.py ''' import types import pysvn import wx import wb_subversion_utils class InfoDialog(wx.Dialog): def __init__( self, app, parent, path, info_entry_or_info2_list ): wx.Dialog.__init__( self, parent, -1, path ) self.g_sizer = None self.g_sizer_list = [] self.addGroup( T_('Entry') ) value_ctrl = self.addRow( T_('Path:') ,path ) value_ctrl.SetFocus() #print 'info_entry_or_info2_list',info_entry_or_info2_list self.initForInfo2( info_entry_or_info2_list[0][1] ) self.button_ok = wx.Button( self, wx.ID_OK, T_(' OK ') ) self.button_ok.SetDefault() self.h_sizer_buttons = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer_buttons.Add( (800, 20), 1, wx.EXPAND) self.h_sizer_buttons.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15 ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) for g_sizer in self.g_sizer_list: self.v_sizer.Add( g_sizer, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer_buttons, 0, wx.EXPAND|wx.ALL, 5 ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() self.CentreOnParent() def initForInfo2( self, entry ): if entry['URL']: self.addRow( T_('URL:'), entry['URL'] ) if entry['repos_root_URL']: self.addRow( T_('Repository root URL:'), entry['repos_root_URL'] ) if entry['repos_UUID']: self.addRow( T_('Repository UUID:'), entry['repos_UUID'] ) if entry['rev'].kind == pysvn.opt_revision_kind.number: self.addRow( T_('Revision:'), entry['rev'].number ) if entry['kind'] == pysvn.node_kind.file: self.addRow( T_('Node kind:'), T_('file') ) elif entry['kind'] == pysvn.node_kind.dir: self.addRow( T_('Node kind:'), T_('directory') ) elif entry['kind'] == pysvn.node_kind.none: self.addRow( T_('Node kind:'), T_('none') ) else: self.addRow( T_('Node kind:'), T_('unknown') ) if entry['last_changed_author']: self.addRow( T_('Last Changed Author:'), entry['last_changed_author'] ) if entry['last_changed_rev'].number > 0: self.addRow( T_('Last Changed Revision:'), entry['last_changed_rev'].number ) if entry['last_changed_date']: self.addRow( T_('Last Changed Date:'), wb_subversion_utils.fmtDateTime( entry['last_changed_date'] ) ) self.addGroup( T_('Lock') ) lock_info = entry['lock'] if lock_info is not None: self.addRow( T_('Lock Owner:'), lock_info['owner'] ) self.addRow( T_('Lock Creation Date:'), wb_subversion_utils.fmtDateTime( lock_info['creation_date'] ) ) if lock_info['expiration_date'] is not None: self.addRow( T_('Lock Expiration Date:'), wb_subversion_utils.fmtDateTime( lock_info['expiration_date'] ) ) self.addRow( T_('Lock Token:'), lock_info['token'] ) self.addRow( T_('Lock Comment:'), lock_info['comment'] ) else: self.addRow( T_('Lock Token:'), '' ) wc_info = entry['wc_info'] if wc_info is None: return self.addGroup( T_('Working copy') ) if wc_info['schedule'] == pysvn.wc_schedule.normal: self.addRow( T_('Schedule:'), T_('normal') ) elif wc_info['schedule'] == pysvn.wc_schedule.add: self.addRow( T_('Schedule:'), T_('add') ) elif wc_info['schedule'] == pysvn.wc_schedule.delete: self.addRow( T_('Schedule:'), T_('delete') ) elif wc_info['schedule'] == pysvn.wc_schedule.replace: self.addRow( T_('Schedule:'), T_('replace') ) else: self.addRow( T_('Schedule:'), unicode(wc_info['schedule'])) if wc_info['copyfrom_url']: self.addRow( T_('Copied From URL:'), wc_info['copyfrom_url'] ) if wc_info['copyfrom_rev'].number: self.addRow( T_('Copied From Revision:'), wc_info['copyfrom_rev'].number ) if wc_info['text_time']: self.addRow( T_('Text Last Updated:'), wb_subversion_utils.fmtDateTime( wc_info['text_time'] ) ) if wc_info['prop_time']: self.addRow( T_('Properties Last Updated:'), wb_subversion_utils.fmtDateTime( wc_info['prop_time'] ) ) if wc_info['checksum']: self.addRow( T_('Checksum:'), wc_info['checksum'] ) def addGroup( self, label ): self.g_sizer = wx.FlexGridSizer( 0, 2, 5, 5 ) self.g_sizer.AddGrowableCol( 1 ) self.border = wx.StaticBox( self, -1, label ) self.box = wx.StaticBoxSizer( self.border, wx.VERTICAL ) self.box.Add( self.g_sizer, 0, wx.EXPAND ) self.g_sizer_list.append( self.box ) def addRow( self, label, value ): label_ctrl = wx.StaticText( self, -1, label, style=wx.ALIGN_RIGHT ) 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, unicode(value), size=wx.Size( -1, 100 ), style=wx.TE_MULTILINE ) else: value_ctrl = wx.TextCtrl( self, -1, unicode(value) ) value_ctrl.SetSelection( -1, -1 ) self.g_sizer.Add( label_ctrl, 1, wx.LEFT|wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 3 ) self.g_sizer.Add( value_ctrl, 0, wx.EXPAND|wx.RIGHT, 3) return value_ctrl WorkBench-1.8.2/Source/run_pylint.sh000755 000765 000024 00000000236 10476344063 017700 0ustar00barrystaff000000 000000 #!/usr/bin/env python import sys import os os.environ[ 'PYLINTHOME' ] = 'pylint.d' from pylint import lint lint.Run( ['--rcfile=./pylintrc'] + sys.argv[1:] ) WorkBench-1.8.2/Source/linux.mak000644 000765 000024 00000000404 12566356017 016770 0ustar00barrystaff000000 000000 # # makefile WorkBench # all: locale/en/LC_MESSAGES/pysvn_workbench.mo wb_version.py wb_images.py locale/en/LC_MESSAGES/pysvn_workbench.mo: wb_version.py wb_images.py ./make-pot-file.sh ./make-po-file.sh en ./make-mo-files.sh locale include wb_common.mak WorkBench-1.8.2/Source/wb_subversion_report_branch_changes.py000644 000765 000024 00000020145 11662230071 024767 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2006-2011 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_subversion_report_branch_changes.py ''' import wx import wb_config import wb_ids import wb_images import wb_exceptions import wb_list_panel_common import wb_subversion_utils import wb_subversion_list_handler_common class ReportBranchChangesFrame(wx.Frame): def __init__( self, app, project_info, all_files ): wx.Frame.__init__( self, None, -1, T_("Branch changes report"), size=(700,500) ) self.app = app self.menu_actions = wx.Menu() self.menu_actions.Append( wb_ids.id_SP_DiffWorkHead, T_('Diff WC vs. HEAD...'), T_('Diff WC vs. HEAD...') ) self.menu_actions.Append( wb_ids.id_SP_DiffWorkBranchOriginBase, T_('Diff WC vs. branch origin BASE...'), T_('Diff WC vs. branch origin BASE...') ) self.menu_actions.Append( wb_ids.id_SP_DiffWorkBranchOriginHead, T_('Diff WC vs. branch origin HEAD...'), T_('Diff WC vs. branch origin HEAD...') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SP_Annotate, T_('Annotate...'), T_('Annotate...') ) self.menu_actions.Append( wb_ids.id_SP_History, T_('Log history...'), T_('Log history...') ) self.menu_actions.Append( wb_ids.id_SP_Info, T_('Information...'), T_('Information...') ) self.menu_bar = wx.MenuBar() self.menu_bar.Append( self.menu_actions, T_("&Actions") ) self.SetMenuBar( self.menu_bar ) # Add tool bar t = self.CreateToolBar( name="main", style=wx.TB_HORIZONTAL ) bitmap_size = (32,32) t.SetToolBitmapSize( bitmap_size ) t.AddSimpleTool( wb_ids.id_SP_DiffWorkHead, wb_images.getBitmap( 'toolbar_images/diff.png', bitmap_size ), T_('Diff changes against HEAD'), T_('Diff changes against HEAD') ) t.AddSimpleTool( wb_ids.id_SP_History, wb_images.getBitmap( 'toolbar_images/history.png', bitmap_size ), T_('Show History log'), T_('Show History log') ) t.AddSimpleTool( wb_ids.id_SP_Info, wb_images.getBitmap( 'toolbar_images/info.png', bitmap_size ), T_('File Information'), T_('File Information') ) try_wrapper = wb_exceptions.TryWrapperFactory( self.app.log ) # Set the application icon self.SetIcon( wb_images.getIcon( 'wb.png' ) ) # create the individule panels self.panel_list = ReportBranchChangesListPanel( app, self, self ) wx.EVT_CLOSE( self, self.OnCloseWindow ) wx.EVT_BUTTON( self.panel_list, wx.ID_OK, self.app.eventWrapper( self.OnOk ) ) self.project_info = ReportBranchChangesProjectInfo( project_info, all_files ) self.list_handler = ReportBranchChangesListHandler( self.app, self.panel_list, self.project_info ) # draw the list - its BranchChanges the status info self.panel_list.setHandler( self.list_handler ) wx.EVT_MENU( self, wb_ids.id_SP_Annotate, self.app.eventWrapper( self.OnSpAnnotate ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkHead, self.app.eventWrapper( self.OnSpDiffWorkHead ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkBranchOriginBase, self.app.eventWrapper( self.OnSpDiffWorkBranchOriginBase ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkBranchOriginHead, self.app.eventWrapper( self.OnSpDiffWorkBranchOriginHead ) ) wx.EVT_MENU( self, wb_ids.id_SP_History, self.app.eventWrapper( self.OnSpHistory ) ) wx.EVT_MENU( self, wb_ids.id_SP_Info, self.app.eventWrapper( self.OnSpInfo ) ) def clearUpdateUiState( self ): pass def getUpdateUiState( self ): pass def setEventHandler( self, handler ): self.handler = handler def OnCloseWindow( self, event ): self.Destroy() def OnCancel( self, event ): self.Destroy() def OnOk( self, event ): self.Destroy() # command events def OnSpAnnotate( self, event ): return self.panel_list.OnSpAnnotate() def OnSpDiffWorkHead( self, event ): return self.panel_list.OnSpDiffWorkHead() def OnSpDiffWorkBranchOriginBase( self, event ): return self.panel_list.OnSpDiffWorkBranchOriginBase() def OnSpDiffWorkBranchOriginHead( self, event ): return self.panel_list.OnSpDiffWorkBranchOriginHead() def OnSpHistory( self, event ): return self.panel_list.OnSpHistory() def OnSpInfo( self, event ): return self.panel_list.OnSpInfo() class ReportBranchChangesListHandler(wb_subversion_list_handler_common.SubversionListHandlerCommon): def __init__( self, app, parent, project_info ): wb_subversion_list_handler_common.SubversionListHandlerCommon.__init__( self, app, parent, project_info ) self.all_excluded_files = {} # use the repos status in the report def getTextStatus( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) return status.repos_text_status # use the repos status in the report def getPropStatus( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) return status.repos_prop_status def statusFormatString( self, file ): text_code = wb_subversion_utils.wc_status_kind_map[ file.text_status ] prop_code = wb_subversion_utils.wc_status_kind_map[ file.prop_status ] if text_code == ' ' and prop_code != ' ': text_code = '_' return file.get('branch_text_states', '') + text_code + prop_code def setupColumnInfo( self ): self.column_info.setFrom( [ T_('State'), T_('Name') ], [5, 100] ) def statusColour( self, file ): # show that a file is on the exclude list if file.path in self.getAllGreyFilenames(): return wb_config.colour_status_disabled else: return wb_config.colour_status_normal def getContextMenu( self ): menu_template = \ [('', wb_ids.id_SP_DiffWorkHead, T_('Diff WC vs. HEAD...') ) ,('', wb_ids.id_SP_DiffWorkBranchOriginBase, T_('Diff WC vs. branch origin BASE...') ) ,('', wb_ids.id_SP_DiffWorkBranchOriginHead, T_('Diff WC vs. branch origin HEAD...') ) ,('-', 0, 0 ) ,('', wb_ids.id_SP_Annotate, T_('Annotate...') ) ,('', wb_ids.id_SP_History, T_('Log history...') ) ,('', wb_ids.id_SP_Info, T_('Information...') ) ] return wb_subversion_utils.populateMenu( wx.Menu(), menu_template ) def getAllGreyFilenames( self ): # show all excluded files in grey return self.all_excluded_files class ReportBranchChangesProjectInfo: def __init__( self, project_info, all_files ): self.all_files = all_files self.need_properties = False self.project_name = project_info.project_name self.url = project_info.url self.wc_path = project_info.wc_path self.need_checkout = False self.need_upgrade = False self.client_fg = project_info.client_fg self.client_bg = project_info.client_bg def getTagsUrl( self, rel_url ): return None def setNeedProperties( self, need_properties ): self.need_properties = need_properties def updateStatus( self ): pass def getFilesStatus( self ): return self.all_files def getProperty( self, filename, prop_name ): return '' def getWorkingDir( self ): return self.wc_path class ReportBranchChangesListPanel(wb_list_panel_common.WbListPanelCommon): def __init__( self, app, frame, parent ): wb_list_panel_common.WbListPanelCommon.__init__( self, app, frame, parent ) def addToSizer( self, v_sizer ): pass def getAcceleratorTableInit( self ): acc_init =[ (wx.ACCEL_CMD, ord('D'), wb_ids.id_SP_DiffWorkBase), (wx.ACCEL_CMD, ord('L'), wb_ids.id_SP_History), ] return acc_init WorkBench-1.8.2/Source/wb_subversion_list_handler.py000644 000765 000024 00000055466 12573324740 023146 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2011 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_subversion_list_handler.py ''' import sys import os import pysvn import wx import wb_ids import wb_subversion_list_handler_common import wb_subversion_info_dialog import wb_subversion_properties_dialog import wb_subversion_utils import wb_subversion_checkin import wb_clipboard import wb_diff_frame import wb_dialogs import wb_platform_specific class SubversionListHandler(wb_subversion_list_handler_common.SubversionListHandlerCommon): def __init__( self, app, list_panel, project_info ): wb_subversion_list_handler_common.SubversionListHandlerCommon.__init__( self, app, list_panel, project_info ) def __repr__( self ): return '' % self.project_info def getContextMenu( self ): if self.project_info.need_upgrade: menu_template = \ [('', wb_ids.id_SP_Upgrade, T_('Upgrade') )] elif self.project_info.need_checkout: menu_template = \ [('', wb_ids.id_SP_Checkout, T_('Checkout') )] else: menu_template = \ [('', wb_ids.id_File_Edit, T_('Edit') )] if wx.Platform in ['__WXMSW__','__WXMAC__']: menu_template += \ [('', wb_ids.id_Shell_Open, T_('Open') )] menu_template += \ [('-', 0, 0 ) ,('', wb_ids.id_SP_DiffWorkBase, T_('Diff WC vs. BASE...') ) ,('', wb_ids.id_SP_DiffWorkHead, T_('Diff WC vs. HEAD...') ) ,('', wb_ids.id_SP_DiffWorkBranchOriginBase, T_('Diff WC vs. branch origin BASE...') ) ,('', wb_ids.id_SP_DiffWorkBranchOriginHead, T_('Diff WC vs. branch origin HEAD...') ) ,('>', wb_ids.id_SP_ConflictMenu, T_('Conflict'), [('', wb_ids.id_SP_DiffOldMine, T_('Diff Conflict Old vs. Mine...') ) ,('', wb_ids.id_SP_DiffMineNew, T_('Diff Conflict Mine vs. New...') ) ,('', wb_ids.id_SP_DiffOldNew, T_('Diff Conflict Old vs. New...') ) ,('-', 0, 0 ) ,('', wb_ids.id_SP_Resolved, T_('Resolved Conflict') ) ]) ,('-', 0, 0 ) ,('', wb_ids.id_SP_Annotate, T_('Annotate...') ) ,('', wb_ids.id_SP_History, T_('Log history...') ) ,('', wb_ids.id_SP_Info, T_('Information...') ) ,('', wb_ids.id_SP_Properties, T_('Properties...') ) ,('-', 0, 0 ) ,('', wb_ids.id_SP_Lock, T_('Lock...') ) ,('', wb_ids.id_SP_Unlock, T_('Unlock...') ) ,('-', 0, 0 ) ,('', wb_ids.id_SP_Update, T_('Update') ) ,('', wb_ids.id_SP_UpdateTo, T_('Update to..') ) ,('-', 0, 0 ) ,('', wb_ids.id_SP_Checkin, T_('Checkin...') ) ,('-', 0, 0 ) ,('', wb_ids.id_SP_Add, T_('Add') ) ,('', wb_ids.id_SP_Rename, T_('Rename...') ) ,('-', 0, 0 ) ,('', wb_ids.id_SP_Delete, T_('Delete...') ) ,('', wb_ids.id_SP_Revert, T_('Revert...') ) ,('-', 0, 0 ) ,('', wb_ids.id_SP_Cleanup, T_('Clean up') ) ] return wb_subversion_utils.populateMenu( wx.Menu(), menu_template ) def getAllGreyFilenames( self ): # show files on the clipboard in grey if self.app.hasPasteData(): all_clipboard_filenames = self.app.getPasteData().getAllFilenames() else: all_clipboard_filenames = [] return all_clipboard_filenames def getBackgroundColour( self ): pi = self.app.frame.tree_panel.getProjectTopProjectInfo() if pi.use_background_colour: return pi.background_colour else: return (255,255,255) #------------------------------------------------------------ def Cmd_File_EditCopy( self, all_rows ): self.app.setPasteData( wb_clipboard.Clipboard( [self.getFilename( row ) for row in all_rows], is_copy=True ) ) print T_('Copied %d files to the Clipboard') % len(all_rows) self.app.refreshFrame() def Cmd_File_EditCut( self, all_rows ): self.app.setPasteData( wb_clipboard.Clipboard( [self.getFilename( row ) for row in all_rows], is_copy=False ) ) print T_('Cut %d files to the Clipboard') % len(all_rows) self.app.refreshFrame() def Cmd_File_EditPaste( self, all_rows ): if not self.app.hasPasteData(): return paste_data = self.app.getPasteData() self.app.clearPasteData() all_status = [] try: for filename in paste_data.getAllFilenames(): if wb_platform_specific.uPathIsdir( filename ): dir_status = self.project_info.client_bg.status( os.path.dirname( filename ), recurse=False ) all_status.extend( [s for s in dir_status if s.path == filename] ) else: all_status.extend( self.project_info.client_bg.status( filename, recurse=False ) ) except pysvn.ClientError, e: self.app.log_client_error( e ) return if paste_data.isCopy(): title = T_('Paste Copy') else: title = T_('Paste Move') confirmed, force = self.app.confirmForceAction( title, self.getStatusAndFilenames( all_status ) ) if not confirmed: return self.app.setProgress( title, 0 ) for status in all_status: ok = False old_filename = status.path basename = os.path.basename( old_filename ) new_filename = os.path.join( self.project_info.wc_path, basename ) if paste_data.isCopy(): rename_title = T_('Save As') else: rename_title = T_('Rename') while wb_platform_specific.uPathExists( new_filename ): new_name, force = self.app.renameFile( rename_title, os.path.basename( old_filename ), None ) if new_name is None: return new_filename = os.path.join( self.project_info.wc_path, new_name ) self.app.log.info( T_('%(title)s: From %(filename)s') % {'title': title ,'filename': old_filename} ) self.app.log.info( T_('%(title)s: To %(filename)s') % {'title': title ,'filename': new_filename} ) is_controlled = self.isControlled( status ) yield self.app.backgroundProcess try: if paste_data.isCopy(): if wb_platform_specific.uPathIsdir( old_filename ): if is_controlled: self.project_info.client_bg.copy( old_filename, new_filename ) else: raise EnvironmentError( 'TBD - implement copy of folder' ) os.copydirtree( old_filename, new_filename ) else: self.__copyFile( old_filename, new_filename, is_controlled, status.text_status, status.prop_status ) ok = True else: if wb_platform_specific.uPathIsdir( old_filename ): if is_controlled: self.project_info.client_bg.move( old_filename, new_filename, force=force ) else: wb_platform_specific.uRename( old_filename, new_filename ) else: text_status = self.getTextStatus( status ) prop_status = self.getPropStatus( status ) self.__moveFile( old_filename, new_filename, is_controlled, text_status, prop_status ) ok = True except EnvironmentError, e: self.app.log.error( str(e) ) except pysvn.ClientError, e: self.app.log_client_error( e ) yield self.app.foregroundProcess if not ok: break self.app.clearProgress() self.app.refreshFrame() #------------------------------------------------------------ def Cmd_File_Add( self, all_rows ): # handles both files and folders try: for filename in [self.getFilename( row ) for row in all_rows]: if wb_platform_specific.uPathIsdir( filename ): name = os.path.basename( filename ) force, recursive = self.app.addFolder( T_('Add Folder'), name, force=False, recursive=True ) if force is None: continue self.project_info.client_fg.add( filename, force=force, recurse=recursive ) else: self.project_info.client_fg.add( filename ) except pysvn.ClientError, e: self.app.log_client_error( e ) self.app.refreshFrame() # Cmd_File_Annotate - from SubversionListHandlerCommon def Cmd_File_Checkin( self, all_rows ): if len(all_rows) == 0: wx.MessageBox( T_("There are no changes to check in"), T_("Warning"), style=wx.OK|wx.ICON_EXCLAMATION ) return all_files = [self.all_files[ row ] for row in all_rows] ci_frame = wb_subversion_checkin.CheckinFrame( self.app, self.project_info, all_files ) ci_frame.Show( True ) def Cmd_File_Cleanup( self, all_rows ): try: for filename in [self.getFilename( row ) for row in all_rows]: self.project_info.client_fg.cleanup( filename ) except pysvn.ClientError, e: self.app.log_client_error( e ) self.app.refreshFrame() def Cmd_File_Delete( self, all_rows ): if not self.app.confirmAction( T_('Delete File'), self.getStatusAndFilenames( all_rows ) ): return for filename, is_controlled, text_status, prop_status in [ (self.getFilename( row ), self.isControlled( row ), self.getTextStatus( row ), self.getPropStatus( row )) for row in all_rows]: try: if is_controlled: if text_status == pysvn.wc_status_kind.added: self.project_info.client_fg.revert( filename ) wb_platform_specific.uRemove( filename ) elif( text_status == pysvn.wc_status_kind.modified or prop_status == pysvn.wc_status_kind.modified ): self.project_info.client_fg.revert( filename ) self.project_info.client_fg.remove( filename ) else: self.project_info.client_fg.remove( filename ) else: wb_platform_specific.uRemove( filename ) except pysvn.ClientError, e: self.app.log_client_error( e ) except EnvironmentError, e: self.app.log.error( str(e) ) self.app.refreshFrame() # Cmd_File_DiffWorkBase - from SubversionListHandlerCommon # Cmd_File_DiffWorkHead - from SubversionListHandlerCommon def Cmd_File_DiffOldNew( self, all_rows ): for row in all_rows: old_filename = self.getConflictOld( row ) new_filename = self.getConflictNew( row ) # qqq diff old_filename@None, new_filename@None 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.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.diffFiles( mine_filename, mine_filename, new_filename, new_filename ) # Cmd_File_History = from SubversionListHandlerCommon # Cmd_File_Info = from SubversionListHandlerCommon # Cmd_File_Lock= from SubversionListHandlerCommon # Cmd_File_Properties - from SubversionListHandlerCommon def __copyFile( self, old_filename, new_full_filename, is_controlled, text_status, prop_status ): if not is_controlled: raise EnvironmentError( 'TBD - copy file' ) wb_platform_specific.uRename( old_filename, new_full_filename ) return if( text_status == pysvn.wc_status_kind.normal and prop_status in [pysvn.wc_status_kind.normal, pysvn.wc_status_kind.none] ): self.project_info.client_fg.copy( old_filename, new_full_filename ) else: raise EnvironmentError( 'Cannot copy an added or modified file' ) def __moveFile( self, old_filename, new_full_filename, is_controlled, text_status, prop_status ): if not is_controlled: wb_platform_specific.uRename( old_filename, new_full_filename ) return if text_status == pysvn.wc_status_kind.added: # need to save and restore the props around the rename dance 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( T_('Rename %(from)s %(to)s') % {'from': old_filename ,'to': new_full_filename} ) wb_platform_specific.uRename( old_filename, new_full_filename ) self.project_info.client_fg.add( 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 ): new_full_tmp_filename = None for tmp_name_index in range( 100 ): tmp_filename = os.path.join( os.path.dirname( old_filename ), '%s.%d.tmp' % (new_full_filename, tmp_name_index) ) if not wb_platform_specific.uPathExists( tmp_filename ): new_full_tmp_filename = tmp_filename break if new_full_tmp_filename is None: self.app.log.error( T_('Failed to create tmp file for rename') ) else: # need to save and restore the props around the rename dance all_props = self.project_info.client_fg.proplist( old_filename, revision=pysvn.Revision( pysvn.opt_revision_kind.working ) ) print( T_('Rename %(from)s %(to)s') % {'from': old_filename ,'to': new_full_tmp_filename} ) wb_platform_specific.uRename( old_filename, new_full_tmp_filename ) self.project_info.client_fg.revert( old_filename ) self.project_info.client_fg.move( old_filename, new_full_filename ) wb_platform_specific.uRemove( new_full_filename ) print( T_('Rename %(from)s %(to)s') % {'from': new_full_tmp_filename ,'to': new_full_tmp_filename} ) wb_platform_specific.uRename( new_full_tmp_filename, new_full_filename ) if len(all_props) > 0: _, prop_dict = all_props[0] for prop_name, prop_value in prop_dict.items(): self.project_info.client_fg.propset( prop_name, prop_value, new_full_filename ) else: self.project_info.client_fg.move( old_filename, new_full_filename ) def Cmd_File_Rename( self, all_rows ): for old_filename, is_controlled, text_status, prop_status in [ (self.getFilename( row ), self.isControlled( row ), self.getTextStatus( row ), self.getPropStatus( row )) for row in all_rows]: old_name = os.path.basename( old_filename ) new_name, force = self.app.renameFile( T_("Rename"), old_name, None ) if new_name is None: break if new_name != old_name: new_full_filename = os.path.join( os.path.dirname( old_filename ), new_name ) print T_('Rename'),old_filename, new_full_filename try: self.__moveFile( old_filename, new_full_filename, is_controlled, text_status, prop_status ) except pysvn.ClientError, e: self.app.log_client_error( e ) break except EnvironmentError, e: self.app.log.error( str(e) ) break self.app.refreshFrame() def Cmd_File_Revert( self, all_rows ): if not self.app.confirmAction( T_('Revert'), self.getStatusAndFilenames( all_rows ) ): return try: for filename in [self.getFilename( row ) for row in all_rows]: self.project_info.client_fg.revert( filename ) except pysvn.ClientError, e: self.app.log_client_error( e ) self.app.refreshFrame() def Cmd_File_Resolved( self, all_rows ): if not self.app.confirmAction( T_('Resolved'), self.getStatusAndFilenames( all_rows ) ): return try: for filename in [self.getFilename( row ) for row in all_rows]: self.project_info.client_fg.resolved( filename ) except pysvn.ClientError, e: self.app.log_client_error( e ) self.app.refreshFrame() # Cmd_File_Unlock= from SubversionListHandlerCommon def Cmd_File_Update( self, all_rows ): rev = pysvn.Revision( pysvn.opt_revision_kind.head ) self.app.setProgress( T_('Updated %(count)d'), 0 ) self.project_info.initNotify() for filename in [self.getFilename( row ) for row in all_rows]: self.app.setAction( T_('Update %s...') % filename ) yield self.app.backgroundProcess ok = False try: rev_list = self.project_info.client_bg.update( filename, recurse=False, revision=rev ) ok = True except pysvn.ClientError, e: self.app.log_client_error( e ) yield self.app.foregroundProcess if not ok: break for rev in rev_list: if rev.number > 0: basename = os.path.basename( filename ) count = self.app.getProgressValue( 'count' ) if count == 0: self.app.log.info( T_('Updated %(filename)s to revision %(rev)d, no new updates') % {'filename': basename ,'rev': rev.number} ) else: self.app.log.info( S_('Updated %(filename)s to revision %(rev)d, %(count)d new update', 'Updated %(filename)s to revision %(rev)d, %(count)d new updates', count) % {'filename': basename ,'rev': rev.number ,'count': count} ) if self.project_info.notification_of_files_in_conflict > 0: wx.MessageBox( S_("%d file is in conflict", "%d files are in conflict", self.project_info.notification_of_files_in_conflict) % self.project_info.notification_of_files_in_conflict, T_("Warning"), style=wx.OK|wx.ICON_EXCLAMATION ) self.app.clearProgress() self.app.setAction( T_('Ready') ) self.app.refreshFrame() def Cmd_File_UpdateTo( self, all_rows ): dialog = wb_dialogs.UpdateTo( None, T_('Update to revision') ) if dialog.ShowModal() != wx.ID_OK: return rev = dialog.getRevision() self.app.setProgress( T_('Updated %(count)d'), 0 ) self.project_info.initNotify() for filename in [self.getFilename( row ) for row in all_rows]: self.app.setAction( T_('Update %s...') % filename ) yield self.app.backgroundProcess ok = False try: rev_list = self.project_info.client_bg.update( filename, recurse=False, revision=rev ) ok = True except pysvn.ClientError, e: self.app.log_client_error( e ) yield self.app.foregroundProcess if not ok: break for rev in rev_list: if rev.number > 0: basename = os.path.basename( filename ) count = self.app.getProgressValue( 'count' ) if count == 0: self.app.log.info( T_('Updated %(filename)s to revision %(rev)d, no new updates') % {'filename': basename ,'rev': rev.number} ) else: self.app.log.info( S_('Updated %(filename)s to revision %(rev)d, %(count)d new update', 'Updated %(filename)s to revision %(rev)d, %(count)d new updates', count) % {'filename': basename ,'rev': rev.number ,'count': count} ) if self.project_info.notification_of_files_in_conflict > 0: wx.MessageBox( S_("%d file is in conflict", "%d files are in conflict", self.project_info.notification_of_files_in_conflict) % self.project_info.notification_of_files_in_conflict, T_("Warning"), style=wx.OK|wx.ICON_EXCLAMATION ) self.app.clearProgress() self.app.setAction( T_('Ready') ) self.app.refreshFrame() WorkBench-1.8.2/Source/wb_subversion_history.py000644 000765 000024 00000115227 12617123054 022161 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_subversion_history.py ''' import wx import wx.lib.splitter import wx.calendar import os import time import types import urllib import pysvn import wb_images import wb_subversion_utils import wb_subversion_diff import wb_subversion_annotate import wb_config id_view_cmd = wx.NewId() id_diff_cmd = wx.NewId() id_annotate_cmd = wx.NewId() id_revision_changes_cmd = wx.NewId() id_list = wx.NewId() id_paths = wx.NewId() action_map = { 'A': 'Add', 'D': 'Delete', 'M': 'Modified', } class LogEntry: def __init__( self, rev_number, url, author, date, label, message, changed_paths ): self.rev_number = rev_number self.url = url self.author = author self.date = date self.label = label try: self.message = message.decode( 'utf-8' ) except ValueError: 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 == T_('Author'): return filter_text.lower() in self.author.lower() elif filter_field == T_('Comment'): return filter_text.lower() in self.message.lower() elif filter_field == T_('Path'): for changed_path in self.changed_paths: try: c_p_p = changed_path.path.decode('utf-8') except ValueError: c_p_p = changed_path.path if filter_text.lower() in c_p_p.lower(): return True return False else: assert( False ) return False def getHistoryEntries( project_info, filename, limit, revision_end, include_tags, revision_start ): 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] all_log_entries = project_info.client_bg.log( filename, strict_node_history=False, discover_changed_paths=True, limit=limit, revision_start=revision_start, revision_end=revision_end ) # Settings an end date can lead to no entries if len( all_log_entries ) == 0: return info.URL, all_history_entries repos_path = urllib.unquote( info.URL[len(info.repos_root_URL):].encode( 'utf-8' ) ) for log in all_log_entries: # author is optional if 'author' not in log: log.author = '' all_history_entries.append( LogEntry( log.revision.number, info.URL, log.author, log.date, '', log.message, log.changed_paths ) ) for changed_path in log.changed_paths: if changed_path.action in ['A','M']: if changed_path.path is not None: c_p_p = changed_path.path.decode( 'utf-8' ) else: c_p_p = None if repos_path == c_p_p: if changed_path.copyfrom_path is not None: 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 include_tags and tags_url: try: tag_info = project_info.client_bg.info2( tags_url, recurse=False ) tag_repos_prefix = tags_url[len(tag_info[0][1].repos_root_URL):] all_tag_names = set() for log in project_info.client_bg.log( tags_url, discover_changed_paths=True ): # author is optional if 'author' not in log: log.author = '' 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 and changed_path.copyfrom_revision.number > oldest_rev ): tag_suffix = changed_path.path[len(tag_repos_prefix)+1:] tag_name = tag_suffix.split('/')[0] if tag_name not in all_tag_names: all_history_entries.append( LogEntry( changed_path.copyfrom_revision.number, __findTaggedUrl( all_history_entries, changed_path.copyfrom_revision.number ), log.author, log.date, 'Tag ' + tag_name, log.message, log.changed_paths) ) all_tag_names.add( tag_name ) break except pysvn.ClientError, e: print '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 # this cannot happen raise RuntimeError( '__findTaggedUrl failed to find tagged url' ) class LogHistoryDialog(wx.Dialog): def __init__( self, app, parent ): wx.Dialog.__init__( self, parent, -1, T_('Log History') ) p = app.prefs.getLogHistory() self.g_sizer = wx.FlexGridSizer( 0, 2, 0, 0 ) self.g_sizer.AddGrowableCol( 1 ) is_show_all = p.default_mode == 'show_all' is_show_limit = p.default_mode == 'show_limit' is_show_since = p.default_mode == 'show_since' is_show_range = p.default_mode == 'show_range' self.all_radio = wx.RadioButton( self, -1, T_('Show all entries') ) self.all_radio.SetValue( is_show_all ) self.g_sizer.Add( self.all_radio, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( (0,0), 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.limit_radio = wx.RadioButton( self, -1, T_('Show only:') ) self.limit_radio.SetValue( is_show_limit ) self.limit_text = wx.TextCtrl( self, -1, str(p.default_limit) ) self.limit_text.Enable( is_show_limit ) self.g_sizer.Add( self.limit_radio, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( self.limit_text, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.date = wx.DateTime_Now() self.date.SubtractDS( wx.DateSpan( days=p.default_since_days_interval ) ) self.since_radio = wx.RadioButton( self, -1, T_('Show since:') ) self.since_radio.SetValue( is_show_since ) self.since_date = wx.calendar.CalendarCtrl( self, -1, self.date, style=wx.calendar.CAL_MONDAY_FIRST ) self.since_date.Enable( is_show_since ) self.g_sizer.Add( self.since_radio, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT|wx.ALIGN_TOP, 3 ) self.g_sizer.Add( self.since_date, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.range_radio = wx.RadioButton( self, -1, T_('Show range:') ) self.range_radio.SetValue( is_show_range ) self.range_start_text = wx.TextCtrl( self, -1, 'head' ) self.range_start_text.Enable( is_show_range ) self.range_end_text = wx.TextCtrl( self, -1, '1' ) self.range_end_text.Enable( is_show_range ) self.g_sizer.Add( self.range_radio, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( self.range_start_text, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( (0,0), 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.g_sizer.Add( self.range_end_text, 1, wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT, 3 ) self.tags_label = wx.StaticText( self, -1, T_('Include tags: '), style=wx.ALIGN_RIGHT) self.tags_ctrl = wx.CheckBox( self, -1, T_('Include tags in log history') ) self.tags_ctrl.SetValue( p.default_include_tags ) self.g_sizer.Add( self.tags_label, 1, wx.EXPAND|wx.ALL, 3 ) self.g_sizer.Add( self.tags_ctrl, 0, wx.EXPAND|wx.ALL, 5 ) self.button_ok = wx.Button( self, wx.ID_OK, T_(' OK ') ) self.button_ok.SetDefault() self.button_cancel = wx.Button( self, wx.ID_CANCEL, T_(' Cancel ') ) self.h_sizer_buttons = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer_buttons.Add( (150, 20), 1, wx.EXPAND ) self.h_sizer_buttons.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15 ) self.h_sizer_buttons.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 2 ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) self.v_sizer.Add( self.g_sizer, 0, wx.EXPAND|wx.ALL, 5 ) self.v_sizer.Add( self.h_sizer_buttons, 0, wx.EXPAND|wx.ALL, 5 ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() self.CentreOnParent() wx.EVT_BUTTON( self, wx.ID_OK, self.OnOk ) wx.EVT_BUTTON( self, wx.ID_CANCEL, self.OnCancel ) wx.EVT_RADIOBUTTON( self, self.all_radio.GetId(), self.OnRadio ) wx.EVT_RADIOBUTTON( self, self.limit_radio.GetId(), self.OnRadio ) wx.EVT_RADIOBUTTON( self, self.since_radio.GetId(), self.OnRadio ) wx.EVT_RADIOBUTTON( self, self.range_radio.GetId(), self.OnRadio ) wx.calendar.EVT_CALENDAR_SEL_CHANGED( self, self.since_date.GetId(), self.OnCalendarSelChanged ) # ---------------------------------------- def OnOk( self, event ): self.EndModal( wx.ID_OK ) def OnCancel( self, event ): self.EndModal( wx.ID_CANCEL ) # ---------------------------------------- def OnRadio( self, event ): self.since_date.Enable( self.since_radio.GetValue() ) self.limit_text.Enable( self.limit_radio.GetValue() ) self.range_end_text.Enable( self.range_radio.GetValue() ) self.range_start_text.Enable( self.range_radio.GetValue() ) def OnCalendarSelChanged( self, event ): if self.since_radio.GetValue(): # ensure that the date stays in the past date = self.since_date.GetDate() # sometimes the event is sent with a bogus value for the date # just ignore these events if date.GetTicks() == ((2**32)-1): return if date.IsLaterThan( wx.DateTime_Now() ): self.since_date.SetDate( self.date ) else: self.date = self.copyDataTime( date ) else: # CalendarCtrl does not disable day changes # force date back to self.date self.since_date.SetDate( self.date ) def copyDataTime( self, date ): t = date.GetTicks() return wx.DateTimeFromTimeT( t ) # ---------------------------------------- def getLimit( self ): if self.limit_radio.GetValue(): try: return int( self.limit_text.GetValue().strip() ) except ValueError: return 10 else: return 0 def getRevisionEnd( self ): if self.since_radio.GetValue(): date = self.since_date.GetDate() return pysvn.Revision( pysvn.opt_revision_kind.date, date.GetTicks() ) elif self.range_radio.GetValue(): try: rev = self.range_end_text.GetValue().strip() if rev == '': return pysvn.Revision( pysvn.opt_revision_kind.number, 0 ) return pysvn.Revision( pysvn.opt_revision_kind.number, int( rev ) ) except ValueError: return pysvn.Revision( pysvn.opt_revision_kind.number, 0 ) else: return pysvn.Revision( pysvn.opt_revision_kind.number, 0 ) def getRevisionStart( self ): try: if self.range_radio.GetValue(): rev = self.range_start_text.GetValue().strip() if rev == '': return pysvn.Revision( pysvn.opt_revision_kind.head ) return pysvn.Revision( pysvn.opt_revision_kind.number, int( rev ) ) else: return pysvn.Revision( pysvn.opt_revision_kind.head ) except ValueError: return pysvn.Revision( pysvn.opt_revision_kind.head ) def getIncludeTags( self ): return self.tags_ctrl.GetValue() class HistoryFileFrame(wx.Frame): def __init__( self, app, project_info, filename, url, all_log_entries ): wx.Frame.__init__( self, None, -1, T_("History of %s") % filename, size=(700,500) ) self.panel = LogHistoryPanel( self, app, project_info, filename, url, all_log_entries ) # Set the application icon self.SetIcon( wb_images.getIcon( 'wb.png' ) ) wx.EVT_CLOSE( self, self.OnCloseWindow ) def OnCloseWindow( self, event ): self.Destroy() def diffFunction( self, app, project_info, info1, info2 ): return wb_subversion_diff.subversionDiffFiles( app, project_info, info1, info2 ) class HistoryDirFrame(wx.Frame): def __init__( self, app, project_info, filename, url, all_log_entries ): wx.Frame.__init__( self, None, -1, T_("History of %s") % filename, size=(700,500) ) self.panel = LogHistoryPanel( self, app, project_info, filename, url, all_log_entries ) # Set the application icon self.SetIcon( wb_images.getIcon( 'wb.png' ) ) wx.EVT_CLOSE( self, self.OnCloseWindow ) def OnCloseWindow( self, event ): self.Destroy() def diffFunction( self, app, project_info, info1, info2 ): return wb_subversion_diff.subversionDiffDir( 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 = [ T_('Author'), T_('Comment'), T_('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 ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() 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 col_date = 2 col_label = 3 col_message = 4 col_action = 0 col_path = 1 col_copyfrom_revision = 2 col_copyfrom_path = 3 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.all_log_entries = all_log_entries # run from recent to old self.all_log_entries.sort( self.by_rev ) self.filter_field = T_('Comment') self.filter_text = '' self.all_filtered_log_entries = all_log_entries # Create the splitter windows self.splitter = wx.lib.splitter.MultiSplitterWindow( parent ) self.splitter.SetOrientation( wx.VERTICAL ) # Make sure the splitters can't be removed by setting a minimum size self.splitter.SetMinimumPaneSize( 100 ) # create the individule panels self.panel_history = wx.Panel( self.splitter, -1 ) self.panel_comment = wx.Panel( self.splitter, -1 ) self.panel_changed_paths = wx.Panel( self.splitter, -1 ) # Arrange the panels with the splitter windows self.splitter.AppendWindow( self.panel_history, 250 ) self.splitter.AppendWindow( self.panel_comment, 100 ) self.splitter.AppendWindow( self.panel_changed_paths, 200 ) 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.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.list_ctrl.SetFocus() self.all_item_attr = {} self.list_ctrl.InsertColumn( self.col_revision, T_("Revision") ) self.list_ctrl.InsertColumn( self.col_author, T_("Author") ) self.list_ctrl.InsertColumn( self.col_date, T_("Date") ) self.list_ctrl.InsertColumn( self.col_label, T_("Label") ) self.list_ctrl.InsertColumn( self.col_message, T_("Message") ) char_width = 9 self.list_ctrl.SetColumnWidth( self.col_revision, 7*char_width ) self.list_ctrl.SetColumnWidth( self.col_author, 14*char_width ) self.list_ctrl.SetColumnWidth( self.col_date, 18*char_width ) self.list_ctrl.SetColumnWidth( self.col_label, 12*char_width ) self.list_ctrl.SetColumnWidth( self.col_message, 40*char_width ) # don't make the ctrl readonly as that prevents copy as well as insert self.comment_ctrl = wx.TextCtrl( self.panel_comment, -1, '', size=wx.Size(-1, -1), style=wx.TE_MULTILINE ) self.comment_ctrl.SetInsertionPoint( 0 ) self.paths_ctrl = wx.ListCtrl( self.panel_changed_paths, id_paths, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT|wx.NO_BORDER) self.paths_ctrl.InsertColumn( self.col_action, T_("Action") ) self.paths_ctrl.InsertColumn( self.col_path, T_("Path") ) self.paths_ctrl.InsertColumn( self.col_copyfrom_revision, T_("Copied Revision") ) self.paths_ctrl.InsertColumn( self.col_copyfrom_path, T_("Copied from") ) char_width = 9 self.paths_ctrl.SetColumnWidth( self.col_action, 7*char_width ) self.paths_ctrl.SetColumnWidth( self.col_path, 60*char_width ) self.paths_ctrl.SetColumnWidth( self.col_copyfrom_revision, 6*char_width ) self.paths_ctrl.SetColumnWidth( self.col_copyfrom_path, 60*char_width ) self.initButtons( self.v_sizer_history ) self.comment_label = wx.StaticText( self.panel_comment, -1, T_('Comment') ) self.paths_label = wx.StaticText( self.panel_changed_paths, -1, T_('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_paths, self.OnPathItemSelected ) wx.EVT_LIST_ITEM_DESELECTED( self.panel_history, id_paths, self.OnPathItemDeselected ) self.initList() wx.EVT_SIZE( self.panel_history, self.OnSizeHistory ) wx.EVT_SIZE( self.panel_comment, self.OnSizeComment ) wx.EVT_SIZE( self.panel_changed_paths, self.OnSizeChangedPaths ) for panel, sizer in [ (self.panel_history, self.v_sizer_history), (self.panel_comment, self.v_sizer_comment), (self.panel_changed_paths, self.v_sizer_changed_paths)]: panel.SetAutoLayout( True ) panel.SetSizer( sizer ) sizer.Fit( panel ) panel.Layout() # ---------------------------------------- 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 ) #self.button_view = wx.Button( self.panel_history, id_view_cmd, " View " ) #self.button_view.Enable( False ) self.button_diff = wx.Button( self.panel_history, id_diff_cmd, T_('Diff') ) self.button_diff.Enable( False ) self.button_annotate = wx.Button( self.panel_history, id_annotate_cmd, T_('Annotate') ) self.button_annotate.Enable( False ) self.button_revision_changes = wx.Button( self.panel_history, id_revision_changes_cmd, T_('Revision Changes') ) self.button_revision_changes.Enable( False ) # leave View off screen until its implemented #self.h_sizer.Add( self.button_view, 0, wx.EXPAND|wx.LEFT, 5 ) self.h_sizer.Add( self.button_diff, 0, wx.EXPAND|wx.LEFT, 5 ) self.h_sizer.Add( self.button_annotate, 0, wx.EXPAND|wx.LEFT, 5 ) self.h_sizer.Add( self.button_revision_changes, 0, wx.EXPAND|wx.LEFT, 5 ) sizer.Add( self.h_sizer, 0, wx.EXPAND|wx.ALL, 5 ) wx.EVT_BUTTON( self.panel_history, id_view_cmd, self.OnViewCommand ) wx.EVT_BUTTON( self.panel_history, id_diff_cmd, self.OnDiffCommand ) wx.EVT_BUTTON( self.panel_history, id_annotate_cmd, self.OnAnnotateCommand ) wx.EVT_BUTTON( self.panel_history, id_revision_changes_cmd, self.OnRevisionChangesCommand ) wx.EVT_UPDATE_UI( self.panel_history, id_diff_cmd, self.OnUpdateUiDiffCommand ) wx.EVT_UPDATE_UI( self.panel_history, id_annotate_cmd, self.OnUpdateUiAnnotateCommand ) wx.EVT_UPDATE_UI( self.panel_history, id_revision_changes_cmd, self.OnUpdateUiRevisionChangesCommand ) 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 ) def OnListItemDeselected( self, event): self.updateComment( -1 ) def OnPathItemSelected( self, event): pass def OnPathItemDeselected( self, event): pass #---------- Comment handlers ------------------------------------------------------------ def updateComment( self, index ): if index >= 0: message = self.all_filtered_log_entries[ index ].message all_paths_info = self.all_filtered_log_entries[ index ].changed_paths else: message = '' all_paths_info = [] self.comment_ctrl.SetValue( message ) self.comment_ctrl.SetInsertionPoint( 0 ) self.paths_ctrl.DeleteAllItems() for index, info in enumerate( all_paths_info ): self.paths_ctrl.InsertStringItem( index, action_map.get( info.action, info.action ) ) self.paths_ctrl.SetStringItem( index, self.col_path, info.path.decode( 'utf-8' ) ) if info.copyfrom_path is not None: self.paths_ctrl.SetStringItem( index, self.col_copyfrom_revision, str( info.copyfrom_revision.number ) ) self.paths_ctrl.SetStringItem( index, self.col_copyfrom_path, info.copyfrom_path ) else: self.paths_ctrl.SetStringItem( index, self.col_copyfrom_revision, '' ) self.paths_ctrl.SetStringItem( index, self.col_copyfrom_path, '' ) def OnSizeHistory( self, event): w,h = self.panel_history.GetClientSizeTuple() self.v_sizer_history.SetDimension( 0, 0, w, h ) def OnSizeComment( self, event): w,h = self.panel_comment.GetClientSizeTuple() self.v_sizer_comment.SetDimension( 0, 0, w, h ) def OnSizeChangedPaths( self, event): w,h = self.panel_changed_paths.GetClientSizeTuple() self.v_sizer_changed_paths.SetDimension( 0, 0, w, h ) def by_rev( self, a, b ): # highest rev first return -cmp( a.rev_number, b.rev_number ) def initList( self ): 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 ) self.list_ctrl.SetItemState( 0, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED ) #---------- Command handlers ---------------------------------------------------------- def OnViewCommand( self, event ): print T_('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.getSelectedRows() indices.sort() indices.reverse() if len( indices ) not in (1,2): return info1 = wb_subversion_diff.PathInfoForDiff() 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() if len( indices ) == 1: info2.path = self.filename info2.revision = pysvn.Revision( pysvn.opt_revision_kind.working ) info2.peg_path = info2.path info2.peg_revision = info2.revision info2.title = self.filename else: 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( self.app, self.project_info, info1, info2 ) # # history does not need to have the diff code run in the background # so just step the generator # if type(generator) == types.GeneratorType: while True: try: generator.next() except StopIteration: # no problem all done break def OnUpdateUiAnnotateCommand( self, event ): all_rows = self.getSelectedRows() self.button_annotate.Enable( len( all_rows ) in (1,2) ) def OnAnnotateCommand( self, event ): indices = self.getSelectedRows() indices.sort() if len( indices ) not in (1,2): return if len( indices ) == 2: rev_start = pysvn.Revision( pysvn.opt_revision_kind.number, self.all_filtered_log_entries[ indices[1] ].rev_number ) rev_end = pysvn.Revision( pysvn.opt_revision_kind.number, self.all_filtered_log_entries[ indices[0] ].rev_number ) elif len( indices ) == 1: rev_start = pysvn.Revision( pysvn.opt_revision_kind.number, 0 ) rev_end = pysvn.Revision( pysvn.opt_revision_kind.number, self.all_filtered_log_entries[ indices[0] ].rev_number ) try: annotation = self.project_info.client_fg.annotate( url_or_path=self.filename, revision_start=rev_start, revision_end=rev_end, peg_revision=rev_end ) except pysvn.ClientError, e: self.app.log_client_error( e ) return h_frame = wb_subversion_annotate.AnnotateFrame( self.app, self.project_info, self.filename, annotation ) h_frame.Show( True ) def OnUpdateUiRevisionChangesCommand( self, event ): self.button_revision_changes.Enable( len( self.getSelectedRows() ) in (1, 2) ) def OnRevisionChangesCommand( self, event ): row_indices = self.getSelectedRows() info1 = wb_subversion_diff.PathInfoForDiff() info1.path = self.all_filtered_log_entries[ row_indices[0] ].url info2 = info1.copy() if len( row_indices ) == 1: # for one selected revision N, show files modified in revision N # and set the diff range to N-1..N. log_entries = [self.all_filtered_log_entries[ row_indices[0] ]] info1.revision = pysvn.Revision( pysvn.opt_revision_kind.number, self.all_filtered_log_entries[ row_indices[0] ].rev_number-1 ) else: # for two selected revisions N and M, show files modified in revisions N+1..M # and set the diff range to N..M. # note that all_filtered_log_entries contains lower revisions at higher indices. log_entries = self.all_filtered_log_entries[ row_indices[0]:row_indices[1] ] info1.revision = pysvn.Revision( pysvn.opt_revision_kind.number, self.all_filtered_log_entries[ row_indices[1] ].rev_number ) info2.revision = pysvn.Revision( pysvn.opt_revision_kind.number, self.all_filtered_log_entries[ row_indices[0] ].rev_number ) info1.peg_revision = info1.revision info2.peg_revision = info2.revision changed_states = {} repos_root_url = self.project_info.client_bg.info2( self.project_info.wc_path, recurse=False )[0][1].repos_root_URL prefix_len = len( self.all_filtered_log_entries[row_indices[0]].url ) - len( repos_root_url ) for log_entry in log_entries: if log_entry.url == self.all_filtered_log_entries[row_indices[0]].url: for changed_path in log_entry.changed_paths: repository_path = changed_path['path'] url = repos_root_url + repository_path if changed_states.has_key(repository_path): branch_text_states = changed_states[repository_path]['branch_text_states'] else: branch_text_states = '' branch_text_states += changed_path['action'] # Create a new status object from the log entry data. entry = pysvn.PysvnEntry( {'checksum': '' ,'commit_author': log_entry.author ,'commit_revision': log_entry.rev_number ,'commit_time': log_entry.date ,'conflict_new': None ,'conflict_old': None ,'conflict_work': None ,'copy_from_revision': pysvn.Revision( pysvn.opt_revision_kind.number, -1 ) ,'copy_from_url': None ,'is_absent': 0 ,'is_copied': 0 ,'is_deleted': 0 ,'kind': pysvn.node_kind.file ,'lock_comment': None ,'lock_creation_date': 0.0 ,'lock_owner': None ,'lock_token': None ,'name': repository_path.split( '/' )[-1] ,'properties_time': 0.0 ,'property_reject_file': None ,'repos': repos_root_url ,'revision': log_entry.rev_number ,'schedule': pysvn.wc_schedule.normal ,'text_time': 0.0 ,'url': url ,'uuid': ''}) status = pysvn.PysvnStatus( {'entry': entry ,'is_copied': 0 ,'is_locked': 0 ,'is_switched': 0 ,'is_versioned': 1 ,'path': repository_path ,'prop_status': pysvn.wc_status_kind.normal ,'repos_lock': None ,'repos_prop_status': pysvn.wc_status_kind.none ,'repos_text_status': pysvn.wc_status_kind.none ,'branch_text_states': branch_text_states ,'text_status': pysvn.wc_status_kind.normal}) changed_states[repository_path] = status else: self.app.log.info( T_('Revision changes for r%(rev1)d ignored. Its URL "%(url1)s" does not match r%(rev2)d URL "%(url2)s".') % {'rev1': log_entry.rev_number ,'url1': log_entry.url ,'rev2': self.all_filtered_log_entries[row_indices[0]].rev_number ,'url2': self.all_filtered_log_entries[row_indices[0]].url} ) changed_files = [changed_states[status] for status in changed_states] self.app.showReportRevisionChangesFrame( self.project_info, changed_files, info1, info2 ) WorkBench-1.8.2/Source/linux-rpmbuild.mak000644 000765 000024 00000002330 12615713720 020575 0ustar00barrystaff000000 000000 # # linux-rpmbuild.mak # # make file that is rpmbuild friendly # # Use File locations as preferred by Fedora # all: cd Source && PYTHON=$(PYTHON) make -f linux.mak install: install --directory $(DESTDIR)/usr/share/pysvn-workbench install --mode=644 Source/wb_*.py $(DESTDIR)/usr/share/pysvn-workbench rm -f $(DESTDIR)/usr/share/pysvn-workbench/wb_main.py install --mode=755 Source/wb_main.py $(DESTDIR)/usr/share/pysvn-workbench install --mode=644 Source/wb.png $(DESTDIR)/usr/share/pysvn-workbench install --directory $(DESTDIR)/usr/share/doc/pysvn-workbench/WorkBench_files install --directory $(DESTDIR)/usr/bin ln -s /usr/share/pysvn-workbench/wb_main.py $(DESTDIR)/usr/bin/pysvn-workbench install --directory $(DESTDIR)/usr/share/applications install --mode=644 Kit/Linux/pysvn-workbench.desktop $(DESTDIR)/usr/share/applications gzip Kit/Linux/pysvn-workbench.1 install --directory $(DESTDIR)/usr/share/man/man1 install --mode=644 Kit/Linux/pysvn-workbench.1.gz $(DESTDIR)/usr/share/man/man1 install-docs: echo "Info: install-docs" install --mode=644 Docs/WorkBench.html $(DESTDIR)/usr/share/doc/pysvn-workbench install --mode=644 Docs/WorkBench_files/*.png $(DESTDIR)/usr/share/doc/pysvn-workbench/WorkBench_files WorkBench-1.8.2/Source/dlog.cmd000644 000765 000024 00000000122 10007315155 016530 0ustar00barrystaff000000 000000 setlocal del "%USERPROFILE%\Application data\Workbench\workbench.log" endlocal WorkBench-1.8.2/Source/make_wb_images.py000644 000765 000024 00000004300 12564053010 020423 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2006 Barry A Scott. All rights reserved. This software is licensed as described in the file LICENSE.txt, which you should have received as part of this distribution. ==================================================================== make_wb_images.py ''' import sys data_slice = 32 argv = [ sys.argv[0], 'wb_images.py', 'toolbar_images/add.png', 'toolbar_images/checkin.png', 'toolbar_images/delete.png', 'toolbar_images/diff.png', 'toolbar_images/edit.png', 'toolbar_images/editcopy.png', 'toolbar_images/editcut.png', 'toolbar_images/editpaste.png', 'toolbar_images/exclude.png', 'toolbar_images/file_browser.png', 'toolbar_images/history.png', 'toolbar_images/include.png', 'toolbar_images/info.png', 'toolbar_images/lock.png', 'toolbar_images/open.png', 'toolbar_images/property.png', 'toolbar_images/revert.png', 'toolbar_images/terminal.png', 'toolbar_images/unlock.png', 'toolbar_images/update.png', 'toolbar_images/flatview.png', 'toolbar_images/onlychanges.png', 'wb.png', ] def main( argv ): f = open( argv[1], 'w' ) f.write( header ) for filename in argv[2:]: f.write( 'images_by_filename["%s"] = (\n' % filename ) i = open( filename, 'rb' ) data = i.read() i.close() for offset in range( 0, len(data), data_slice ): f.write( ' %r\n' % data[offset:offset+data_slice] ) f.write( ' )\n' ) f.write( footer ) f.close() header = ''' import wx import cStringIO def getBitmap( name, size=None ): return wx.BitmapFromImage( getImage( name, size ) ) def getImage( name, size=None ): stream = cStringIO.StringIO( images_by_filename[ name ] ) image = wx.ImageFromStream( stream ) if size is not None: w, h = size if image.GetWidth() != w or image.GetHeight() != h: image.Rescale( w, h ) return image def getIcon( name, size=None ): icon = wx.EmptyIcon() icon.CopyFromBitmap( getBitmap( name, size ) ) return icon images_by_filename = {} ''' footer = ''' ''' if __name__ == '__main__': sys.exit( main( argv ) ) WorkBench-1.8.2/Source/wb.tiff000644 000765 000024 00000200466 10365523613 016424 0ustar00barrystaff000000 000000 MM* @```D( ?O(0gwH$0_H$H{&&&LLLSSSZZZ000`4 $(4@0  HLLLaaag ?GOg_?7/ o555bbbnnnBBB wG  <XlwX,/jjj|||+++/ ?wX7444UUU***_00X{ ***555666888444222w ?---ZZZTTT***_0_ &&&???TTTjjjllloooiiiccc777 /'---lll>>>{H$;o $$$<<>>Ho HHHxxxqqq888S<<>>dddhhh888_0_;;;VVV @h...pppqqq888_00tttXXX,,,X @CCCvvveee888o0pVVVRRRp@"""pppqqq888_0H222ddd]]]g888jjjeee888o0 ?~~~hhh  ^^^qqq888_0 bbbxxx??? G$(O666wwwnnn888o0 @P`XP,`EEE^^^///o8@000```JJJ_0@W@)))RRRrrr999WTDDDMMM0(PO( ?sssCCCwhXXXPPPhPH wQQQddd%%%g4t ddd]]] w(P***NNN'''TP...]]]JJJ%%%` rrrjjj PTTTNNN`0_...TTTzzzWWW P('''xxxvvv0007(P666ddd```h&&&\\\...P555HHH$$$_0PdddrrrpW ...JJJ___tttFFF0666hhh444`P@HP666dddrrrt ?...lllvvv_0888DDDdddrrrx @` 999\\\lll666g 0222bbb"""666dddoool ?tttddd? 0_+++~~~DDD((( dddlll` HpEEEJJJ%%%W 0o...]]]yyykkk```VVVJJJ>>>...@@@ddd]]]XP,,,JJJ/0_HHH}}}[[[999zzzOOOP(P [[[zzz222C0o$$$HHHzzz===@h...]]]eee222o0_HHHVVV+++_08o...vvvggg***g40o$$$HHH+++7o>>>}}}SSS***`0_HHHdddC>>>RRR P(0o$$$HHHzzz===_/LLLzzz(((P0_HHH+++@ W&&&LLLwww>>>840o$$$HHHRRR @ ?___iii og_HHHGGG$$$` g333fffXXX///$$$HHHRRR$$$g40_jjjXXX HHHlll+++o0???xxx|||BBB!!!:::xxxoooVVV666 o;P)))zzzwww<<<:::ssssssOOO+++_(P NNNccc---$$$HHHnnnEEEMMMTTTUUUVVVVVVVVVHHH:::((( X0`"""CCCZZZ---hhhSSS***O(4g"""\\\RRR <<>>{{{aaaHHHSSS***o g>>>ttt}}}VVV000 /(VJjYM*mX\H.$(((OOOaaa***o;@LLLQQQ  ^Pz* *0**}*\HvvvSSS***o0w(((QQQxxxfe^<:-UL\*** **/***O*IIIZZZ***o;0_ LLLzxkgcH }***0*/*.*.*-*0*=/777o0o+++$$$...VVV{{{rL }*0*0*0*0*/*.*(*"*=*.$ w?0_+++VVVHHH:::\\\p~U }*0*0*0*0*0*0*/*$**J* 0o$$$HHHhhhpppVVV222 666\\\xuaeH }*0*0*0*0*0*0*.*+*|**jL W(0_HHHrrrFFF  _X0? }*0*0*0*0*0*0*0*,*'*|*"*T8O(0o$$$HHHnnn>>> (#OF~ }*0*0*0*0*0*1*/*.*(*$* w* *pJ"*3'8.>46,.#k00_HHHjjj555?6L* }*0*0*0*0*0*2*1*.*+*&* *s* *>*D+U<fNY*N*lX\F.#_00o$$$HHHJJJ%%%("ODD* {**0*0*0*0*2*1*/*,*)*$* *u*{**8*****}****L*\F.#_ 0_HHHlll)))OD,**3*3*0*0*0*3*3*0*-***'*#*~*w*w*y*z* *,***'*!**u**\F/0o$$$HHH``` fW**2*3*3*2*2*/*,*+***'*$*!*{*y*w*|*!*'*-**}****y*F*D4"K0_HHHTTTL*2*2*2*2*3*2*0*+*&*&*&*$*"*}*w*{*~*&*-*.*.*y*!*"*$**t**A*D4 ,8<@DH{$$$HHH``` \**"*2*0*.***&*%*#*"*"* *}*}* }*$*(*+*.*.*.* u** *$** ***oT."o;'?WowHHHlll+++^Q {**3*.*(*"*}** **{*y*v*}*#***0*0*/*.*.* u***$*"* **}*0*[D."o <Xo$$$HHHSSS***3,fXH*,*4*<*B*H** z*|***!*%***!***z*|*~*v*w* * *!*!****,*^G W, ?wHHH{{{>>> '"?75++$nW v* y**"*'*,*0**{*** **x*#*|** *"*****;*<-P0X{&&&...555666888888999DDDOOOeee000  cM~* ~*&*)*,*)*&* u******* u*y*******x**>*Q= @ _ ,,,LLL[[[jjjllloooppprrraaa000WD* *.*0*1*&**** * * * ** *v***** ***z**F*-!@;o $$$<<>>dddbbb>>>>4.*1**z* w* t* v*w*y********* *{*w*|* ***** *y*7*`H0$c h...pppZZZ111 ?6,* }*******y*|**********y* u* u* **** *u* **YB/ @CCCvvv```<<< /(5*2********z*{* *********v*z*}***** *~*w*B*J6%O@"""ppp<<<=*1**{******x*u*|* ********{** t******}***6*J6g555ddd[[[fX{***w******y*w*}******** *u* s*w******{*|*}*tX, w? XXXSSS***>6M*s*'* ******}** u**********s* * t* *****u**,*X@, o$G000qqqaaa***osw>6H**~*{*}**** *x*u* u*********}* y* u*u******w****^G _08o***TTTSSS***o/NB.*|* *y******w**t* ********|* w**w***** *u**9*:*XL>>>|||aaa***o;[+$VI2** *t* ***** s* s* s******** *w*}* u*z*****y***>*P< H$`RRR}}}gggSSS***o(O ^** **t***** * u**u*********v*z*~*{**** *u* {**F*+Hp___yyyOOOAAA444@@@LLLYYYfffrrr***o;(k:1^*!** *w* *** *y*z* u*w******* *}*u** u*~* *** *s* * *M*B1{0 lllOOO  $$$===]]]}}}hhh0_ N*** *v*****x*y**w********u**}*w*~****{*}*y*t*nR _0 vvvyyyOOO***...>>>lll0I>?*w**}*}* ** **s** t*|*******~*s* {** u** ** *y*v* *,*fI3$g 555OOO ZZZpppP.'?***v*|****y* z*y*y*{********w***t*~****t**w* *_D? 666yyyOOO***XXXlLLLqqq(P YJ0* t**{** **~*v* w**s** ******w* }*~**u** **}*u*z* *?*@- w<888OOO PP>>>}}}rrr`& M@)*+**u*}* * * *u**{*u*}****** *z* {**{*{***** ~*"*~*Z@-X###jjjOOO***P(<w333fffrrrt4g& lY{*z**x* * * *w* y*{*}*s*~** * * * * *v***y*w** **v**u**[ChRRRPPP P(O(((OOOsssho6,lY}*'**y*}* *}*y* {**}*v*}* * * * * *u***"*y*}** *y*y**"*vXxt)))KKK(((P(/555aaa`C6,L*|***u* *~*|*u**}*{*u** * * * *{*t* ~***u*x**t**}*y*nPthP~~~OOOX/L@1*x*!* *}*~* *w*w***y*{*}* * * * * *y****w*z*z** x*~*eHp@P(7tttzzz===D W& L@7** * }*s*}*y*u* |***w*w*~*~* * * *u*z**$**s*w*{**zV=+X/@0_kkkVVV+++_0 ?dR|* ** }*|*|*|*y**** v*|*}*}*}*}*}*~**)*"*y*s* y*X+@ Dptx\@ 0o$$$HHH+++7 g6,kX&** *z*s*{*s*{**** t*w*{*{*{*x*u**'**}*%*zTH1 G$0_HHHddd0_ X* ***{*y*y*y* ***~* u*y*y*z*s*{**%* w**lJ o0o$$$HHHzzz===_0{D8I*~** *w*t*x*x* ***~* s*s*s* y**w*y*4*dE9' o;0_HHH+++@H-%B*** *w*w*u*s****z*z*z****,*V; W0o$$$HHHRRR @ $H WG2* u***w*u*s* ****~*y*y**D*O6+P,0_HHHGGG$$$hX&K=,*&** ~*x*t*|* **#* *y**;*H2@ c$$$HHHzzzNNN$$$o80_&eR%*u***w* *** **,*sPH2#w@ /HHHdddoo-$ZI&*%** }****}*+*V;+_07 UUUlllVVV222o;?w-$V*w*}***}**:*^B+_0 ?***cccsssHHH_D6C*x*&**z*H*9'// 222NNNRRRVVVRRROOODDD:::$$$X0O!D6^JR*oTfJA. K O(/oKw`D( So;Gowo_?/oG$8<@@@@@80  8Xdph`P@$ 3@&.(R/Users/barry/wc/svn/pysvn/WorkBench/Source/wb.tiffHHWorkBench-1.8.2/Source/wb_platform_win32_specific.py000644 000765 000024 00000001634 11510666000 022702 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2011 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_platform_win32_specific.py ''' from win32com.shell import shell, shellcon import os SHGFP_TYPE_CURRENT = 0 SHGFP_TYPE_DEFAULT = 1 def getApplicationDir(): app_folder = shell.SHGetFolderPath( 0, shellcon.CSIDL_APPDATA, 0, SHGFP_TYPE_CURRENT ) return os.path.join( app_folder, 'WorkBench' ) def getLocalePath( app ): return os.path.join( app.app_dir, 'locale' ) def getNullDevice(): return 'NUL' uPathExists = os.path.exists uPathIsdir = os.path.isdir uAccess = os.access uRemove = os.remove uRename = os.rename uOpen = open uChdir = os.chdir WorkBench-1.8.2/Source/wb_main.py000644 000765 000024 00000013342 12575764146 017140 0ustar00barrystaff000000 000000 #!/usr/bin/python ''' ==================================================================== Copyright (c) 2003-2012 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_main.py ''' import warnings import sys import os import locale import tempfile # need to import so that it is noticed by MEINC Installer import _strptime # help debug when stdout goes nowhere useful # Mac OS X and Windows are the main problems stdout_filename = None if( os.environ.get( 'PYSVN_WORKBENCH_STDOUT_LOG', None ) is not None or (sys.platform == 'darwin' and '--noredirect' not in sys.argv) ): stdout_filename = os.environ.get( 'PYSVN_WORKBENCH_STDOUT_LOG', os.path.join( tempfile.gettempdir(), 'workbench.tmp' ) ) elif sys.platform.startswith( 'win' ) and '--noredirect' not in sys.argv: stdout_filename = os.path.join( tempfile.gettempdir(), 'workbench.tmp' ) elif sys.platform.startswith( 'linux' ): # assume we are in fedora standard install location if os.path.exists( '/usr/share/pysvn-workbench/wb_main.py' ): sys.path.append( '/usr/share/pysvn-workbench' ) if stdout_filename is not None: try: sys.stdout = open( stdout_filename, 'w' ) sys.stderr = sys.stdout print 'PySVN WorkBench starting' sys.stdout.flush() except EnvironmentError: pass # make sure that we get 3.0 or 2.8 and not an earlier version try: import wxversion wxversion.select( ['3.0', '2.8'] ) except: pass import wx import wb_app import wb_subversion_provider def prerequesitChecks(): return 1 def main( args ): startup_dir = os.getcwd() if True: if sys.platform == 'win32': os.chdir( os.environ['USERPROFILE'] ) # Fix for wxPython bug on multi-processor machines - limit us to 1 processor #win32process.SetProcessAffinityMask(win32api.GetCurrentProcess(), 1) else: os.chdir( os.environ['HOME'] ) # don't pollute any subprocesses with env vars # from packaging processing for envvar in ['PYTHONPATH', 'PYTHONHOME', 'PYTHONEXECUTABLE']: if os.environ.has_key( envvar ): del os.environ[ envvar ] # Register all supported source control providers wb_subversion_provider.registerProvider() # Create the win application and start its message loop app = wb_app.WbApp( startup_dir, args ) if not prerequesitChecks(): return 1 app.frame.Show( 1 ) app.MainLoop() return 0 # # needed to make MEINC Installer notice these packages # Include all the codecs # import encodings import encodings.aliases import encodings.ascii import encodings.base64_codec import encodings.big5 import encodings.big5hkscs import encodings.bz2_codec import encodings.charmap import encodings.cp037 import encodings.cp1006 import encodings.cp1026 import encodings.cp1140 import encodings.cp1250 import encodings.cp1251 import encodings.cp1252 import encodings.cp1253 import encodings.cp1254 import encodings.cp1255 import encodings.cp1256 import encodings.cp1257 import encodings.cp1258 import encodings.cp424 import encodings.cp437 import encodings.cp500 import encodings.cp737 import encodings.cp775 import encodings.cp850 import encodings.cp852 import encodings.cp855 import encodings.cp856 import encodings.cp857 import encodings.cp860 import encodings.cp861 import encodings.cp862 import encodings.cp863 import encodings.cp864 import encodings.cp865 import encodings.cp866 import encodings.cp869 import encodings.cp874 import encodings.cp875 import encodings.cp932 import encodings.cp949 import encodings.cp950 import encodings.euc_jis_2004 import encodings.euc_jisx0213 import encodings.euc_jp import encodings.euc_kr import encodings.gb18030 import encodings.gb2312 import encodings.gbk import encodings.hp_roman8 import encodings.hz import encodings.iso2022_jp import encodings.iso2022_jp_1 import encodings.iso2022_jp_2 import encodings.iso2022_jp_2004 import encodings.iso2022_jp_3 import encodings.iso2022_jp_ext import encodings.iso2022_kr import encodings.iso8859_1 import encodings.iso8859_10 import encodings.iso8859_11 import encodings.iso8859_13 import encodings.iso8859_14 import encodings.iso8859_15 import encodings.iso8859_16 import encodings.iso8859_2 import encodings.iso8859_3 import encodings.iso8859_4 import encodings.iso8859_5 import encodings.iso8859_6 import encodings.iso8859_7 import encodings.iso8859_8 import encodings.iso8859_9 import encodings.johab import encodings.koi8_r import encodings.koi8_u import encodings.latin_1 import encodings.mac_cyrillic import encodings.mac_greek import encodings.mac_iceland import encodings.mac_latin2 import encodings.mac_roman import encodings.mac_turkish import encodings.palmos import encodings.ptcp154 import encodings.punycode import encodings.quopri_codec import encodings.raw_unicode_escape import encodings.rot_13 import encodings.shift_jis import encodings.shift_jis_2004 import encodings.shift_jisx0213 import encodings.string_escape import encodings.tis_620 import encodings.undefined import encodings.unicode_escape import encodings.unicode_internal import encodings.utf_16 import encodings.utf_16_be import encodings.utf_16_le import encodings.utf_7 import encodings.utf_8 import encodings.uu_codec import encodings.zlib_codec if sys.version_info >= (2,5,0,'',0): import encodings.mac_arabic import encodings.mac_centeuro import encodings.mac_croatian import encodings.mac_farsi import encodings.mac_romanian import encodings.utf_8_sig # keep pychecker quite def __pychecker(): return encodings.utf_8 and warnings if __name__ == '__main__': sys.exit( main( sys.argv ) ) WorkBench-1.8.2/Source/make_po_file.py000644 000765 000024 00000004064 11261605162 020116 0ustar00barrystaff000000 000000 #!/bin/sh import sys import os import wb_version import datetime args = {'WB_LOCALE': sys.argv[1]} if os.path.exists( 'I18N/pysvn_workbench_%(WB_LOCALE)s.po' % args ): print 'Info: Update %(WB_LOCALE)s from I18N/pysvn_workbench.current.pot' % args rc = os.system( 'msginit ' '--input=I18N/pysvn_workbench.current.pot ' '--locale=${WB_LOCALE} ' '--no-wrap ' '--no-translator ' '--output-file=I18N/pysvn_workbench_%(WB_LOCALE)s.tmp.po' % args ) if rc != 0: sys.exit( rc ) rc = os.system( 'msgmerge ' 'I18N/pysvn_workbench_%(WB_LOCALE)s.po ' 'I18N/pysvn_workbench_%(WB_LOCALE)s.tmp.po ' '--quiet ' '--no-wrap ' '--output-file=I18N/pysvn_workbench_%(WB_LOCALE)s.current.po' % args ) if rc != 0: sys.exit( rc ) else: print 'Info: Create %(WB_LOCALE)s from I18N/pysvn_workbench.current.pot' % args rc = os.system( 'msginit ' '--input=I18N/pysvn_workbench.current.pot ' '--locale=%(WB_LOCALE)s.UTF-8 ' '--no-wrap ' '--no-translator ' '--output-file=I18N/pysvn_workbench_%(WB_LOCALE)s.current.po' % args ) if rc != 0: sys.exit( rc ) print 'Info: Version brand %(WB_LOCALE)s from I18N/pysvn_workbench.current.pot' % args po_filename = 'I18N/pysvn_workbench_%(WB_LOCALE)s.current.po' % args all_po_lines = open( po_filename, 'r' ).readlines() for index, line in enumerate( all_po_lines ): if line.startswith( '"Project-Id-Version:' ): all_po_lines[ index ] = ('"Project-Id-Version: WorkBench %d.%d.%d.%d\\n"\n' % (wb_version.major ,wb_version.minor ,wb_version.patch ,wb_version.build)) elif line.startswith( '"PO-Revision-Date:' ): all_po_lines[ index ] = '"PO-Revision-Date: %s\\n"\n' % (datetime.datetime.now().isoformat(' '),) elif line.startswith( '"Content-Type: text/plain; charset=' ): all_po_lines[ index ] = '"Content-Type: text/plain; charset=UTF-8\\n"\n' open( po_filename, 'w' ).write( ''.join( all_po_lines ) ) sys.exit( 0 ) WorkBench-1.8.2/Source/wb_config.py000644 000765 000024 00000002257 11507143125 017442 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2006-2010 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_config.py ''' import wx # # 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 WorkBench-1.8.2/Source/make_pot_file.py000644 000765 000024 00000001363 11655513604 020307 0ustar00barrystaff000000 000000 #!/usr/bin/python import sys import os import glob all_py_files = set( glob.glob( '*.py' ) ) all_py_files.remove( 'wb_diff_images.py' ) all_py_files.remove( 'wb_images.py' ) f = open( 'wb_files.tmp', 'wt' ) for py_file in all_py_files: f.write( '%s\n' % py_file ) f.close() cmd = ('xgettext ' '--files-from=wb_files.tmp ' '--default-domain=pysvn_workbench ' '--output=I18N/pysvn_workbench.current.pot ' '--msgid-bugs-address=barryscott@tigris.org ' '--copyright-holder="Barry Scott" ' '--keyword=U_ ' '--keyword=T_ ' '--keyword=S_:1,2 ' '--add-comments ' '--no-wrap ' '--width=2047 ' '--add-comments=Translat ' '--language=Python') print 'Info: %s' % (cmd,) rc = os.system( cmd ) sys.exit( rc ) WorkBench-1.8.2/Source/wb_version.py.template000644 000765 000024 00000000666 10356247553 021511 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2006 Barry A Scott. All rights reserved. This software is licensed as described in the file LICENSE.txt, which you should have received as part of this distribution. ==================================================================== wb_version.py.template ''' major = %(MAJOR)s minor = %(MINOR)s patch = %(PATCH)s build = %(BUILD)s WorkBench-1.8.2/Source/wb_subversion_report_updates.py000644 000765 000024 00000027220 11662230071 023510 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2006-2011 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_subversion_report_updates.py ''' import wx import sys import wb_images import time import pysvn import wb_config import wb_ids import wb_images import wb_exceptions import wb_list_panel_common import wb_subversion_utils import wb_subversion_list_handler_common id_exclude = wx.NewId() id_include = wx.NewId() class ReportUpdatesFrame(wx.Frame): def __init__( self, app, project_info, all_files ): wx.Frame.__init__( self, None, -1, T_("Updates Report"), size=(700,500) ) self.app = app self.menu_actions = wx.Menu() self.menu_actions.Append( wb_ids.id_SP_DiffWorkHead, T_('Diff WC vs. HEAD...'), T_('Diff WC vs. HEAD...') ) self.menu_actions.Append( wb_ids.id_SP_DiffWorkBranchOriginBase, T_('Diff WC vs. branch origin BASE...'), T_('Diff WC vs. branch origin BASE...') ) self.menu_actions.Append( wb_ids.id_SP_DiffWorkBranchOriginHead, T_('Diff WC vs. branch origin HEAD...'), T_('Diff WC vs. branch origin HEAD...') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SP_Annotate, T_('Annotate...'), T_('Annotate...') ) self.menu_actions.Append( wb_ids.id_SP_History, T_('Log history...'), T_('Log history...') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( id_exclude, T_('Exclude...'), T_('Exclude from update') ) self.menu_actions.Append( id_include, T_('Include...'), T_('Include in update') ) self.menu_bar = wx.MenuBar() self.menu_bar.Append( self.menu_actions, T_("&Actions") ) self.SetMenuBar( self.menu_bar ) # Add tool bar t = self.CreateToolBar( name="main", style=wx.TB_HORIZONTAL ) bitmap_size = (32,32) t.SetToolBitmapSize( bitmap_size ) t.AddSimpleTool( wb_ids.id_SP_DiffWorkHead, wb_images.getBitmap( 'toolbar_images/diff.png', bitmap_size ), T_('Diff changes against HEAD'), T_('Diff changes against HEAD') ) t.AddSimpleTool( wb_ids.id_SP_History, wb_images.getBitmap( 'toolbar_images/history.png', bitmap_size ), T_('Show History log'), T_('Show History log') ) t.AddSeparator() t.AddSimpleTool( id_exclude, wb_images.getBitmap( 'toolbar_images/exclude.png', bitmap_size ), T_('Exclude from check in'), T_('Exclude from check in') ) t.AddSimpleTool( id_include, wb_images.getBitmap( 'toolbar_images/include.png', bitmap_size ), T_('Include in check in'), T_('Include in check in') ) t.Realize() try_wrapper = wb_exceptions.TryWrapperFactory( self.app.log ) # Set the application icon self.SetIcon( wb_images.getIcon( 'wb.png' ) ) # create the individule panels self.panel_list = ReportUpdatesListPanel( app, self, self ) wx.EVT_CLOSE( self, self.OnCloseWindow ) wx.EVT_BUTTON( self.panel_list, wx.ID_OK, self.app.eventWrapper( self.OnOk ) ) wx.EVT_BUTTON( self.panel_list, wx.ID_CANCEL, try_wrapper( self.OnCancel ) ) self.project_info = ReportUpdatesProjectInfo( project_info, all_files ) self.list_handler = ReportUpdatesListHandler( self.app, self.panel_list, self.project_info ) # draw the list - its updates the status info self.panel_list.setHandler( self.list_handler ) wx.EVT_MENU( self, wb_ids.id_SP_Annotate, self.app.eventWrapper( self.OnSpAnnotate ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkHead, self.app.eventWrapper( self.OnSpDiffWorkHead ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkBranchOriginBase, self.app.eventWrapper( self.OnSpDiffWorkBranchOriginBase ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkBranchOriginHead, self.app.eventWrapper( self.OnSpDiffWorkBranchOriginHead ) ) wx.EVT_MENU( self, wb_ids.id_SP_History, self.app.eventWrapper( self.OnSpHistory ) ) wx.EVT_MENU( self, id_exclude, self.app.eventWrapper( self.OnExcludeItem ) ) wx.EVT_MENU( self, id_include, self.app.eventWrapper( self.OnIncludeItem ) ) def OnExcludeItem( self, event ): self.list_handler.Cmd_ReportUpdates_ExcludeItem( self.panel_list.getSelectedRows() ) self.panel_list.drawList() def OnIncludeItem( self, event ): self.list_handler.Cmd_ReportUpdates_IncludeItem( self.panel_list.getSelectedRows() ) self.panel_list.drawList() def clearUpdateUiState( self ): pass def getUpdateUiState( self ): pass def setEventHandler( self, handler ): self.handler = handler def OnCloseWindow( self, event ): self.Destroy() def OnCancel( self, event ): self.Destroy() def OnOk( self, event ): self.Hide() all_filenames = self.list_handler.getReportUpdatesFiles() self.app.setAction( T_('Update %s...') % self.project_info.wc_path ) self.app.setProgress( T_('Updated %(count)d'), 0 ) yield self.app.backgroundProcess try: for filename in all_filenames: rev_list = self.project_info.client_bg.update( filename, recurse=False ) for rev in rev_list: if rev.number > 0: count = self.app.getProgressValue( 'count' ) if count == 0: self.app.log.info( T_('Updated %(filename)s to revision %(rev)d, no new updates') % {'filename': filename ,'rev': rev.number} ) else: self.app.log.info( S_('Updated %(filename)s to revision %(rev)d, %(count)d new update', 'Updated %(filename)s to revision %(rev)d, %(count)d new updates', count) % {'filename': filename ,'rev': rev.number ,'count': count} ) except pysvn.ClientError, e: self.app.log_client_error( e ) yield self.app.foregroundProcess self.app.clearProgress() self.app.setAction( T_('Ready') ) self.app.refreshFrame() self.Destroy() # command events def OnSpAnnotate( self, event ): return self.panel_list.OnSpAnnotate() def OnSpDiffWorkHead( self, event ): return self.panel_list.OnSpDiffWorkHead() def OnSpDiffWorkBranchOriginBase( self, event ): return self.panel_list.OnSpDiffWorkBranchOriginBase() def OnSpDiffWorkBranchOriginHead( self, event ): return self.panel_list.OnSpDiffWorkBranchOriginHead() def OnSpHistory( self, event ): return self.panel_list.OnSpHistory() class ReportUpdatesListHandler(wb_subversion_list_handler_common.SubversionListHandlerCommon): def __init__( self, app, parent, project_info ): wb_subversion_list_handler_common.SubversionListHandlerCommon.__init__( self, app, parent, project_info ) self.all_excluded_files = {} # use the repos status in the report def getTextStatus( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) return status.repos_text_status # use the repos status in the report def getPropStatus( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) return status.repos_prop_status def statusFormatString( self, file ): text_code = wb_subversion_utils.wc_status_kind_map[ file.repos_text_status ] prop_code = wb_subversion_utils.wc_status_kind_map[ file.repos_prop_status ] if text_code == ' ' and prop_code != ' ': text_code = '_' return '%s%s' % (text_code, prop_code) def setupColumnInfo( self ): self.column_info.setFrom( [ T_('State'), T_('Name') ], [5, 100] ) def statusColour( self, file ): # show that a file is on the exclude list if file.path in self.getAllGreyFilenames(): return wb_config.colour_status_disabled else: return wb_config.colour_status_normal def getContextMenu( self ): menu_template = \ [('', wb_ids.id_File_Edit, T_('Edit') )] if wx.Platform in ['__WXMSW__','__WXMAC__']: menu_template += \ [('', wb_ids.id_Shell_Open, T_('Open') )] menu_template += \ [('-', 0, 0 ) ,('', wb_ids.id_SP_DiffWorkHead, T_('Diff WC vs. HEAD...') ) ,('', wb_ids.id_SP_DiffWorkBranchOriginBase, T_('Diff WC vs. branch origin BASE...') ) ,('', wb_ids.id_SP_DiffWorkBranchOriginHead, T_('Diff WC vs. branch origin HEAD...') ) ,('-', 0, 0 ) ,('', wb_ids.id_SP_Annotate, T_('Annotate...') ) ,('', wb_ids.id_SP_History, T_('Log history...') ) ] return wb_subversion_utils.populateMenu( wx.Menu(), menu_template ) def Cmd_ReportUpdates_ExcludeItem( self, all_rows ): for row in all_rows: self.all_excluded_files[ self.getFilename( row ) ] = None def Cmd_ReportUpdates_IncludeItem( self, all_rows ): for row in all_rows: if self.getFilename( row ) in self.all_excluded_files: del self.all_excluded_files[ self.getFilename( row ) ] def getAllGreyFilenames( self ): # show all excluded files in grey return self.all_excluded_files def getReportUpdatesFiles( self ): return [entry.path for entry in self.project_info.all_files if entry.path not in self.all_excluded_files] class ReportUpdatesProjectInfo: def __init__( self, project_info, all_files ): self.all_files = all_files self.need_properties = False self.project_name = project_info.project_name self.url = project_info.url self.wc_path = project_info.wc_path self.need_checkout = False self.need_upgrade = False self.client_fg = project_info.client_fg self.client_bg = project_info.client_bg def getTagsUrl( self, rel_url ): return None def setNeedProperties( self, need_properties ): self.need_properties = need_properties def updateStatus( self ): pass def getFilesStatus( self ): return self.all_files def getProperty( self, filename, prop_name ): return '' def getWorkingDir( self ): return self.wc_path class ReportUpdatesListPanel(wb_list_panel_common.WbListPanelCommon): def __init__( self, app, frame, parent ): wb_list_panel_common.WbListPanelCommon.__init__( self, app, frame, parent ) def addToSizer( self, v_sizer ): self.h_sizer = wx.BoxSizer( wx.HORIZONTAL ) self.button_ok = wx.Button( self, wx.ID_OK, T_(" Update ") ) self.button_cancel = wx.Button( self, wx.ID_CANCEL, T_(" Cancel ") ) self.button_ok.SetDefault() self.button_ok.Enable( True ) self.h_sizer.Add( (60, 20), 1, wx.EXPAND) self.h_sizer.Add( self.button_ok, 0, wx.EXPAND|wx.EAST, 15) self.h_sizer.Add( self.button_cancel, 0, wx.EXPAND|wx.EAST, 2 ) v_sizer.Add( self.h_sizer, 0, wx.EXPAND|wx.ALL, 5 ) def getAcceleratorTableInit( self ): acc_init =[ (wx.ACCEL_CMD, ord('D'), wb_ids.id_SP_DiffWorkHead), (wx.ACCEL_CMD, ord('L'), wb_ids.id_SP_History), ] return acc_init WorkBench-1.8.2/Source/make_wb_version.py000644 000765 000024 00000001134 10605662704 020657 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 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. ==================================================================== make_wb_version.py ''' import sys sys.path.append( '../Builder' ) import brand_version argv = [ sys.argv[0], '../Builder/version.info', 'wb_version.py.template', ] if __name__ == '__main__': sys.exit( brand_version.main( argv ) ) WorkBench-1.8.2/Source/wb_app.py000644 000765 000024 00000064674 12723051552 016773 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2011 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 distributio. ==================================================================== wb_app.py ''' import sys import os import types import logging import threading import inspect import gettext import locale import wx import wx.lib import wx.lib.newevent def _print( msg ): print( msg ) def _showCallers( depth, printFunc ): stack = inspect.stack() for index in range( 1, depth+1 ): if index >= len(stack): break caller = stack[ index ] filename = os.path.basename( caller[1] ) printFunc( 'File: %s:%d, Function: %s' % (filename, caller[2], caller[3]) ) del caller del stack def checkTranslateUsage( msg ): print( 'Error: T_(%r) called before translations are setup. Change to U_ or call after initTranslations.' % (msg,) ) _showCallers( 30, _print ) return msg def checkPluralTranslateUsage( msg ): print( 'Error: T_(%r) called before translations are setup. Change to U_ or call after initTranslations.' % (msg,) ) _showCallers( 30, _print ) return msg def noTranslate( msg ): return msg import __builtin__ # defered translation __builtin__.__dict__['U_'] = noTranslate # translat text __builtin__.__dict__['T_'] = checkTranslateUsage # Plurals translation __builtin__.__dict__['S_'] = checkPluralTranslateUsage import wb_frame import wb_preferences import wb_platform_specific import wb_exceptions import wb_diff_frame import wb_dialogs import wb_background_thread import wb_shell_commands import wb_subversion_report_revision_changes import wb_subversion_utils # 2.8 has the convenient ACCEL_CMD # on 2.6 we can add the feature try: wx.ACCEL_CMD except AttributeError: if 'wxMac' in wx.PlatformInfo: wx.ACCEL_CMD = wx.ACCEL_ALT else: wx.ACCEL_CMD = wx.ACCEL_CTRL AppCallBackEvent, EVT_APP_CALLBACK = wx.lib.newevent.NewEvent() class WbApp(wx.App): def __init__( self, startup_dir, args ): # Debug settings # don't redirect IO into the log window self.__debug_noredirect = '--noredirect' in args # enable debug messages self.__debug = '--debug' in args self.__trace = '--trace' in args # use test preferences self.__test = '--test' in args self.__last_client_error = [] self.args = args self.app_name = os.path.basename( args[0] ) self.app_dir = os.path.dirname( args[0] ) if self.app_dir == '': self.app_dir = startup_dir self.translation = None self.prefs = None self._wx_locale = None self.main_thread = threading.currentThread() self.progress_format = None self.progress_values = {} wb_platform_specific.setupPlatform() wb_shell_commands.setupCommands() self.setupLogging() self.log.info( 'Work Bench starting' ) # --project automatically creates a project entry for # if is a subversion working copy and no project entry exists for it. self.auto_project_dir = None if '--project' in args: project_arg_index = args.index( '--project' ) if project_arg_index < len( args ) - 1: self.auto_project_dir = os.path.abspath( os.path.join( startup_dir, args[ project_arg_index+1 ] ) ) self.lock_ui = 0 self.need_activate_app_action = False self.frame = None self.all_diff_frames = [] self.all_temp_files = [] self.__paste_data = None self.background_thread = wb_background_thread.BackgroundThread( self ) self.background_thread.start() wx.App.__init__( self, 0 ) try_wrapper = wb_exceptions.TryWrapperFactory( self.log ) wx.EVT_ACTIVATE_APP( self, try_wrapper( self.OnActivateApp ) ) EVT_APP_CALLBACK( self, try_wrapper( self.OnAppCallBack ) ) def isStdIoRedirect( self ): return not self.__debug_noredirect def eventWrapper( self, function ): return EventScheduling( self, function, update=False ) def eventUpdateWrapper( self, function ): return EventScheduling( self, function, update=True ) def isMainThread( self ): 'return true if the caller is running on the main thread' return self.main_thread is threading.currentThread() # codes determined by: # 1. System Preferences... # 2. Language and Text # 3. Drag lang to the top of the list # 4. look in ~/.CFUserTextEncoding _all_supported_mac_locales = { (0, 0): wx.LANGUAGE_ENGLISH_US, (0, 2): wx.LANGUAGE_ENGLISH_UK, (0, 3): wx.LANGUAGE_GERMAN, (0, 4): wx.LANGUAGE_ITALIAN, (0x1D, 0x2B): wx.LANGUAGE_HUNGARIAN, } # just init the locale using wx.Locale. Use of python's locale module # conflicts with wxPython and leads to all sorts of crashes or date failures # like JDN problems on windows in history # # NOTE: wx.Locale needs wx.Application object to exist # def __initLocale( self ): if self._wx_locale is not None: return self.log.info( 'initLocale ----------------------------------------' ) # init the locale if sys.platform == 'win32': self._wx_locale = wx.Locale( wx.LANGUAGE_DEFAULT, wx.LOCALE_LOAD_DEFAULT ) elif sys.platform == 'darwin': # on Mac this will set to the default # note: cannot find any docs on this to confirm its supported if '__CF_USER_TEXT_ENCODING' in os.environ: self.log.info( 'wx.setLocale from __CF_USER_TEXT_ENCODING %s' % os.environ['__CF_USER_TEXT_ENCODING'] ) # __CF_USER_TEXT_ENCODING may use 0x1f or 31 depending of OS X version lang_code = [int( num, 0 ) for num in os.environ['__CF_USER_TEXT_ENCODING'].split( ':' )] wx_lang_key = tuple( lang_code[1:3] ) if wx_lang_key not in self._all_supported_mac_locales: self.log.info( 'Unsupported langauge with code %r' % (wx_lang_key,) ) wx_lang_code = wx.LANGUAGE_ENGLISH else: wx_lang_code = self._all_supported_mac_locales[ wx_lang_key ] self.log.info( 'wx language code %d' % (wx_lang_code,) ) self._wx_locale = wx.Locale( wx_lang_code, wx.LOCALE_LOAD_DEFAULT ) else: self._wx_locale = wx.Locale( wx.LANGUAGE_ENGLISH, wx.LOCALE_LOAD_DEFAULT ) else: # generic Posix locale code self._wx_locale = wx.Locale( wx.LANGUAGE_DEFAULT, wx.LOCALE_LOAD_DEFAULT ) self.log.info( 'wx locale GetCanonicalName: %r' % (self._wx_locale.GetCanonicalName(),) ) def initTranslations( self ): self.__initLocale() # only run once if self.translation is not None: return assert( self._wx_locale is not None ) self.log.info( 'Setting up translations' ) if sys.platform == 'win32': languages = [locale.getdefaultlocale()[0]] else: languages = [locale.getlocale()[0]] locale_path = wb_platform_specific.getLocalePath( self ) all_mofiles = gettext.find( 'pysvn_workbench', locale_path, languages, all=1 ) self.translation = gettext.translation( 'pysvn_workbench', locale_path, languages, fallback=True ) __builtin__.__dict__['T_'] = self.translation.ugettext __builtin__.__dict__['S_'] = self.translation.ungettext # U_ is defined above for pre translation markup # debug output for locale issue debugging self.log.info( 'app_name %s' % (self.app_name,) ) self.log.info( 'app_dir %s' % (self.app_dir,) ) self.log.info( 'main thread is %r' % (self.main_thread,) ) if sys.platform == 'win32': self.log.info( 'locale set to %r' % (locale.getdefaultlocale(),) ) else: self.log.info( 'locale set to %r' % (locale.getlocale(),) ) self.log.info( 'locale_path %s' % (locale_path) ) self.log.info( 'languages %s' % (languages,) ) self.log.info( 'find %r' % (gettext.find( 'pysvn_workbench', locale_path, languages, all=1 ),) ) for name in sorted( self.translation.info().keys() ): self.log.info( 'translation info %s: %r' % (name, self.translation.info()[ name ]) ) self.log.info( 'Translations setup complete.' ) def setupLogging( self ): self.log = logging.getLogger( 'WorkBench' ) self.trace = logging.getLogger( 'WorkBench.Trace' ) if self.__debug: self.log.setLevel( logging.DEBUG ) else: self.log.setLevel( logging.INFO ) if self.__trace: self.trace.setLevel( logging.INFO ) else: self.trace.setLevel( logging.CRITICAL ) log_filename = wb_platform_specific.getLogFilename() # keep 10 logs of 100K each handler = RotatingFileHandler( log_filename, 'a', 100*1024, 10 ) formatter = logging.Formatter( '%(asctime)s %(levelname)s %(message)s' ) handler.setFormatter( formatter ) self.log.addHandler( handler ) if not self.isStdIoRedirect(): handler = StdoutLogHandler() formatter = logging.Formatter( '%(asctime)s %(levelname)s %(message)s' ) handler.setFormatter( formatter ) self.log.addHandler( handler ) handler = StdoutLogHandler() formatter = logging.Formatter( '%(asctime)s %(levelname)s %(message)s' ) handler.setFormatter( formatter ) self.trace.addHandler( handler ) self.log.debug( 'debug enabled' ) self.trace.info( 'trace enabled' ) def log_client_error( self, e, title='Error' ): # must run on the main thread if not self.isMainThread(): self.foregroundProcess( self.log_client_error, (e, title) ) return self.__last_client_error = [] for message, _ in e.args[1]: self.__last_client_error.append( message ) self.log.error( message ) wx.MessageBox( '\n'.join( self.__last_client_error ), title, style=wx.OK|wx.ICON_ERROR ); def log_error( self, e, title='Error' ): # must run on the main thread if not self.isMainThread(): self.foregroundProcess( self.log_error, (e, title) ) return message = str( e ) self.log.error( message ) wx.MessageBox( message, title, style=wx.OK|wx.ICON_ERROR ); def refreshFrame( self ): self.frame.refreshFrame() def expandSelectedTreeNode( self ): self.frame.expandSelectedTreeNode() def selectTreeNodeInParent( self, filename ): self.frame.selectTreeNodeInParent( filename ) def selectTreeNode( self, filename ): self.frame.selectTreeNode( filename ) def setAction( self, msg ): self.frame.setAction( msg ) def setProgress( self, fmt, total ): self.progress_format = fmt self.progress_values['total'] = total self.progress_values['count'] = 0 self.progress_values['percent'] = 0 self.frame.setProgress( self.progress_format % self.progress_values ) def incProgress( self ): if self.progress_format is None: return self.progress_values['count'] += 1 if self.progress_values['total'] > 0: self.progress_values['percent'] = self.progress_values['count']*100/self.progress_values['total'] self.frame.setProgress( self.progress_format % self.progress_values ) def getProgressValue( self, name ): return self.progress_values[ name ] def clearProgress( self ): self.progress_format = None self.frame.setProgress( '' ) def setPasteData( self, data ): self.__paste_data = data def clearPasteData( self ): self.__paste_data = None def hasPasteData( self ): return self.__paste_data is not None def getPasteData( self ): return self.__paste_data # called from wb_subversion_history to avoid circular imports def showReportRevisionChangesFrame( self, project_info, changed_files, info1, info2 ): revision_changes_frame = wb_subversion_report_revision_changes.ReportRevisionChangesFrame( self, project_info, changed_files, info1, info2 ) revision_changes_frame.Show( True ) 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 ) def confirmAction( self, title, all_filenames ): dialog = wb_dialogs.ConfirmAction( self.frame, title, all_filenames ) result = dialog.ShowModal() return result == wx.ID_OK def confirmForceAction( self, title, all_filenames ): dialog = wb_dialogs.ConfirmAction( self.frame, title, all_filenames, force_field=True ) result = dialog.ShowModal() return result == wx.ID_OK, dialog.getForce() def getLogMessage( self, title, all_filenames ): dialog = wb_dialogs.LogMessage( self.frame, title, all_filenames, wb_platform_specific.getLastCheckinMessageFilename() ) result = dialog.ShowModal() if result == wx.ID_OK: return dialog.getLogMessage() return None def getLockMessage( self, title, all_filenames ): dialog = wb_dialogs.LogMessage( self.frame, title, all_filenames, wb_platform_specific.getLastLockMessageFilename(), force_field=True ) result = dialog.ShowModal() if result == wx.ID_OK: return dialog.getLogMessage(), dialog.getForce() return None, False def addFile( self, title, name, force=None ): dialog = wb_dialogs.AddDialog( self.frame, title, name, force ) result = dialog.ShowModal() if result == wx.ID_OK: return dialog.getForce() return None def addFolder( self, title, name, force, recursive=None ): dialog = wb_dialogs.AddDialog( self.frame, title, name, force, recursive=recursive ) result = dialog.ShowModal() if result == wx.ID_OK: return dialog.getForce(), dialog.getRecursive() return None, None def renameFile( self, title, old_name, force=None ): dialog = wb_dialogs.RenameFile( self.frame, title, old_name, force ) result = dialog.ShowModal() if result == wx.ID_OK: return dialog.getNewFilename(), dialog.getForce() return None, None def getCredentials( self, realm, username, may_save ): # signature allows use a pysvn callback dialog = wb_dialogs.GetCredentials( self.frame, realm, username, may_save ) result = dialog.ShowModal() if result == wx.ID_OK: return (True, dialog.getUsername().encode('UTF-8'), dialog.getPassword().encode('UTF-8'), dialog.getSaveCredentials()) else: return False, '', '', False def getServerTrust( self, realm, info_list, may_save ): # signature allows use a pysvn callback dialog = wb_dialogs.GetServerTrust( self.frame, realm, info_list, may_save ) result = dialog.ShowModal() if result == wx.ID_OK: # Trust, save return True, dialog.getSaveTrust() else: # don't trust, don't save return False, False def getFilename( self, title, border_title ): dialog = wb_dialogs.GetFilename( self.frame, title, border_title ) result = dialog.ShowModal() if result == wx.ID_OK: return True, dialog.getNewFilename() else: return False, '' def savePreferences( self ): self.prefs.writePreferences() def exitAppNow( self ): if self.lock_ui > 0: # return False to veto a close return False # o.k. to exit for temp_file in self.all_temp_files: self.log.info( 'Removing "%s".' % temp_file ) try: os.remove( temp_file ) except OSError: pass self.frame.savePreferences() self.prefs.writePreferences() self.frame = None return True def OnInit(self): self.initTranslations() if self.__test: self.prefs = wb_preferences.Preferences( self, wb_platform_specific.getPreferencesFilename() + '.test', wb_platform_specific.getOldPreferencesFilename() + '.test' ) else: self.prefs = wb_preferences.Preferences( self, wb_platform_specific.getPreferencesFilename(), wb_platform_specific.getOldPreferencesFilename() ) self.frame = wb_frame.WbFrame( self ) self.frame.Show( True ) self.SetTopWindow( self.frame ) self.foregroundProcess( self.frame.tree_panel.initFrame, () ) return True def OnActivateApp( self, event ): if self.frame is None: # too early or too late return if self.lock_ui == 0: self.frame.OnActivateApp( event.GetActive() ) else: if event.GetActive(): self.need_activate_app_action = True def backgroundProcess( self, function, args ): self.background_thread.addWork( AppBackgroundFunction( self, function, args ) ) def foregroundProcess( self, function, args ): wx.PostEvent( self, AppCallBackEvent( callback=function, args=args ) ) def OnAppCallBack( self, event ): try: event.callback( *event.args ) except: self.log.exception( 'OnAppCallBack<%s.%s>\n' % (event.callback.__module__, event.callback.__name__ ) ) def debugShowCallers( self, depth ): if self.__debug: _showCallers( depth, self.log.debug ) class AppBackgroundFunction: def __init__( self, app, function, args ): self.app = app self.function = function self.args = args def __call__( self ): self.app.trace.info( 'AppBackgroundFunction<%s.%s>.__call__()' % (self.function.__module__, self.function.__name__) ) try: self.function( *self.args ) except: self.app.log.exception( 'AppBackgroundFunction<%s.%s>\n' % (self.function.__module__, self.function.__name__) ) class EventScheduling: def __init__( self, app, function, update ): self.app = app self.function = function self.update = update def __call__( self, *args, **kwds ): if not self.update: self.app.trace.info( 'EventScheduling<%s.%s>.__call__()' % (self.function.__module__, self.function.__name__) ) try: # call the function result = self.function( *args, **kwds ) # did the function run or make a generator? if type(result) != types.GeneratorType: # it ran - we are all done return # step the generator stepGenerator( self.app, result ) except: self.app.log.exception( 'EventScheduling<%s.%s>\n' % (self.function.__module__, self.function.__name__ ) ) def stepGenerator( app, generator ): app.trace.info( 'stepGenerator<%r>() next_fn=%r' % (generator, generator.next) ) # result tells where to schedule the generator to next try: where_to_go_next = generator.next() app.trace.info( 'stepGenerator<%r>() next=>%r' % (generator, where_to_go_next) ) except StopIteration: # no problem all done return # will be one of app.foregroundProcess or app.backgroundProcess where_to_go_next( stepGenerator, (app, generator) ) #-------------------------------------------------------------------------------- # # RotatingFileHandler - based on python lib class # #-------------------------------------------------------------------------------- class RotatingFileHandler(logging.FileHandler): def __init__(self, filename, mode="a", maxBytes=0, backupCount=0): """ Open the specified file and use it as the stream for logging. By default, the file grows indefinitely. You can specify particular values of maxBytes and backupCount to allow the file to rollover at a predetermined size. Rollover occurs whenever the current log file is nearly maxBytes in length. If backupCount is >= 1, the system will successively create new files with the same pathname as the base file, but with extensions ".1", ".2" etc. appended to it. For example, with a backupCount of 5 and a base file name of "app.log", you would get "app.log", "app.log.1", "app.log.2", ... through to "app.log.5". The file being written to is always "app.log" - when it gets filled up, it is closed and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc. exist, then they are renamed to "app.log.2", "app.log.3" etc. respectively. If maxBytes is zero, rollover never occurs. """ logging.FileHandler.__init__(self, filename, mode) self.maxBytes = maxBytes self.backupCount = backupCount if maxBytes > 0: self.mode = "a" def doRollover(self): """ Do a rollover, as described in __init__(). """ self.stream.close() if self.backupCount > 0: prefix, suffix = os.path.splitext( self.baseFilename ) for i in range(self.backupCount - 1, 0, -1): sfn = "%s.%d%s" % (prefix, i, suffix) dfn = "%s.%d%s" % (prefix, i+1, suffix) if os.path.exists(sfn): #print "%s -> %s" % (sfn, dfn) if os.path.exists(dfn): os.remove(dfn) os.rename(sfn, dfn) dfn = self.baseFilename + ".1" if os.path.exists(dfn): os.remove(dfn) os.rename(self.baseFilename, dfn) #print "%s -> %s" % (self.baseFilename, dfn) self.stream = open(self.baseFilename, "w") def emit(self, record): """ Emit a record. Output the record to the file, catering for rollover as described in setRollover(). """ if self.maxBytes > 0: # are we rolling over? msg = "%s\n" % self.format(record) try: self.stream.seek(0, 2) #due to non-posix-compliant Windows feature if self.stream.tell() + len(msg) >= self.maxBytes: self.doRollover() except ValueError: # on Windows we get "ValueError: I/O operation on closed file" # when a second copy of workbench is run self.doRollover() logging.FileHandler.emit(self, record) class StdoutLogHandler(logging.Handler): def __init__( self ): logging.Handler.__init__( self ) def emit( self, record ): try: msg = self.format(record) + '\n' if type( msg ) == types.UnicodeType: msg = msg.encode( 'utf-8' ) sys.__stdout__.write( msg ) except: self.handleError(record) #- QQQ ------------------------------------------------------------------------------- # Locate a .mo file using the gettext strategy def find(log, domain, localedir=None, languages=None, all=0): log.info( 'find( %r, %r, %r, %r )' % (domain, localedir, languages, all) ) # Get some reasonable defaults for arguments that were not supplied if localedir is None: localedir = _default_localedir if languages is None: languages = [] for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'): val = os.environ.get(envar) if val: languages = val.split(':') break if 'C' not in languages: languages.append('C') # now normalize and expand the languages nelangs = [] for lang in languages: for nelang in _expand_lang(log, lang): if nelang not in nelangs: nelangs.append(nelang) # select a language if all: result = [] else: result = None log.info( 'find: nelangs %r' % (nelangs,) ) for lang in nelangs: if lang == 'C': break mofile = os.path.join(localedir, lang, 'LC_MESSAGES', '%s.mo' % domain) log.info( 'find: mofile %r' % (mofile,) ) if os.path.exists(mofile): if all: result.append(mofile) else: return mofile return result def _expand_lang(log, locale): from locale import normalize locale = normalize(locale) COMPONENT_CODESET = 1 << 0 COMPONENT_TERRITORY = 1 << 1 COMPONENT_MODIFIER = 1 << 2 # split up the locale into its base components mask = 0 pos = locale.find('@') if pos >= 0: modifier = locale[pos:] locale = locale[:pos] mask |= COMPONENT_MODIFIER else: modifier = '' pos = locale.find('.') if pos >= 0: codeset = locale[pos:] locale = locale[:pos] mask |= COMPONENT_CODESET else: codeset = '' pos = locale.find('_') if pos >= 0: territory = locale[pos:] locale = locale[:pos] mask |= COMPONENT_TERRITORY else: territory = '' language = locale ret = [] for i in range(mask+1): if not (i & ~mask): # if all components for this combo exist ... val = language if i & COMPONENT_TERRITORY: val += territory if i & COMPONENT_CODESET: val += codeset if i & COMPONENT_MODIFIER: val += modifier ret.append(val) ret.reverse() return ret WorkBench-1.8.2/Source/resource.h000644 000765 000024 00000000670 10315471775 017143 0ustar00barrystaff000000 000000 //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by rb_user.rc // #define IDI_ICON1 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 101 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif WorkBench-1.8.2/Source/wb_subversion_diff.py000644 000765 000024 00000031162 12575546016 021375 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_subversion_diff.py ''' import sys import os import tempfile import shlex import pysvn import wb_shell_commands import wb_show_diff_frame import wb_read_file import wb_diff_frame import wb_platform_specific debug_diff = False # # Hold the information about each path to be compared # class PathInfoForDiff: def __init__( self ): # peg_path and peg_revision of the starting point for this path self.peg_path = None self.peg_revision = None # path and revision to be compared found starting the peg self.path = None self.revision = None # title for the diff program self.title = None def copy( self ): path_info = PathInfoForDiff() path_info.peg_path = self.peg_path path_info.peg_revision = self.peg_revision path_info.path = self.path path_info.revision = self.revision path_info.title = self.title return path_info def pegIsEqual( self, path_info ): # is either is has None in the peg fields # say it does not match if( self.peg_revision is None or self.peg_path is None ): return False if( path_info.peg_revision is None or path_info.peg_path is None ): return False return (self.peg_path == path_info.peg_path and self.peg_revision == path_info.peg_revision) def printDescription( self, title ): print '%s PathInfoForDiff' % title print ' peg_path: %r' % self.peg_path print ' peg_revision: %r' % self.peg_revision print ' path: %r' % self.path print ' revision: %r' % self.revision print ' title: %r' % self.title # # Diff two files using the mode selected by the user # # The files can be on disk or at any revision # def subversionDiffFiles( app, project_info, old_path_info, new_path_info ): # if revision is None cannot ask svn for the contents mode = app.prefs.getDiffTool().diff_tool_mode app.log.debug( 'subversionDiffFiles: mode %r' % mode ) # svn diff only works on versioned files if mode == 'svn-diff' and (old_path_info.revision is None or new_path_info.revision is None): mode = 'built-in' # Check for possible errors in advance to give the user a clear message. for path_info in (old_path_info, new_path_info): if( path_info.revision is None or path_info.revision.kind == pysvn.opt_revision_kind.working ): if not wb_platform_specific.uPathExists( path_info.path ): app.log_error( '"%s" does not exist.' % path_info.path ) fd, path_info.path = tempfile.mkstemp( suffix=os.path.basename( path_info.path ) ) os.close( fd ) # keep track of the temp file app.all_temp_files.append( path_info.path ) if wb_platform_specific.uPathIsdir( path_info.path ): app.log_error( '"%s" refers to a directory.' % path_info.path ) return if not wb_platform_specific.uAccess( path_info.path, os.R_OK ): app.log_error( '"%s" cannot be read.' % path_info.path ) return if mode == 'svn-diff': # svn will do all the work yield app.backgroundProcess diff_text = project_info.client_bg.diff( tempfile.gettempdir(), old_path_info.path, old_path_info.revision, new_path_info.path, new_path_info.revision ) yield app.foregroundProcess showDiffText( app, diff_text, old_path_info.title, new_path_info.title ) elif mode == 'external-gui-diff': yield app.backgroundProcess ok = False try: old_temp_filename = __getLocalFilename( app, project_info, old_path_info ) new_temp_filename = __getLocalFilename( app, project_info, new_path_info ) ok = True except IOError, e: app.log_error( e ) yield app.foregroundProcess if ok: wb_shell_commands.GuiDiffFiles( app, __processExternalCommandOptions( app, app.prefs.getDiffTool().gui_diff_tool_options[:], old_temp_filename, old_path_info.title, new_temp_filename, new_path_info.title ) ) elif mode == 'external-shell-diff': yield app.backgroundProcess ok = False try: old_temp_filename = __getLocalFilename( app, project_info, old_path_info ) new_temp_filename = __getLocalFilename( app, project_info, new_path_info ) diff_text = wb_shell_commands.ShellDiffFiles( app, __processExternalCommandOptions( app, app.prefs.getDiffTool().shell_diff_tool_options[:], old_temp_filename, old_path_info.title, new_temp_filename, new_path_info.title ) ) ok = True except IOError, e: app.log_error( e ) yield app.foregroundProcess if ok: showDiffText( app, diff_text, old_path_info.title, new_path_info.title ) else: ok = False yield app.backgroundProcess try: all_old_lines = __getFileContents( app, project_info, old_path_info ) all_new_lines = __getFileContents( app, project_info, new_path_info ) ok = True except IOError, e: app.log_error( e ) yield app.foregroundProcess # built-in if ok: diff_frame = wb_diff_frame.DiffFrame( app, app.frame, all_old_lines, old_path_info.title, all_new_lines, new_path_info.title ) # only show if the files could be read if diff_frame.isOk(): diff_frame.showAllFolds( False ) diff_frame.Show( True ) app.all_diff_frames.append( diff_frame ) def subversionDiffDir( app, project_info, old_path_info, new_path_info ): # if revision is None cannot ask svn for the contents mode = app.prefs.getDiffTool().diff_tool_mode app.log.debug( 'subversionDiffDir: mode %r' % mode ) # svn diff only works on versioned files if mode == 'svn-diff' and (old_path_info.revision is None or new_path_info.revision is None): mode = 'built-in' if mode == 'svn-diff' or True: # svn will do all the work yield app.backgroundProcess if debug_diff: old_path_info.printDescription( 'old_path_info' ) new_path_info.printDescription( 'new_path_info' ) try: if old_path_info.pegIsEqual( new_path_info ): if debug_diff: print 'diff_peg:' print ' url_or_path %r' % new_path_info.peg_path print ' peg_revision %r' % new_path_info.peg_revision print 'revision_start %r' % old_path_info.revision print ' revision_end %r' % new_path_info.revision print ' recurse %r' % True diff_text = project_info.client_bg.diff_peg( tmp_path=tempfile.gettempdir(), url_or_path=new_path_info.peg_path, peg_revision=new_path_info.peg_revision, revision_start=old_path_info.revision, revision_end=new_path_info.revision, recurse=True ) else: if debug_diff: print 'diff:' print ' url_or_path %r' % old_path_info.path print ' revision1 %r' % old_path_info.revision print 'url_or_path2 %r' % new_path_info.path print ' revision2 %r' % new_path_info.revision print ' recurse %r' % True diff_text = project_info.client_bg.diff( tmp_path=tempfile.gettempdir(), url_or_path =old_path_info.path, revision1=old_path_info.revision, url_or_path2=new_path_info.path, revision2=new_path_info.revision, recurse=True ) except pysvn.ClientError, e: # can get here when there are missing files in the WC print 'Error: %s' % e.args[0] return yield app.foregroundProcess showDiffText( app, diff_text, old_path_info.title, new_path_info.title ) def __processExternalCommandOptions( app, options, old_filename, old_title, new_filename, new_title ): options = shlex.split( options ) # must have the left and right files if '%nl' not in options: options.append( '%nl' ) if '%nr' not in options: options.append( '%nr' ) # quote all replacements all_replacements = {'%tl': old_title ,'%tr': new_title ,'%nl': old_filename ,'%nr': new_filename} args = [all_replacements.get( opt, opt ) for opt in options] return args # # throws IOError. # def __getFileContents( app, project_info, path_info ): all_content_lines = '' try: if path_info.revision is None or path_info.revision.kind == pysvn.opt_revision_kind.working: all_content_lines = wb_read_file.readFileContentsAsUnicode( path_info.path ).split('\n') else: if path_info.revision.kind == pysvn.opt_revision_kind.base: all_content_lines = project_info.client_bg.cat( url_or_path=path_info.path, revision=path_info.revision ) else: 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 ) else: all_content_lines = project_info.client_bg.cat( url_or_path=path_info.path, revision=path_info.revision ) all_content_lines = wb_read_file.contentsAsUnicode( all_content_lines ).split( '\n' ) except pysvn.ClientError, e: app.log_client_error( e ) return all_content_lines # # throws IOError. # def __getLocalFilename( app, project_info, path_info ): rev_description = '' all_content = '' try: if path_info.revision is None or path_info.revision.kind == pysvn.opt_revision_kind.working: return path_info.path elif path_info.revision.kind == pysvn.opt_revision_kind.base: rev_description = 'BASE' 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 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 ) except pysvn.ClientError, e: app.log_client_error( e ) # create a temp file with a name that is based on the original filename 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 ) # keep track of the temp file app.all_temp_files.append( filename ) # return name to caller return filename def showDiffText( app, text, old_title, new_title ): show_diff_frame = wb_show_diff_frame.ShowDiffFrame( app, text, old_title, new_title ) app.all_diff_frames.append( show_diff_frame ) WorkBench-1.8.2/Source/dump_wx.py000644 000765 000024 00000000123 10217035304 017152 0ustar00barrystaff000000 000000 import wx wx_names = dir(wx) wx_names.sort() for name in wx_names: print name WorkBench-1.8.2/Source/make-pot-file.sh000755 000765 000024 00000000047 12617124047 020124 0ustar00barrystaff000000 000000 #!/bin/bash ${PYTHON} make_pot_file.py WorkBench-1.8.2/Source/wb_shell_commands.py000644 000765 000024 00000001073 12575605632 021174 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_shell_commands.py ''' import sys if sys.platform == 'win32': from wb_shell_win32_commands import * elif sys.platform == 'darwin': from wb_shell_macosx_commands import * else: from wb_shell_unix_commands import * WorkBench-1.8.2/Source/make_mo_files.py000644 000765 000024 00000001154 11425265407 020301 0ustar00barrystaff000000 000000 #!/usr/bin/python import sys import os localedir = sys.argv[1] all_lang = ['en', 'de', 'hu'] for lang in all_lang: mo_output_dir = '%s/%s/LC_MESSAGES' % (localedir, lang) os.makedirs( mo_output_dir ) if lang == 'en': po_file = 'I18N/pysvn_workbench_en.current.po' else: po_file = 'I18N/pysvn_workbench_%s.po' % lang rc = os.system( 'msgfmt ' '%s ' '--check-format ' '--output-file=%s/pysvn_workbench.mo' % (po_file, mo_output_dir) ) if rc != 0: sys.exit( rc ) print 'Info: %s/pysvn_workbench.mo' % mo_output_dir sys.exit( 0 ) WorkBench-1.8.2/Source/wb.sh000755 000765 000024 00000000761 12617124047 016105 0ustar00barrystaff000000 000000 #!/bin/bash export PYSVN_WORKBENCH_STDOUT_LOG=$(tty) if [ "$PYTHONPATH" = "" ] then export PYTHONPATH=${BUILDER_TOP_DIR}/Source else export PYTHONPATH=${BUILDER_TOP_DIR}/Source:$PYTHONPATH fi PYTHON=${PYTHON:-python} BASENAME=$( basename ${PYTHON} ) SUFFIX=${BASENAME#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 WorkBench-1.8.2/Source/wb_diff_images.py000644 000765 000024 00000020343 11507143125 020426 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_diff_images.py ''' import cPickle import cStringIO import zlib import wx #---------------------------------------------------------------------- def getDownArrowData(): return cPickle.loads(zlib.decompress( 'x\xdae\xcc=\x0e\xc3 \x0c\x86\xe1=\xa7\xf8\xa4\x0etBEIPw$.\x90\x85\xb5\xca\ \x1a\x89\xdc\x7f\x8a\xcd\x8fcR\x8b\xc1\xef#\xe0}\x9cn\xda\x8c[Ag\x813\xd3o3\ \xc0\x8eW\xf0a\x0e\xbet\xe6\xfe\xd0\xc4X\xdar\xc7\xc8R:q\x7f\x17i\x00\xb6N\ \xca\xb4\x8f\x96\xf2\x9f\x11=\x8d\xe9a\x85F\xab4X#1Z;\xf1\xd4{YO\x7f\xabI\ \xfeS$\x86\x9bn\x83\x902t\xd2\x86Fl\xf6\x02\x96\x15\\\xc5' )) def getDownArrowBitmap(): return wx.BitmapFromXPMData(getDownArrowData()) def getDownArrowImage(): return wx.ImageFromBitmap(getDownArrowBitmap()) #---------------------------------------------------------------------- def getUpArrowData(): return cPickle.loads(zlib.decompress( 'x\xdae\xcd\xb1\n\x80 \x10\xc6\xf1\xbd\xa78h\xb0\xe9#\xa9\xa4]\xf0\x05\\\\\ \xa35\xb0\xf7\x9f\xf2\xd4L\xedP\x8e\xffo\xd0\xe9\xba\xe5`\x85\xdc(\x9c\x95\ \xa4\x18\x0e+\x88N\x1a\xb5\xd2\x8bV\xb1\xc1=\x871&\xb6\xe76\x86%\xb6\xe3\xde\ \xd7\xd2\x94\x06yW\x06\xa07\xe0\xc5b@\xc1\xd7\x80\x0f\xb3\x01\x15&C=\xc9*p\ \xe1v\xef9\xff\xfb\xc3\xf9\x9f\x05\xea\x8d\xa9\xb3H\xad%j,S6<\xba>L\xc6' )) def getUpArrowBitmap(): return wx.BitmapFromXPMData(getUpArrowData()) def getUpArrowImage(): return wx.ImageFromBitmap(getUpArrowBitmap()) #---------------------------------------------------------------------- def getWhiteSpaceData(): return cPickle.loads(zlib.decompress( 'x\xda\xd3\xc8)0\xe4\nV74S\x00"c\x05Cu\xae\xc4`\xf5\x08\x85d\x05\xa7\x9c\xc4\ \xe4l0O\x01\xc8Sv6s6v6\x03\xf3\xf5@|\x0b\x13\x03\x03\x0b\x13\xa8<*\xa0\xb2\ \xa0\x1e\x01A===\xb8 \x90\tu"T\x89\x02HHA\x0f\xae\x12\xc8\x89\x88\x80\xc8\ \xe9!\xab\xc4\x10\xd4\xc3\xa6\x1dn&\xdc"\xea\xfb]\x0f\x00\xb6(B\x0c' )) def getWhiteSpaceBitmap(): return wx.BitmapFromXPMData(getWhiteSpaceData()) def getWhiteSpaceImage(): return wx.ImageFromBitmap(getWhiteSpaceBitmap()) #---------------------------------------------------------------------- def getDinoData(): return cPickle.loads(zlib.decompress( 'x\xda\x85\xd2\xbbn\x830\x14\x06\xe0=Oa\t\x0cU\x8et\xc4-\xb4#4m\xc7z\xc8\xe2\ 5\x8a25\xea\xe9\xfbO\xf5\x85\x8b\xed\xd8pD\x86_\xf9\xf4\xdb\x18\xbf<\xfe\xea\ \xc3\xa5l[\xa6\x9e\xbaauy\xb8^Jd7\xf6\xfe\xb8\xde~L\x02\x95\xb2\xf146\xe3\ \xc9d\xd2\xb9o\xab\xea\xad3\xf9hsSU\x95\xc9B\xe7s\xff\xf1u\xeeM\xceu\xfe|\ \x1d\x9b)\x0f:w\xcd\xe2\xb9\xed_\xfa\xe4\x9c\'\xcfT\xfe\xa6\xdf\xbb\t\x85-\ \xeb\xeb\tgv\xf1\xbe\xee\x9a\t\xefLB!\xea\x1fn+D\x9f%\xbb\x94\xd8Uf\xcd\x1d5\ 7m\xef\x0b\xe7a\xfb+\xaehS\xed\x9d\x97"\x92ho_B\x10\x91\x94\xb4\xf5\x8e(\x98\ \x10\x0e\x8b\xab\x01H0XYD!e\x03\x12\xa0.\x93)\x85@4\x10e\x98ie\xcb\x9e\x14\ \x02(\xa6\x1et^ P\x88\x90\x9bQ\x8cs\xc5bJ\x15\xe5\xce(\xfa\xac\x96"=\xc5\xc4\ B\xe5\x17\x01/\x8a\x88\nV\xcb\xe1\x18\xe9\n\x91Q\x14\x9e*B\xc1SU\xae\xb2\x1b\ v\xd4\x82\xbc}\xe97\x9b\xfb\x00s\x88+\x98\x0f \x87\xc2C\xf1\x93\x00\xb4\xdf)\ z\xbf\xcc\x7fj\x10\xb9F\xeb%\x0c\xbf\x90\x19m8\xcd\xb7&y\xbf\xc8\xbf\xcfI%=\ \x94PR\xba\xeb%\x94\x0c\xaa\xb4\xc2\x7f\xe4\xb0\x02J' )) def getDinoBitmap(): return wx.BitmapFromXPMData(getDinoData()) def getDinoImage(): return wx.ImageFromBitmap(getDinoBitmap()) #---------------------------------------------------------------------- def getCollapseData(): return cPickle.loads(zlib.decompress( 'x\xda\xd3\xc8)0\xe4\nV74S\x00"\x13\x05Cu\xae\xc4`\xf5\x08\x85d\x05e#C#\x03\ \x0b\x130_\x0f\xc47\x00\x02(?\x1f\xc4w\x03\x030_\x01\xc8\xf7\xcb\xcfK\x85r\ \xf4@@\x01\x06\x90\x05\xf50\x05#\x10\xa2H\x82\x08Q\x14\xed0QtA=\xac\x82zH\ \x82\xf90\x00\x12\xc5+\x88U;\x04D`\xb7\x08\x9b\x93\xb0\xf9\x08\x9b\xdf\xb1\ \x85\x12r\xd0\xe9\x01\x00\xf6.O\xc0' )) def getCollapseBitmap(): return wx.BitmapFromXPMData(getCollapseData()) def getCollapseImage(): return wx.ImageFromBitmap(getCollapseBitmap()) #---------------------------------------------------------------------- def getExpandData(): return cPickle.loads(zlib.decompress( 'x\xda\xd3\xc8)0\xe4\nV74S\x00"\x13\x05Cu\xae\xc4`\xf5\x08\x85d\x05e#C#\x03\ \x0b\x130_\x0f\xc47\x00\x02(?\x1f\xc4w\x03\x030_\x01\xc8\xf7\xcb\xcfK\x85r\ \xf4@@\x01\x06\x90\x05\xf50\x05#\x10\xa2H\x82\x08Q$\xed\xf9\xf9\xf9PQtA=\xac\ \x82zH\x82\xf90\x00\x12\xc5+\x88U;D0\x02\xbbE\x98\xde\xc4t|D\x046ob\x0b\x10\ \xd4\xa0\xd3\x03\x00\x01\xa7TR' )) def getExpandBitmap(): return wx.BitmapFromXPMData(getExpandData()) def getExpandImage(): return wx.ImageFromBitmap(getExpandBitmap()) #---------------------------------------------------------------------- def getAppIconData(): return zlib.decompress( 'x\xda\x01!\x04\xde\xfb\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\ \x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\ \x08d\x88\x00\x00\x03\xd8IDATx\x9c\xed\x97\xbfk\xe3f\x18\xc7?\n\x06\x0f1\xc4\ r\xd6K\x08\xaf\n\xed\xc1\x95\x96JCq!\rT\x1a\xca\r]j\r\xe1\x86\xe0AZ\x1a\xb8L\ V\x97\x0eG\x07\xbfS\x0cI\x06{\x08\xdcp\xc39\x8b\x0bwt\xb0:\xa4\xc3\xd1!*\x94\ \x1e\xa4\x85Z\x7f\x82\x95\x0e7\x04\no\x07Y\xba8\x8e/"\xa1\xd7\xe5\x9eE\xef\ \x0bz\x9f\xef\xf7\xfd>\xbf$\xadZ\xad\xf2\x7f\xda\xc2m\x1d8\x8e\xa3,\xcbRo\ \x9d\x80eYJ\xd34\x05\x10E\xd1M\xdd\xdc\x8c\x80\xef\xfb*\x8ec\xda\xed6\xb6m\ \xdf\x18<\'\xd0\xe9\xec\xabNg\xbf\xb0\x8cRJZ\xadV\xbe\x17B\xe4\xebNg_\xd5j5e\ \x18F!\x7f\x0b\x00/^\xfc\xcc\xe9\xe9\xefX\x96\xa5\x06\x83\xc1\xb5\x07M\xd3\ \x9c\xda\x0b!\xb0,KI)\xd5\xde\xde.\x87\x87\x87x\x9eW\xe82\xa5\xcc\xa1m\xdb\ \x84I\xc2\x8fGG\xf8\xbe\xaf\xba\xdd\xaeV\xc4A\xb9\xbc\xc8\xfd\xfb_q~\xfe\x8a\ 8\x8e9\xf9\xe5\'(-qppP\x88\xc0\x02@\xab\xd5\xd2\xa4\x94x\x1f\xbd\x87\xe7y$I\ \x82\xef\xfbs\x95\xd0u}j\xbf\xb6v\x07\xd34i\x7f\xff-\x94\x968>>.\x9c\x98\xa5\ l\xd1\xef\xf7\xb58\x8e\x95\x10"\x070\x0cCmo\xef\xf0\xf0\xe17Sjd1/\x97\x17\ \x01\xa8T*\xf9\x99(\x8ah6\x9b\x8c\xc7\xe3B\nj\xf3\x1a\x91\xe38*\x03\n\xc3\ \x90\x8c\xc8`0P\x1b\x1b\x1bt\xbb]\xca\xe5E\xd6\xd6\xee\xb0\xb2\xb2\x82X]\xc6\ \xfa\xf4\x0bF\xa3Q!\xe0\xcc\xe6\x96\xe1p8\xd4\xee\xde\xfd\x90(\x8a\xd8\xde\ \xde\xc9\x93\xb4\xd9l\xd2h4r\xf0J\xa5\x02\xc0\xe3\'\xcf\xa6\xaa\xa1\xa8\xcdU\ \xe0\xa29\x8e\xa3l\xdb\xa6\\^dgg\x9b\xdd\xdd=\x00\xee\xdd\xfb \x97\xdeq\x9c\ \xc2\xb2_\xb4B\x8dh8\x1cj\xe5\xf2"[[\x0fh4\x1a@\x9ax\x00bu\x19)\xe5\x8d\xc0\ \x0b\x13\x008?\x7f\x85\x10\x82z}=\x97^\xd7u\x1e?y\xf6vZq\x10\x04S\xdd/\x93\ \xfe\xd1\xa3\xef\xf2\xd8w:\xfb\xea\xacv\xa6\xa4\x94\x85\xbbj\xe9\xfaW\xd2\ \xc1#\x84\x98J<\xb1\xba\x8c\xf3\xe5\xd7\xd8\xb6\x9d\x11S[\xa7\x0f\xa0\x0f\ \xbd\xc8\x07\x8a\x8d\xf97* \xa5T\xae\xeb\xaa$I\xa8\xd7\xd7\x81\xd75\xdf;|\ \x8a\xae\xeb\xd4\xeb\xeb\x04A@\xac\xc7\x90\xa6\x07Q\x02\xae\xeb*H\x07\xd7\ \x9b\xe6\xcc\x95U\xe0\xfb\xbe\x92R\x925%\xcf\xf3\xa6j>I\x12\\\xd7\xcd\x13OJ\ \xa9|\xdd\x87\x8bU\xd8\x03\x12RR=h\x99-\xaej\xef3\x04\xa4\x94\xca\xf7}<\xcf\ \xcb\x87\xceE\xe9u]GJI\xbd\xbe\x9ew\xc8Z\xad\xa6\xe2v\x0c\xf1\x04\xb8\x05d\ \xf3*\x01\x8e\x80\x18llNNN\xa6H\xcc\x84\xe02x\x96\xe1\x19\xb8X]fss\x93\xe7\ \xcf\x7f\xc8\xcf\x8c\xc7c\xcd\x0b=\xaa\xed\xaaV\x1dW5z\x17\xc0C J\xd7\xa1\ \x1erf\x9d)\xdf\xf7\x951\t\xd1\x0c\x01!\x04\xa6i\x12\x86!A\x10\x90$\t{{\xbb\ \xbc|\xf9\x07bu\x19JK|\xfe\xd9\xc73\xa5\xd7\xef\xf7_\xdf\xac\x91\xde\x18\x1f\ \xd0\'\x8a0Q\x02\x90\xba\xc4\xf4\xe6\x84\xc00\x0c%\x84 \x8a\xa2\xa9\xe6bY\ \x96\x1a\x0e\x87\xe9\xe6\x9f\xbf1\xde\xffdn\xf31\x0cCE\x8d(\rCoB\xc6\x9b<% @\ \x18\x8f\xc7\xda\x8c\x02\xa3\xd1H\xbb\x0c\x0e\x10\xc7q\xbe\xee\x1d>\x9d\xf9\ (\xb9\xec\xa3\x95\xb4R0{\xa2\xc0$\x0f\xe8\xa6\xefd}\xa4\xd0,\x804\xd1F\x7f\ \xfeJ\xf4\xdb_8\x8e\x83R\xaaP\xebu]W\xf5\xa2^^\xa2\x84\xe0\t/\x0fY!\x02\x96e\ \xa9l\x06\x04A\x80\xe7yW\x96\xd4u>BB\x00\xaa\'\xd5\xfcl\xe1i\x18\xc71\xba\ \xae\xcf\x94\xd1m\xadp\x08\xfe+\xbb\xf5\x9f\xd1;\x02\xef\x08\xdc\xd6\xfe\x05\ \x1f\x13\x86\xf5J2\x91\x15\x00\x00\x00\x00IEND\xaeB`\x82\xc4\x9f\xf5}' ) def getAppIconBitmap(): return wx.BitmapFromImage(getAppIconImage()) def getAppIconImage(): stream = cStringIO.StringIO(getAppIconData()) return wx.ImageFromStream(stream) def getAppIconIcon(): icon = wx.EmptyIcon() icon.CopyFromBitmap(getAppIconBitmap()) return icon WorkBench-1.8.2/Source/pylintrc000644 000765 000024 00000023337 10543315465 016733 0ustar00barrystaff000000 000000 # lint Python modules using external checkers. # # This is the main checker controling the other ones and the reports # generation. It is itself both a raw checker and an astng checker in order # to: # * handle message activation / deactivation at the module level # * handle some basic but necessary stats'data (number of classes, methods...) # # This checker also defines the following reports: # * R0001: Total errors / warnings # * R0002: % errors / warnings by module # * R0003: Messages # * R0004: Global evaluation [MASTER] # Profiled execution. profile=no # Add to the black list. It should be a base name, not a # path. You may set this option multiple times. ignore=CVS # Pickle collected data for later comparisons. persistent=yes # Set the cache size for astng objects. cache-size=500 # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. load-plugins= [REPORTS] # Tells wether to display a full report or only the messages reports=no # Use HTML as output format instead of text html=no # Use a parseable text output format, so your favorite text editor will be able # to jump to the line corresponding to a message. parseable=no # Colorizes text output using ansi escape codes color=no # Put messages in a separate file for each module / package specified on the # command line instead of printing them on stdout. Reports (if any) will be # written in a file name "pylint_global.[txt|html]". files-output=no # Python expression which should return a note less than 10 (10 is the highest # note).You have access to the variables errors warning, statement which # respectivly contain the number of errors / warnings messages and the total # number of statements analyzed. This is used by the global evaluation report # (R0004). evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) # Add a comment according to your evaluation note. This is used by the global # evaluation report (R0004). comment=no # disable: W0704 Except doesn't do anything # disable: R0201 Method could be a function # disable: W0142 Used * or ** magic # disable: W0613: Unused argument 'xxx' # disable: W0703: Catch "Exception" disable-msg=W0704,R0201,W0142,W0613,W0703 # Include message's id in output include-ids=yes # checks for : # * doc strings # * modules / classes / functions / methods / arguments / variables name # * number of arguments, local variables, branchs, returns and statements in # functions, methods # * required module attributes # * dangerous default values as arguments # * redefinition of function / method / class # * uses of the global statement # # This checker also defines the following reports: # * R0101: Statistics by type [BASIC] # Regular expression which should only match correct module names module-rgx=(([a-z][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ # Regular expression which should only match correct class names class-rgx=[A-Z][a-zA-Z0-9]+$ # Regular expression which should only match correct function names function-rgx=[a-z_][a-zA-Z0-9]*|__pychecker$ # Regular expression which should only match correct method names method-rgx=(__)?[a-zA-Z0-9][a-zA-Z0-9_]*$ # Regular expression which should only match correct argument names argument-rgx=[a-z][a-z0-9_]*$ # Regular expression which should only match correct variable names variable-rgx=[a-z][a-z0-9_]*$|__pychecker$ # Enable / disable this checker enable-basic=yes # Required attributes for module, separated by a comma required-attributes= # Regular expression which should only match functions or classes name which do # not require a docstring no-docstring-rgx=.* # Regular expression which should only match correct module level names const-rgx=(([A-Z_][A-Z0-9_]*)|([a-z_][a-z0-9_]*)|(__[a-z0-9]*__))$ # Regular expression which should only match correct instance attribute names attr-rgx=[a-z_][a-z0-9_]*$ # Regular expression which should only match correct list comprehension / # generator expression variable names inlinevar-rgx=[a-z][a-z0-9_]*$ # Good variable names which should always be accepted, separated by a comma good-names=_,event,item # Bad variable names which should always be refused, separated by a comma bad-names= # List of builtins function names that should not be used, separated by a comma bad-functions=map,filter,apply,input # try to find bugs in the code using type inference # [TYPECHECK] # Enable / disable this checker enable-typecheck=yes # Tells wether missing members accessed in mixin class should be ignored. A # mixin class is detected if its name ends with "mixin" (case insensitive). ignore-mixin-members=yes # When zope mode is activated, consider the acquired-members option to ignore # access to some undefined attributes. zope=no # List of members which are usually get through zope's acquisition mecanism and # so shouldn't trigger E0201 when accessed (need zope=yes to be considered. acquired-members=REQUEST,acl_users,aq_parent # checks for # * unused variables / imports # * undefined variables # * redefinition of variable from builtins or from an outer scope # * use of variable before assigment # [VARIABLES] # Enable / disable this checker enable-variables=yes # Tells wether we should check for unused import in __init__ files. init-import=no # A regular expression matching names used for dummy variables (i.e. not used). dummy-variables-rgx=_|dummy # List of additional names supposed to be defined in builtins. Remember that # you should avoid to define new builtins when possible. additional-builtins= # checks for sign of poor/misdesign: # * number of methods, attributes, local variables... # * size, complexity of functions, methods # [DESIGN] # Enable / disable this checker enable-design=no # Maximum number of arguments for function / method max-args=5 # Maximum number of locals for function / method body max-locals=15 # Maximum number of return / yield for function / method body max-returns=6 # Maximum number of branch for function / method body max-branchs=12 # Maximum number of statements in function / method body max-statements=50 # Maximum number of parents for a class (see R0901). max-parents=7 # Maximum number of attributes for a class (see R0902). max-attributes=7 # Minimum number of public methods for a class (see R0903). min-public-methods=1 # Maximum number of public methods for a class (see R0904). max-public-methods=200 # checks for : # * methods without self as first argument # * overriden methods signature # * access only to existant members via self # * attributes not defined in the __init__ method # * supported interfaces implementation # * unreachable code # [CLASSES] # Enable / disable this checker enable-classes=yes # List of interface methods to ignore, separated by a comma. This is used for # instance to not check methods defines in Zope's Interface base class. ignore-iface-methods= # List of method names used to declare (i.e. assign) instance attributes. defining-attr-methods=__init__,initControls,setupLogging,initExtraButtons # checks for # * external modules dependencies # * relative / wildcard imports # * cyclic imports # * uses of deprecated modules # # This checker also defines the following reports: # * R0401: External dependencies # * R0402: Modules dependencies graph [IMPORTS] # Enable / disable this checker enable-imports=yes # Deprecated modules which should not be used, separated by a comma deprecated-modules=regsub,string,TERMIOS,Bastion,rexec # Create a graph of every (i.e. internal and external) dependencies in the # given file (report R0402 must not be disabled) import-graph= # Create a graph of external dependencies in the given file (report R0402 must # not be disabled) ext-import-graph= # Create a graph of internal dependencies in the given file (report R0402 must # not be disabled) int-import-graph= # checks for usage of new style capabilities on old style classes and # other new/old styles conflicts problems # * use of property, __slots__, super # * "super" usage # * raising a new style class as exception # [NEWSTYLE] # Enable / disable this checker enable-newstyle=yes # checks for # * excepts without exception filter # * string exceptions # [EXCEPTIONS] # Enable / disable this checker enable-exceptions=yes # checks for similarities and duplicated code. This computation may be # memory / CPU intensive, so you should disable it if you experiments some # problems. # # This checker also defines the following reports: # * R0801: Duplication [SIMILARITIES] # Enable / disable this checker enable-similarities=no # Minimum lines number of a similarity. min-similarity-lines=4 # Ignore comments when computing similarities. ignore-comments=yes # Ignore docstrings when computing similarities. ignore-docstrings=yes # checks for : # * unauthorized constructions # * strict indentation # * line length # * use of <> instead of != # [FORMAT] # Enable / disable this checker enable-format=yes # Maximum number of characters on a single line. max-line-length=200 # Maximum number of lines in a module max-module-lines=10000 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 # tab). indent-string=' ' # checks for: # * warning notes in the code like FIXME, XXX # * PEP 263: source code with non ascii character but no encoding declaration # [MISCELLANEOUS] # Enable / disable this checker enable-miscellaneous=yes # List of note tags to take in consideration, separated by a comma. Default to # FIXME, XXX, TODO notes= # does not check anything but gives some raw metrics : # * total number of lines # * total number of code lines # * total number of docstring lines # * total number of comments lines # * total number of empty lines # # This checker also defines the following reports: # * R0701: Raw metrics [METRICS] # Enable / disable this checker enable-metrics=no WorkBench-1.8.2/Source/run_diff.cmd000644 000765 000024 00000000034 10007315155 017401 0ustar00barrystaff000000 000000 python wb_diff_main.py %* WorkBench-1.8.2/Source/wb.png000644 000765 000024 00000003523 10370001161 016234 0ustar00barrystaff000000 000000 PNG  IHDR szz pHYs  tIMEªIDATX]PSIrHBHAѵeP8S3 ?*:]7Uh/ZL՝i[K\KCl$"쮺(fA)8ۋi{̙ϼ?4O<oxÇoߞ#pfn)65@n񫍍a/6mڔe TNV lק#JzLlzK ^, Sj]QQ=vhoob M鴏 FX,rҥT*l؆ܙb-lnۻwo^^^ׯ_СCã_` p8H5C;v{,WNSΝn[x<z>O 6a4d(B\z*++Bb,4; R0DQ & N`h4ٹswޙx(煒ܪ*F㬞׭[dR0@e \x}E{{{_B|T3ǯ8`V^0/f…R)oO[ˀiF x,v qqQSS#f199U uuu1I^\ـvf4L6Nƍr8 ";iϷ'J'H:ίnܸQ:u C%C`&UTN`Yx%˻D_Dģ %Ee-+@Y _:ۜNR x]lфm&$Ր Ƞ@$GoƬiOx),un^}}D͒%Kx,5FÅ [obXJ> ttF vE_NEI;𭊊&sMM$ 2EEE̚e?~X R&MRH6x_y>ሟHӶ{nfCVXdYFד(455}>5\l=F5 $f@bYjt5BuшF`Emm+Vnݺř3gbf, YYYȲ̖-[n{W:~=2 禓+s4BJ25(QejLQՎ'xgrx /˲-.] mhhz=&`7ɪHIRAu ҠRQ$K7RxH}:7'ɤ]K$vNG"@p!hy>PXy*G$ЀJ|D^K{#׻o߇nN$~_GäIx23A.O$iEɓ'࠸r动ln=Wh_+rDS@LeY~v#GGjw_ Q& (Hi$~4DoK vf]t?M#߫6ijۀLɐ'Szh^r''>5 |epjk&IENDB`WorkBench-1.8.2/Source/wb_frame.py000644 000765 000024 00000167445 12672507133 017311 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2011 Barry A Scott. All rights reserved. This software is licensed as described in the file LICENSE.txt, which you should have received as part of this distribution. ==================================================================== wb_frame.py ''' import sys import wx import wx.stc import logging import wb_tree_panel import wb_list_panel import wb_list_panel_common import wb_ids import wb_exceptions import wb_version import wb_images import wb_preferences_dialog import wb_source_control_providers import wb_platform_specific import wb_bookmarks_dialogs import wb_toolbars import wb_subversion_utils import wb_config class WbFrame(wx.Frame): status_general = 0 status_search = 0 # use the general area status_progress = 1 status_action = 2 status_num_fields = 3 status_widths = [-1, 150, -2] def __init__( self, app ): self.app = app title = T_('PySVN WorkBench') win_prefs = self.app.prefs.getWindow() extra_style = 0 if win_prefs.maximized: extra_style = wx.MAXIMIZE wx.Frame.__init__(self, None, -1, title, win_prefs.frame_position, win_prefs.getFrameSize(), wx.DEFAULT_FRAME_STYLE|extra_style ) # Reset the size after startup to workaround a potential # problem on OSX with incorrect first size event saving the # wrong size in the preferences wx.CallAfter( self.SetSize, win_prefs.getFrameSize() ) self.menu_edit = wx.Menu() self.menu_edit.Append( wb_ids.id_SP_EditCopy, T_("&Copy"), T_("Copy Files") ) self.menu_edit.Append( wb_ids.id_SP_EditCut, T_("&Cut"), T_("Cut Files") ) self.menu_edit.Append( wb_ids.id_SP_EditPaste, T_("&Paste"), T_("Paste Files") ) self.menu_edit.AppendSeparator() self.menu_edit.Append( wb_ids.id_ClearLog, T_("&Clear log"), T_("Clear the log window") ) if wx.Platform != '__WXMAC__': self.menu_file = wx.Menu() else: self.menu_file = self.menu_edit self.menu_file.Append( wx.ID_PREFERENCES, T_("&Preferences..."), T_("Preferences") ) self.menu_file.Append( wx.ID_EXIT, T_("E&xit"), T_("Exit the application") ) self.menu_actions = wx.Menu() self.menu_actions.Append( wb_ids.id_Command_Shell, T_('&Command Shell'), T_('Command Shell') ) self.menu_actions.Append( wb_ids.id_File_Browser, T_('&File Browser'), T_('File Browser') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_File_Edit, T_('Edit'), T_('Edit') ) self.menu_actions.Append( wb_ids.id_Shell_Open, T_('Open'), T_('Open') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SP_DiffWorkBase, T_('Diff WC vs. BASE...'), T_('Diff WC vs. BASE...') ) self.menu_actions.Append( wb_ids.id_SP_DiffWorkHead, T_('Diff WC vs. HEAD...'), T_('Diff WC vs. HEAD...') ) self.menu_actions.Append( wb_ids.id_SP_DiffWorkBranchOriginBase, T_('Diff WC vs. branch origin BASE...'), T_('Diff WC vs. branch origin BASE...') ) self.menu_actions.Append( wb_ids.id_SP_DiffWorkBranchOriginHead, T_('Diff WC vs. branch origin HEAD...'), T_('Diff WC vs. branch origin HEAD...') ) self.menu_actions_conflict = wx.Menu() self.menu_actions.AppendMenu( wb_ids.id_SP_ConflictMenu, T_('Conflict'), self.menu_actions_conflict ) self.menu_actions_conflict.Append( wb_ids.id_SP_DiffOldMine, T_('Diff Conflict Old vs. Mine...'), T_('Diff Conflict Old vs. Mine...') ) self.menu_actions_conflict.Append( wb_ids.id_SP_DiffMineNew, T_('Diff Conflict Mine vs. New...'), T_('Diff Conflict Mine vs. New...') ) self.menu_actions_conflict.Append( wb_ids.id_SP_DiffOldNew, T_('Diff Conflict Old vs. New...'), T_('Diff Conflict Old vs. New...') ) self.menu_actions_conflict.AppendSeparator() self.menu_actions_conflict.Append( wb_ids.id_SP_Resolved, T_('Resolved Conflict'), T_('Resolved Conflict') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SP_Annotate, T_('Annotate...'), T_('Annotate...') ) self.menu_actions.Append( wb_ids.id_SP_History, T_('Log history...'), T_('Log history...') ) self.menu_actions.Append( wb_ids.id_SP_Info, T_('Information...'), T_('Information...') ) self.menu_actions.Append( wb_ids.id_SP_Properties, T_('Properties...'), T_('Properties...') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SP_Update, T_('Update'), T_('Update') ) self.menu_actions.Append( wb_ids.id_SP_UpdateTo, T_('Update to...'), T_('Update to...') ) self.menu_actions.Append( wb_ids.id_SP_Checkout, T_('Checkout'), T_('Checkout') ) self.menu_actions.Append( wb_ids.id_SP_CheckoutTo, T_('Checkout to...'), T_('Checkout to...') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SP_Checkin, T_('Checkin...'), T_('Checkin...') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SP_Lock, T_('Lock...'), T_('Lock...') ) self.menu_actions.Append( wb_ids.id_SP_Unlock, T_('Unlock...'), T_('Unlock...') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SP_NewFile, T_('New File...'), T_('New File...') ) self.menu_actions.Append( wb_ids.id_SP_Mkdir, T_('Make directory...'), T_('Make directory...') ) self.menu_actions.Append( wb_ids.id_SP_Add, T_('Add'), T_('Add') ) self.menu_actions.Append( wb_ids.id_SP_Rename, T_('Rename...'), T_('Rename') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SP_Delete, T_('Delete...'), T_('Delete') ) self.menu_actions.Append( wb_ids.id_SP_Revert, T_('Revert...'), T_('Revert') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SP_Cleanup, T_('Clean up'), T_('Clean up working copy') ) if wb_subversion_utils.version_info.has_upgrade: self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SP_Upgrade, T_('Upgrade'), T_('Upgrade working copy') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SP_CreateTag, T_('Create Tag...'), T_('Create Tag') ) self.menu_actions.Append( wb_ids.id_SP_CreateBranch, T_('Create Branch...'), T_('Create Branch') ) self.menu_reports = wx.Menu() self.menu_reports.Append( wb_ids.id_SP_Report_LocksWc, T_('Working copy Locks...'), T_('Locks held in Working Copy') ) self.menu_reports.Append( wb_ids.id_SP_Report_LocksRepos, T_('Repository Locks...'), T_('Locks held in Repository') ) self.menu_reports.AppendSeparator() self.menu_reports.Append( wb_ids.id_SP_Checkin, T_('Changes...'), T_('Changes available for checkin') ) self.menu_reports.Append( wb_ids.id_SP_Report_Updates, T_('Updates...'), T_('Updates available in the Repository') ) self.menu_reports.Append( wb_ids.id_SP_Report_BranchChanges, T_('Branch changes...'), T_('Files changed in this branch') ) self.menu_view = wx.Menu() self.menu_view.AppendCheckItem( wb_ids.id_View_ControlledFiles, T_("Show &Controlled files"), T_("Show Controlled files") ) self.menu_view.AppendCheckItem( wb_ids.id_View_UncontrolledFiles, T_("Show &Uncontrolled files"), T_("Show Uncontrolled files") ) self.menu_view.AppendCheckItem( wb_ids.id_View_IgnoredFiles, T_("Show &Ignored files"), T_("Show ignored files") ) self.menu_view.AppendCheckItem( wb_ids.id_View_OnlyChanges, T_("Show &Only changed files"), T_("Filter out unchanged files") ) self.menu_view.AppendSeparator() self.menu_view.AppendCheckItem( wb_ids.id_View_Recursive, T_("Show &Recursive files"), T_("Show recursive files") ) self.menu_view.AppendSeparator() self.menu_view.AppendCheckItem( wb_ids.id_View_Diff_WbDiff, T_('Use WorkBench Diff') ) self.menu_view.AppendCheckItem( wb_ids.id_View_Diff_ExtGuiDiff, T_('Use External GUI Diff') ) self.menu_view.AppendCheckItem( wb_ids.id_View_Diff_ExtTextDiff, T_('Use External Text Diff') ) self.menu_view.AppendCheckItem( wb_ids.id_View_Diff_SvnDiff, T_('Use SVN Diff') ) self.menu_view.AppendSeparator() self.menu_view.Append( wb_ids.id_View_Refresh, T_("&Refresh\tF5"), T_("Refresh display") ) self.menu_view.AppendCheckItem( wb_ids.id_View_AutoRefresh, T_("&Automatic Refresh"), T_("Automatic refresh") ) self.bookmark_menu_tree = BookmarkMenu('') self.all_bookmarks_id_to_pi = {} self.all_bookmark_top_level_menu_ids = [] self.all_bookmark_folders = {} self.all_bookmark_folders2 = {} self.menu_bookmarks = wx.Menu() self.menu_bookmarks.Append( wb_ids.id_Bookmark_Add, T_('Add'), T_('Add Bookmark') ) self.menu_bookmarks.Append( wb_ids.id_Bookmark_Manage, T_('Manage...'), T_('Manage Bookmarks') ) self.menu_bookmarks.AppendSeparator() self.__bookmarkMenuReorder() self.menu_project = wx.Menu() self.menu_project.Append( wb_ids.id_Project_Add, T_('Add...'), T_('Project Add') ) self.menu_project.Append( wb_ids.id_Project_Update, T_('Settings...'), T_('Project Settings') ) self.menu_project.AppendSeparator() self.menu_project.Append( wb_ids.id_Project_Delete, T_('Delete...'), T_('Delete Project') ) self.menu_help = wx.Menu() self.menu_help.Append( wx.ID_ABOUT, T_("&About..."), T_("About the application") ) self.menu_bar = wx.MenuBar() if wx.Platform != '__WXMAC__': self.menu_bar.Append( self.menu_file, T_("&File") ) self.menu_bar.Append( self.menu_edit, T_("&Edit") ) self.menu_bar.Append( self.menu_view, T_("&View") ) self.menu_bar.Append( self.menu_actions, T_("&Actions") ) self.menu_bar.Append( self.menu_reports, T_("&Reports") ) self.menu_bar.Append( self.menu_bookmarks, T_("&Bookmarks") ) self.menu_bar.Append( self.menu_project, T_("&Project") ) self.menu_bar.Append( self.menu_help, T_("&Help") ) self.SetMenuBar( self.menu_bar ) # Set the application icon self.SetIcon( wb_images.getIcon( 'wb.png') ) # Initialize tool bar # Remark: The order of the groups and their activity (enabled/disabled) # is managed in the preferences... self.compileToolBar() # Add the status bar s = self.CreateStatusBar() s.SetFieldsCount( WbFrame.status_num_fields ) s.SetStatusWidths( WbFrame.status_widths ) s.SetStatusText( T_("Work Bench"), WbFrame.status_general ) s.SetStatusText( "", WbFrame.status_progress ) s.SetStatusText( T_("Ready"), WbFrame.status_action ) if WbFrame.status_search != WbFrame.status_general: s.SetStatusText( "", WbFrame.status_search ) # Create the splitter windows if 'wxMac' in wx.PlatformInfo: style = wx.SP_LIVE_UPDATE | wx.SP_3DSASH else: style = wx.SP_LIVE_UPDATE self.h_split = wx.SplitterWindow( self, -1, style=style ) self.v_split = wx.SplitterWindow( self.h_split, -1, style=style ) # Make sure the splitters can't be removed by setting a minimum size self.v_split.SetMinimumPaneSize( 100 ) self.h_split.SetMinimumPaneSize( 100 ) # Create the main panels self.log_panel = LogCtrlPanel( self.app, self.h_split ) self.log_panel.SetZoom( win_prefs.zoom ) self.list_panel = wb_list_panel.WbListPanel( self.app, self, self.v_split ) self.tree_panel = wb_tree_panel.WbTreePanel( self.app, self, self.v_split ) # Fixup the tab order that results from creating the tree and # list panels in the wrong order self.list_panel.MoveAfterInTabOrder( self.tree_panel ) try_wrapper = wb_exceptions.TryWrapperFactory( self.app.log ) size = self.GetClientSize() h_sash_pos = max( 200, int( size.height * win_prefs.h_sash_ratio) ) v_sash_pos = max( 200, int( size.width * win_prefs.v_sash_ratio) ) # Arrange the panels with the splitter windows self.v_split.SplitVertically( self.tree_panel, self.list_panel, v_sash_pos ) self.h_split.SplitHorizontally( self.v_split, self.log_panel, h_sash_pos ) # for some unknown reason MENU events get blocked by tree and list controls for event_source in [self, self.tree_panel.tree_ctrl, self.list_panel.list_ctrl]: # Set up the event handlers wx.EVT_MENU( event_source, wx.ID_ABOUT, try_wrapper( self.OnCmdAbout ) ) wx.EVT_MENU( event_source, wx.ID_PREFERENCES, try_wrapper( self.OnCmdPreferences ) ) wx.EVT_MENU( event_source, wx.ID_EXIT, try_wrapper( self.OnCmdExit ) ) wx.EVT_MENU( event_source, wb_ids.id_ClearLog, try_wrapper( self.OnCmdClearLog ) ) wx.EVT_MENU( event_source, wb_ids.id_View_Refresh, try_wrapper( self.OnRefresh ) ) wx.EVT_MENU( event_source, wb_ids.id_View_AutoRefresh, try_wrapper( self.OnToggleAutoRefresh ) ) wx.EVT_UPDATE_UI( event_source, wb_ids.id_View_AutoRefresh, try_wrapper( self.OnUpdateAutoRefresh ) ) wx.EVT_MENU( event_source, wb_ids.id_View_ControlledFiles, try_wrapper( self.OnToggleViewControlled ) ) wx.EVT_UPDATE_UI( event_source, wb_ids.id_View_ControlledFiles, try_wrapper( self.OnUpdateViewControlled ) ) wx.EVT_MENU( event_source, wb_ids.id_View_UncontrolledFiles, try_wrapper( self.OnToggleViewUncontrolled ) ) wx.EVT_UPDATE_UI( event_source, wb_ids.id_View_UncontrolledFiles, try_wrapper( self.OnUpdateViewUncontrolled ) ) wx.EVT_MENU( event_source, wb_ids.id_View_IgnoredFiles, try_wrapper( self.OnToggleViewIgnored ) ) wx.EVT_UPDATE_UI( event_source, wb_ids.id_View_IgnoredFiles, try_wrapper( self.OnUpdateViewIgnored ) ) wx.EVT_MENU( event_source, wb_ids.id_View_Recursive, try_wrapper( self.OnToggleViewRecursive ) ) wx.EVT_UPDATE_UI( event_source, wb_ids.id_View_Recursive, try_wrapper( self.OnUpdateViewRecursive ) ) wx.EVT_MENU( event_source, wb_ids.id_View_OnlyChanges, try_wrapper( self.OnToggleViewOnlyChanges ) ) wx.EVT_UPDATE_UI( event_source, wb_ids.id_View_OnlyChanges, try_wrapper( self.OnUpdateViewOnlyChanges ) ) wx.EVT_MENU( event_source, wb_ids.id_SP_EditCopy, self.app.eventWrapper( self.OnSpEditCopy ) ) wx.EVT_UPDATE_UI( event_source, wb_ids.id_SP_EditCopy, self.app.eventUpdateWrapper( self.OnUpdateUiSpEditCopy ) ) wx.EVT_MENU( event_source, wb_ids.id_SP_EditCut, self.app.eventWrapper( self.OnSpEditCut ) ) wx.EVT_UPDATE_UI( event_source, wb_ids.id_SP_EditCut, self.app.eventUpdateWrapper( self.OnUpdateUiSpEditCut ) ) wx.EVT_MENU( event_source, wb_ids.id_SP_EditPaste, self.app.eventWrapper( self.OnSpEditPaste ) ) wx.EVT_UPDATE_UI( event_source, wb_ids.id_SP_EditPaste, self.app.eventUpdateWrapper( 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.eventUpdateWrapper( 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.eventUpdateWrapper( 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.eventUpdateWrapper( 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.eventUpdateWrapper( 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 ) ) wx.EVT_MENU( self, wb_ids.id_Shell_Open, try_wrapper( self.OnShellOpen ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_Shell_Open, try_wrapper( self.OnUpdateUiShellOpen ) ) wx.EVT_MENU( self, wb_ids.id_Command_Shell, try_wrapper( self.OnCommandShell ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_Command_Shell, try_wrapper( self.OnUpdateUiCommandShell ) ) wx.EVT_MENU( self, wb_ids.id_File_Browser, try_wrapper( self.OnFileBrowser ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_File_Browser, try_wrapper( self.OnUpdateUiFileBrowser ) ) wx.EVT_MENU( self, wb_ids.id_SP_Add, self.app.eventWrapper( self.OnSpAdd ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Add, self.app.eventUpdateWrapper( self.OnUpdateUiSpAdd ) ) wx.EVT_MENU( self, wb_ids.id_SP_Annotate, self.app.eventWrapper( self.OnSpAnnotate ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Annotate, self.app.eventUpdateWrapper( self.OnUpdateUiSpAnnotate ) ) wx.EVT_MENU( self, wb_ids.id_SP_Checkin, self.app.eventWrapper( self.OnSpCheckin ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Checkin, self.app.eventUpdateWrapper( self.OnUpdateUiSpCheckin ) ) wx.EVT_MENU( self, wb_ids.id_SP_Checkout, self.app.eventWrapper( self.OnSpCheckout ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Checkout, self.app.eventUpdateWrapper( self.OnUpdateUiSpCheckout ) ) wx.EVT_MENU( self, wb_ids.id_SP_CheckoutTo, self.app.eventWrapper( self.OnSpCheckoutTo ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_CheckoutTo, self.app.eventUpdateWrapper( self.OnUpdateUiSpCheckoutTo ) ) wx.EVT_MENU( self, wb_ids.id_SP_Cleanup, self.app.eventWrapper( self.OnSpCleanup ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Cleanup, self.app.eventUpdateWrapper( self.OnUpdateUiSpCleanup ) ) wx.EVT_MENU( self, wb_ids.id_SP_CreateTag, self.app.eventWrapper( self.OnSpCreateTag ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_CreateTag, self.app.eventUpdateWrapper( self.OnUpdateUiSpCreateTag ) ) wx.EVT_MENU( self, wb_ids.id_SP_CreateBranch, self.app.eventWrapper( self.OnSpCreateBranch ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_CreateBranch, self.app.eventUpdateWrapper( self.OnUpdateUiSpCreateBranch ) ) wx.EVT_MENU( self, wb_ids.id_SP_Delete, self.app.eventWrapper( self.OnSpDelete ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Delete, self.app.eventUpdateWrapper( self.OnUpdateUiSpDelete ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffMineNew, self.app.eventWrapper( self.OnSpDiffMineNew ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_DiffMineNew, self.app.eventUpdateWrapper( self.OnUpdateUiSpDiffMineNew ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffOldMine, self.app.eventWrapper( self.OnSpDiffOldMine ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_DiffOldMine, self.app.eventUpdateWrapper( self.OnUpdateUiSpDiffOldMine ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffOldNew, self.app.eventWrapper( self.OnSpDiffOldNew ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_DiffOldNew, self.app.eventUpdateWrapper( self.OnUpdateUiSpDiffOldNew ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkBase, self.app.eventWrapper( self.OnSpDiffWorkBase ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_DiffWorkBase, self.app.eventUpdateWrapper( self.OnUpdateUiSpDiffWorkBase ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkHead, self.app.eventWrapper( self.OnSpDiffWorkHead ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_DiffWorkHead, self.app.eventUpdateWrapper( self.OnUpdateUiSpDiffWorkHead ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkBranchOriginBase, self.app.eventWrapper( self.OnSpDiffWorkBranchOriginBase ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_DiffWorkBranchOriginBase, self.app.eventUpdateWrapper( self.OnUpdateUiSpDiffWorkBranchOriginBase ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkBranchOriginHead, self.app.eventWrapper( self.OnSpDiffWorkBranchOriginHead ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_DiffWorkBranchOriginHead, self.app.eventUpdateWrapper( self.OnUpdateUiSpDiffWorkBranchOriginHead ) ) wx.EVT_MENU( self, wb_ids.id_SP_History, self.app.eventWrapper( self.OnSpHistory ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_History, self.app.eventUpdateWrapper( self.OnUpdateUiSpHistory ) ) wx.EVT_MENU( self, wb_ids.id_SP_Info, self.app.eventWrapper( self.OnSpInfo ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Info, self.app.eventUpdateWrapper( self.OnUpdateUiSpInfo ) ) wx.EVT_MENU( self, wb_ids.id_SP_Lock, self.app.eventWrapper( self.OnSpLock ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Lock, self.app.eventUpdateWrapper( self.OnUpdateUiSpLock ) ) wx.EVT_MENU( self, wb_ids.id_SP_NewFile, self.app.eventWrapper( self.OnSpNewFile ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_NewFile, self.app.eventUpdateWrapper( self.OnUpdateUiSpNewFile ) ) wx.EVT_MENU( self, wb_ids.id_SP_Mkdir, self.app.eventWrapper( self.OnSpMkdir ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Mkdir, self.app.eventUpdateWrapper( self.OnUpdateUiSpMkdir ) ) wx.EVT_MENU( self, wb_ids.id_SP_Properties, self.app.eventWrapper( self.OnSpProperties ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Properties, self.app.eventUpdateWrapper( self.OnUpdateUiSpProperties ) ) wx.EVT_MENU( self, wb_ids.id_SP_Rename, self.app.eventWrapper( self.OnSpRename ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Rename, self.app.eventUpdateWrapper( self.OnUpdateUiSpRename ) ) wx.EVT_MENU( self, wb_ids.id_SP_Resolved, self.app.eventWrapper( self.OnSpResolved ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Resolved, self.app.eventUpdateWrapper( self.OnUpdateUiSpResolved ) ) wx.EVT_MENU( self, wb_ids.id_SP_Revert, self.app.eventWrapper( self.OnSpRevert ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Revert, self.app.eventUpdateWrapper( self.OnUpdateUiSpRevert ) ) wx.EVT_MENU( self, wb_ids.id_SP_Unlock, self.app.eventWrapper( self.OnSpUnlock ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Unlock, self.app.eventUpdateWrapper( self.OnUpdateUiSpUnlock ) ) wx.EVT_MENU( self, wb_ids.id_SP_Update, self.app.eventWrapper( self.OnSpUpdate ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Update, self.app.eventUpdateWrapper( self.OnUpdateUiSpUpdate ) ) wx.EVT_MENU( self, wb_ids.id_SP_UpdateTo, self.app.eventWrapper( self.OnSpUpdateTo ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_UpdateTo, self.app.eventUpdateWrapper( self.OnUpdateUiSpUpdateTo ) ) if wb_subversion_utils.version_info.has_upgrade: wx.EVT_MENU( self, wb_ids.id_SP_Upgrade, self.app.eventWrapper( self.OnSpUpgrade ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Upgrade, self.app.eventUpdateWrapper( self.OnUpdateUiSpUpgrade ) ) wx.EVT_MENU( self, wb_ids.id_SP_Report_Updates, self.app.eventWrapper( self.OnSpReportUpdates ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Report_Updates, self.app.eventUpdateWrapper( self.OnUpdateUiSpReportUpdates ) ) wx.EVT_MENU( self, wb_ids.id_SP_Report_LocksWc, self.app.eventWrapper( self.OnSpReportLocksWc ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Report_LocksWc, self.app.eventUpdateWrapper( self.OnUpdateUiSpReportLocksWc ) ) wx.EVT_MENU( self, wb_ids.id_SP_Report_LocksRepos, self.app.eventWrapper( self.OnSpReportLocksRepos ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Report_LocksRepos, self.app.eventUpdateWrapper( self.OnUpdateUiSpReportLocksRepos ) ) wx.EVT_MENU( self, wb_ids.id_SP_Report_BranchChanges, self.app.eventWrapper( self.OnSpReportBranchChanges ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_SP_Report_BranchChanges, self.app.eventUpdateWrapper( self.OnUpdateUiSpReportBranchChanges ) ) wx.EVT_MENU( self, wb_ids.id_Project_Add, try_wrapper( self.app.eventWrapper( self.tree_panel.OnProjectAdd ) ) ) wx.EVT_MENU( self, wb_ids.id_Project_Update, try_wrapper( self.app.eventWrapper( self.tree_panel.OnProjectUpdate ) ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_Project_Update, try_wrapper( self.app.eventUpdateWrapper( self.OnUpdateUiProjectUpdateOrDelete ) ) ) wx.EVT_MENU( self, wb_ids.id_Project_Delete, try_wrapper( self.app.eventWrapper( self.tree_panel.OnProjectDelete ) ) ) wx.EVT_UPDATE_UI( self, wb_ids.id_Project_Delete, try_wrapper( self.app.eventUpdateWrapper( self.OnUpdateUiProjectUpdateOrDelete ) ) ) wx.EVT_MENU( self, wb_ids.id_Bookmark_Add, try_wrapper( self.OnBookmarkAdd ) ) wx.EVT_MENU( self, wb_ids.id_Bookmark_Manage, try_wrapper( self.OnBookmarkManage ) ) wx.EVT_SIZE( self, self.OnSize ) wx.EVT_MOVE( self, self.OnMove ) wx.EVT_SIZE( self.v_split, self.OnVertSize ) wx.EVT_SIZE( self.h_split, self.OnHorizSize ) wx.EVT_SPLITTER_SASH_POS_CHANGED( self.v_split, -1, self.OnVertSashPositionChanged ) wx.EVT_SPLITTER_SASH_POS_CHANGED( self.h_split, -1, self.OnHorizSashPositionChanged ) wx.stc.EVT_STC_ZOOM(self, -1, self.OnZoom) wx.EVT_CLOSE(self, try_wrapper( self.OnCloseWindow )) # default to the tree panel as the first set_focus can go missing self.event_handler = None self.ui_state_tree = None self.ui_state_list = None self.ui_state_focus = None self.setEventHandler( self.tree_panel ) # need to set focus away from the filter controls # should not have to reach through these levels self.list_panel.list_ctrl.SetFocus() def compileToolBar( self ): # # Create the toolbar, if it is enabled. # toolbar_prefs = self.app.prefs.getToolbar() if not toolbar_prefs.toolbar_enable: return if toolbar_prefs.horizontal_orientation: tb_style = wx.TB_HORIZONTAL else: tb_style = wx.TB_VERTICAL toolbar = self.CreateToolBar( name="main", style=tb_style ) toolbar.SetToolBitmapSize( (toolbar_prefs.bitmap_size, toolbar_prefs.bitmap_size) ) wb_toolbars.toolbar_main.populateToolBar( toolbar_prefs, toolbar ) toolbar.Realize() def setEventHandler( self, 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( ' ' ) if self.event_handler is not handler: self.event_handler = handler self.clearUpdateUiState() self.app.log.debug( 'self.ui_state_focus %r' % (self.ui_state_focus,) ) 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 ) def setProgress( self, text ): self.GetStatusBar().SetStatusText( text, WbFrame.status_progress ) def setAction( self, text ): self.GetStatusBar().SetStatusText( text, WbFrame.status_action ) def setSearch( self, text ): self.GetStatusBar().SetStatusText( text, WbFrame.status_search ) def savePreferences( self ): win_prefs = self.app.prefs.getWindow() # Size and Position are already saved win_prefs.maximized = self.IsMaximized() self.tree_panel.savePreferences() self.list_panel.savePreferences() # Handler for the Exit menu command def OnCmdExit(self, event): self.Close() # Handler for the About menu command def OnCmdAbout(self, event): ver_str = ('%d.%d.%d-%d\n' % (wb_version.major, wb_version.minor, wb_version.patch, wb_version.build)) str_message_parts = [T_('Work Bench version: %s') % ver_str ,wb_source_control_providers.getProviderAboutStrings() ,'wxPython %d.%d.%d.%d %s' % wx.VERSION ,'Python %d.%d.%d %s %d\n' % (sys.version_info[0] ,sys.version_info[1] ,sys.version_info[2] ,sys.version_info[3] ,sys.version_info[4]) ,T_('\nCopyright Barry Scott (c) 2003-2011. All rights reserved')] wx.LogMessage( '\n'.join( str_message_parts ) ) def OnCmdPreferences( self, event ): pref_dialog = wb_preferences_dialog.PreferencesDialog( self, self.app ) rc = pref_dialog.ShowModal() if rc == wx.ID_OK: self.app.savePreferences() self.list_panel.updateHandler() self.refreshFrame() def OnUnlockedUi( self ): self.setAction( T_('Ready') ) self.tree_panel.updateTree() def OnSize( self, event ): pref = self.app.prefs.getWindow() if not self.IsMaximized(): pref.setFrameSize( self.GetSize() ) event.Skip() def OnMove( self, event ): pref = self.app.prefs.getWindow() if not self.IsMaximized() and not self.IsIconized(): # don't use the event.GetPosition() as it # is off by the window frame thickness pt = self.GetPosition() pref.frame_position = pt pref.maximized = self.IsMaximized() event.Skip() def OnHorizSashPositionChanged( self, event ): _, h = self.h_split.GetClientSizeTuple() win_prefs = self.app.prefs.getWindow() win_prefs.h_sash_ratio = float( event.GetSashPosition() ) / float( h ) event.Skip() def OnVertSashPositionChanged( self, event ): w, _ = self.v_split.GetClientSizeTuple() win_prefs = self.app.prefs.getWindow() win_prefs.v_sash_ratio = float( event.GetSashPosition() ) / float( w ) event.Skip() def OnHorizSize( self, event ): win_prefs = self.app.prefs.getWindow() _, h = self.h_split.GetClientSizeTuple() self.h_split.SetSashPosition( max( 200, int( h * win_prefs.h_sash_ratio ) ) ) event.Skip() def OnVertSize( self, event ): win_prefs = self.app.prefs.getWindow() w, _ = self.v_split.GetClientSizeTuple() self.v_split.SetSashPosition( max( 200, int( w * win_prefs.v_sash_ratio ) ) ) event.Skip() def OnZoom(self, evt): win_prefs = self.app.prefs.getWindow() win_prefs.zoom = self.log_panel.GetZoom() #------------------------------------------------------------------------ def OnActivateApp( self, is_active ): if is_active and self.app.prefs.getView().auto_refresh: self.refreshFrame() def OnToggleAutoRefresh( self, event ): view_prefs = self.app.prefs.getView() view_prefs.auto_refresh = not view_prefs.auto_refresh if view_prefs.auto_refresh: self.refreshFrame() def OnUpdateAutoRefresh( self, event ): view_prefs = self.app.prefs.getView() event.Check( view_prefs.auto_refresh ) def OnToggleViewControlled( self, event ): view_prefs = self.app.prefs.getView() view_prefs.view_controlled = not view_prefs.view_controlled self.refreshFrame() def OnUpdateViewControlled( self, event ): view_prefs = self.app.prefs.getView() event.Check( view_prefs.view_controlled ) def OnToggleViewUncontrolled( self, event ): view_prefs = self.app.prefs.getView() view_prefs.view_uncontrolled = not view_prefs.view_uncontrolled self.refreshFrame() def OnUpdateViewUncontrolled( self, event ): view_prefs = self.app.prefs.getView() event.Check( view_prefs.view_uncontrolled ) def OnToggleViewIgnored( self, event ): view_prefs = self.app.prefs.getView() view_prefs.view_ignored = not view_prefs.view_ignored self.refreshFrame() def OnUpdateViewIgnored( self, event ): view_prefs = self.app.prefs.getView() event.Check( view_prefs.view_ignored ) def OnToggleViewRecursive( self, event ): view_prefs = self.app.prefs.getView() view_prefs.view_recursive = not view_prefs.view_recursive self.refreshFrame() def OnUpdateViewRecursive( self, event ): view_prefs = self.app.prefs.getView() event.Check( view_prefs.view_recursive ) def OnToggleViewOnlyChanges( self, event ): view_prefs = self.app.prefs.getView() view_prefs.view_onlychanges = not view_prefs.view_onlychanges self.refreshFrame() def OnUpdateViewOnlyChanges( self, event ): view_prefs = self.app.prefs.getView() event.Check( view_prefs.view_onlychanges ) 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() def expandSelectedTreeNode( self ): self.tree_panel.expandSelectedTreeNode() def selectTreeNodeInParent( self, filename ): self.tree_panel.selectTreeNodeInParent( filename ) def selectTreeNode( self, filename ): self.tree_panel.selectTreeNode( filename ) #------------------------------------------------------------------------ def __bookmarkMenuReorder( self ): self.bookmark_menu_tree.unrealise() self.all_bookmarks_id_to_pi = {} bm_prefs = self.app.prefs.getBookmarks() self.bookmark_menu_tree = BookmarkMenu( '' ) for bm_name in bm_prefs.getBookmarkNames(): if bm_name == 'last position': continue pi = bm_prefs.getBookmark( bm_name ) path = [] if pi.menu_folder != '': path.append( pi.menu_folder ) if pi.menu_folder2 != '': path.append( pi.menu_folder2 ) if pi.menu_folder3 != '': path.append( pi.menu_folder3 ) path.append( pi.menu_name ) self.bookmark_menu_tree.add( path, pi ) self.bookmark_menu_tree.realise( self.menu_bookmarks, self ) def OnBookmarkAdd( self, event ): pi = self.tree_panel.getSelectionProjectInfo() if pi is None: return bm_prefs = self.app.prefs.getBookmarks() if not bm_prefs.hasBookmark( pi.url ): print T_('Adding bookmark to %s') % pi.wc_path bm_prefs.addBookmark( pi ) self.__bookmarkMenuReorder() self.app.savePreferences() def OnBookmarkManage( self, event ): bookmarks = self.app.prefs.getBookmarks() dialog = wb_bookmarks_dialogs.BookmarkManageDialog( self, self.app, bookmarks ) rc = dialog.ShowModal() if rc != wx.ID_OK: return dialog.setPreferences() self.app.savePreferences() self.__bookmarkMenuReorder() def OnBookmarkGoto( self, event ): self.tree_panel.gotoBookmark( self.all_bookmarks_id_to_pi[event.GetId()].wc_path ) #------------------------------------------------------------------------ def OnCmdClearLog( self, event ): self.log_panel.ClearLog() def OnCloseWindow( self, event ): if self.app.exitAppNow(): self.Destroy() #------------------------------------------------------------------------ def OnFileEdit( self, event ): return self.list_panel.OnFileEdit() def OnUpdateUiFileEdit( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_list.file_exists and self.event_handler.isListHandler()) def OnShellOpen( self, event ): return self.list_panel.OnShellOpen() def OnUpdateUiShellOpen( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_list.file_exists and self.event_handler.isListHandler() ) def OnCommandShell( self, event ): return self.tree_panel.OnCommandShell() def OnUpdateUiCommandShell( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_tree.file_exists ) def OnFileBrowser( self, event ): return self.tree_panel.OnFileBrowser() def OnUpdateUiFileBrowser( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_tree.file_exists ) def OnSpEditCopy( self, event ): return self.Sp_Dispatch( 'OnSpEditCopy' ) def OnUpdateUiSpEditCopy( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_focus.file_exists ) def OnSpEditCut( self, event ): return self.Sp_Dispatch( 'OnSpEditCut' ) def OnUpdateUiSpEditCut( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_focus.file_exists ) def OnSpEditPaste( self, event ): return self.Sp_Dispatch( 'OnSpEditPaste' ) def OnUpdateUiSpEditPaste( self, event ): self.getUpdateUiState() event.Enable( self.app.hasPasteData() ) #---------------------------------------- def OnSpAdd( self, event ): return self.Sp_Dispatch( 'OnSpAdd' ) def OnUpdateUiSpAdd( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_focus.unversioned and (self.ui_state_focus.is_folder or self.ui_state_focus.file_exists) ) def OnSpAnnotate( self, event ): return self.Sp_Dispatch( 'OnSpAnnotate' ) def OnUpdateUiSpAnnotate( self, event ): self.getUpdateUiState() if( self.ui_state_focus.need_checkout or self.ui_state_focus.need_upgrade ): event.Enable( False ) else: event.Enable( self.ui_state_focus.versioned and (not self.ui_state_focus.is_folder) ) def OnSpCheckin( self, event ): return self.Sp_Dispatch( 'OnSpCheckin' ) def OnUpdateUiSpCheckin( self, event ): self.getUpdateUiState() if( self.ui_state_focus.need_checkout or self.ui_state_focus.need_upgrade ): event.Enable( False ) else: event.Enable( self.ui_state_focus.need_checkin or (self.ui_state_focus.versioned and self.event_handler is not None and self.event_handler.isTreeHandler()) ) def OnSpCheckout( self, event ): self.clearUpdateUiState() return self.tree_panel.OnSpCheckout() def OnUpdateUiSpCheckout( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_tree.is_project_parent and self.ui_state_focus.need_checkout ) def OnSpCheckoutTo( self, event ): self.clearUpdateUiState() return self.tree_panel.OnSpCheckoutTo() def OnUpdateUiSpCheckoutTo( self, event ): # same rules are checkout self.OnUpdateUiSpCheckout( event ) def OnSpCleanup( self, event ): return self.Sp_Dispatch( 'OnSpCleanup' ) def OnUpdateUiSpCleanup( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_focus.file_exists and self.ui_state_focus.versioned ) def OnSpCreateTag( self, event ): return self.Sp_Dispatch( 'OnSpCreateTag' ) def OnUpdateUiSpCreateTag( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_tree.file_exists and self.ui_state_tree.versioned ) def OnSpCreateBranch( self, event ): return self.Sp_Dispatch( 'OnSpCreateBranch' ) def OnUpdateUiSpCreateBranch( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_tree.file_exists and self.ui_state_tree.versioned ) def OnSpDelete( self, event ): return self.Sp_Dispatch( 'OnSpDelete' ) def OnUpdateUiSpDelete( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_focus.file_exists or self.ui_state_focus.versioned ) def OnSpDiffMineNew( self, event ): return self.Sp_Dispatch( 'OnSpDiffMineNew' ) def OnUpdateUiSpDiffMineNew( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_list.conflict ) def OnSpDiffOldMine( self, event ): return self.Sp_Dispatch( 'OnSpDiffOldMine' ) def OnUpdateUiSpDiffOldMine( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_list.conflict ) def OnSpDiffOldNew( self, event ): return self.Sp_Dispatch( 'OnSpDiffOldNew' ) def OnUpdateUiSpDiffOldNew( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_list.conflict ) def OnSpDiffWorkBase( self, event ): return self.Sp_Dispatch( 'OnSpDiffWorkBase' ) def OnUpdateUiSpDiffWorkBase( self, event ): self.getUpdateUiState() if self.ui_state_list is self.ui_state_focus: event.Enable( self.ui_state_list.modified ) else: event.Enable( True ) def OnSpDiffWorkHead( self, event ): return self.Sp_Dispatch( 'OnSpDiffWorkHead' ) def OnUpdateUiSpDiffWorkHead( self, event ): self.getUpdateUiState() if( self.ui_state_focus.need_checkout or self.ui_state_focus.need_upgrade ): event.Enable( False ) else: if self.ui_state_list is self.ui_state_focus: event.Enable( self.ui_state_list.versioned and not self.ui_state_list.new_versioned ) else: event.Enable( self.ui_state_tree.versioned ) def OnSpDiffWorkBranchOriginBase( self, event ): return self.Sp_Dispatch( 'OnSpDiffWorkBranchOriginBase' ) def OnSpDiffWorkBranchOriginHead( self, event ): return self.Sp_Dispatch( 'OnSpDiffWorkBranchOriginHead' ) def OnUpdateUiSpDiffWorkBranchOriginBase( self, event ): self.getUpdateUiState() if( self.ui_state_focus.need_checkout or self.ui_state_focus.need_upgrade ): event.Enable( False ) else: event.Enable( self.ui_state_list.versioned and not self.ui_state_list.new_versioned ) def OnUpdateUiSpDiffWorkBranchOriginHead( self, event ): self.getUpdateUiState() if( self.ui_state_focus.need_checkout or self.ui_state_focus.need_upgrade ): event.Enable( False ) else: event.Enable( self.ui_state_list.versioned and not self.ui_state_list.new_versioned ) def OnSpHistory( self, event ): return self.Sp_Dispatch( 'OnSpHistory' ) def OnUpdateUiSpHistory( self, event ): self.getUpdateUiState() if 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 or self.ui_state_focus.need_upgrade ): event.Enable( False ) else: event.Enable( self.ui_state_focus.versioned and not self.ui_state_focus.new_versioned ) def OnSpInfo( self, event ): return self.Sp_Dispatch( 'OnSpInfo' ) def OnUpdateUiSpInfo( self, event ): self.getUpdateUiState() if( self.ui_state_focus.need_checkout or self.ui_state_focus.need_upgrade ): event.Enable( False ) else: event.Enable( self.ui_state_focus.versioned ) def OnSpLock( self, event ): return self.Sp_Dispatch( 'OnSpLock' ) def OnUpdateUiSpLock( self, event ): self.getUpdateUiState() event.Enable( (not self.ui_state_focus.is_folder) and self.ui_state_focus.file_exists ) def OnSpMkdir( self, event ): # 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.tree_panel.OnSpNewFile() def OnUpdateUiSpNewFile( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_tree.file_exists ) def OnSpProperties( self, event ): return self.Sp_Dispatch( 'OnSpProperties' ) def OnUpdateUiSpProperties( self, event ): self.getUpdateUiState() if( self.ui_state_focus.need_checkout or self.ui_state_focus.need_upgrade ): event.Enable( False ) else: event.Enable( self.ui_state_focus.versioned ) def OnSpRename( self, event ): return self.Sp_Dispatch( 'OnSpRename' ) def OnUpdateUiSpRename( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_focus.file_exists ) def OnSpReportUpdates( self, event ): return self.tree_panel.OnReportUpdates() def OnUpdateUiSpReportUpdates( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_tree.file_exists ) def OnSpReportLocksWc( self, event ): return self.Sp_Dispatch( 'OnReportLocksWc' ) def OnUpdateUiSpReportLocksWc( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_focus.versioned ) def OnSpReportBranchChanges( self, event ): return self.tree_panel.OnReportBranchChanges() def OnUpdateUiSpReportBranchChanges( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_tree.file_exists ) def OnSpReportLocksRepos( self, event ): return self.Sp_Dispatch( 'OnReportLocksRepos' ) def OnUpdateUiSpReportLocksRepos( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_focus.versioned ) def OnSpResolved( self, event ): return self.Sp_Dispatch( 'OnSpResolved' ) def OnUpdateUiSpResolved( self, event ): self.getUpdateUiState() event.Enable( self.ui_state_list.conflict ) def OnSpRevert( self, event ): return self.Sp_Dispatch( 'OnSpRevert' ) def OnUpdateUiSpRevert( self, event ): self.getUpdateUiState() if( self.ui_state_focus.need_checkout or self.ui_state_focus.need_upgrade ): event.Enable( False ) else: event.Enable( self.ui_state_focus.revertable ) def OnSpUnlock( self, event ): return self.Sp_Dispatch( 'OnSpUnlock' ) def OnUpdateUiSpUnlock( self, event ): self.getUpdateUiState() 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' ) def OnUpdateUiSpUpdate( self, event ): self.getUpdateUiState() if( self.ui_state_focus.need_checkout or self.ui_state_focus.need_upgrade ): event.Enable( False ) elif self.ui_state_focus.is_project_parent: event.Enable( self.ui_state_focus.versioned and self.ui_state_focus.file_exists ) else: event.Enable( self.ui_state_focus.versioned ) def OnUpdateUiProjectUpdateOrDelete( self, event ): handler = self.tree_panel.getSelectionProjectHandler() if handler and handler.isProjectParent(): event.Enable( True ) else: event.Enable( False ) def OnSpUpdateTo( self, event ): return self.Sp_Dispatch( 'OnSpUpdateTo' ) def OnUpdateUiSpUpdateTo( self, event ): self.OnUpdateUiSpUpdate(event) def OnSpUpgrade( self, event ): self.clearUpdateUiState() return self.tree_panel.OnSpUpgrade() def OnUpdateUiSpUpgrade( self, event ): self.getUpdateUiState() # this is a tree only command event.Enable( self.list_panel.list_handler is not None and self.list_panel.list_handler.project_info.need_upgrade ) #---------------------------------------- def Sp_Dispatch( self, sp_func_name ): self.clearUpdateUiState() if self.event_handler is None: print 'No event_handler, cannot call %r' % (sp_func_name,) return None fn = getattr( self.event_handler, sp_func_name, None ) if fn is None: print 'Not implemented: %r in %r' % (sp_func_name, self.event_handler) return None else: return fn() def getUpdateUiState( self ): all_debug_messages = [] if self.ui_state_tree is None: self.ui_state_tree = self.tree_panel.getUpdateUiState() if self.ui_state_tree is None: self.ui_state_tree = wb_tree_panel.TreeState( None, True ) all_debug_messages.append( 'tree place_holder %s' % (self.ui_state_tree.place_holder,) ) if self.ui_state_list is None: self.ui_state_list = self.list_panel.getUpdateUiState() if self.ui_state_list is None: self.ui_state_list = wb_list_panel_common.ListItemState( None, 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: all_debug_messages.append( 'event_handler is None set tree' ) self.ui_state_focus = self.ui_state_tree elif self.event_handler.isTreeHandler(): all_debug_messages.append( 'event_handler is Tree set tree' ) self.ui_state_focus = self.ui_state_tree else: all_debug_messages.append( 'event_handler is List set list' ) self.ui_state_focus = self.ui_state_list all_debug_messages.append( 'focus place_holder %s' % self.ui_state_focus.place_holder ) self.ui_state_focus.debugPrintState( self.app.log.debug, 'getUpdateUiState set ui_state_focus' ) 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 'ZF: clearUpdateUiState()' self.ui_state_tree = None self.ui_state_list = None self.ui_state_focus = None #-------------------------------------------------------------------------------- class LogCtrlPanel(wx.Panel): def __init__( self, app, parent ): wx.Panel.__init__(self, parent, -1) self.app = app self.text_ctrl = StyledLogCtrl( self.app, self ) # Redirect the console IO to this panel sys.stdin = wb_platform_specific.uOpen( wb_platform_specific.getNullDevice(), 'r' ) if self.app.isStdIoRedirect(): sys.stdout = self sys.stderr = self # Redirect log to the Log panel log_handler = LogHandler( self.text_ctrl ) self.app.log.addHandler( log_handler ) wx.EVT_SIZE( self, wb_exceptions.TryWrapper( self.app.log, self.OnSize ) ) #---------- Event handlers ------------------------------------------------------------ def OnSize( self, event ): self.text_ctrl.SetWindowSize( self.GetSize() ) #---------- Public methods ------------------------------------------------------------ def write( self, string ): # only allowed to use GUI objects on the foreground thread if not self.app.isMainThread(): self.app.foregroundProcess( self.write, (string,) ) return if string[:6] == 'Error:': self.text_ctrl.WriteError(string) elif string[:5] == 'Info:': self.text_ctrl.WriteInfo(string) elif string[:8] == 'Warning:': self.text_ctrl.WriteWarning(string) elif string[:5] == 'Crit:': self.text_ctrl.WriteCritical(string) else: self.text_ctrl.WriteNormal(string) if not self.app.isStdIoRedirect(): sys.__stdout__.write( string ) def close( self ): pass def ClearLog( self ): self.text_ctrl.ClearText() def GetZoom(self): return self.text_ctrl.GetZoom() def SetZoom(self, zoom): self.text_ctrl.SetZoom(zoom) #-------------------------------------------------------------------------------- class LogHandler(logging.Handler): def __init__( self, log_ctrl ): self.log_ctrl = log_ctrl logging.Handler.__init__( self ) def emit( self, record ): try: msg = self.format(record) + '\n' level = record.levelno if level >= logging.CRITICAL: self.log_ctrl.WriteCritical( msg ) elif level >= logging.ERROR: self.log_ctrl.WriteError( msg ) elif level >= logging.WARNING: self.log_ctrl.WriteWarning( msg ) elif level >= logging.INFO: self.log_ctrl.WriteInfo( msg ) elif level >= logging.DEBUG: self.log_ctrl.WriteDebug( msg ) else: self.log_ctrl.WriteError( msg ) except Exception: self.handleError(record) #-------------------------------------------------------------------------------- class StyledLogCtrl(wx.stc.StyledTextCtrl): 'StyledLogCtrl' def __init__(self, app, parent): self.app = app wx.stc.StyledTextCtrl.__init__(self, parent) self.SetReadOnly( True ) self.style_normal = 0 self.style_error = 1 self.style_info = 2 self.style_warning = 3 self.style_critical = 4 self.style_debug = 4 self.SetMarginWidth(0, 0) self.SetMarginWidth(1, 0) self.SetMarginWidth(2, 0) self.StyleSetSpec( wx.stc.STC_STYLE_DEFAULT, "size:%d,face:%s,fore:#000000" % (wb_config.point_size, wb_config.face) ) self.StyleSetSpec( self.style_normal, "fore:#000000" ) self.StyleSetSpec( self.style_error, "fore:#DC143C" ) # Crimson self.StyleSetSpec( self.style_info, "fore:#191970" ) # Midnight Blue self.StyleSetSpec( self.style_warning, "fore:#008000" ) # Green self.StyleSetSpec( self.style_critical, "fore:#BA55D3" ) # Medium Orchid self.StyleSetSpec( self.style_debug, "fore:#DC143C" ) # Crimson wx.EVT_KEY_DOWN( self, self.OnKeyDown ) def OnKeyDown( self, event ): """ Don't let the STC treat the TAB normally (insert a tab character.) Turn it into a navigation event instead. """ if event.GetKeyCode() == wx.WXK_TAB: flags = wx.NavigationKeyEvent.IsForward if event.ShiftDown(): flags = wx.NavigationKeyEvent.IsBackward if event.ControlDown(): flags |= wx.NavigationKeyEvent.WinChange self.Navigate(flags) else: event.Skip() def SetWindowSize( self, size ): wx.stc.StyledTextCtrl.SetSize( self, size ) self.EnsureCaretVisible() def WriteStyledText( self, text, style ): # only allowed to use GUI objects on the foreground thread if not self.app.isMainThread(): self.app.foregroundProcess( self.WriteStyledText, (text, style) ) return self.SetReadOnly(False) carot_pos = self.GetCurrentPos() insert_pos = self.GetLength() self.InsertText( insert_pos, text ) self.StartStyling( insert_pos, 0xff ) self.SetStyling( len(text), style ) if carot_pos == insert_pos: new_carot_pos = self.GetLength() self.SetCurrentPos( new_carot_pos ) self.SetSelectionStart( new_carot_pos ) self.SetSelectionEnd( new_carot_pos ) self.EnsureCaretVisible() self.SetReadOnly(True) def WriteNormal( self, text ): self.WriteStyledText( text, self.style_normal ) def WriteError( self, text ): self.WriteStyledText( text, self.style_error ) def WriteInfo( self, text ): self.WriteStyledText( text, self.style_info ) def WriteWarning( self, text ): self.WriteStyledText( text, self.style_warning ) def WriteCritical( self, text ): self.WriteStyledText( text, self.style_critical ) def WriteDebug( self, text ): self.WriteStyledText( text, self.style_debug ) def ClearText( self ): self.SetReadOnly(False) self.ClearAll() self.SetReadOnly( True ) class BookmarkMenu: def __init__( self, name ): self.name = name self.menu_items = [] self.menu_folders = {} self.parent_menu = None self.all_menu_ids = [] def add( self, path, pi ): if len(path) == 1: self.menu_items.append( (path[0], pi) ) else: folder = path[0] if folder not in self.menu_folders: self.menu_folders[ folder ] = BookmarkMenu( folder ) self.menu_items.append( (self.menu_folders[ folder ], None) ) self.menu_folders[ folder ].add( path[1:], pi ) def realise( self, parent_menu, frame ): self.parent_menu = parent_menu self.menu_items.sort( key=_keyMenuItem ) for name_or_menu, pi in self.menu_items: if isinstance( name_or_menu, BookmarkMenu ): menu_id = wx.NewId() submenu = wx.Menu() self.all_menu_ids.append( menu_id ) parent_menu.AppendMenu( menu_id, name_or_menu.name, submenu ) name_or_menu.realise( submenu, frame ) else: bm_id = wx.NewId() frame.all_bookmarks_id_to_pi[ bm_id ] = pi self.all_menu_ids.append( bm_id ) parent_menu.Append( bm_id, pi.menu_name, pi.wc_path ) try_wrapper = wb_exceptions.TryWrapperFactory( frame.app.log ) wx.EVT_MENU( frame, bm_id, try_wrapper( frame.OnBookmarkGoto ) ) def unrealise( self ): for name_or_menu, pi in self.menu_items: if isinstance( name_or_menu, BookmarkMenu ): name_or_menu.unrealise() else: # unrealise a bm item pass for menu_id in self.all_menu_ids: self.parent_menu.Delete( menu_id ) def dump( self, indent ): prefix = ' '*indent for name, pi in self.menu_items: if isinstance( name, BookmarkMenu ): print '%s Menu: %r' % (prefix, name) name.dump( indent + 4 ) else: print '%s Item: %r' % (prefix, name) def __repr__( self ): return '' % self.name def _keyMenuItem( item ): if isinstance( item[0], BookmarkMenu ): return item[0].name else: return item[0] WorkBench-1.8.2/Source/wb_list_panel_common.py000644 000765 000024 00000046200 12136221163 021671 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2011 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_list_panel_common.py ''' import os import sys import cPickle import wx import wb_config import wb_exceptions import wb_ids import wb_shell_commands import wb_dialogs import wb_platform_specific class ListSortData: def __init__( self, order=1, field=0 ): self.order = order self.field = field def setField( self, field ): if self.field == field: # toggle order self.order *= -1 else: # new field forward self.field = field self.order = 1 def getField( self ): return self.field def getOrder( self ): return self.order class ListItemState: def __init__( self, project_info, place_holder=False ): self.debug_project_info = project_info # 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 self.unversioned = False self.need_checkin = False self.need_checkout = False self.need_upgrade = False self.conflict = False self.file_exists = False self.is_folder = False self.is_project_parent = False self.revertable = False def debugPrintState( self, print_fn, title='' ): print_fn( '----------- %s' % (title,) ) for item in self.__dict__.items(): print_fn( 'ListItemState: %s -> %r' % item ) class WbListCtrl(wx.ListCtrl): def __init__( self, parent, list_id ): wx.ListCtrl.__init__( self, parent, list_id, style=wx.LC_REPORT|wx.LC_HRULES|wx.LC_VIRTUAL ) self.parent = parent image_size = (16,16) il = wx.ImageList( *image_size ) self.il_folder = il.Add( wx.ArtProvider.GetBitmap( wx.ART_FOLDER, size=image_size ) ) self.il_file = il.Add( wx.ArtProvider.GetBitmap( wx.ART_NORMAL_FILE, size=image_size ) ) self.AssignImageList( il, wx.IMAGE_LIST_SMALL ) def OnGetItemText( self, item, col ): if self.parent.list_handler is not None: return self.parent.list_handler.OnGetItemText( item, col ) else: return '' def OnGetItemImage( self, item ): if self.parent.list_handler is not None: if self.parent.list_handler.isItemImageFolder( item ): return self.il_folder else: return self.il_file return -1 def OnGetItemAttr( self, item ): if self.parent.list_handler is not None: return self.parent.list_handler.OnGetItemAttr( item ) else: return None class WbListPanelCommon(wx.Panel): def __init__( self, app, frame, parent ): wx.Panel.__init__( self, parent ) self.app = app self.frame = frame try_wrapper = wb_exceptions.TryWrapperFactory( self.app.log ) self.filter_field = T_('Name') self.filter_text = '' self.header_panel = HeaderPanel( self, app, self.filter_field ) self.header_panel.setFilterChangedHandler( self.OnFilterChanged ) self.v_sizer = wx.BoxSizer( wx.VERTICAL ) 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( list_window, 1, wx.EXPAND|wx.ALL, 0 ) acc_tab = wx.AcceleratorTable( self.getAcceleratorTableInit() ) self.list_ctrl.SetAcceleratorTable( acc_tab ) wx.EVT_SIZE( self, try_wrapper( self.OnSize ) ) wx.EVT_LIST_COL_CLICK( self.list_ctrl, self.id_list, self.app.eventWrapper( self.OnColClick )) wx.EVT_LIST_ITEM_ACTIVATED( self.list_ctrl, self.id_list, self.app.eventWrapper( self.OnItemActivated ) ) wx.EVT_LIST_ITEM_RIGHT_CLICK( self.list_ctrl, self.id_list, self.app.eventWrapper( self.OnRightClick ) ) if wx.Platform in ['__WXMSW__','__WXMAC__']: wx.EVT_LIST_BEGIN_DRAG( self.list_ctrl, self.id_list, self.app.eventWrapper( self.OnDragBegin ) ) wx.EVT_LIST_ITEM_SELECTED( self.list_ctrl, self.id_list, self.OnItemSelected ) wx.EVT_LIST_ITEM_DESELECTED( self.list_ctrl, self.id_list, self.OnItemDeselected ) wx.EVT_SET_FOCUS( self.list_ctrl, self.OnSetFocus ) wx.EVT_KILL_FOCUS( self.list_ctrl, self.OnKillFocus ) self.addToSizer( self.v_sizer ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() view_prefs = self.app.prefs.getView() self.sort_data = ListSortData( view_prefs.sort_order, view_prefs.sort_field ) self.list_handler = None self.last_char_timestamp = 0 self.inc_search_string = '' self.__first_time_select_allowed = True def firstTimeSelect( self ): if self.__first_time_select_allowed: if self.list_handler is not None: if len( self.list_handler.all_files ) > 0: wx.CallAfter( self.list_ctrl.Select, 0 ) self.__first_time_select_allowed = False 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 ) if wb_config.focus_ring: self.Refresh() event.Skip() def OnKillFocus( self, event ): #self.frame.clearEventHandler() if wb_config.focus_ring: self.Refresh() event.Skip() def OnItemSelected( self, event ): self.frame.clearUpdateUiState() self.frame.setEventHandler( self ) def OnItemDeselected( self, event ): self.frame.clearUpdateUiState() self.frame.setEventHandler( self ) def OnDragBegin( self, event ): #print 'WbListPanel.OnDragBegin' all_filenames = [self.list_handler.getFilename( row ) for row in self.getSelectedRows()] # create our own data format and use it in a # custom data object svn_data = wx.CustomDataObject(wx.CustomDataFormat("WorkBench.svn_wc_path")) svn_data.SetData( cPickle.dumps( all_filenames ) ) # Make a data object that other apps will recognize as a filename drag/drop fn_data = wx.FileDataObject() for name in all_filenames: fn_data.AddFile(name) # What is this for? It just screws up the dragging of files to a text editor # that also accepts files... # # Now make a data object for the text and also a composite # # data object holding both of the others. # text_data = wx.TextDataObject( 'the text of the list ctrl file name' ) data = wx.DataObjectComposite() data.Add( svn_data ) # data.Add( text_data ) data.Add( fn_data ) # And finally, create the drop source and begin the drag # and drop opperation src = wx.DropSource( self ) src.SetData( data ) #print 'Begining DragDrop' src.DoDragDrop( wx.Drag_AllowMove ) #print 'DragDrop completed: %d' % result def savePreferences( self ): view_prefs = self.app.prefs.getView() view_prefs.sort_order = self.sort_data.getOrder() view_prefs.sort_field = self.sort_data.getField() def getProjectInfo( self ): if self.list_handler is not None: return self.list_handler.getProjectInfo() return None def clearHandler( self ): self.app.log.debug( 'WbListPanelCommon.clearHandler()' ) self.list_handler = None # empty the list self.list_ctrl.DeleteAllItems() def updateHandler( self ): self.app.log.debug( 'WbListPanelCommon.updateHandler() %r' % self.list_handler ) if self.list_handler is not None: self.list_handler.setupColumns() self.list_handler.updateStatus() self.drawList() def setHandler( self, list_handler ): self.app.log.debug( 'WbListPanelCommon.setHandler( %r )' % list_handler ) self.list_handler = list_handler self.list_handler.setupColumns() self.list_handler.updateStatus() self.list_ctrl.SetBackgroundColour( self.list_handler.getBackgroundColour() ) self.filter_field = '' self.header_panel.clearFilterText() self.drawList() def drawList( self ): self.app.log.debug( 'WbListPanelCommon.drawList() %r' % self.list_handler ) if self.list_handler: self.list_handler.initList( self.sort_data, self.filter_field, self.filter_text ) def getHandler( self ): return self.list_handler def updateHeader( self, url_name, path_name ): self.header_panel.updateHeader( url_name, path_name ) def OnFilterChanged( self, field, text ): self.filter_field = field self.filter_text = text self.drawList() def OnSize( self, event ): w, h = self.GetClientSizeTuple() self.v_sizer.SetDimension( 0, 0, w, h ) def OnItemActivated(self, event): if not self.list_handler: return for row in self.getSelectedRows(): filename = self.list_handler.getFilename( row ) if self.list_handler.mayOpen( row ): self.app.selectTreeNode( filename ) elif not wb_platform_specific.uPathIsdir( filename ): wb_shell_commands.EditFile( self.app, self.list_handler.getProjectInfo(), filename ) def isTreeHandler( self ): return False def isListHandler( self ): return True def getUpdateUiState( self ): if self.list_handler is None: return None return self.list_handler.getState( self.getSelectedRows() ) def OnRightClick( self, event ): if not self.list_handler: return self.frame.getUpdateUiState() if True: menu = self.list_handler.getContextMenu() self.list_ctrl.PopupMenu( menu, event.GetPoint() ) menu.Destroy() def OnColClick( self, event ): self.app.log.debug( 'OnColClick %r' % self.list_handler ) self.sort_data.setField( self.list_handler.getColumnId( event.m_col ) ) if not self.list_handler: return self.list_handler.sortList( self.sort_data ) # command handlers def OnFileEdit( self ): if not self.list_handler: return for row in self.getSelectedRows(): filename = self.list_handler.getFilename( row ) wb_shell_commands.EditFile( self.app, self.list_handler.getProjectInfo(), filename ) def OnShellOpen( self ): if not self.list_handler: return for row in self.getSelectedRows(): filename = self.list_handler.getFilename( row ) wb_shell_commands.ShellOpen( self.app, self.list_handler.getProjectInfo(), filename ) def OnSpEditCopy( self ): return self.Sp_Dispatch( 'Cmd_File_EditCopy' ) def OnSpEditCut( self ): return self.Sp_Dispatch( 'Cmd_File_EditCut' ) def OnSpEditPaste( self ): return self.Sp_Dispatch( 'Cmd_File_EditPaste' ) def OnSpAdd( self ): return self.Sp_Dispatch( 'Cmd_File_Add' ) def OnSpAnnotate( self ): return self.Sp_Dispatch( 'Cmd_File_Annotate' ) def OnSpCheckin( self ): return self.Sp_Dispatch( 'Cmd_File_Checkin' ) def OnSpCleanup( self ): return self.Sp_Dispatch( 'Cmd_File_Cleanup' ) def OnSpDelete( self ): return self.Sp_Dispatch( 'Cmd_File_Delete' ) def OnSpDiffWorkBase( self ): return self.Sp_Dispatch( 'Cmd_File_DiffWorkBase' ) def OnSpDiffWorkHead( self ): return self.Sp_Dispatch( 'Cmd_File_DiffWorkHead' ) def OnSpDiffWorkBranchOriginBase( self ): return self.Sp_Dispatch( 'Cmd_File_DiffWorkBranchOriginBase' ) def OnSpDiffWorkBranchOriginHead( self ): return self.Sp_Dispatch( 'Cmd_File_DiffWorkBranchOriginHead' ) def OnSpDiffMineNew( self ): return self.Sp_Dispatch( 'Cmd_File_DiffMineNew' ) def OnSpDiffOldMine( self ): return self.Sp_Dispatch( 'Cmd_File_DiffOldMine' ) def OnSpDiffOldNew( self ): return self.Sp_Dispatch( 'Cmd_File_DiffOldNew' ) def OnSpHistory( self ): return self.Sp_Dispatch( 'Cmd_File_History' ) def OnSpInfo( self ): return self.Sp_Dispatch( 'Cmd_File_Info' ) def OnSpLock( self ): return self.Sp_Dispatch( 'Cmd_File_Lock' ) def OnSpProperties( self ): return self.Sp_Dispatch( 'Cmd_File_Properties' ) def OnSpRename( self ): return self.Sp_Dispatch( 'Cmd_File_Rename' ) def OnSpRevert( self ): return self.Sp_Dispatch( 'Cmd_File_Revert' ) def OnSpResolved( self ): return self.Sp_Dispatch( 'Cmd_File_Resolved' ) def OnSpUnlock( self ): return self.Sp_Dispatch( 'Cmd_File_Unlock' ) def OnSpUpdate( self ): return self.Sp_Dispatch( 'Cmd_File_Update' ) def OnSpUpdateTo( self ): return self.Sp_Dispatch( 'Cmd_File_UpdateTo' ) def Sp_Dispatch( self, sp_func_name ): self.app.trace.info( 'WbListPanel.Sp_Dispatch( %s ) event' % sp_func_name ) if not self.list_handler: return sp_func = getattr( self.list_handler, sp_func_name ) self.app.trace.info( 'WbListPanel.Sp_Dispatch( %s ) calling' % sp_func_name ) return sp_func( self.getSelectedRows() ) def selectAll( self ): item_index = -1 while True: item_index = self.list_ctrl.GetNextItem( item_index, wx.LIST_NEXT_ALL ) if item_index < 0: break focused = self.list_ctrl.GetItemState( item_index, wx.LIST_STATE_FOCUSED ) self.list_ctrl.SetItemState( item_index, wx.LIST_STATE_SELECTED | focused, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED ) 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 ) all_rows.sort() #print 'getSelectedRows() %r' % all_rows return all_rows class HeaderPanel(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_sizer1 = wx.BoxSizer( wx.HORIZONTAL ) self.h_sizer2 = wx.BoxSizer( wx.HORIZONTAL ) # Don't set the controls readonly as that prevents copy of the text self.url_text_ctrl = wx.TextCtrl( self, 0, '' ) self.path_text_ctrl = wx.TextCtrl( self, 0, '' ) self.filter_changed_handler = None # ToDo: A wx.SearchCtrl would go great here instead of these widgets self.filter_field_choices = [ T_('Name'), T_('Author') ] 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 ) ) self.filter_text_ctrl = wx.TextCtrl( self ) self.filter_clear_button = wx.Button( self, wx.NewId(), 'X', style=wx.BU_EXACTFIT, size=(30, -1) ) # share the space 50/50 if 'wxMac' in wx.PlatformInfo: filter_flags = wx.TOP|wx.BOTTOM|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL border = 5 left = wx.LEFT else: filter_flags = wx.BOTTOM|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL border = 3 left = 0 self.h_sizer1.Add( self.url_text_ctrl, 1, left|wx.TOP|wx.BOTTOM|wx.RIGHT, border ) self.h_sizer1.Add( self.path_text_ctrl, 1, wx.TOP|wx.BOTTOM|wx.RIGHT, border ) self.h_sizer2.Add( self.filter_choice_ctrl, 0, left|filter_flags, border ) self.h_sizer2.Add( self.filter_text_ctrl, 1, filter_flags, border ) self.h_sizer2.Add( self.filter_clear_button, 0, filter_flags, border ) self.v_sizer.Add( self.h_sizer1, 0, wx.EXPAND|wx.ALL, 0 ) 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 ) self.SetAutoLayout( True ) self.SetSizer( self.v_sizer ) self.v_sizer.Fit( self ) self.Layout() 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 ListHandler: def __init__( self, list_panel ): self.list_panel = list_panel def sortList( self, sort_data ): raise wb_exceptions.InternalError( 'sortList not implemented' ) WorkBench-1.8.2/Source/wb_list_panel.py000644 000765 000024 00000003240 11662230071 020317 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2011 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_list_panel.py ''' import wb_ids import wb_list_panel_common import wx class WbListPanel(wb_list_panel_common.WbListPanelCommon): def __init__( self, app, frame, parent ): wb_list_panel_common.WbListPanelCommon.__init__( self, app, frame, parent ) def __repr__( self ): return '' % self.list_handler def getAcceleratorTableInit( self ): acc_init =[ (wx.ACCEL_CMD, ord('C'), wb_ids.id_SP_EditCopy), (wx.ACCEL_CMD, ord('X'), wb_ids.id_SP_EditCut), (wx.ACCEL_CMD, ord('V'), wb_ids.id_SP_EditPaste), (wx.ACCEL_CMD, ord('A'), wb_ids.id_SP_Add), (wx.ACCEL_CMD, ord('D'), wb_ids.id_SP_DiffWorkBase), (wx.ACCEL_CMD, ord('E'), wb_ids.id_File_Edit), (wx.ACCEL_CMD, ord('L'), wb_ids.id_SP_History), (wx.ACCEL_CMD, ord('I'), wb_ids.id_SP_Info), (wx.ACCEL_CMD, ord('P'), wb_ids.id_SP_Properties), (wx.ACCEL_CMD, ord('R'), wb_ids.id_SP_Revert), (wx.ACCEL_CMD, ord('T'), wb_ids.id_SP_UpdateTo), (wx.ACCEL_CMD, ord('U'), wb_ids.id_SP_Update), (wx.ACCEL_NORMAL, wx.WXK_DELETE, wb_ids.id_SP_Delete), (wx.ACCEL_CMD, ord('O'), wb_ids.id_Shell_Open), ] return acc_init WorkBench-1.8.2/Source/wb_debug.py000644 000765 000024 00000001116 10545315015 017254 0ustar00barrystaff000000 000000 ''' ==================================================================== 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 WorkBench-1.8.2/Source/wb_ids.py000644 000765 000024 00000004277 11662230071 016757 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2011 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_ids.py ''' import wx id_View_Refresh = wx.NewId() id_View_AutoRefresh = wx.NewId() id_View_ControlledFiles = wx.NewId() id_View_IgnoredFiles = wx.NewId() id_View_UncontrolledFiles = wx.NewId() id_View_Recursive = wx.NewId() id_View_OnlyChanges = wx.NewId() id_ClearLog = wx.NewId() id_SelectAll = 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() id_Bookmark_Add = wx.NewId() id_Bookmark_Manage = wx.NewId() id_Command_Shell = wx.NewId() id_File_Browser = wx.NewId() id_Shell_Open = wx.NewId() id_File_Edit = wx.NewId() id_SP_NewFile = wx.NewId() id_SP_ConflictMenu = wx.NewId() id_SP_DetailsMenu = wx.NewId() id_SP_EditCopy = wx.NewId() id_SP_EditCut = wx.NewId() id_SP_EditPaste = wx.NewId() id_SP_Add = wx.NewId() id_SP_Annotate = wx.NewId() id_SP_Checkin = wx.NewId() id_SP_Checkout = wx.NewId() id_SP_CheckoutTo = wx.NewId() id_SP_Cleanup = wx.NewId() id_SP_Delete = wx.NewId() id_SP_DiffMineNew = wx.NewId() id_SP_DiffOldMine = wx.NewId() id_SP_DiffOldNew = wx.NewId() id_SP_DiffRevisionRevision = wx.NewId() id_SP_DiffWorkBase = wx.NewId() id_SP_DiffWorkHead = wx.NewId() id_SP_DiffWorkBranchOriginBase = wx.NewId() id_SP_DiffWorkBranchOriginHead = wx.NewId() id_SP_History = wx.NewId() id_SP_Info = wx.NewId() id_SP_Lock = wx.NewId() id_SP_Mkdir = wx.NewId() id_SP_Properties = wx.NewId() id_SP_Rename = wx.NewId() id_SP_Resolved = wx.NewId() id_SP_Revert = wx.NewId() id_SP_Unlock = wx.NewId() id_SP_Update = wx.NewId() id_SP_UpdateTo = wx.NewId() id_SP_Upgrade = wx.NewId() id_SP_CreateTag = wx.NewId() id_SP_CreateBranch = wx.NewId() id_SP_Report_Updates = wx.NewId() id_SP_Report_LocksWc = wx.NewId() id_SP_Report_LocksRepos = wx.NewId() id_SP_Report_BranchChanges = wx.NewId() WorkBench-1.8.2/Source/wb_subversion_list_handler_common.py000644 000765 000024 00000125521 12717610335 024502 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2011 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_subversion_list_handler_common.py ''' import os import types import locale import pysvn import wx import wb_subversion_history import wb_subversion_annotate import wb_list_panel_common import wb_exceptions import wb_subversion_utils import wb_read_file import wb_subversion_info_dialog import wb_subversion_properties_dialog import wb_subversion_diff import wb_config import wb_platform_specific col_labels = [ ('Name', U_('Name'), 25, 10, 100, wx.LIST_FORMAT_LEFT), ('State', U_('State'), 4, 2, 8, wx.LIST_FORMAT_LEFT), ('Date', U_('Date'), 12, 4, 20, wx.LIST_FORMAT_RIGHT), ('Rev', U_('Rev'), 4, 2, 8, wx.LIST_FORMAT_RIGHT), ('Author', U_('Author'), 10, 4, 80, wx.LIST_FORMAT_LEFT), # ('Size', U_('Size'), 6, 4, 10, wx.LIST_FORMAT_RIGHT), ('Mimetype', U_('Mimetype'), 10, 6, 50, wx.LIST_FORMAT_LEFT), ('EOL', U_('EOL'), 6, 2, 10, wx.LIST_FORMAT_LEFT), ('Type', U_('Type'), 4, 4, 10, wx.LIST_FORMAT_LEFT), ('Lock Owner', U_('Lock Owner'), 10, 4, 80, wx.LIST_FORMAT_LEFT), ('Lock Comment', U_('Lock Comment'),60, 10, 100, wx.LIST_FORMAT_LEFT), ] class ColumnInfo: def __init__( self, col_id, name, label, width, min_width, max_width, alignment ): self.col_id = col_id self.name = name self.label = label self.width = int(width) self.min_width = int(min_width) self.max_width = int(max_width) self.alignment = alignment self.included = False self.column = 0 class ViewColumnInfo: def __init__( self ): self.column_info_by_name = {} self.column_info_by_id = {} self.column_order = [] for col_id, col_info in enumerate( col_labels ): name, label, width, min_width, max_width, alignment = col_info ci = ColumnInfo( col_id, name, label, width, min_width, max_width, alignment ) self.column_info_by_name[ name ] = ci self.column_info_by_id[ col_id ] = ci def setFromPreferenceData( self, p ): self.setFrom( p.column_order[:], p.column_widths[:] ) def setFrom( self, column_order, column_widths ): # must have Name if 'Name' not in column_order: column_order.insert( 0, 'Name' ) column_widths.insert( 0, '0' ) for index, name in enumerate( column_order ): if self.column_info_by_name.has_key( name ): if len(column_widths) > index: try: width = int(column_widths[index]) info = self.column_info_by_name[ name ] if( width >= info.min_width and width <= info.max_width ): info.width = width except ValueError: pass self.setColumnOrder( column_order ) def getInfoByName( self, name ): return self.column_info_by_name[ name ] def getInfoById( self, col_id ): return self.column_info_by_id[ col_id ] def getNameById( self, col_id ): return self.column_info_by_id[ col_id ].name def excludedInfo( self ): return [info for info in self.column_info_by_name.values() if not info.included] def setColumnOrder( self, column_order ): self.column_order = column_order[:] for index, name in enumerate( self.column_order ): self.column_info_by_name[ name ].column = index def getColumnWidth( self, column_name ): return self.column_info_by_name[ column_name ].width def setColumnWidth( self, column_name, width ): info = self.column_info_by_name[ column_name ] width = max( info.min_width, width ) width = min( info.max_width, width ) info.width = width def getColumnOrder( self ): return self.column_order def getNameByColumn( self, col ): return self.column_order[ col ] def getInfoByColumn( self, col ): return self.column_info_by_name[ self.column_order[ col ] ] def getColumnWidths( self ): return [str(self.column_info_by_name[name].width) for name in self.column_order] class SubversionListHandlerCommon(wb_list_panel_common.ListHandler): 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' col_lock_owner = 'Lock Owner' col_lock_comment = 'Lock Comment' def __init__( self, app, list_panel, project_info ): wb_list_panel_common.ListHandler.__init__( self, list_panel ) self.project_info = project_info self.app = app self.all_files = [] self.all_item_attr = {} self.column_info = ViewColumnInfo() self.need_properties = False self.is_project_parent = False self.__last_wc_path = None self.__branch_origin_revision = None self.__branch_url = None self.__branch_origin_url = None def setIsProjectParent( self ): self.is_project_parent = True def isProjectParent( self ): return self.is_project_parent def updateStatus( self ): try: self.project_info.updateStatus() except pysvn.ClientError, e: print 'Error: %s' % (e.args[0],) def getBackgroundColour( self ): return (255,255,255) def getBranchOriginRevision( self ): if self.__branch_origin_revision is None: try: log_entries = self.project_info.client_bg.log( self.project_info.url, discover_changed_paths=True ) except pysvn.ClientError, e: self.app.log_client_error( e ) return None branch_log_entry = log_entries[-1] changed_paths = branch_log_entry['changed_paths'][0] # If the oldest revision has a copyfrom_path, this is a branch, # so we set __branch_origin_url, __branch_url and __branch_origin_revision. if changed_paths['copyfrom_path'] is not None: base_url = self.client_bg.info2( self.project_info.url )[0][1]['repos_root_URL'] self.__branch_origin_url = base_url + changed_paths['copyfrom_path'] self.__branch_url = base_url + changed_paths['path'] self.__branch_origin_revision = changed_paths['copyfrom_revision'] return self.__branch_origin_revision def getBranchOriginUrl( self ): self.getBranchOriginRevision() return self.__branch_origin_url def getBranchUrl( self ): self.getBranchOriginRevision() return self.__branch_url def getProjectInfo( self ): return self.project_info def setupColumnInfo( self ): self.column_info.setFromPreferenceData( self.app.prefs.getView() ) self.overrideColumnInfo() def overrideColumnInfo( self ): pass def setupColumns( self ): self.setupColumnInfo() g = self.list_panel.list_ctrl # should update in place in case the list was used by other code while g.GetColumnCount() > 0: g.DeleteColumn( 0 ) need_properties = False char_width = 11 for index, name in enumerate( self.column_info.getColumnOrder() ): info = self.column_info.getInfoByName( name ) g.InsertColumn( index, T_(info.label), info.alignment, info.width*char_width ) if name in [self.col_mime_type, self.col_eol_style]: need_properties = True self.project_info.setNeedProperties( need_properties ) def initList( self, sort_data, filter_field, filter_text ): self.app.trace.info( 'SubversionListHandlerCommon.initList %s', self.project_info.project_name ) g = self.list_panel.list_ctrl self.list_panel.updateHeader( self.project_info.url, self.project_info.wc_path ) # nothing doing if the wc does not exist or needs upgrade if( self.project_info.need_checkout or self.project_info.need_upgrade ): # empty the list g.DeleteAllItems() self.__last_wc_path = None self.app.trace.info( 'initList no wc_path %s', self.project_info.project_name ) g.SetItemCount( 1 ) return self.app.log.debug( 'initList - last wc_path %r' % self.__last_wc_path ) if self.__last_wc_path == self.project_info.wc_path: selection_state = self.__saveListSelectionState() else: self.app.log.debug( 'initList - changed wc_path to %r' % self.project_info.wc_path ) selection_state = (None, []) self.__last_wc_path = self.project_info.wc_path # empty the list g.DeleteAllItems() prefix_len = len( self.project_info.wc_path ) + 1 if len(filter_text) > 0: if filter_field == T_('Name'): self.all_files = [f for f in self.project_info.getFilesStatus() if filter_text.lower() in f.path[prefix_len:].lower()] elif filter_field == T_('Author'): self.all_files = [f for f in self.project_info.getFilesStatus() if f.entry is not None and filter_text.lower() in f.entry.commit_author.lower()] else: self.all_files = self.project_info.getFilesStatus() view_prefs = self.app.prefs.getView() if view_prefs.view_onlychanges: modified_states = [ pysvn.wc_status_kind.modified, pysvn.wc_status_kind.added, pysvn.wc_status_kind.deleted, pysvn.wc_status_kind.conflicted, ] af = self.all_files self.all_files = [] for f in af: text_status = self.getTextStatus(f) prop_status = self.getPropStatus(f) if text_status in modified_states or prop_status in modified_states: self.all_files.append( f ) self.all_files.sort( SortList( sort_data, self.project_info ) ) self.__restoreListSelectionState( selection_state ) def sortList( self, sort_data ): self.app.log.debug('sortList' ) if( self.project_info.need_checkout or self.project_info.need_upgrade ): # nothing to sort return selection_state = self.__saveListSelectionState() self.all_files.sort( SortList( sort_data, self.project_info ) ) self.__restoreListSelectionState( selection_state ) self.list_panel.list_ctrl.RefreshItems( 0, len(self.all_files)-1 ) def __saveListSelectionState( self ): row = self.__focusedRow() if row is None: focused_filename = None else: focused_filename = self.getFilename( row ) all_selected_filenames = [self.getFilename( row ) for row in self.list_panel.getSelectedRows()] self.app.log.debug( '__saveListSelectionState %r %r' % (focused_filename, all_selected_filenames) ) return (focused_filename, all_selected_filenames) def __restoreListSelectionState( self, (focused_filename, all_selected_filenames) ): g = self.list_panel.list_ctrl self.app.log.debug( '__restoreListSelectionState In %r %r' % (focused_filename, all_selected_filenames) ) focused_index = 0 g.SetItemCount( len(self.all_files) ) for index, status in enumerate( self.all_files ): state = 0 if status.path in all_selected_filenames: state = wx.LIST_STATE_SELECTED if focused_filename is not None and status.path == focused_filename: focused_index = index state |= wx.LIST_STATE_FOCUSED g.SetItemState( index, state, wx.LIST_STATE_SELECTED|wx.LIST_STATE_FOCUSED ) if len(self.all_files) > 0: g.EnsureVisible( focused_index ) self.app.log.debug( '__restoreListSelectionState Out %r' % (len(self.all_files),) ) def __focusedRow( self ): g = self.list_panel.list_ctrl for index in range( len( self.all_files ) ): if g.GetItemState( index, wx.LIST_STATE_FOCUSED ) != 0: return index return None def columnRequired( self, col ): return self.column_info.getInfo( col ).included def columnIndex( self, col ): return self.column_info.getInfo( col ).column def getColumnId( self, col ): return self.column_info.getColumnOrder()[col] def _getNameColPrefix( self ): prefix_len = len( self.project_info.wc_path ) if not self.project_info.wc_path.endswith( os.sep ): prefix_len += 1 return prefix_len def OnGetItemText( self, index, col ): column = self.column_info.getNameByColumn( col ) if self.project_info.need_upgrade: if column == self.col_name: return T_('Use the Upgrade command to convert the working copy to the required format') else: return '' if self.project_info.need_checkout: if column == self.col_name: if self.isProjectParent(): return T_('Use the Checkout command to fetch files') else: return T_('Use Add command to add this folder to the working copy') else: return '' status = self.all_files[ index ] if column == self.col_name: value = self.__get_NameColumn( status, self._getNameColPrefix() ) elif column == self.col_state: value = self.__get_StateColumn( status ) elif column == self.col_date: value = self.__get_DateColumn( status ) elif column == self.col_revision: value = self.__get_RevisionColumn( status ) elif column == self.col_author: value = self.__get_AuthorColumn( status ) elif column == self.col_type: value = self.__get_TypeColumn( status ) elif column == self.col_size: value = self.__get_SizeColumn( status ) elif column == self.col_mime_type: value = self.__get_MimeTypeColumn( self.project_info.getProperty( status.path, 'svn:mime-type' ) ) elif column == self.col_eol_style: value = self.__get_EolStyleColumn( self.project_info.getProperty( status.path, 'svn:eol-style' ) ) elif column == self.col_lock_owner: value = self.__get_LockOwnerColumn( status ) elif column == self.col_lock_comment: value = self.__get_LockCommentColumn( status ) else: value = 'Opss' return value def OnGetItemAttr( self, index ): if( self.project_info.need_checkout or self.project_info.need_upgrade ): colour = wb_config.colour_status_need_checkout else: colour = self.statusColour( self.all_files[ index ] ) 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 isItemImageFolder(self, item): if( self.project_info.need_checkout or self.project_info.need_upgrade ): return True elif self.GetItemIsDir( item ): return True else: return False def GetItemIsDir(self, item): status = self.all_files[ item ] if status.entry is None: is_dir = wb_platform_specific.uPathIsdir( status.path ) else: is_dir = status.entry.kind == pysvn.node_kind.dir return is_dir def __get_NameColumn( self, status, prefix_len ): if status.entry is None: is_dir = wb_platform_specific.uPathIsdir( status.path ) else: is_dir = status.entry.kind == pysvn.node_kind.dir if is_dir: filename = status.path + os.sep else: filename = status.path return filename[prefix_len:] def __get_SizeColumn( self, status ): type( status ) return '%d' % 0 def __get_EolStyleColumn( self, value ): if value is None: value = '' return value def __get_MimeTypeColumn( self, value ): if value is None: value = '' return value def __get_StateColumn( self, status ): if status.path in self.getAllGreyFilenames(): return '--' return self.statusFormatString( status ) def __get_TypeColumn( self, status ): if status.entry is None: return '' else: return str(status.entry.kind) def __get_AuthorColumn( self, status ): if status.entry is None or status.entry.commit_author is None: return '' else: return status.entry.commit_author def __get_RevisionColumn( self, status ): if status.entry is None or status.entry.commit_revision.number < 0: return '' else: return str(status.entry.commit_revision.number) def __get_DateColumn( self, status ): if status.entry is None or status.entry.commit_time == 0: return '' else: #qqq# should not need to force the locale! #qqq# locale.setlocale( locale.LC_ALL, self.app._setlocale_value ) return wb_subversion_utils.fmtDateTime( status.entry.commit_time ) def __get_LockCommentColumn( self, status ): if status.repos_lock is not None and status.repos_lock.comment is not None: comment = status.repos_lock.comment.replace( '\n', ' ' ) elif status.entry is not None and status.entry.lock_comment is not None: comment = status.entry.lock_comment.replace( '\n', ' ' ) else: comment = '' return comment def __get_LockOwnerColumn( self, status ): if status.repos_lock is not None: owner = status.repos_lock.owner elif status.entry is not None and status.entry.lock_owner is not None: owner = status.entry.lock_owner else: owner = '' return owner def getState( self, all_rows ): if len(all_rows) == 0: return None state = wb_list_panel_common.ListItemState( self.project_info ) if self.project_info.need_upgrade: state.need_upgrade = True return state if self.project_info.need_checkout: state.need_checkout = True state.ui_project_parent = True state.versioned = True return state state.modified = True state.new_versioned = True state.versioned = True state.unversioned = True state.need_checkin = True state.conflict = True state.file_exists = True state.is_folder = True state.revertable = True for row in all_rows: filename = self.all_files[ row ].path if not wb_platform_specific.uPathExists( filename ): state.file_exists = False if wb_platform_specific.uPathIsdir( filename ): state.modified = False state.conflict = False state.file_exists = False state.revertable = False else: state.is_folder = False text_status = self.getTextStatus( row ) if text_status in [pysvn.wc_status_kind.unversioned, pysvn.wc_status_kind.ignored]: state.versioned = False if text_status not in [pysvn.wc_status_kind.unversioned, pysvn.wc_status_kind.ignored]: state.unversioned = False if text_status not in [pysvn.wc_status_kind.added]: state.new_versioned = False prop_status = self.getPropStatus( row ) if( text_status not in [pysvn.wc_status_kind.modified ,pysvn.wc_status_kind.conflicted] and prop_status not in [pysvn.wc_status_kind.modified ,pysvn.wc_status_kind.conflicted] ): state.modified = False if( text_status not in [pysvn.wc_status_kind.added ,pysvn.wc_status_kind.deleted ,pysvn.wc_status_kind.replaced ,pysvn.wc_status_kind.modified] and prop_status not in [pysvn.wc_status_kind.added ,pysvn.wc_status_kind.deleted ,pysvn.wc_status_kind.replaced ,pysvn.wc_status_kind.modified] ): state.need_checkin = False if text_status not in [pysvn.wc_status_kind.conflicted]: state.conflict = False # revertable status calculation text_reverable = (text_status in [pysvn.wc_status_kind.added ,pysvn.wc_status_kind.deleted ,pysvn.wc_status_kind.missing ,pysvn.wc_status_kind.replaced ,pysvn.wc_status_kind.modified ,pysvn.wc_status_kind.conflicted]) prop_revertable = (prop_status in [pysvn.wc_status_kind.modified ,pysvn.wc_status_kind.deleted ,pysvn.wc_status_kind.replaced ,pysvn.wc_status_kind.conflicted]) if not text_reverable and not prop_revertable: state.revertable = False return state def getStatusFromRowOrStatus( self, row_or_status ): if type(row_or_status) == types.IntType: return self.all_files[ row_or_status ] return row_or_status def mayOpen( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) if status.entry is None: return not wb_platform_specific.uPathIsdir( self.getFilename( row_or_status ) ) else: return status.entry.kind == pysvn.node_kind.dir def getFilename( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) return status.path def getStatusString( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) return wb_subversion_utils._status_format( status ) def getStatusAndFilenames( self, all_rows ): return [(self.getStatusString( row_or_status ), self.getFilename( row_or_status )) for row_or_status in all_rows] def isControlled( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) return status.entry is not None def getUrl( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) if status.entry is None: return '' else: return status.entry.url def getConflictOld( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) if status.entry is None: return '' else: return os.path.join( os.path.dirname( status.path ), status.entry.conflict_old ) def getConflictNew( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) if status.entry is None: return '' else: return os.path.join( os.path.dirname( status.path ), status.entry.conflict_new ) def getConflictMine( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) if status.entry is None: return '' else: return os.path.join( os.path.dirname( status.path ), status.entry.conflict_work ) def getTextStatus( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) return status.text_status def getPropStatus( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) return status.prop_status def statusFormatString( self, status ): return wb_subversion_utils._status_format( status ) def getAllGreyFilenames( self ): raise NotImplementedError def statusColour( self, status ): # default colour when nothing special is know for the file colour = wb_config.colour_status_normal # show that a file is on the clipboard if status.path in self.getAllGreyFilenames(): colour = wb_config.colour_status_disabled # show that a file is uncontrolled elif status.entry is None: colour = wb_config.colour_status_unversioned else: # show a file is locked if( status.is_locked ): 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 = wb_config.colour_status_modified return colour #------------------------------------------------------------ def Cmd_File_Annotate( self, all_rows ): for filename in [self.getFilename( row ) for row in all_rows]: self.app.setProgress( T_('Annotating %(count)d'), 0 ) self.app.setAction( T_('Annotate %s...') % filename ) yield self.app.backgroundProcess ok = False try: annotation = self.project_info.client_bg.annotate( filename ) ok = True except pysvn.ClientError, e: self.app.log_client_error( e ) yield self.app.foregroundProcess if not ok: break h_frame = wb_subversion_annotate.AnnotateFrame( self.app, self.project_info, filename, annotation ) h_frame.Show( True ) self.app.clearProgress() self.app.setAction( T_('Ready') ) def Cmd_File_DiffWorkBase( self, all_rows ): for filename in [self.getFilename( row ) for row in all_rows]: self.app.setAction( T_('Diff BASE %s...') % filename ) info1 = wb_subversion_diff.PathInfoForDiff() info1.path = filename info1.revision = pysvn.Revision( pysvn.opt_revision_kind.base ) info1.title = filename + '@BASE' info2 = wb_subversion_diff.PathInfoForDiff() info2.path = filename info2.revision = pysvn.Revision( pysvn.opt_revision_kind.working ) info2.title = filename generator = wb_subversion_diff.subversionDiffFiles( self.app, self.project_info, info1, info2 ) 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 self.app.setAction( T_('Ready') ) def Cmd_File_DiffWorkHead( self, all_rows ): for filename, url in [(self.getFilename( row ), self.getUrl( row )) for row in all_rows]: self.app.setAction( T_('Diff HEAD %s...') % filename ) info1 = wb_subversion_diff.PathInfoForDiff() info1.path = filename info1.revision = pysvn.Revision( pysvn.opt_revision_kind.head ) info1.title = filename + '@HEAD' info2 = wb_subversion_diff.PathInfoForDiff() info2.path = filename info2.revision = pysvn.Revision( pysvn.opt_revision_kind.working ) info2.title = filename generator = wb_subversion_diff.subversionDiffFiles( self.app, self.project_info, info1, info2 ) 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 self.app.setAction( T_('Ready') ) def Cmd_File_DiffWorkBranchOriginBase( self, all_rows ): self.app.setAction( T_("Retrieving branch info...") ) yield self.app.backgroundProcess branch_url = self.getBranchUrl() yield self.app.foregroundProcess if branch_url is None: wx.MessageBox( T_('"%s" is not a branch.') % self.project_info.url, T_("Error"), style=wx.OK|wx.ICON_ERROR ) else: branch_origin_url = self.getBranchOriginUrl() for filename, url in [(self.getFilename( row ), self.getUrl( row )) for row in all_rows]: origin_url = branch_origin_url + url[len(branch_url):] self.app.setAction( T_('Diff branch origin BASE %s...') % filename ) info1 = wb_subversion_diff.PathInfoForDiff() info1.path = origin_url info1.revision = self.getBranchOriginRevision() info1.title = '%s@%d' % (filename, self.getBranchOriginRevision().number) info2 = wb_subversion_diff.PathInfoForDiff() info2.path = filename info2.revision = pysvn.Revision( pysvn.opt_revision_kind.working ) info2.title = filename generator = wb_subversion_diff.subversionDiffFiles( self.app, self.project_info, info1, info2 ) 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 self.app.setAction( T_('Ready') ) def Cmd_File_DiffWorkBranchOriginHead( self, all_rows ): self.app.setAction( T_("Retrieving branch info...") ) yield self.app.backgroundProcess branch_url = self.getBranchUrl() yield self.app.foregroundProcess if branch_url is None: wx.MessageBox( T_('"%s" is not a branch.') % self.project_info.url, T_("Error"), style=wx.OK|wx.ICON_ERROR ) else: branch_origin_url = self.getBranchOriginUrl() for filename, url in [(self.getFilename( row ), self.getUrl( row )) for row in all_rows]: origin_url = branch_origin_url + url[len(branch_url):] self.app.setAction( T_('Diff branch origin HEAD %s...') % filename ) info1 = wb_subversion_diff.PathInfoForDiff() info1.path = origin_url info1.revision = pysvn.Revision( pysvn.opt_revision_kind.head ) info1.title = '%s@HEAD' % filename info2 = wb_subversion_diff.PathInfoForDiff() info2.path = filename info2.revision = pysvn.Revision( pysvn.opt_revision_kind.working ) info2.title = filename generator = wb_subversion_diff.subversionDiffFiles( self.app, self.project_info, info1, info2 ) 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 self.app.setAction( T_('Ready') ) def Cmd_File_History( self, all_rows ): dialog = wb_subversion_history.LogHistoryDialog( self.app, self.app.frame.tree_panel.tree_ctrl ) result = dialog.ShowModal() if result != wx.ID_OK: return for filename in [self.getFilename( row ) for row in all_rows]: self.app.setAction( T_('Log history %s...') % filename ) yield self.app.backgroundProcess ok = False try: file_url, history_entries = wb_subversion_history.getHistoryEntries( self.project_info, filename, dialog.getLimit(), dialog.getRevisionEnd(), dialog.getIncludeTags(), dialog.getRevisionStart() ) ok = True except pysvn.ClientError, e: self.app.log_client_error( e ) yield self.app.foregroundProcess if not ok: break h_frame = wb_subversion_history.HistoryFileFrame( self.app, self.project_info, filename, file_url, history_entries ) h_frame.Show( True ) self.app.setAction( T_('Ready') ) def Cmd_File_Info( self, all_rows ): for filename in [self.getFilename( row ) for row in all_rows]: try: entry = self.project_info.client_fg.info2( filename, recurse=False ) dialog = wb_subversion_info_dialog.InfoDialog( self.app, self.list_panel.list_ctrl, filename, entry ) dialog.ShowModal() except pysvn.ClientError, e: self.app.log_client_error( e ) def Cmd_File_Lock( self, all_rows ): all_filenames = [self.getFilename( row ) for row in all_rows] comment, force = self.app.getLockMessage( T_('Lock'), self.getStatusAndFilenames( all_rows ) ) if not comment: return for filename in all_filenames: self.app.setProgress( T_('Locking %(count)d'), 0 ) self.app.setAction( T_('Locking %s...') % filename ) yield self.app.backgroundProcess ok = False try: self.project_info.client_bg.lock( filename, comment=comment, force=force ) ok = True except pysvn.ClientError, e: self.app.log_client_error( e ) yield self.app.foregroundProcess if not ok: break self.app.clearProgress() self.app.setAction( T_('Ready') ) self.app.refreshFrame() def Cmd_File_Properties( self, all_rows ): client_fg = self.project_info.client_fg for filename in [self.getFilename( row ) for row in all_rows]: try: prop_list = client_fg.proplist( filename, revision=pysvn.Revision( pysvn.opt_revision_kind.working ) ) if len(prop_list) == 0: prop_dict = {} else: _, prop_dict = prop_list[0] if wb_platform_specific.uPathIsdir( filename ): dialog = wb_subversion_properties_dialog.DirPropertiesDialog( self.app, self.list_panel.list_ctrl, filename, prop_dict ) else: dialog = wb_subversion_properties_dialog.FilePropertiesDialog( self.app, self.list_panel.list_ctrl, filename, prop_dict ) if dialog.ShowModal() == wx.OK: for present, name, value in dialog.getModifiedProperties(): if not present: # delete name client_fg.propdel( name, filename ) else: # add/update name value client_fg.propset( name, value, filename ) except pysvn.ClientError, e: self.app.log_client_error( e ) break self.app.refreshFrame() def Cmd_File_Unlock( self, all_rows ): confirmed, force = self.app.confirmForceAction( T_('Unlock'), self.getStatusAndFilenames( all_rows ) ) if not confirmed: return for filename, url in [(self.getFilename( row ), self.getUrl( row )) for row in all_rows]: self.app.setProgress( T_('Unlocking %(count)d'), 0 ) self.app.setAction( T_('Unlocking %s...') % filename ) yield self.app.backgroundProcess ok = False try: if force: self.project_info.client_bg.unlock( url, force=True ) else: self.project_info.client_bg.unlock( filename, force=False ) ok = True except pysvn.ClientError, e: self.app.log_client_error( e ) yield self.app.foregroundProcess if not ok: break self.app.clearProgress() self.app.setAction( T_('Ready') ) self.app.refreshFrame() class SortList: def __init__( self, sort_data, project_info ): self.sort_data = sort_data self.project_info = project_info def __call__( self, a, b ): # called to cmp a_field = self.getField( a, self.sort_data.getField() ) b_field = self.getField( b, self.sort_data.getField() ) ordering = cmp( a_field, b_field ) if ordering == 0: a_path = self.getField( a, SubversionListHandlerCommon.col_name ) b_path = self.getField( b, SubversionListHandlerCommon.col_name ) ordering = cmp( a_path, b_path ) return ordering else: return ordering * self.sort_data.getOrder() def getField( self, status, field ): __pychecker__ = '--no-returnvalues' if field == SubversionListHandlerCommon.col_name: return status.path.lower() if field == SubversionListHandlerCommon.col_state: # Use positive text_status first # then positive prop_status # other wise use negative text_status text_val = wb_subversion_utils.wc_status_kind_text_sort_map[ status.text_status ] if text_val > 0: return text_val prop_val = wb_subversion_utils.wc_status_kind_text_sort_map[ status.prop_status ] if prop_val > 0: return prop_val + wb_subversion_utils.prop_sort_offset if text_val < 0: return -text_val if self.sort_data.getOrder(): return 999 else: return 0 if field == SubversionListHandlerCommon.col_revision: if status.entry is None or status.entry.commit_revision.number == 0: if self.sort_data.getOrder() > 0: return 9999999 else: return 0 else: return status.entry.commit_revision.number if field == SubversionListHandlerCommon.col_author: if status.entry is None or status.entry.commit_author is None: if self.sort_data.getOrder() > 0: return (1, u'') else: return (-1, u'') else: return (0, status.entry.commit_author) if field == SubversionListHandlerCommon.col_date: if status.entry is None: return 0 else: return status.entry.commit_time if field == SubversionListHandlerCommon.col_type: if status.entry is None: return pysvn.node_kind.none else: return status.entry.kind if field == SubversionListHandlerCommon.col_eol_style: value = self.project_info.getProperty( status.path, 'svn:eol-style' ) if value in ['', None]: if self.sort_data.getOrder() > 0: value = (1, u'') else: value = (-1, u'') else: value = (0, value) return value if field == SubversionListHandlerCommon.col_mime_type: return self.project_info.getProperty( status.path, 'svn:mime-type' ) if field == SubversionListHandlerCommon.col_lock_owner: if status.repos_lock is not None: value = status.repos_lock.owner elif status.entry is not None: value = status.entry.lock_owner else: value = u'' if value in ['', None]: if self.sort_data.getOrder() > 0: value = (1, u'') else: value = (-1, u'') else: value = (0, value) return value if field == SubversionListHandlerCommon.col_lock_comment: if status.repos_lock is not None: value = status.repos_lock.comment.replace( '\n', ' ' ) elif status.entry is not None and status.entry.lock_comment is not None: value = status.entry.lock_comment.replace( '\n', ' ' ) else: value = u'' if value in ['', None]: if self.sort_data.getOrder() > 0: value = (1, u'') else: value = (-1, u'') else: value = (0, value) return value raise wb_exceptions.InternalError( 'SortList does not support field %s' % field ) class SortListCtrl(SortList): def __init__( self, all_files, sort_data, project_info ): SortList.__init__( self, sort_data, project_info ) self.all_files = all_files def getField( self, index, field ): return SortList.getField( self, self.all_files[index], field ) WorkBench-1.8.2/Source/I18N/000755 000765 000024 00000000000 13221441402 015576 5ustar00barrystaff000000 000000 WorkBench-1.8.2/Source/wb_platform_specific.py000644 000765 000024 00000002253 11507143125 021662 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_platform_specific.py ''' import sys if sys.platform == 'win32': from wb_platform_win32_specific import * elif sys.platform == 'darwin': from wb_platform_macosx_specific import * else: from wb_platform_unix_specific import * 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' ) def getLastCheckinMessageFilename(): return os.path.join( getApplicationDir(), 'log_message.txt' ) def getLastLockMessageFilename(): return os.path.join( getApplicationDir(), 'lock_message.txt' ) def setupPlatform(): app_dir = getApplicationDir() if not os.path.exists( app_dir ): os.makedirs( app_dir ) WorkBench-1.8.2/Source/wb_diff_main.py000644 000765 000024 00000003255 12565047474 020127 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_diff_main.py ''' VERSION_STRING = "Uncontrolled" import sys # make sure that we get 2.8 or 3.0 and not an earlier version if not hasattr(sys, 'frozen'): import wxversion wxversion.select( ['3.0', '2.8'] ) def noTranslate(msg): return msg import __builtin__ __builtin__.__dict__['T_'] = noTranslate __builtin__.__dict__['U_'] = noTranslate import wx import wb_diff_frame import wb_preferences import wb_platform_specific class WbDiffApp(wx.App): def __init__( self, file1, file2 ): self.file1 = file1 self.file2 = file2 self.log = self self.prefs = wb_preferences.Preferences( self, wb_platform_specific.getPreferencesFilename(), wb_platform_specific.getOldPreferencesFilename() ) wx.App.__init__( self, 0 ) def OnInit( self ): self.frame = wb_diff_frame.DiffFrame( self, None, self.file1, self.file1, self.file2, self.file2 ) self.frame.Show( True ) self.SetTopWindow( self.frame ) return True def info( self, *arg ): pass def main(): if len(sys.argv) < 3: print 'Usages: wb_diff file1 file2' return 1 file1 = sys.argv[1] file2 = sys.argv[2] diff_app = WbDiffApp( file1, file2 ) diff_app.MainLoop() return 0 if __name__ == '__main__': main() WorkBench-1.8.2/Source/wb_read_file.py000644 000765 000024 00000003427 11507143125 020107 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2005-2010 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_read_file.py ''' import locale import codecs import wb_platform_specific def readFileContentsAsUnicode( filename ): f = wb_platform_specific.uOpen( filename, 'r' ) contents = f.read() f.close() return contentsAsUnicode( contents ) def contentsAsUnicode( contents ): encoding = encodingFromContents( contents ) try: return contents.decode( encoding ) except UnicodeDecodeError: try: # use the choosen encoding and replace chars in error return contents.decode( encoding, 'replace' ) except UnicodeDecodeError: # fall back to latin-1 return contents.decode( 'iso8859-1', 'replace' ) def encodingFromContents( contents ): if( len(contents) > len(codecs.BOM_UTF8) and contents[0:len(codecs.BOM_UTF8)] == codecs.BOM_UTF8 ): encoding = 'utf-8' elif( len(contents) > len(codecs.BOM_UTF16_LE) and contents[0:len(codecs.BOM_UTF16_LE)] in [codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE]): encoding = 'utf-16' elif( len(contents) > len(codecs.BOM_UTF32_LE) and contents[0:len(codecs.BOM_UTF32_LE)] in [codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE]): encoding = 'utf-32' else: encoding = locale.getdefaultlocale()[1] # Mac says mac-roman when utf-8 is what is required if encoding == 'mac-roman': encoding = 'utf-8' if encoding is None: encoding = 'iso8859-1' return encoding WorkBench-1.8.2/Source/wb_version.py000644 000765 000024 00000000631 13221441402 017646 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2006 Barry A Scott. All rights reserved. This software is licensed as described in the file LICENSE.txt, which you should have received as part of this distribution. ==================================================================== wb_version.py.template ''' major = 1 minor = 8 patch = 2 build = 1931 WorkBench-1.8.2/Source/wb_common.mak000644 000765 000024 00000000536 11062162543 017604 0ustar00barrystaff000000 000000 wb_version.py: wb_version.py.template ../Builder/brand_version.py ../Builder/version.info $(PYTHON) -u make_wb_version.py wb_images.py: make_wb_images.py $(PYTHON) -u make_wb_images.py run: $(PYTHON) -u wb_main.py check: $(PYTHON) -c "import wb_pychecker;import wb_main;wb_pychecker.report()" clean:: rm -f wb_version.py rm -f wb_images.py WorkBench-1.8.2/Source/tlog.cmd000644 000765 000024 00000000123 10007315155 016551 0ustar00barrystaff000000 000000 setlocal type "%USERPROFILE%\Application data\Workbench\workbench.log" endlocal WorkBench-1.8.2/Source/wb_subversion_project_info.py000644 000765 000024 00000041407 12575253342 023146 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2012 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_subversion_project_info.py ''' import sys import os import time import fnmatch import threading import types import pysvn import wx import wb_source_control_providers import wb_subversion_history import wb_subversion_annotate import wb_ids import wb_exceptions import wb_subversion_tree_handler import wb_subversion_list_handler import wb_subversion_utils import wb_platform_specific import wb_subversion_utils _fast_proplist = True class ErrorWrapper: def __init__( self, args ): self.args = args class ProjectInfo(wb_source_control_providers.ProjectInfo): def __init__( self, app, parent ): wb_source_control_providers.ProjectInfo.__init__( self, app, parent, 'subversion' ) self.url = None self.wc_path = None self.client_fg = None self.client_bg = None self.dir_status = None self.all_files_status = [] self.all_tree_files_status = [] self.need_checkout = True self.need_upgrade = False self.need_properties = False self.files_properties = {} self.tags_url = None self.branches_url = None self.notification_of_files_in_conflict = 0 def __repr__( self ): return '' % (self.wc_path,) #return '' % (self.url, self.wc_path) def init( self, project_name, **kws): wb_source_control_providers.ProjectInfo.init( self, project_name ) # subversion specific values self.url = kws['url'] self.wc_path = kws['wc_path'] if kws.has_key( 'client_fg' ): self.client_fg = kws['client_fg'] self.client_bg = kws['client_bg'] else: # need one client/project/thread self.client_fg = pysvn.Client() self.client_fg.exception_style = 1 self.client_fg.commit_info_style = 1 self.client_fg.callback_get_login = wb_exceptions.TryWrapper( self.app.log, self.app.getCredentials ) self.client_fg.callback_ssl_server_trust_prompt = wb_exceptions.TryWrapper( self.app.log, self.getServerTrust ) self.client_bg = pysvn.Client() self.client_bg.exception_style = 1 self.client_bg.commit_info_style = 1 self.client_bg.callback_get_login = CallFunctionOnMainThread( self.app, self.app.getCredentials ) self.client_bg.callback_ssl_server_trust_prompt = CallFunctionOnMainThread( self.app, self.getServerTrust ) self.initNotify() self.tags_url = kws.get( 'tags_url', '' ) self.branches_url = kws.get( 'branches_url', '' ) # default the tags and branches URLs url_parts = self.url.split('/') if self.tags_url == '' and 'trunk' in url_parts: trunk_index = url_parts.index('trunk') url_parts[ trunk_index ] = 'tags' self.tags_url = '/'.join( url_parts[:trunk_index+1] ) url_parts = self.url.split('/') if self.branches_url == '' and 'trunk' in url_parts: trunk_index = url_parts.index('trunk') url_parts[ trunk_index ] = 'branches' self.branches_url = '/'.join( url_parts[:trunk_index+1] ) def getTagsUrl( self, rel_url ): if self.parent is not None: return self.parent.getTagsUrl( rel_url ) return self.expandedLabelUrl( self.tags_url, rel_url ) def getBranchesUrl( self, rel_url ): if self.parent is not None: return self.parent.getBranchesUrl( rel_url ) return self.expandedLabelUrl( self.branches_url, rel_url ) def expandedLabelUrl( self, label_url, rel_url ): if label_url is '': return '' label_url_parts = label_url.split('/') wild_parts = 0 while label_url_parts[-1] == '*': del label_url_parts[-1] wild_parts += 1 if wild_parts == 0: return label_url # replace wild_part dirs from the rel_url assert( rel_url[0:len(self.url)] == self.url ) suffix_parts = rel_url[len(self.url)+1:].split('/') label_url_parts.extend( suffix_parts[0:wild_parts] ) return '/'.join( label_url_parts ) def initNotify( self ): self.notification_of_files_in_conflict = 0 self.client_fg.callback_notify = wb_exceptions.TryWrapper( self.app.log, self.callback_notify ) self.client_bg.callback_notify = wb_exceptions.TryWrapper( self.app.log, self.callback_notify ) def callback_notify( self, arg_dict ): # must send messages to the foreground thread to do IO or Linux pthreads hangs #print 'Notify: %r' % arg_dict # nothing to print if no path if arg_dict['path'] == '': return action = arg_dict['action'] if( action == pysvn.wc_notify_action.commit_postfix_txdelta or action == pysvn.wc_notify_action.annotate_revision ): self.app.foregroundProcess( self.app.incProgress, () ) return if wb_subversion_utils.version_info.notify_action_has_failed_lock: if action in [pysvn.wc_notify_action.failed_lock, pysvn.wc_notify_action.failed_unlock]: self.app.log_client_error( ErrorWrapper( arg_dict['error'] ) ) return # see if we want to handle this action if wb_subversion_utils.wc_notify_action_lookup( arg_dict['action'] ) is None: return # reject updates for paths that have no change if( action == pysvn.wc_notify_action.update_update and arg_dict['content_state'] == pysvn.wc_notify_state.unknown and arg_dict['prop_state'] == pysvn.wc_notify_state.unknown ): return if wb_subversion_utils.wc_notify_type_lookup( action ) == 'U': # count the interesting update event self.app.foregroundProcess( self.app.incProgress, () ) # count the number of files in conflict action_letter = wb_subversion_utils.wc_notify_action_lookup( action ) if( arg_dict['content_state'] == pysvn.wc_notify_state.conflicted or arg_dict['prop_state'] == pysvn.wc_notify_state.conflicted ): action_letter = 'C' self.notification_of_files_in_conflict += 1 # print anything that gets through the filter try: path = arg_dict['path'].decode( 'utf-8' ) except ValueError: path = arg_dict['path'] msg = u'%s %s\n' % (action_letter, path) self.app.foregroundProcess( sys.stdout.write, (msg.encode( 'utf-8' ),) ) def readPreferences( self, get_option ): wb_source_control_providers.ProjectInfo.readPreferences( self, get_option ) # load state from a preference file name = get_option.getstr( 'name' ) url = get_option.getstr( 'url' ) wc_path = get_option.getstr( 'wc_path' ) if get_option.has( 'tags_url' ): tags_url = get_option.getstr( 'tags_url' ) else: tags_url = '' if get_option.has( 'branches_url' ): branches_url = get_option.getstr( 'branches_url' ) else: branches_url = '' # expand any ~/ or ~user/ in the path wc_path = os.path.expanduser( wc_path ) self.init( name, url=url, wc_path=wc_path, tags_url=tags_url, branches_url=branches_url ) def writePreferences( self, pref_dict ): # save state into a preference file wb_source_control_providers.ProjectInfo.writePreferences( self, pref_dict ) pref_dict[ 'url' ] = self.url pref_dict[ 'wc_path' ] = self.wc_path pref_dict[ 'tags_url' ] = self.tags_url pref_dict[ 'branches_url' ] = self.branches_url def isEqual( self, pi ): return (self.provider_name == pi.provider_name and self.wc_path == pi.wc_path) def isChild( self, pi ): if type(pi) == types.StringType: # see if the wc path of the parent is a prefix of the child wc_path_dir = self.wc_path + os.path.sep return pi[:len(wc_path_dir)] == wc_path_dir else: # true if pi is a child of this node # only look at our pi's if self.provider_name != pi.provider_name: return False # see if the wc path of the parent is a prefix of the child wc_path_dir = self.wc_path + os.path.sep return pi.wc_path[:len(wc_path_dir)] == wc_path_dir def getWorkingDir( self ): return self.wc_path def getServerTrust( self, trust_data ): realm = trust_data['realm'] info_list = [] info_list.append( ( T_('Hostname'), trust_data['hostname']) ) info_list.append( ( T_('Valid From'), trust_data['valid_from']) ) info_list.append( ( T_('Valid Until'), trust_data['valid_until']) ) info_list.append( ( T_('Issuer Name'), trust_data['issuer_dname']) ) info_list.append( ( T_('Finger Print'), trust_data['finger_print']) ) trust, save = self.app.getServerTrust( realm, info_list, True ) return trust, trust_data['failures'], save def setNeedProperties( self, state ): self.need_properties = state def updateStatus( self ): self.app.log.debug( 'updateStatus() %r' % self ) self.all_files_status = [] self.files_properties = {} self.dir_status = None self.need_checkout = True if not wb_platform_specific.uPathExists( self.wc_path ): self.app.log.debug( 'updateStatus() wc_path does not exist' ) return p = self.app.prefs.getView() try: self.need_upgrade = False entry = self.client_fg.info2( self.wc_path, recurse=False )[0][1] except pysvn.ClientError, e: self.app.log.debug( 'updateStatus() info2 exception %r' % (e,) ) if wb_subversion_utils.version_info.has_upgrade: # is it the 'Working copy XXX is too old' error? if e.args[1][0][1] == pysvn.svn_err.wc_upgrade_required: self.need_checkout = False self.need_upgrade = True return # is it the 'is not a working copy' error? # of the 'Node not found' error? if e.args[1][0][1] in (pysvn.svn_err.wc_not_directory, pysvn.svn_err.wc_path_not_found): # need_checkout return print 'Error: %s' % e.args[0] return if entry is None: self.url = '' else: self.url = entry.URL self.all_files_status = self.client_fg.status( self.wc_path, recurse=p.view_recursive, ignore=False ) self.need_checkout = False # sort list self.all_files_status.sort( wb_subversion_utils.by_path ) # remember dir_status before filtering if len(self.all_files_status) > 0 and os.path.normcase( self.all_files_status[0].path ) == os.path.normcase( self.wc_path ): self.dir_status = self.all_files_status[0] del self.all_files_status[0] self.app.log.debug( 'updateStatus() self.dir_status %r' % (self.dir_status,) ) # filter show only the files that the user is interested in self.all_files_status = self.filterFilenames( self.all_files_status ) if self.need_properties: # see if the depth API is available if wb_subversion_utils.version_info.has_depth: try: if p.view_recursive: path_properties = self.client_fg.proplist( self.wc_path, depth=pysvn.depth.infinity ) else: path_properties = self.client_fg.proplist( self.wc_path, depth=pysvn.depth.immediates ) for path, prop_dict in path_properties: self.files_properties[ os.path.abspath( path ) ] = prop_dict except pysvn.ClientError, e: pass else: if _fast_proplist: for status in self.all_files_status: if status.is_versioned: self.files_properties[ os.path.abspath( status.path ) ] = self.__proplist( status.path ) else: try: name_list = [status.path for status in self.all_files_status if status.is_versioned] path_properties = self.client_fg.proplist( name_list, recurse=False ) for path, prop_dict in path_properties: self.files_properties[ os.path.abspath( path ) ] = prop_dict except pysvn.ClientError, e: pass if p.view_recursive: wc_path_num_parts = len( self.wc_path.split( os.sep ) ) + 1 self.all_tree_files_status = [] for file in self.all_files_status: if len( file.path.split( os.sep ) ) == wc_path_num_parts: self.all_tree_files_status.append( file ) else: self.all_tree_files_status = self.all_files_status def filterFilenames( self, all_files ): p = self.app.prefs.getView() filtered_all_files = [] # divide the files into # ignored, uncontroller and controlled # and see if the user wishes to see them for f in all_files: if f.text_status == pysvn.wc_status_kind.ignored: if p.view_ignored: filtered_all_files.append( f ) elif f.text_status == pysvn.wc_status_kind.unversioned: if p.view_uncontrolled: filtered_all_files.append( f ) else: if p.view_controlled: filtered_all_files.append( f ) return filtered_all_files def getProperty( self, filename, propname ): d = self.files_properties.get( filename, {} ) prop = d.get( propname, None ) return prop def getTreeFilesStatus( self ): return self.all_tree_files_status def getFilesStatus( self ): return self.all_files_status def getDirStatus( self ): return self.dir_status def __proplist( self, path ): if wb_platform_specific.uPathIsdir( 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 = wb_platform_specific.uOpen( prop_file ) except EnvironmentError: try: f = wb_platform_specific.uOpen( base_prop_file ) except EnvironmentError: return result while True: line = f.readline() if line == '': break if line == 'END\n': break code, length = line.split() body = f.read( int(length)+1 ) if code == 'K': key = body[:-1] elif code == 'V': result[ key ] = body[:-1] else: raise ValueError( 'Unparsed line %s' % line ) f.close() return result # # Used to allow a call to function on the background thread # to block until the result return on the main thread is available # class CallFunctionOnMainThread: def __init__( self, app, function ): self.app = app self.function = function self.cv = threading.Condition() self.result = None def __call__( self, *args ): self.app.log.debug( 'CallFunctionOnMainThread.__call__ calling %r' % self.function ) self.cv.acquire() self.app.foregroundProcess( self._onMainThread, args ) self.cv.wait() self.cv.release() self.app.log.debug( 'CallFunctionOnMainThread.__call__ returning %r' % self.function ) return self.result def _onMainThread( self, *args ): self.app.log.debug( 'CallFunctionOnMainThread._onMainThread calling %r' % self.function ) try: self.result = self.function( *args ) finally: pass self.cv.acquire() self.cv.notify() self.cv.release() self.app.log.debug( 'CallFunctionOnMainThread._onMainThread returning %r' % self.function ) WorkBench-1.8.2/Source/wb_clipboard.py000644 000765 000024 00000001261 11507143125 020126 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_clipboard.py ''' class Clipboard: def __init__( self, all_filenames, is_copy ): self.__is_copy = is_copy self.__all_filenames = all_filenames def isCopy( self ): return self.__is_copy def isCut( self ): return not self.__is_copy def getAllFilenames( self ): return self.__all_filenames WorkBench-1.8.2/Source/macosx.mak000644 000765 000024 00000000421 10724204653 017112 0ustar00barrystaff000000 000000 # # Mac OS X makefile for WorkBench # all: wb_version.py wb_images.py locale/en/LC_MESSAGES/pysvn_workbench.mo locale/en/LC_MESSAGES/pysvn_workbench.mo: ./make-pot-file.sh ./make-po-file.sh en ./make-mo-files.sh locale clean:: rm -rf locale/* include wb_common.mak WorkBench-1.8.2/Source/wb_subversion_report_lock.py000644 000765 000024 00000023410 11662230071 022770 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2006-2011 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_subversion_report_lock.py ''' import wx import sys import wb_images import time import pysvn import wb_config import wb_ids import wb_images import wb_exceptions import wb_list_panel_common import wb_subversion_utils import wb_subversion_list_handler_common class ReportLockFrame(wx.Frame): def __init__( self, app, project_info, all_files, show_repos_locks=False ): if show_repos_locks: title = T_("Repository Lock Report") else: title = T_("Working Copy Lock Report") wx.Frame.__init__( self, None, -1, title, size=(700,500) ) self.app = app self.menu_actions = wx.Menu() self.menu_actions.Append( wb_ids.id_SP_DiffWorkHead, T_('Diff WC vs. HEAD...'), T_('Diff WC vs. HEAD...') ) self.menu_actions.Append( wb_ids.id_SP_DiffWorkBranchOriginBase, T_('Diff WC vs. branch origin BASE...'), T_('Diff WC vs. branch origin BASE...') ) self.menu_actions.Append( wb_ids.id_SP_DiffWorkBranchOriginHead, T_('Diff WC vs. branch origin HEAD...'), T_('Diff WC vs. branch origin HEAD...') ) self.menu_actions.AppendSeparator() self.menu_actions.Append( wb_ids.id_SP_Annotate, T_('Annotate...'), T_('Annotate...') ) self.menu_actions.Append( wb_ids.id_SP_History, T_('Log history...'), T_('Log history...') ) self.menu_actions.Append( wb_ids.id_SP_Info, T_('Information...'), T_('Information...') ) self.menu_bar = wx.MenuBar() self.menu_bar.Append( self.menu_actions, T_("&Actions") ) self.SetMenuBar( self.menu_bar ) # Add tool bar t = self.CreateToolBar( name="main", style=wx.TB_HORIZONTAL ) bitmap_size = (32,32) t.SetToolBitmapSize( bitmap_size ) t.AddSimpleTool( wb_ids.id_SP_DiffWorkHead, wb_images.getBitmap( 'toolbar_images/diff.png', bitmap_size ), T_('Diff changes against HEAD'), T_('Diff changes against HEAD') ) t.AddSimpleTool( wb_ids.id_SP_History, wb_images.getBitmap( 'toolbar_images/history.png', bitmap_size ), T_('Show History log'), T_('Show History log') ) t.AddSimpleTool( wb_ids.id_SP_Info, wb_images.getBitmap( 'toolbar_images/info.png', bitmap_size ), T_('File Information'), T_('File Information') ) t.AddSeparator() t.AddSimpleTool( wb_ids.id_SP_Lock, wb_images.getBitmap( 'toolbar_images/lock.png', bitmap_size ), T_('Lock File'), T_('Lock File') ) t.AddSimpleTool( wb_ids.id_SP_Unlock, wb_images.getBitmap( 'toolbar_images/unlock.png', bitmap_size ), T_('Unlock File'), T_('Unlock File') ) t.Realize() try_wrapper = wb_exceptions.TryWrapperFactory( self.app.log ) # Set the application icon self.SetIcon( wb_images.getIcon( 'wb.png' ) ) # create the individule panels self.panel_list = ReportLockListPanel( app, self, self, show_repos_locks ) wx.EVT_CLOSE( self, self.OnCloseWindow ) wx.EVT_BUTTON( self.panel_list, wx.ID_OK, self.app.eventWrapper( self.OnOk ) ) self.project_info = ReportLockProjectInfo( project_info, all_files ) self.list_handler = ReportLockListHandler( self.app, self.panel_list, self.project_info, show_repos_locks ) # draw the list - its Lock the status info self.panel_list.setHandler( self.list_handler ) wx.EVT_MENU( self, wb_ids.id_SP_Annotate, self.app.eventWrapper( self.OnSpAnnotate ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkHead, self.app.eventWrapper( self.OnSpDiffWorkHead ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkBranchOriginBase, self.app.eventWrapper( self.OnSpDiffWorkBranchOriginBase ) ) wx.EVT_MENU( self, wb_ids.id_SP_DiffWorkBranchOriginHead, self.app.eventWrapper( self.OnSpDiffWorkBranchOriginHead ) ) wx.EVT_MENU( self, wb_ids.id_SP_History, self.app.eventWrapper( self.OnSpHistory ) ) wx.EVT_MENU( self, wb_ids.id_SP_Info, self.app.eventWrapper( self.OnSpInfo ) ) wx.EVT_MENU( self, wb_ids.id_SP_Lock, self.app.eventWrapper( self.OnSpLock ) ) wx.EVT_MENU( self, wb_ids.id_SP_Unlock, self.app.eventWrapper( self.OnSpUnlock ) ) def clearUpdateUiState( self ): pass def getUpdateUiState( self ): pass def setEventHandler( self, handler ): self.handler = handler def OnCloseWindow( self, event ): self.Destroy() def OnCancel( self, event ): self.Destroy() def OnOk( self, event ): self.Destroy() # command events def OnSpAnnotate( self, event ): return self.panel_list.OnSpAnnotate() def OnSpDiffWorkHead( self, event ): return self.panel_list.OnSpDiffWorkHead() def OnSpDiffWorkBranchOriginBase( self, event ): return self.panel_list.OnSpDiffWorkBranchOriginBase() def OnSpDiffWorkBranchOriginHead( self, event ): return self.panel_list.OnSpDiffWorkBranchOriginHead() def OnSpHistory( self, event ): return self.panel_list.OnSpHistory() def OnSpInfo( self, event ): return self.panel_list.OnSpInfo() def OnSpLock( self, event ): return self.panel_list.OnSpLock() def OnSpUnlock( self, event ): return self.panel_list.OnSpUnlock() class ReportLockListHandler(wb_subversion_list_handler_common.SubversionListHandlerCommon): def __init__( self, app, parent, project_info, show_repos_locks ): wb_subversion_list_handler_common.SubversionListHandlerCommon.__init__( self, app, parent, project_info ) self.show_repos_locks = show_repos_locks self.all_excluded_files = {} # use the repos status in the report def getTextStatus( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) return status.repos_text_status # use the repos status in the report def getPropStatus( self, row_or_status ): status = self.getStatusFromRowOrStatus( row_or_status ) return status.repos_prop_status def statusFormatString( self, status ): text_code = wb_subversion_utils.wc_status_kind_map[ status.repos_text_status ] prop_code = wb_subversion_utils.wc_status_kind_map[ status.repos_prop_status ] if text_code == ' ': text_code = '_' if prop_code != ' ': prop_code = '_' wc_lock = ' ' if status.entry is not None and status.entry.lock_token is not None: wc_lock = 'K' repos_lock = ' ' if status.repos_lock is not None: repos_lock = 'O' if wc_lock == ' ': wc_lock = '_' return '%s%s%s%s' % (text_code, prop_code, wc_lock, repos_lock) def setupColumnInfo( self ): self.column_info.setFrom( [ T_('State'), T_('Name'), T_('Lock Owner'), T_('Lock Comment') ], [5, 40, 10, 60] ) def statusColour( self, file ): # show that a file is on the exclude list if file.path in self.getAllGreyFilenames(): return wb_config.colour_status_disabled else: return wb_config.colour_status_normal def getContextMenu( self ): menu_template = \ [('', wb_ids.id_SP_DiffWorkHead, T_('Diff WC vs. HEAD...') ) ,('', wb_ids.id_SP_DiffWorkBranchOriginBase, T_('Diff WC vs. branch origin BASE...') ) ,('', wb_ids.id_SP_DiffWorkBranchOriginHead, T_('Diff WC vs. branch origin HEAD...') ) ,('-', 0, 0 ) ,('', wb_ids.id_SP_Annotate, T_('Annotate...') ) ,('', wb_ids.id_SP_History, T_('Log history...') ) ,('', wb_ids.id_SP_Info, T_('Information...') ) ,('-', 0, 0 ) ,('', wb_ids.id_SP_Lock, T_('Lock...') ) ,('', wb_ids.id_SP_Unlock, T_('Unlock...') ) ] return wb_subversion_utils.populateMenu( wx.Menu(), menu_template ) def getAllGreyFilenames( self ): # show all excluded files in grey return self.all_excluded_files def getReportLockFiles( self ): return [entry.path for entry in self.project_info.all_files if entry.path not in self.all_excluded_files] class ReportLockProjectInfo: def __init__( self, project_info, all_files ): self.all_files = all_files self.need_properties = False self.project_name = project_info.project_name self.url = project_info.url self.wc_path = project_info.wc_path self.need_checkout = False self.need_upgrade = False self.client_fg = project_info.client_fg self.client_bg = project_info.client_bg def getTagsUrl( self, rel_url ): return None def setNeedProperties( self, need_properties ): self.need_properties = need_properties def updateStatus( self ): pass def getFilesStatus( self ): return self.all_files def getProperty( self, filename, prop_name ): return '' def getWorkingDir( self ): return self.wc_path class ReportLockListPanel(wb_list_panel_common.WbListPanelCommon): def __init__( self, app, frame, parent, show_repos_locks ): wb_list_panel_common.WbListPanelCommon.__init__( self, app, frame, parent ) self.show_repos_locks = show_repos_locks def addToSizer( self, v_sizer ): pass def getAcceleratorTableInit( self ): acc_init =[ (wx.ACCEL_CMD, ord('D'), wb_ids.id_SP_DiffWorkHead), (wx.ACCEL_CMD, ord('L'), wb_ids.id_SP_History), ] return acc_init WorkBench-1.8.2/Source/wb_preferences.py000644 000765 000024 00000102747 12575764146 020525 0ustar00barrystaff000000 000000 ''' ==================================================================== Copyright (c) 2003-2010 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_preferences.py ''' import pprint import os import types import ConfigParser import UserDict import copy import xml.parsers.expat import xml.dom.minidom import xml.sax.saxutils import wx import wb_platform_specific import wb_source_control_providers import wb_toolbars 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, pref_filename, old_pref_filename ): self.app = app self.pref_filename = pref_filename self.old_pref_filename = old_pref_filename self.pref_data = None # all the preference section handles get created here self.pref_handlers = {} self.pref_handlers['Projects'] = ProjectsPreferences( self.app ) self.pref_handlers['Bookmarks'] = BookmarksPreferences( self.app ) self.pref_handlers['Window'] = WindowPreferences( self.app ) self.pref_handlers['DiffWindow'] = DiffWindowPreferences( self.app ) self.pref_handlers['View'] = ViewPreferences( self.app ) self.pref_handlers['Editor'] = EditorPreferences( self.app ) self.pref_handlers['Shell'] = ShellPreferences( self.app ) self.pref_handlers['DiffTool'] = DiffToolPreferences( self.app ) self.pref_handlers['LogHistory'] = LogHistoryPreferences( self.app ) self.pref_handlers['Toolbar'] = ToolbarPreferences( self.app ) self.pref_handlers['Advanced'] = AdvancedPreferences( self.app ) # read preferences into the handlers self.readPreferences() def readPreferences( self ): 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 ): try: handler.readPreferences( self.pref_data ) except ConfigParser.Error: self.app.log.error( 'Preferences.readPreferences()', exc_info=1 ) def __getattr__( self, name ): # support getProjects(), getFoobars() etc. if name[0:3] == 'get': section_name = name[3:] if self.pref_handlers.has_key( section_name ): return self.pref_handlers[ section_name ] raise AttributeError, '%s has no attribute %s' % (self.__class__.__name__, name ) def writePreferences( self ): try: for handler in self.pref_handlers.values(): self.pref_data.remove_section( handler.section_name ) self.pref_data.add_section( handler.section_name ) handler.writePreferences( self.pref_data ) # write the prefs so that a failure to write does not # destroy the original # also keep one backup copy new_name = self.pref_filename + '.tmp' old_name = self.pref_filename + '.old' f = wb_platform_specific.uOpen( new_name, 'w' ) self.pref_data.write( f ) f.close() if wb_platform_specific.uPathExists( self.pref_filename ): if wb_platform_specific.uPathExists( old_name ): # os.rename does not delete automatically on Windows. wb_platform_specific.uRemove( old_name ) wb_platform_specific.uRename( self.pref_filename, old_name ) wb_platform_specific.uRename( new_name, self.pref_filename ) self.app.log.info( T_('Wrote preferences to %s') % self.pref_filename ) except EnvironmentError, 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 wb_platform_specific.uPathExists( xml_pref_filename ): log.info( T_('Reading preferences from %s') % xml_pref_filename ) self.__readXml( xml_pref_filename ) else: log.info( T_('Reading preferences from %s') % ini_pref_filename ) self.__readIni( ini_pref_filename ) def __readXml( self, xml_pref_filename ): try: f = wb_platform_specific.uOpen( 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 not self.all_sections[ section_name ].has_key( option_name ): return 0 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 getfloat( self, section_name, option_name ): return float( 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: quoted_value = xml.sax.saxutils.escape( unicode( value ) ).encode('utf-8') f.write( '%*s<%s>%s\n' % (indent, '', key_name, quoted_value, key_name) ) class PreferenceSection: def __init__( self, section_name ): self.section_name = section_name def readPreferences( self, pref_data ): pass def writePreferences( self, pref_data ): pass # support being returned by the __getattr__ above def __call__( self ): return self class GetOption: def __init__( self, pref_data, section_name ): self.pref_data = pref_data self.section_name = section_name def has( self, name ): return self.pref_data.has_option( self.section_name, name ) def getstr( self, name ): return self.pref_data.get( self.section_name, name ).strip() def getint( self, name ): return self.pref_data.getint( self.section_name, name ) def getfloat( self, name ): return self.pref_data.getfloat( self.section_name, name ) def getbool( self, name ): return self.pref_data.getboolean( self.section_name, name ) def getstrlist( self, name, sep ): s = self.getstr( name ) if len(s) == 0: return [] return [p.strip() for p in s.split( sep )] class SetOption: def __init__( self, pref_data, section_name ): self.pref_data = pref_data self.section_name = section_name def set( self, name, value, sep='' ): if type(value) == types.ListType: value = sep.join( value ) self.pref_data.set( self.section_name, name, value ) 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 name in self.pref_list[ self.index ] def get( self, name ): return self.pref_list[ self.index ][ name ] def getstr( self, name ): return self.get( name ).strip() def getint( self, name ): return int( self.getstr( name ) ) def getfloat( self, name ): return float( self.getstr( name ) ) def getbool( self, name ): return self.getstr( name ).lower() == 'true' class ProjectsPreferences(PreferenceSection): def __init__( self, app ): PreferenceSection.__init__( self, 'Projects' ) self.app = app self.all_projects = {} def readPreferences( self, pref_data ): if not pref_data.has_section( self.section_name ): return 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, 'project' ) provider = get_option.getstr( 'provider' ) if wb_source_control_providers.hasProvider( provider ): provider = wb_source_control_providers.getProvider( provider ) pi = provider.getProjectInfo( self.app ) pi.readPreferences( get_option ) self.all_projects[ pi.project_name ] = pi def writePreferences( self, pref_data ): pref_data.remove_section( self.section_name ) pref_data.add_section( self.section_name ) 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.lower(), b.project_name.lower() ) def getProjectList( self ): pl = self.all_projects.values() pl.sort( self._by_project_name ) return pl def addProject( self, pi ): self.all_projects[ pi.project_name ] = pi def delProject( self, pi ): del self.all_projects[ pi.project_name ] class BookmarksPreferences(PreferenceSection): def __init__( self, app ): PreferenceSection.__init__( self, 'Bookmarks' ) self.app = app self.all_bookmarks = {} # no longer support menu style - # self.menu_style = 'leaf_only' # self.leaf_names_to_ignore = ['main','source','src', 'inc','include'] self.menu_style = None self.leaf_names_to_ignore = [] def readPreferences( self, pref_data ): if not pref_data.has_section( self.section_name ): return get_option = GetOption( pref_data, self.section_name ) # look for menu_style and leaf_names_to_ignore to # allow for preference file update if get_option.has( 'menu_style' ): self.menu_style = get_option.getstr( 'menu_style' ) if get_option.has( 'leaf_names_to_ignore' ): self.leaf_names_to_ignore = get_option.getstrlist( 'leaf_names_to_ignore', ',' ) 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, 'bookmark' ) bookmark_name = get_option.getstr( 'bookmark_name' ) provider = get_option.getstr( 'provider' ) if wb_source_control_providers.hasProvider( provider ): provider = wb_source_control_providers.getProvider( provider ) pi = provider.getProjectInfo( self.app ) pi.readPreferences( get_option ) # default the menu name if required if pi.menu_name is None: # see if update required if self.menu_style is not None: # default the name as if used to be done pi.menu_name = self.__getMenuName( pi.wc_path ) else: self.__defaultMenuName( pi ) self.all_bookmarks[ bookmark_name ] = pi def writePreferences( self, pref_data ): pref_data.remove_section( self.section_name ) pref_data.add_section( self.section_name ) set_option = SetOption( pref_data, self.section_name ) all_bookmarks = self.all_bookmarks.items() all_bookmarks.sort( key=self.__keyBookmarksMenuAndName ) for bookmark_name, bookmark in all_bookmarks: pref_dict = {} pref_dict[ 'bookmark_name' ] = bookmark_name bookmark.writePreferences( pref_dict ) pref_data.append_dict( self.section_name, 'bookmark', pref_dict ) def __keyBookmarksMenuAndName( self, a_kv ): a = a_kv[1] k = [] if a.menu_folder != '': k.append( a.menu_folder ) if a.menu_folder2 != '': k.append( a.menu_folder2 ) if a.menu_folder3 != '': k.append( a.menu_folder3 ) k.append( a.menu_name ) return k def addBookmark( self, pi, name=None ): if name is None: name = pi.wc_path self.all_bookmarks[ name ] = pi self.__defaultMenuName( pi ) def delBookmark( self, bookmark_name ): del self.all_bookmarks[ bookmark_name ] def delAllBookmarks( self, name ): self.all_bookmarks = {} def getBookmarkNames( self ): names = self.all_bookmarks.keys() names.sort() return names def __defaultMenuName( self, pi ): if os.environ.has_key( 'HOME' ): home_dir = os.environ[ 'HOME' ] + '/' if pi.wc_path.startswith( home_dir ): pi.menu_name = pi.wc_path[len(home_dir):] else: pi.menu_name = pi.wc_path else: pi.menu_name = pi.wc_path # only used to update pref file now def __getMenuName( self, wc_path, menu_style=None, leaf_names_to_ignore=None ): if menu_style is None: menu_style = self.menu_style if leaf_names_to_ignore is None: leaf_names_to_ignore = self.leaf_names_to_ignore if menu_style in ['leaf_in_parent', 'leaf_only']: path_parts = wc_path.split( os.path.sep ) for leaf_index in range( len(path_parts)-1, 1, -1 ): if path_parts[ leaf_index ].lower() not in leaf_names_to_ignore: if menu_style == 'leaf_in_parent': return '%s in %s' % (os.path.sep.join( path_parts[leaf_index:] ) ,os.path.sep.join( path_parts[:leaf_index] )) else: return os.path.sep.join( path_parts[leaf_index:] ) # cannot reach here? return wc_path else: return wc_path def hasBookmark( self, name ): return self.all_bookmarks.has_key( name ) def getBookmark( self, name ): return self.all_bookmarks[ name ] class WindowPreferences(PreferenceSection): def __init__( self, app ): PreferenceSection.__init__( self, 'Window' ) self.app = app self.h_sash_ratio = 0.7 self.v_sash_ratio = 0.2 self.__frame_size = wx.Size( 700, 500 ) self.frame_position = wx.DefaultPosition self.maximized = False self.zoom = 0 def readPreferences( self, pref_data ): get_option = GetOption( pref_data, self.section_name ) x = get_option.getint( 'pos_x' ) if x < 0: x = 0 y = get_option.getint( 'pos_y' ) if y < 0: y = 0 self.frame_position = wx.Point( x, y ) w = get_option.getint( 'width' ) h = get_option.getint( 'height' ) self.__frame_size = wx.Size( w, h ) self.maximized = get_option.getbool( 'maximized' ) if get_option.has( 'zoom' ): self.zoom = get_option.getint( 'zoom' ) if get_option.has( 'h_sash_ratio' ): self.h_sash_ratio = get_option.getfloat( 'h_sash_ratio' ) if get_option.has( 'v_sash_ratio' ): self.v_sash_ratio = get_option.getfloat( 'v_sash_ratio' ) def writePreferences( self, pref_data ): set_option = SetOption( pref_data, self.section_name ) set_option.set( 'pos_x', self.frame_position.x ) set_option.set( 'pos_y', self.frame_position.y ) set_option.set( 'width', self.__frame_size.GetWidth() ) set_option.set( 'height', self.__frame_size.GetHeight() ) set_option.set( 'maximized', self.maximized ) set_option.set( 'zoom', self.zoom ) set_option.set( 'h_sash_ratio', self.h_sash_ratio ) set_option.set( 'v_sash_ratio', self.v_sash_ratio ) def getFrameSize( self ): return self.__frame_size def setFrameSize( self, size ): self.__frame_size = size class DiffWindowPreferences(PreferenceSection): def __init__( self, app ): PreferenceSection.__init__( self, 'DiffWindow' ) self.app = app self.__frame_size = wx.Size( 700, 500 ) self.frame_position = wx.DefaultPosition self.maximized = False self.zoom = 0 def readPreferences( self, pref_data ): get_option = GetOption( pref_data, self.section_name ) x = get_option.getint( 'pos_x' ) if x < 0: x = 0 y = get_option.getint( 'pos_y' ) if y < 0: y = 0 self.frame_position = wx.Point( x, y ) w = get_option.getint( 'width' ) h = get_option.getint( 'height' ) self.__frame_size = wx.Size( w, h ) self.maximized = get_option.getbool( 'maximized' ) if get_option.has( 'zoom' ): self.zoom = get_option.getint( 'zoom' ) def writePreferences( self, pref_data ): set_option = SetOption( pref_data, self.section_name ) set_option.set( 'pos_x', self.frame_position.x ) set_option.set( 'pos_y', self.frame_position.y ) set_option.set( 'width', self.__frame_size.GetWidth() ) set_option.set( 'height', self.__frame_size.GetHeight() ) set_option.set( 'maximized', self.maximized ) set_option.set( 'zoom', self.zoom ) def getFrameSize( self ): return self.__frame_size def setFrameSize( self, size ): self.__frame_size = size class ViewPreferences(PreferenceSection): def __init__( self, app ): PreferenceSection.__init__( self, 'View' ) self.app = app self.auto_refresh = True self.sort_order = 1 self.sort_field = 'Name' self.view_ignored = False self.view_controlled = True self.view_uncontrolled = True self.view_recursive = False self.view_onlychanges = False self.column_order = ['State','Name','Date','Rev','Author','Type'] self.column_widths = ['4','25','14','4','10','4'] def readPreferences( self, pref_data ): get_option = GetOption( pref_data, self.section_name ) if get_option.has( 'auto_refresh' ): self.auto_refresh = get_option.getbool( 'auto_refresh' ) if get_option.has( 'sort_order' ): self.sort_order = get_option.getint( 'sort_order' ) if get_option.has( 'sort_field' ): self.sort_field = get_option.getstr( 'sort_field' ) if get_option.has( 'view_ignored' ): self.view_ignored = get_option.getbool( 'view_ignored' ) if get_option.has( 'view_controlled' ): self.view_controlled = get_option.getbool( 'view_controlled' ) if get_option.has( 'view_uncontrolled' ): self.view_uncontrolled = get_option.getbool( 'view_uncontrolled' ) if get_option.has( 'view_recursive' ): self.view_recursive = get_option.getbool( 'view_recursive' ) if get_option.has( 'view_onlychanges' ): self.view_onlychanges = get_option.getbool( 'view_onlychanges' ) if get_option.has( 'column_order' ): self.column_order = get_option.getstrlist( 'column_order', ',' ) if get_option.has( 'column_widths' ): self.column_widths = get_option.getstrlist( 'column_widths', ',' ) if self.sort_field not in self.column_order: self.sort_field = 'Name' # always view controlled on startup self.view_controlled = True # avoid a blank list box on startup self.view_onlychanges = False def writePreferences( self, pref_data ): set_option = SetOption( pref_data, self.section_name ) set_option.set( 'auto_refresh', self.auto_refresh ) set_option.set( 'sort_order', self.sort_order ) set_option.set( 'sort_field', self.sort_field ) set_option.set( 'view_ignored', self.view_ignored ) set_option.set( 'view_controlled', self.view_controlled ) set_option.set( 'view_uncontrolled', self.view_uncontrolled ) set_option.set( 'view_recursive', self.view_recursive ) set_option.set( 'view_onlychanges', self.view_onlychanges ) set_option.set( 'column_order', self.column_order, ',' ) set_option.set( 'column_widths', self.column_widths, ',' ) class EditorPreferences(PreferenceSection): def __init__( self, app ): PreferenceSection.__init__( self, 'Editor' ) self.app = app self.editor_image = '' self.editor_options = '' def readPreferences( self, pref_data ): get_option = GetOption( pref_data, self.section_name ) if get_option.has( 'editor' ): self.editor_image = get_option.getstr( 'editor' ) if get_option.has( 'editor_options' ): self.editor_options = get_option.getstr( 'editor_options' ) def writePreferences( self, pref_data ): set_option = SetOption( pref_data, self.section_name ) if self.editor_image: set_option.set( 'editor', self.editor_image ) if self.editor_options: set_option.set( 'editor_options', self.editor_options ) class ShellPreferences(PreferenceSection): def __init__( self, app ): PreferenceSection.__init__( self, 'Shell' ) self.app = app self.shell_init_command = '' self.shell_terminal = '' self.shell_file_browser = '' def readPreferences( self, pref_data ): get_option = GetOption( pref_data, self.section_name ) if get_option.has( 'init_command' ): self.shell_init_command = get_option.getstr( 'init_command' ) if get_option.has( 'terminal' ): self.shell_terminal = get_option.getstr( 'terminal' ) if get_option.has( 'file_browser' ): self.shell_file_browser = get_option.getstr( 'file_browser' ) def writePreferences( self, pref_data ): set_option = SetOption( pref_data, self.section_name ) if self.shell_init_command: set_option.set( 'init_command', self.shell_init_command ) if self.shell_terminal: set_option.set( 'terminal', self.shell_terminal ) if self.shell_file_browser: set_option.set( 'file_browser', self.shell_file_browser ) class DiffToolPreferences(PreferenceSection): def __init__( self, app ): PreferenceSection.__init__( self, 'DiffTool' ) self.app = app self.diff_tool_mode = 'built-in' self.gui_diff_tool = '' self.shell_diff_tool = '' self.gui_diff_tool_options = '' self.shell_diff_tool_options = '' def readPreferences( self, pref_data ): get_option = GetOption( pref_data, self.section_name ) if get_option.has( 'diff_tool_mode' ): self.diff_tool_mode = get_option.getstr( 'diff_tool_mode' ) if get_option.has( 'diff_tool' ): self.gui_diff_tool = get_option.getstr( 'diff_tool' ) if get_option.has( 'shell_diff_tool' ): self.shell_diff_tool = get_option.getstr( 'shell_diff_tool' ) if get_option.has( 'diff_tool_options' ): self.gui_diff_tool_options = get_option.getstr( 'diff_tool_options' ) if get_option.has( 'shell_diff_tool_options' ): self.shell_diff_tool_options = get_option.getstr( 'shell_diff_tool_options' ) def writePreferences( self, pref_data ): set_option = SetOption( pref_data, self.section_name ) if self.diff_tool_mode != 'built-in': set_option.set( 'diff_tool_mode', self.diff_tool_mode ) if self.gui_diff_tool != '': set_option.set( 'diff_tool', self.gui_diff_tool ) if self.shell_diff_tool != '': set_option.set( 'shell_diff_tool', self.shell_diff_tool ) if self.gui_diff_tool_options != '': 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 ) class LogHistoryPreferences(PreferenceSection): def __init__( self, app ): PreferenceSection.__init__( self, 'LogHistory' ) self.app = app self.default_mode = 'show_all' self.default_limit = 20 self.default_since_days_interval = 7 self.default_include_tags = False def readPreferences( self, pref_data ): get_option = GetOption( pref_data, self.section_name ) if get_option.has( 'default_mode' ): self.default_mode = get_option.getstr( 'default_mode' ) if get_option.has( 'default_limit' ): self.default_limit = get_option.getint( 'default_limit' ) if get_option.has( 'default_since_days_interval' ): self.default_since_days_interval = get_option.getint( 'default_since_days_interval' ) if get_option.has( 'default_include_tags' ): self.default_include_tags = get_option.getbool( 'default_include_tags' ) def writePreferences( self, pref_data ): set_option = SetOption( pref_data, self.section_name ) set_option.set( 'default_mode', self.default_mode ) set_option.set( 'default_limit', self.default_limit ) set_option.set( 'default_since_days_interval', self.default_since_days_interval ) set_option.set( 'default_include_tags', self.default_include_tags ) class ToolbarPreferences(PreferenceSection): def __init__( self, app ): PreferenceSection.__init__( self, 'Toolbar' ) self.app = app self.toolbar_enable = True self.horizontal_orientation = True self.bitmap_size = 32 self.group_order = wb_toolbars.toolbar_main.getAllGroupNames() def readPreferences( self, pref_data ): get_option = GetOption( pref_data, self.section_name ) if get_option.has( 'toolbar_enable' ): self.toolbar_enable = get_option.getbool( 'toolbar_enable' ) if get_option.has( 'horizontal_orientation' ): self.horizontal_orientation = get_option.getbool( 'horizontal_orientation' ) if get_option.has( 'bitmap_size' ): self.bitmap_size = get_option.getint( 'bitmap_size' ) if get_option.has( 'group_order' ): self.group_order = get_option.getstrlist( 'group_order', ',' ) def writePreferences( self, pref_data ): set_option = SetOption( pref_data, self.section_name ) set_option.set( 'toolbar_enable', self.toolbar_enable ) set_option.set( 'horizontal_orientation', self.horizontal_orientation ) set_option.set( 'bitmap_size', self.bitmap_size ) set_option.set( 'group_order', self.group_order, ',' ) class AdvancedPreferences(PreferenceSection): def __init__( self, app ): PreferenceSection.__init__( self, 'Advanced' ) self.app = app self.arbitrary_tag_branch = False def readPreferences( self, pref_data ): get_option = GetOption( pref_data, self.section_name ) if get_option.has( 'arbitrary_tag_branch' ): self.arbitrary_tag_branch = get_option.getbool('arbitrary_tag_branch') def writePreferences( self, pref_data ): set_option = SetOption( pref_data, self.section_name ) set_option.set( 'arbitrary_tag_branch', self.arbitrary_tag_branch ) 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() # only used in development so not using tempfile module # as the file names need to be easy to find p = Preferences( FakeApp(), '/tmp/t.xml', '/tmp/t.ini' ) pprint.pprint( p.pref_data.all_sections ) p.writePreferences() WorkBench-1.8.2/Source/I18N/pysvn_workbench_de.po000644 000765 000024 00000160354 11261604745 022055 0ustar00barrystaff000000 000000 # translation of pysvn_workbench_de.current.po to # translation of pysvn_workbench_de.current.po to Deutsch # translation of de.po to # This file is distributed under the same license as the pysvn/WorkBench package. # Copyright (C) 2009. # Dirk Bächle , 2009. # Dirk Baechle , 2009. # msgid "" msgstr "" "Project-Id-Version: pysvn_workbench_de.current\n" "Report-Msgid-Bugs-To: barryscott@tigris.org\n" "POT-Creation-Date: 2009-09-30 19:53+0200\n" "PO-Revision-Date: 2009-09-30 22:26+0200\n" "Last-Translator: Dirk Bächle \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: KBabel 1.10\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: wb_app.py:111 wb_frame.py:43 msgid "PySVN WorkBench" msgstr "PySVN WorkBench" #: wb_app.py:185 msgid "Work Bench starting" msgstr "WorkBench wird gestartet" #: wb_bookmarks_dialogs.py:58 wb_frame.py:158 msgid "Manage Bookmarks" msgstr "Lesezeichen verwalten" #: wb_bookmarks_dialogs.py:73 wb_bookmarks_dialogs.py:74 #: wb_bookmarks_dialogs.py:75 wb_bookmarks_dialogs.py:76 msgid "Menu" msgstr "Menü" #: wb_bookmarks_dialogs.py:77 msgid "WC Path" msgstr "WC Pfad" #: wb_bookmarks_dialogs.py:93 wb_preferences_dialog.py:374 msgid " Delete " msgstr " Löschen " #: wb_bookmarks_dialogs.py:96 msgid " Properties " msgstr "Eigenschaften" #: wb_bookmarks_dialogs.py:99 wb_bookmarks_dialogs.py:306 wb_dialogs.py:59 #: wb_dialogs.py:187 wb_dialogs.py:244 wb_dialogs.py:314 wb_dialogs.py:388 #: wb_dialogs.py:444 wb_dialogs.py:511 wb_dialogs.py:610 wb_dialogs.py:736 #: wb_preferences_dialog.py:59 wb_project_dialogs.py:103 #: wb_subversion_history.py:228 wb_subversion_info_dialog.py:35 #: wb_subversion_properties_dialog.py:175 msgid " OK " msgstr " OK " #: wb_bookmarks_dialogs.py:100 wb_bookmarks_dialogs.py:307 wb_dialogs.py:60 #: wb_dialogs.py:189 wb_dialogs.py:246 wb_dialogs.py:316 wb_dialogs.py:390 #: wb_dialogs.py:446 wb_dialogs.py:513 wb_dialogs.py:612 wb_dialogs.py:739 #: wb_preferences_dialog.py:61 wb_project_dialogs.py:105 #: wb_subversion_checkin.py:137 wb_subversion_history.py:230 #: wb_subversion_properties_dialog.py:177 wb_subversion_report_updates.py:287 msgid " Cancel " msgstr " Abbrechen " #: wb_bookmarks_dialogs.py:244 msgid "Bookmark Properties" msgstr "Eigenschaften" #: wb_bookmarks_dialogs.py:286 msgid "WC Path: " msgstr "WC Pfad: " #: wb_bookmarks_dialogs.py:290 msgid "Menu1: " msgstr "Menü1: " #: wb_bookmarks_dialogs.py:294 msgid "Menu2: " msgstr "Menü2: " #: wb_bookmarks_dialogs.py:298 msgid "Menu3: " msgstr "Menü3: " #: wb_bookmarks_dialogs.py:302 msgid "Menu Name: " msgstr "Menü Name: " #: wb_dialogs.py:40 msgid "Status" msgstr "Status" #: wb_dialogs.py:42 msgid "Filename" msgstr "Dateiname" #: wb_dialogs.py:56 msgid "Force" msgstr "Erzwingen" #: wb_dialogs.py:134 wb_subversion_checkin.py:140 msgid "Insert Last Message" msgstr "Letzte Lognachricht" #: wb_dialogs.py:164 msgid "Credentials" msgstr "Zugangsdaten" #: wb_dialogs.py:168 msgid "Username:" msgstr "User:" #: wb_dialogs.py:176 msgid "Password:" msgstr "Passwort:" #: wb_dialogs.py:182 msgid "Always uses these credentials" msgstr "Immer diese Logindaten benutzen" #: wb_dialogs.py:227 #, python-format msgid "Trust server %s" msgstr "Server %s vertrauen" #: wb_dialogs.py:232 msgid "Server Certificate" msgstr "Serverzertifikat" #: wb_dialogs.py:239 msgid "Always trust this server" msgstr "Diesem Server immer vertrauen" #: wb_dialogs.py:290 wb_frame.py:114 wb_frame.py:157 #: wb_subversion_list_handler.py:73 msgid "Add" msgstr "Hinzufügen" #: wb_dialogs.py:294 wb_dialogs.py:366 msgid "From:" msgstr "Von:" #: wb_dialogs.py:300 msgid "Force Add" msgstr "Hinzufügen erzwingen" #: wb_dialogs.py:306 msgid "Recursive Add" msgstr "Rekursiv hinzufügen" #: wb_dialogs.py:362 wb_frame.py:115 wb_subversion_list_handler.py:150 #: wb_subversion_list_handler.py:379 wb_subversion_list_handler.py:386 #: wb_subversion_tree_handler.py:794 msgid "Rename" msgstr "Umbenennen" #: wb_dialogs.py:372 msgid "To:" msgstr "Nach:" #: wb_dialogs.py:383 msgid "Force rename" msgstr "Umbenennen erzwingen" #: wb_dialogs.py:436 wb_subversion_info_dialog.py:56 #: wb_subversion_tree_handler.py:513 msgid "Name:" msgstr "Name:" #: wb_dialogs.py:437 msgid "New Folder" msgstr "Neues Verzeichnis" #: wb_dialogs.py:480 wb_dialogs.py:485 msgid "New File" msgstr "Neue Datei" #: wb_dialogs.py:489 msgid "New Filename:" msgstr "Neuer Dateiname:" #: wb_dialogs.py:504 msgid "Template:" msgstr "Template:" #: wb_dialogs.py:554 msgid "Empty directory" msgstr "Leeres Verzeichnis" #: wb_dialogs.py:556 msgid "Children files only" msgstr "Nur Kind-Dateien" #: wb_dialogs.py:557 msgid "Immediate children" msgstr "Direkte Kinder" #: wb_dialogs.py:558 msgid "Only already checked out descendants" msgstr "Nur bereits ausgecheckte Einträge" #: wb_dialogs.py:559 msgid "All descendants (Full recursion)" msgstr "Alles (rekursiv)" #: wb_dialogs.py:570 msgid "HEAD revision" msgstr "HEAD Revision" #: wb_dialogs.py:578 wb_subversion_info_dialog.py:64 #: wb_subversion_info_dialog.py:108 msgid "Revision:" msgstr "Revision:" #: wb_dialogs.py:590 msgid "Apply on" msgstr "Anwenden auf" #: wb_dialogs.py:593 msgid "Recursive (all)" msgstr "Rekursiv (Alle)" #: wb_dialogs.py:599 msgid "Depth:" msgstr "Tiefe:" #: wb_dialogs.py:648 msgid "Please enter a revision number > 0!" msgstr "Bitte geben Sie eine Revisionsnummer > 0 ein!" #: wb_dialogs.py:653 msgid "Please enter digits only!" msgstr "Bitte geben Sie nur Ziffern ein!" #: wb_dialogs.py:705 msgid "Copy From:" msgstr "Kopieren von:" #: wb_dialogs.py:708 msgid "Copy To:" msgstr "Kopieren nach:" #: wb_dialogs.py:733 wb_subversion_checkin.py:133 msgid "Log message" msgstr "Lognachricht" #: wb_dialogs.py:791 wb_frame.py:122 msgid "Create Tag" msgstr "Tag erzeugen" #: wb_dialogs.py:795 wb_frame.py:123 msgid "Create Branch" msgstr "Branch erzeugen" #: wb_diff_frame.py:49 wb_show_diff_frame.py:32 #, python-format msgid "Diff %(title1)s and %(title2)s" msgstr "Diff von %(title1)s gegen %(title2)s" #: wb_diff_frame.py:59 msgid "Expand folds" msgstr "Entfalten" #: wb_diff_frame.py:59 msgid "Expand all folds" msgstr "Alle entfalten" #: wb_diff_frame.py:60 msgid "Collapse folds" msgstr "Zusammenfalten" #: wb_diff_frame.py:60 msgid "Collapse all folds" msgstr "Alle zusammenfalten" #: wb_diff_frame.py:62 msgid "Toggle whitespace" msgstr "Leerzeichen umschalten" #: wb_diff_frame.py:62 msgid "Show/hide whitespace" msgstr "Zeige/Verstecke Leerzeichen" #: wb_diff_frame.py:64 msgid "Previous difference" msgstr "Vorheriger Unterschied" #: wb_diff_frame.py:64 msgid "Positions the cursor at the previous difference between the files" msgstr "Positioniert den Cursor auf den vorherigen Unterschied" #: wb_diff_frame.py:65 msgid "Next difference" msgstr "Nächster Unterschied" #: wb_diff_frame.py:65 msgid "Positions the cursor at the next difference between the files" msgstr "Positioniert den Cursor auf den nächsten Unterschied" #: wb_diff_frame.py:85 msgid "Key: " msgstr "Marke: " #: wb_diff_frame.py:86 msgid "Inserted text " msgstr "Eingefügter Text " #: wb_diff_frame.py:87 msgid "Deleted text " msgstr "Gelöschter Text " #: wb_diff_frame.py:88 msgid "Changed text" msgstr "Geänderter Text" #: wb_diff_frame.py:176 #, python-format msgid "Diff %(change1)d of %(change2)d" msgstr "Diff von %(change1)d gegen %(change2)d" #: wb_frame.py:62 msgid "&Copy" msgstr "Kopieren (&C)" #: wb_frame.py:62 msgid "Copy Files" msgstr "Dateien kopieren" #: wb_frame.py:63 msgid "&Cut" msgstr "Ausschneiden (&X)" #: wb_frame.py:63 msgid "Cut Files" msgstr "Dateien ausschneiden" #: wb_frame.py:64 msgid "&Paste" msgstr "Einfügen (&P)" #: wb_frame.py:64 msgid "Paste Files" msgstr "Dateien einfügen" #: wb_frame.py:66 msgid "&Clear log" msgstr "Logdatei löschen (&L)" #: wb_frame.py:66 msgid "Clear the log window" msgstr "Logfenster löschen" #: wb_frame.py:73 msgid "&Preferences..." msgstr "Einstellungen (&P)" #: wb_frame.py:73 wb_preferences_dialog.py:24 msgid "Preferences" msgstr "Einstellungen" #: wb_frame.py:74 msgid "E&xit" msgstr "Ende (&X)" #: wb_frame.py:74 msgid "Exit the application" msgstr "Anwendung beenden" #: wb_frame.py:77 wb_subversion_tree_handler.py:174 msgid "&Command Shell" msgstr "Shell (&C)" #: wb_frame.py:77 wb_toolbars.py:91 msgid "Command Shell" msgstr "Shell" #: wb_frame.py:78 wb_subversion_tree_handler.py:175 msgid "&File Browser" msgstr "Dateibrowser (&F)" #: wb_frame.py:78 wb_toolbars.py:95 msgid "File Browser" msgstr "Dateibrowser" #: wb_frame.py:80 wb_subversion_checkin.py:38 wb_subversion_checkin.py:335 #: wb_subversion_list_handler.py:42 wb_subversion_report_updates.py:216 msgid "Edit" msgstr "Editieren" #: wb_frame.py:81 wb_subversion_checkin.py:40 wb_subversion_checkin.py:339 #: wb_subversion_list_handler.py:45 wb_subversion_report_updates.py:219 msgid "Open" msgstr "Öffnen" #: wb_frame.py:83 wb_subversion_checkin.py:42 wb_subversion_checkin.py:343 #: wb_subversion_list_handler.py:48 wb_subversion_tree_handler.py:177 msgid "Diff WC vs. BASE..." msgstr "Diff WC vs. BASE..." #: wb_frame.py:84 wb_subversion_checkin.py:43 wb_subversion_checkin.py:344 #: wb_subversion_list_handler.py:49 wb_subversion_report_branch_changes.py:30 #: wb_subversion_report_branch_changes.py:158 wb_subversion_report_lock.py:38 #: wb_subversion_report_lock.py:194 wb_subversion_report_updates.py:38 #: wb_subversion_report_updates.py:222 wb_subversion_tree_handler.py:178 msgid "Diff WC vs. HEAD..." msgstr "Diff WC vs. HEAD..." #: wb_frame.py:85 wb_subversion_checkin.py:44 wb_subversion_checkin.py:345 #: wb_subversion_list_handler.py:50 wb_subversion_report_branch_changes.py:31 #: wb_subversion_report_branch_changes.py:159 wb_subversion_report_lock.py:39 #: wb_subversion_report_lock.py:195 wb_subversion_report_updates.py:39 #: wb_subversion_report_updates.py:223 msgid "Diff WC vs. branch origin BASE..." msgstr "Diff WC vs. Originalbranch BASE..." #: wb_frame.py:86 wb_subversion_checkin.py:45 wb_subversion_checkin.py:346 #: wb_subversion_list_handler.py:51 wb_subversion_report_branch_changes.py:32 #: wb_subversion_report_branch_changes.py:160 wb_subversion_report_lock.py:40 #: wb_subversion_report_lock.py:196 wb_subversion_report_updates.py:40 #: wb_subversion_report_updates.py:224 msgid "Diff WC vs. branch origin HEAD..." msgstr "Diff WC vs. Originalbranch HEAD..." #: wb_frame.py:89 wb_subversion_list_handler.py:52 msgid "Conflict" msgstr "Konflikt" #: wb_frame.py:90 wb_subversion_list_handler.py:53 msgid "Diff Conflict Old vs. Mine..." msgstr "Diff Konflikt Old vs. Mine..." #: wb_frame.py:91 wb_subversion_list_handler.py:54 msgid "Diff Conflict Mine vs. New..." msgstr "Diff Konflikt Mine vs. New..." #: wb_frame.py:92 wb_subversion_list_handler.py:55 msgid "Diff Conflict Old vs. New..." msgstr "Diff Konflikt Old vs. New..." #: wb_frame.py:94 wb_subversion_list_handler.py:57 msgid "Resolved Conflict" msgstr "Konflikt gelöst" #: wb_frame.py:97 wb_subversion_checkin.py:48 wb_subversion_checkin.py:348 #: wb_subversion_list_handler.py:60 wb_subversion_report_branch_changes.py:35 #: wb_subversion_report_branch_changes.py:162 wb_subversion_report_lock.py:43 #: wb_subversion_report_lock.py:198 #: wb_subversion_report_revision_changes.py:50 #: wb_subversion_report_revision_changes.py:279 #: wb_subversion_report_updates.py:43 wb_subversion_report_updates.py:226 msgid "Annotate..." msgstr "Zeilenhistorie..." #: wb_frame.py:98 wb_subversion_checkin.py:49 wb_subversion_checkin.py:349 #: wb_subversion_list_handler.py:61 wb_subversion_report_branch_changes.py:36 #: wb_subversion_report_branch_changes.py:163 wb_subversion_report_lock.py:44 #: wb_subversion_report_lock.py:199 #: wb_subversion_report_revision_changes.py:51 #: wb_subversion_report_revision_changes.py:280 #: wb_subversion_report_updates.py:44 wb_subversion_report_updates.py:227 #: wb_subversion_tree_handler.py:180 msgid "Log history..." msgstr "Logeinträge..." #: wb_frame.py:99 wb_subversion_checkin.py:50 wb_subversion_checkin.py:350 #: wb_subversion_list_handler.py:62 wb_subversion_report_branch_changes.py:37 #: wb_subversion_report_branch_changes.py:164 wb_subversion_report_lock.py:45 #: wb_subversion_report_lock.py:200 #: wb_subversion_report_revision_changes.py:52 #: wb_subversion_report_revision_changes.py:281 #: wb_subversion_tree_handler.py:181 msgid "Information..." msgstr "Information..." #: wb_frame.py:100 wb_subversion_checkin.py:51 wb_subversion_checkin.py:351 #: wb_subversion_list_handler.py:63 wb_subversion_tree_handler.py:182 msgid "Properties..." msgstr "Eigenschaften..." #: wb_frame.py:102 wb_subversion_list_handler.py:68 #: wb_subversion_tree_handler.py:188 msgid "Update" msgstr "Update" #: wb_frame.py:103 msgid "Update to..." msgstr "Update auf..." #: wb_frame.py:104 wb_subversion_list_handler.py:39 #: wb_subversion_tree_handler.py:185 msgid "Checkout" msgstr "Checkout" #: wb_frame.py:105 wb_subversion_tree_handler.py:186 msgid "Checkout to..." msgstr "Checkout auf..." #: wb_frame.py:107 wb_subversion_list_handler.py:71 #: wb_subversion_tree_handler.py:191 msgid "Checkin..." msgstr "Checkin..." #: wb_frame.py:109 wb_subversion_list_handler.py:65 #: wb_subversion_report_lock.py:202 msgid "Lock..." msgstr "Lock..." #: wb_frame.py:110 wb_subversion_list_handler.py:66 #: wb_subversion_report_lock.py:203 msgid "Unlock..." msgstr "Unlock..." #: wb_frame.py:112 wb_subversion_tree_handler.py:193 msgid "New File..." msgstr "Neue Datei..." #: wb_frame.py:113 wb_subversion_tree_handler.py:194 msgid "Make directory..." msgstr "Verzeichnis erstellen..." #: wb_frame.py:115 wb_subversion_list_handler.py:74 #: wb_subversion_tree_handler.py:196 msgid "Rename..." msgstr "Umbenennen..." #: wb_frame.py:117 wb_frame.py:167 wb_subversion_list_handler.py:76 #: wb_subversion_tree_handler.py:198 msgid "Delete..." msgstr "Löschen..." #: wb_frame.py:117 msgid "Delete" msgstr "Löschen" #: wb_frame.py:118 wb_subversion_checkin.py:352 #: wb_subversion_list_handler.py:77 wb_subversion_tree_handler.py:199 msgid "Revert..." msgstr "Revert..." #: wb_frame.py:118 wb_subversion_checkin.py:93 wb_subversion_checkin.py:378 #: wb_subversion_list_handler.py:399 wb_subversion_tree_handler.py:819 msgid "Revert" msgstr "Revert" #: wb_frame.py:120 wb_subversion_list_handler.py:79 #: wb_subversion_tree_handler.py:201 msgid "Clean up" msgstr "Cleanup" #: wb_frame.py:120 msgid "Clean up working copy" msgstr "Cleanup der Arbeitskopie" #: wb_frame.py:122 msgid "Create Tag..." msgstr "Tag erzeugen..." #: wb_frame.py:123 msgid "Create Branch..." msgstr "Branch erzeugen..." #: wb_frame.py:126 msgid "Working copy Locks..." msgstr "Locks der Arbeitskopie..." #: wb_frame.py:126 msgid "Locks held in Working Copy" msgstr "Gehaltene Locks in der Arbeitskopie" #: wb_frame.py:127 msgid "Repository Locks..." msgstr "Locks im Repository..." #: wb_frame.py:127 msgid "Locks held in Repository" msgstr "Gehaltene Locks im Repository" #: wb_frame.py:129 msgid "Changes..." msgstr "Änderungen..." #: wb_frame.py:129 msgid "Changes available for checkin" msgstr "Änderungen für den nächsten Checkin" #: wb_frame.py:130 msgid "Updates..." msgstr "Updates..." #: wb_frame.py:130 msgid "Updates available in the Repository" msgstr "Updates die im Repository bereitstehen" #: wb_frame.py:131 msgid "Branch changes..." msgstr "Branchänderungen..." #: wb_frame.py:131 msgid "Files changed in this branch" msgstr "Dateien die in diesem Branch geändert wurden" #: wb_frame.py:134 msgid "Show &Controlled files" msgstr "Zeige überwachte Dateien (&C)" #: wb_frame.py:134 msgid "Show Controlled files" msgstr "Zeige überwachte Dateien" #: wb_frame.py:135 msgid "Show &Uncontrolled files" msgstr "Zeige nicht überwachte Dateien (&U)" #: wb_frame.py:135 msgid "Show Uncontrolled files" msgstr "Zeige nicht überwachte Dateien" #: wb_frame.py:136 msgid "Show &Ignored files" msgstr "Zeige &ignorierte Dateien" #: wb_frame.py:136 msgid "Show ignored files" msgstr "Zeige ignorierte Dateien" #: wb_frame.py:137 msgid "Show &Only changed files" msgstr "Zeige nur geänderte Dateien (&O)" #: wb_frame.py:137 msgid "Filter out unchanged files" msgstr "Zeige nur geänderte Dateien" #: wb_frame.py:139 msgid "Show &Recursive files" msgstr "Zeige Dateien &Rekursiv" #: wb_frame.py:139 msgid "Show recursive files" msgstr "Zeige Dateien rekursiv" #: wb_frame.py:141 msgid "Use WorkBench Diff" msgstr "WorkBench Diff" #: wb_frame.py:142 msgid "Use External GUI Diff" msgstr "Externes Diff (GUI)" #: wb_frame.py:143 msgid "Use External Text Diff" msgstr "Externes Diff (Text)" #: wb_frame.py:144 msgid "Use SVN Diff" msgstr "SVN Diff" #: wb_frame.py:146 msgid "&Refresh\tF5" msgstr "Aktualisieren (&R)\tF5" #: wb_frame.py:146 msgid "Refresh display" msgstr "Ansicht aktualisieren" #: wb_frame.py:147 msgid "&Automatic Refresh" msgstr "&Automatisch aktualisieren" #: wb_frame.py:147 msgid "Automatic refresh" msgstr "Automatisch aktualisieren" #: wb_frame.py:157 msgid "Add Bookmark" msgstr "Lesezeichen hinzufügen" #: wb_frame.py:158 msgid "Manage..." msgstr "Verwalten..." #: wb_frame.py:164 wb_subversion_tree_handler.py:195 msgid "Add..." msgstr "Hinzufügen..." #: wb_frame.py:164 msgid "Project Add" msgstr "Projekt hinzufügen" #: wb_frame.py:165 msgid "Settings..." msgstr "Eigenschaften..." #: wb_frame.py:165 wb_project_dialogs.py:303 wb_tree_panel.py:574 msgid "Project Settings" msgstr "Projekteigenschaften" #: wb_frame.py:167 wb_tree_panel.py:575 wb_tree_panel.py:616 msgid "Delete Project" msgstr "Projekt löschen" #: wb_frame.py:170 msgid "&About..." msgstr "Über... (&A)" #: wb_frame.py:170 msgid "About the application" msgstr "Über WorkBench" #: wb_frame.py:174 msgid "&File" msgstr "Datei (&F)" #: wb_frame.py:175 msgid "&Edit" msgstr "&Editieren" #: wb_frame.py:176 msgid "&View" msgstr "Ansicht (&V)" #: wb_frame.py:177 wb_subversion_checkin.py:60 #: wb_subversion_report_branch_changes.py:40 wb_subversion_report_lock.py:48 #: wb_subversion_report_revision_changes.py:55 #: wb_subversion_report_updates.py:51 msgid "&Actions" msgstr "&Aktionen" #: wb_frame.py:178 msgid "&Reports" msgstr "Berichte (&R)" #: wb_frame.py:179 msgid "&Bookmarks" msgstr "Lesezeichen (&B)" #: wb_frame.py:180 msgid "&Project" msgstr "&Projekt" #: wb_frame.py:181 msgid "&Help" msgstr "&Hilfe" #: wb_frame.py:198 msgid "Work Bench" msgstr "Work Bench" #: wb_frame.py:200 wb_frame.py:483 wb_subversion_checkin.py:257 #: wb_subversion_list_handler_common.py:704 #: wb_subversion_list_handler_common.py:737 #: wb_subversion_list_handler_common.py:773 #: wb_subversion_list_handler_common.py:821 #: wb_subversion_list_handler_common.py:868 #: wb_subversion_list_handler_common.py:906 #: wb_subversion_list_handler_common.py:951 #: wb_subversion_list_handler_common.py:1017 wb_subversion_list_handler.py:467 #: wb_subversion_list_handler.py:520 #: wb_subversion_report_revision_changes.py:143 #: wb_subversion_report_revision_changes.py:180 #: wb_subversion_report_revision_changes.py:219 #: wb_subversion_report_updates.py:159 wb_subversion_tree_handler.py:244 #: wb_subversion_tree_handler.py:282 wb_subversion_tree_handler.py:296 #: wb_subversion_tree_handler.py:325 wb_subversion_tree_handler.py:348 #: wb_subversion_tree_handler.py:416 wb_subversion_tree_handler.py:451 #: wb_subversion_tree_handler.py:490 wb_subversion_tree_handler.py:629 #: wb_subversion_tree_handler.py:672 wb_subversion_tree_handler.py:779 #: wb_subversion_tree_handler.py:894 msgid "Ready" msgstr "Fertig" #: wb_frame.py:465 #, python-format msgid "Work Bench version: %s" msgstr "Work Bench version: %s" #: wb_frame.py:469 msgid "" "\n" "Copyright Barry Scott (c) 2003-2009. All rights reserved" msgstr "" "\n" "Copyright Barry Scott (c) 2003-2009. All rights reserved" #: wb_frame.py:673 #, python-format msgid "Adding bookmark to %s" msgstr "Lesezeichen zu %s hinzufügen" #: wb_list_panel_common.py:108 wb_list_panel_common.py:511 #: wb_subversion_list_handler_common.py:31 #: wb_subversion_list_handler_common.py:110 #: wb_subversion_list_handler_common.py:243 #: wb_subversion_report_branch_changes.py:147 wb_subversion_report_lock.py:183 #: wb_subversion_report_revision_changes.py:266 #: wb_subversion_report_updates.py:205 msgid "Name" msgstr "Name" #: wb_list_panel_common.py:511 wb_subversion_annotate.py:59 #: wb_subversion_history.py:71 wb_subversion_history.py:377 #: wb_subversion_history.py:503 wb_subversion_list_handler_common.py:35 #: wb_subversion_list_handler_common.py:114 #: wb_subversion_list_handler_common.py:246 msgid "Author" msgstr "Autor" #: wb_preferences_dialog.py:32 msgid "PreferencesDialog" msgstr "Einstellungen" #: wb_preferences_dialog.py:106 msgid "Editor" msgstr "Editor" #: wb_preferences_dialog.py:111 msgid "Editor: " msgstr "Editor:" #: wb_preferences_dialog.py:114 msgid "Edit Arguments: " msgstr "Argumente: " #: wb_preferences_dialog.py:117 wb_preferences_dialog.py:210 #: wb_project_dialogs.py:58 wb_project_dialogs.py:87 wb_project_dialogs.py:477 #: wb_project_dialogs.py:636 msgid " Browse... " msgstr " Suchen... " #: wb_preferences_dialog.py:143 wb_preferences_dialog.py:267 msgid "Executable files (*.exe)|*.exe" msgstr "Ausführbare Dateien (*.exe)|*.exe" #: wb_preferences_dialog.py:145 wb_preferences_dialog.py:269 msgid "Applications|*.app|Executable files|*" msgstr "Anwendungen|*.app|Ausführbare Dateien|*" #: wb_preferences_dialog.py:147 wb_preferences_dialog.py:271 msgid "Executable files|*" msgstr "Ausführbare Dateien|*" #: wb_preferences_dialog.py:152 wb_preferences_dialog.py:276 msgid "Choose an Executable file" msgstr "Wählen Sie bitte eine ausführbare Datei" #: wb_preferences_dialog.py:182 msgid "You must enter a valid editor executable" msgstr "Bitte geben Sie einen ausführbaren Editor an" #: wb_preferences_dialog.py:183 wb_preferences_dialog.py:573 #: wb_preferences_dialog.py:584 wb_preferences_dialog.py:631 #: wb_preferences_dialog.py:644 wb_preferences_dialog.py:695 #: wb_preferences_dialog.py:761 wb_preferences_dialog.py:773 #: wb_preferences_dialog.py:968 wb_subversion_checkin.py:314 #: wb_subversion_list_handler.py:224 wb_subversion_list_handler.py:464 #: wb_subversion_list_handler.py:517 wb_subversion_properties_dialog.py:93 #: wb_subversion_properties_dialog.py:114 wb_subversion_tree_handler.py:355 #: wb_subversion_tree_handler.py:637 wb_subversion_tree_handler.py:640 #: wb_subversion_tree_handler.py:669 wb_subversion_tree_handler.py:815 #: wb_subversion_tree_handler.py:891 msgid "Warning" msgstr "Warnung" #: wb_preferences_dialog.py:193 msgid "Diff Tool" msgstr "Diff" #: wb_preferences_dialog.py:201 msgid "Work Bench Diff" msgstr "Work Bench Diff" #: wb_preferences_dialog.py:201 msgid "External GUI Diff Command" msgstr "Externes Diff (GUI)" #: wb_preferences_dialog.py:201 msgid "External Text Diff" msgstr "Externes Diff (Text)" #: wb_preferences_dialog.py:201 msgid "SVN diff" msgstr "SVN Diff" #: wb_preferences_dialog.py:215 msgid "Mode: " msgstr "Modus: " #: wb_preferences_dialog.py:219 msgid "Diff Tool: " msgstr "Diff Tool: " #: wb_preferences_dialog.py:223 msgid "Tool Arguments: " msgstr "Argumente: " #: wb_preferences_dialog.py:227 msgid "Use" msgstr "Verwenden Sie" #: wb_preferences_dialog.py:228 msgid "" "%nl for left file name, %nr for right file name,\n" "%tl for left title, %tr for right title" msgstr "" "%nl für die linke Datei, %nr für die rechte Datei,\n" "%tl für die linke Überschrift, %tr für die rechte Überschrift" #: wb_preferences_dialog.py:293 msgid "Shell" msgstr "Shell" #: wb_preferences_dialog.py:298 msgid "Terminal Init Command: " msgstr "Terminal-Initkommando: " #: wb_preferences_dialog.py:306 msgid "Terminal Program: " msgstr "Terminal-Programm: " #: wb_preferences_dialog.py:323 msgid "File Browser Program: " msgstr "Dateibrowser: " #: wb_preferences_dialog.py:358 msgid "View" msgstr "Ansicht" #: wb_preferences_dialog.py:366 msgid "Exclude filename" msgstr "Dateinamen ausschließen" #: wb_preferences_dialog.py:372 msgid " Add " msgstr " Hinzufügen " #: wb_preferences_dialog.py:484 msgid "Columns" msgstr "Spalten" #: wb_preferences_dialog.py:492 wb_preferences_dialog.py:499 msgid "Column" msgstr "Spalte" #: wb_preferences_dialog.py:494 wb_preferences_dialog.py:501 msgid "Width" msgstr "Breite" #: wb_preferences_dialog.py:519 wb_preferences_dialog.py:818 msgid " Include --> " msgstr " Hinzufügen --> " #: wb_preferences_dialog.py:521 wb_preferences_dialog.py:820 msgid " <-- Exclude " msgstr " <-- Ausschließen " #: wb_preferences_dialog.py:524 wb_preferences_dialog.py:823 msgid " Move Up " msgstr " Hoch " #: wb_preferences_dialog.py:526 wb_preferences_dialog.py:825 msgid " Move Down " msgstr " Runter " #: wb_preferences_dialog.py:571 wb_preferences_dialog.py:629 #, python-format msgid "Width for %(name)s must be an number between %(min)d and %(max)d" msgstr "Die Breite für %(name)s muss zwischen %(min)d und %(max)d liegen" #: wb_preferences_dialog.py:582 wb_preferences_dialog.py:642 #, python-format msgid "Width for %(name)s must be between %(min)d and %(max)d" msgstr "Die Breite für %(name)s muss zwischen %(min)d und %(max)d liegen" #: wb_preferences_dialog.py:694 msgid "You must include the Name column" msgstr "Die Namen-Spalte darf nicht gelöscht werden" #: wb_preferences_dialog.py:705 wb_subversion_history.py:183 msgid "Log History" msgstr "Logeinträge" #: wb_preferences_dialog.py:711 wb_subversion_history.py:194 msgid "Show all entries" msgstr "Alle Einträge zeigen" #: wb_preferences_dialog.py:711 msgid "Show only" msgstr "Zeige die letzten" #: wb_preferences_dialog.py:711 msgid "Show since" msgstr "Zeige Einträge seit" #: wb_preferences_dialog.py:712 msgid "Default mode: " msgstr "Modus: " #: wb_preferences_dialog.py:716 msgid "Default limit: " msgstr "Anzahl: " #: wb_preferences_dialog.py:719 msgid "Default since interval (days): " msgstr "Zeitintervall (Tage): " #: wb_preferences_dialog.py:722 msgid "Default Include tags: " msgstr "Tags anzeigen: " #: wb_preferences_dialog.py:723 wb_subversion_history.py:222 msgid "Include tags in log history" msgstr "Tags in Loghistorie anzeigen" #: wb_preferences_dialog.py:760 msgid "Limit must be greater then 0" msgstr "Die Anzahl muss größer als 0 sein" #: wb_preferences_dialog.py:772 msgid "Since days must be greater then 0" msgstr "Die Anzahl der Tage muss größer als 0 sein" #: wb_preferences_dialog.py:791 msgid "Toolbar" msgstr "Toolbar" #: wb_preferences_dialog.py:800 wb_preferences_dialog.py:805 msgid "Toolbar Group" msgstr "Toolbar-Gruppe" #: wb_preferences_dialog.py:829 msgid "Display toolbar: " msgstr "Toolbar anzeigen: " #: wb_preferences_dialog.py:830 msgid "Enabled" msgstr "Aktiviert" #: wb_preferences_dialog.py:832 msgid "Orientation: " msgstr "Richtung: " #: wb_preferences_dialog.py:833 msgid "Horizontal" msgstr "Horizontal" #: wb_preferences_dialog.py:833 msgid "Vertical" msgstr "Vertikal" #: wb_preferences_dialog.py:839 msgid "Small" msgstr "Klein" #: wb_preferences_dialog.py:839 msgid "Large" msgstr "Groß" #: wb_preferences_dialog.py:839 msgid "Huge" msgstr "Riesig" #: wb_preferences_dialog.py:841 msgid "Icon size: " msgstr "Icongröße: " #: wb_preferences_dialog.py:967 msgid "You must include at least one Toolbar group" msgstr "Sie müssen mindestens eine Toolbar-Gruppe hinzufügen" #: wb_preferences_dialog.py:978 msgid "Advanced" msgstr "Erweitert" #: wb_preferences_dialog.py:985 msgid "Allow arbitrary paths for tag/branch" msgstr "Beliebige Pfade für Tag/Branch zulassen" #: wb_preferences.py:117 #, python-format msgid "Wrote preferences to %s" msgstr "Einstellungen in %s gespeichert" #: wb_preferences.py:127 wb_preferences.py:130 #, python-format msgid "Reading preferences from %s" msgstr "Lese Einstellungen aus %s" #: wb_project_dialogs.py:48 msgid "Project" msgstr "Projekt" #: wb_project_dialogs.py:52 msgid "Project Name:" msgstr "Projektname:" #: wb_project_dialogs.py:60 wb_project_dialogs.py:475 #: wb_project_dialogs.py:634 wb_project_dialogs.py:744 msgid "Working copy Path:" msgstr "Pfad der Arbeitskopie:" #: wb_project_dialogs.py:67 msgid "Subversion Trunk URL:" msgstr "Subversion Trunk URL:" #: wb_project_dialogs.py:73 msgid "Subversion Tags URL:" msgstr "Subversion Tags URL:" #: wb_project_dialogs.py:79 msgid "Subversion Branches URL:" msgstr "Subversion Branches URL:" #: wb_project_dialogs.py:85 msgid "New File Template Folder: " msgstr "Ordner für Templates \"Neue Datei\"" #: wb_project_dialogs.py:93 msgid "Background Colour: " msgstr "Hintergrundfarbe: " #: wb_project_dialogs.py:94 msgid "Use custom background colour" msgstr "Benutzerdefinierte Farbe verwenden" #: wb_project_dialogs.py:95 msgid "Example" msgstr "Beispiel" #: wb_project_dialogs.py:96 msgid " Pick Colour... " msgstr "Farbe wählen... " #: wb_project_dialogs.py:185 msgid "Enter a project name" msgstr "Geben Sie einen Projektnamen ein" #: wb_project_dialogs.py:189 #, python-format msgid "Project %s already exist. Choose another name" msgstr "Das Projekt %s existiert bereits. Geben Sie bitte einen anderen Namen ein" #: wb_project_dialogs.py:195 msgid "Enter a Subversion trunk URL" msgstr "Geben Sie eine Subversion Trunk URL ein" #: wb_project_dialogs.py:199 #, python-format msgid "%s is not a valid Subversion trunk URL" msgstr "Der Pfad %s ist keine gültige Subversion Trunk URL" #: wb_project_dialogs.py:205 wb_project_dialogs.py:211 #, python-format msgid "%s is not a valid Subversion tags URL" msgstr "Der Pfad %s ist keine gültige Subversion Tags URL" #: wb_project_dialogs.py:216 msgid "Enter a Working copy path" msgstr "Geben Sie den Pfad der Arbeitskopie ein" #: wb_project_dialogs.py:228 wb_project_dialogs.py:538 #: wb_project_dialogs.py:714 msgid "Select Working Copy directory" msgstr "Wählen Sie das Verzeichnis der Arbeitskopie" #: wb_project_dialogs.py:247 msgid "Select New File Template directory" msgstr "Wählen Sie das \"Neue Datei\" Templateverzeichnis" #: wb_project_dialogs.py:347 msgid "Add Project" msgstr "Projekt hinzufügen" #: wb_project_dialogs.py:444 msgid "Working Copy" msgstr "Arbeitskopie" #: wb_project_dialogs.py:445 msgid " Use new working copy directory " msgstr "Erstelle neues Verzeichnis für die Arbeitskopie" #: wb_project_dialogs.py:446 msgid " Use existing Working copy directory " msgstr "Verweise auf eine bereits bestehende Arbeitskopie" #: wb_project_dialogs.py:470 msgid "Select Working Copy" msgstr "Wähle die Arbeitskopie" #: wb_project_dialogs.py:487 wb_project_dialogs.py:583 #: wb_project_dialogs.py:646 wb_project_dialogs.py:751 msgid "Subversion URL:" msgstr "Subversion URL:" #: wb_project_dialogs.py:512 #, python-format msgid "" "Path %s\n" "Does not exist\n" "Choose an existing subversion working copy directory" msgstr "" "Der Pfad %s existiert nicht!\n" "Wählen Sie bitte ein anderes Verzeichnis für die Arbeitskopie." #: wb_project_dialogs.py:519 #, python-format msgid "" "Path %s\n" "Is not a directory\n" "Choose an existing subversion working copy directory" msgstr "" "Der Pfad %s ist kein Verzeichnis!\n" "Wählen Sie bitte ein anderes Verzeichnis für die Arbeitskopie." #: wb_project_dialogs.py:526 #, python-format msgid "" "Path %s\n" "Is not a subversion working copy\n" "Choose an existing subversion working copy directory" msgstr "" "Der Pfad %s ist keine Subversion Arbeitskopie!\n" "Wählen Sie bitte ein anderes Verzeichnis für die Arbeitskopie." #: wb_project_dialogs.py:577 msgid "Select Subversion URL" msgstr "Wähle Subversion URL" #: wb_project_dialogs.py:603 msgid "Enter a Subversion URL" msgstr "Geben Sie eine Subversion URL ein" #: wb_project_dialogs.py:607 #, python-format msgid "%s is not a valid Subversion URL" msgstr "Der Pfad %s ist keine gültige Subversion URL" #: wb_project_dialogs.py:615 #, python-format msgid "" "%(url)s is not a accessable Subversion URL\n" "%(error)s" msgstr "" "Auf die Subversion URL %(url)s besteht kein Zugriff!\n" "%(error)s" #: wb_project_dialogs.py:628 msgid "Create new Working Copy" msgstr "Erzeuge neue Arbeitskopie" #: wb_project_dialogs.py:664 msgid "Choose a directory that is empty and not in used by subversion" msgstr "Wählen Sie ein leeres Verzeichnis das nicht bereits unter der Kontrolle von Subversion steht." #: wb_project_dialogs.py:670 #, python-format msgid "" "Path %s\n" "Is not a directory\n" "Choose a directory that is empty and not in use by subversion" msgstr "" "Der Pfad %s ist kein Verzeichnis!\n" "Wählen Sie ein leeres Verzeichnis das nicht bereits unter der Kontrolle von Subversion steht." #: wb_project_dialogs.py:678 #, python-format msgid "" "Path %s\n" "Is a subversion working copy\n" "Choose a directory that is empty and not in use by subversion" msgstr "" "Der Pfad %s ist eine Subversion Arbeitskopie!\n" "Wählen Sie ein leeres Verzeichnis das nicht bereits unter der Kontrolle von Subversion steht." #: wb_project_dialogs.py:691 #, python-format msgid "" "Path %s\n" "Is not an empty directory\n" "Choose a directory that is empty and not in use by subversion" msgstr "" "Das Verzeichnis %s ist nicht leer!\n" "Wählen Sie ein leeres Verzeichnis das nicht bereits unter der Kontrolle von Subversion steht." #: wb_project_dialogs.py:700 #, python-format msgid "" "Path %(path)s\n" "%(error)s\n" "Choose a directory that is empty and not in use by subversion" msgstr "" "Pfad %(path)s\n" "%(error)s\n" "Wählen Sie ein leeres Verzeichnis das nicht bereits unter der Kontrolle von Subversion steht." #: wb_project_dialogs.py:727 msgid "Project Name" msgstr "Projektname" #: wb_project_dialogs.py:733 msgid "Project name:" msgstr "Projektname:" #: wb_project_dialogs.py:768 msgid "" "Project name is blank.\n" "Enter a project name" msgstr "" "Der Projektname ist leer!\n" "Geben Sie bitte einen Projektnamen ein." #: wb_project_dialogs.py:774 #, python-format msgid "" "Project %s already exist.\n" "Choose another name" msgstr "" "Das Projekt %s existiert bereits!\n" "Geben Sie bitte einen anderen Namen ein." #: wb_shell_macosx_commands.py:58 wb_shell_unix_commands.py:51 #: wb_shell_win32_commands.py:53 #, python-format msgid "Open %s" msgstr "Öffne %s" #: wb_shell_win32_commands.py:61 #, python-format msgid "" "Unable to shell open %s\n" "Is an application associated with this file type?" msgstr "" "Kann %s nicht in der Shell öffnen!\n" "Ist eine Anwendung mit diesem Dateityp verknüpft?" #: wb_shell_win32_commands.py:64 #, python-format msgid "Unable to shell open %(filename)s - %(error)s" msgstr "Kann %(filename)s nicht in der Shell öffnen! - %(error)s" #: wb_shell_win32_commands.py:122 #, python-format msgid "" "Create process failed for command - %(command)s\n" "Reason %(reason)s" msgstr "" "Konnte den Prozess nicht starten für das Kommando - %(command)s\n" "Ursache %(reason)s" #: wb_shell_win32_commands.py:131 #, python-format msgid "Created directory %s" msgstr "Verzeichnis %s erstellt" #: wb_shell_win32_commands.py:134 #, python-format msgid "Create directory %(dir)s - %(error)s" msgstr "Erstelle Verzeichnis %(dir)s - %(error)s" #: wb_shell_win32_commands.py:140 #, python-format msgid "%s is not a directory" msgstr "%s ist kein Verzeichnis" #: wb_subversion_annotate.py:23 #, python-format msgid "Annotation of %s" msgstr "Zeilenhistorie von %s" #: wb_subversion_annotate.py:57 msgid "Line" msgstr "Zeile" #: wb_subversion_annotate.py:58 wb_subversion_history.py:502 msgid "Revision" msgstr "Revision" #: wb_subversion_annotate.py:60 wb_subversion_history.py:504 #: wb_subversion_list_handler_common.py:33 #: wb_subversion_list_handler_common.py:112 msgid "Date" msgstr "Datum" #: wb_subversion_annotate.py:61 msgid "Text" msgstr "Text" #: wb_subversion_checkin.py:33 #, python-format msgid "Check in for %s" msgstr "Checkin für %s" #: wb_subversion_checkin.py:54 msgid "Select All" msgstr "Alle selektieren" #: wb_subversion_checkin.py:54 #, fuzzy msgid "Select ALl" msgstr "Alle selektieren" #: wb_subversion_checkin.py:56 wb_subversion_report_updates.py:47 msgid "Exclude..." msgstr "Ausschließen..." #: wb_subversion_checkin.py:56 wb_subversion_checkin.py:97 #: wb_subversion_report_updates.py:70 msgid "Exclude from check in" msgstr "Von Checkin ausschließen" #: wb_subversion_checkin.py:57 wb_subversion_report_updates.py:48 msgid "Include..." msgstr "Hinzufügen..." #: wb_subversion_checkin.py:57 msgid "Include from check in" msgstr "Dem Checkin hinzufügen" #: wb_subversion_checkin.py:72 wb_toolbars.py:98 msgid "Edit File" msgstr "Datei editieren" #: wb_subversion_checkin.py:76 wb_toolbars.py:101 msgid "Open File" msgstr "Datei öffnen" #: wb_subversion_checkin.py:80 wb_toolbars.py:104 msgid "Diff changes against base" msgstr "Diff der lokalen Änderungen" #: wb_subversion_checkin.py:83 wb_subversion_report_branch_changes.py:55 #: wb_subversion_report_lock.py:63 wb_subversion_report_revision_changes.py:70 #: wb_subversion_report_updates.py:66 wb_toolbars.py:107 msgid "Show History log" msgstr "Zeige Loghistorie" #: wb_subversion_checkin.py:86 wb_subversion_report_branch_changes.py:58 #: wb_subversion_report_lock.py:66 wb_subversion_report_revision_changes.py:73 #: wb_toolbars.py:110 msgid "File Information" msgstr "Dateiinformation" #: wb_subversion_checkin.py:89 wb_toolbars.py:113 msgid "File Properties" msgstr "Dateieigenschaften" #: wb_subversion_checkin.py:93 wb_toolbars.py:122 msgid "Revert selected Files and Folders" msgstr "Revert für selektierte Dateien/Verzeichnisse" #: wb_subversion_checkin.py:100 wb_subversion_report_updates.py:73 msgid "Include in check in" msgstr "Dem Checkin hinzufügen" #: wb_subversion_checkin.py:136 msgid " Check In " msgstr "Checkin" #: wb_subversion_checkin.py:242 #, python-format msgid "Check in %s..." msgstr "Checkin für %s..." #: wb_subversion_checkin.py:243 #, python-format msgid "Sent %(count)d of %(total)d" msgstr "%(count)d von %(total)d übertragen" #: wb_subversion_checkin.py:265 #, python-format msgid "Checkin created revision %d" msgstr "Checkin zu neuer Revision %d" #: wb_subversion_checkin.py:267 msgid "No changes to checkin " msgstr "Keine Änderungen für einen Checkin vorhanden" #: wb_subversion_checkin.py:272 msgid "Post commit error" msgstr "Post-Commit Fehler" #: wb_subversion_checkin.py:313 wb_subversion_list_handler.py:223 #: wb_subversion_tree_handler.py:354 msgid "There are no changes to check in" msgstr "Es sind keine Änderungen für einen Checkin vorhanden" #: wb_subversion_history.py:73 wb_subversion_history.py:377 #: wb_subversion_history.py:467 wb_subversion_history.py:534 msgid "Comment" msgstr "Kommentar" #: wb_subversion_history.py:75 wb_subversion_history.py:377 #: wb_subversion_history.py:522 msgid "Path" msgstr "Pfad" #: wb_subversion_history.py:200 msgid "Show only:" msgstr "Zeige die letzten" #: wb_subversion_history.py:211 msgid "Show since:" msgstr "Zeige Einträge seit" #: wb_subversion_history.py:221 msgid "Include tags: " msgstr "Tags anzeigen: " #: wb_subversion_history.py:312 wb_subversion_history.py:331 #, python-format msgid "History of %s" msgstr "Loghistorie von %s" #: wb_subversion_history.py:505 msgid "Label" msgstr "Label" #: wb_subversion_history.py:506 msgid "Message" msgstr "Nachricht" #: wb_subversion_history.py:521 msgid "Action" msgstr "Aktion" #: wb_subversion_history.py:523 msgid "Copied Revision" msgstr "Kopierte Revision" #: wb_subversion_history.py:524 msgid "Copied from" msgstr "Kopiert aus" #: wb_subversion_history.py:536 msgid "Changed Paths" msgstr "Geänderte Pfade" #: wb_subversion_history.py:623 msgid "Diff" msgstr "Diff" #: wb_subversion_history.py:625 msgid "Annotate" msgstr "Zeilenhistorie" #: wb_subversion_history.py:627 msgid "Revision Changes" msgstr "Änderungen der Revision" #: wb_subversion_history.py:724 msgid "Log history View not implemented" msgstr "Ansicht der Loghistorie nicht implementiert" #: wb_subversion_history.py:906 #, python-format msgid "Revision changes for r%(rev1)d ignored. Its URL \"%(url1)s\" does not match r%(rev2)d URL \"%(url2)s\"." msgstr "Änderungen der Revision r%(rev1)d werden ignoriert. Die URL \"%(url1)s\" stimmt nicht mit der URL von r%(rev2)d \"%(url2)s\" überein." #: wb_subversion_info_dialog.py:25 msgid "Entry" msgstr "Eintrag" #: wb_subversion_info_dialog.py:26 msgid "Path:" msgstr "Pfad:" #: wb_subversion_info_dialog.py:58 wb_subversion_info_dialog.py:102 msgid "URL:" msgstr "URL:" #: wb_subversion_info_dialog.py:60 msgid "Repository:" msgstr "Repository:" #: wb_subversion_info_dialog.py:62 wb_subversion_info_dialog.py:106 msgid "Repository UUID:" msgstr "Repository UUID:" #: wb_subversion_info_dialog.py:66 wb_subversion_info_dialog.py:68 #: wb_subversion_info_dialog.py:70 wb_subversion_info_dialog.py:72 #: wb_subversion_info_dialog.py:110 wb_subversion_info_dialog.py:112 #: wb_subversion_info_dialog.py:114 wb_subversion_info_dialog.py:116 msgid "Node kind:" msgstr "Typ:" #: wb_subversion_info_dialog.py:66 wb_subversion_info_dialog.py:110 msgid "file" msgstr "Datei" #: wb_subversion_info_dialog.py:68 wb_subversion_info_dialog.py:112 msgid "directory" msgstr "Verzeichnis" #: wb_subversion_info_dialog.py:70 wb_subversion_info_dialog.py:114 msgid "none" msgstr "Kein" #: wb_subversion_info_dialog.py:72 wb_subversion_info_dialog.py:116 msgid "unknown" msgstr "Unbekannt" #: wb_subversion_info_dialog.py:75 wb_subversion_info_dialog.py:77 #: wb_subversion_info_dialog.py:79 wb_subversion_info_dialog.py:81 #: wb_subversion_info_dialog.py:143 wb_subversion_info_dialog.py:145 #: wb_subversion_info_dialog.py:147 wb_subversion_info_dialog.py:149 #: wb_subversion_info_dialog.py:151 msgid "Schedule:" msgstr "Markiert für:" #: wb_subversion_info_dialog.py:75 wb_subversion_info_dialog.py:143 msgid "normal" msgstr "Normal" #: wb_subversion_info_dialog.py:77 wb_subversion_info_dialog.py:145 msgid "add" msgstr "Hinzufügen" #: wb_subversion_info_dialog.py:79 wb_subversion_info_dialog.py:147 msgid "delete" msgstr "Löschen" #: wb_subversion_info_dialog.py:81 wb_subversion_info_dialog.py:149 msgid "replace" msgstr "Ersetzen" #: wb_subversion_info_dialog.py:84 wb_subversion_info_dialog.py:153 msgid "Copied From URL:" msgstr "Kopiert von URL:" #: wb_subversion_info_dialog.py:86 wb_subversion_info_dialog.py:155 msgid "Copied From Revision:" msgstr "Kopiert von Revision:" #: wb_subversion_info_dialog.py:88 wb_subversion_info_dialog.py:119 msgid "Last Changed Author:" msgstr "Autor der letzten Änderung:" #: wb_subversion_info_dialog.py:90 wb_subversion_info_dialog.py:121 msgid "Last Changed Revision:" msgstr "Revision der letzten Änderung:" #: wb_subversion_info_dialog.py:92 wb_subversion_info_dialog.py:123 msgid "Last Changed Date:" msgstr "Datum der letzten Änderung:" #: wb_subversion_info_dialog.py:94 wb_subversion_info_dialog.py:157 msgid "Text Last Updated:" msgstr "Letztes Update Text:" #: wb_subversion_info_dialog.py:96 wb_subversion_info_dialog.py:159 msgid "Properties Last Updated:" msgstr "Letztes Update Eigenschaften:" #: wb_subversion_info_dialog.py:98 wb_subversion_info_dialog.py:161 msgid "Checksum:" msgstr "Checksumme:" #: wb_subversion_info_dialog.py:104 msgid "Repository root URL:" msgstr "Repository Root URL:" #: wb_subversion_info_dialog.py:125 wb_subversion_list_handler_common.py:927 msgid "Lock" msgstr "Lock" #: wb_subversion_info_dialog.py:128 msgid "Lock Owner:" msgstr "Lock durch:" #: wb_subversion_info_dialog.py:129 msgid "Lock Creation Date:" msgstr "Lock erzeugt um:" #: wb_subversion_info_dialog.py:131 msgid "Lock Expiration Date:" msgstr "Lock läuft ab um:" #: wb_subversion_info_dialog.py:132 wb_subversion_info_dialog.py:135 msgid "Lock Token:" msgstr "Lock Token:" #: wb_subversion_info_dialog.py:133 msgid "Lock Comment:" msgstr "Lock Kommentar:" #: wb_subversion_info_dialog.py:141 msgid "Working copy" msgstr "Arbeitskopie" #: wb_subversion_list_handler_common.py:32 #: wb_subversion_list_handler_common.py:111 #: wb_subversion_report_branch_changes.py:147 wb_subversion_report_lock.py:183 #: wb_subversion_report_revision_changes.py:266 #: wb_subversion_report_updates.py:205 msgid "State" msgstr "Status" #: wb_subversion_list_handler_common.py:34 #: wb_subversion_list_handler_common.py:113 msgid "Rev" msgstr "Rev" #: wb_subversion_list_handler_common.py:37 #: wb_subversion_list_handler_common.py:117 msgid "Mimetype" msgstr "Mimetyp" #: wb_subversion_list_handler_common.py:38 #: wb_subversion_list_handler_common.py:118 msgid "EOL" msgstr "EOL" #: wb_subversion_list_handler_common.py:39 #: wb_subversion_list_handler_common.py:115 msgid "Type" msgstr "Typ" #: wb_subversion_list_handler_common.py:40 #: wb_subversion_list_handler_common.py:119 wb_subversion_report_lock.py:183 msgid "Lock Owner" msgstr "Lock durch" #: wb_subversion_list_handler_common.py:41 #: wb_subversion_list_handler_common.py:120 wb_subversion_report_lock.py:183 msgid "Lock Comment" msgstr "Lock Kommentar" #: wb_subversion_list_handler_common.py:116 msgid "Size" msgstr "Größe" #: wb_subversion_list_handler_common.py:348 msgid "Use the Checkout command to fetch files" msgstr "Benutzen Sie das Kommando Checkout, um Dateien zu übertragen" #: wb_subversion_list_handler_common.py:350 msgid "Use the Update command to fetch files" msgstr "Benutzen Sie das Kommando Update, um Dateien zu übertragen" #: wb_subversion_list_handler_common.py:682 #: wb_subversion_report_revision_changes.py:121 #, python-format msgid "Annotating %(count)d" msgstr "%(count)d Zeilenhistorien erstellt" #: wb_subversion_list_handler_common.py:684 #: wb_subversion_report_revision_changes.py:123 #, python-format msgid "Annotate %s..." msgstr "Zeilenhistorie für %s..." #: wb_subversion_list_handler_common.py:708 wb_subversion_tree_handler.py:385 #, python-format msgid "Diff BASE %s..." msgstr "Diff BASE für %s..." #: wb_subversion_list_handler_common.py:743 wb_subversion_tree_handler.py:421 #, python-format msgid "Diff HEAD %s..." msgstr "Diff HEAD für %s..." #: wb_subversion_list_handler_common.py:776 #: wb_subversion_list_handler_common.py:824 msgid "Retrieving branch info..." msgstr "Ermittle Branch Informationen..." #: wb_subversion_list_handler_common.py:783 #: wb_subversion_list_handler_common.py:830 wb_subversion_tree_handler.py:772 #, python-format msgid "\"%s\" is not a branch." msgstr "\"%s\" ist kein Branch." #: wb_subversion_list_handler_common.py:783 #: wb_subversion_list_handler_common.py:830 wb_subversion_tree_handler.py:772 #: wb_subversion_tree_handler.py:774 msgid "Error" msgstr "Fehler" #: wb_subversion_list_handler_common.py:791 #, python-format msgid "Diff branch origin BASE %s..." msgstr "Diff Originalbranch BASE für %s..." #: wb_subversion_list_handler_common.py:838 #, python-format msgid "Diff branch origin HEAD %s..." msgstr "Diff Originalbranch HEAD für %s..." #: wb_subversion_list_handler_common.py:877 wb_subversion_tree_handler.py:462 #, python-format msgid "Log history %s..." msgstr "Loghistorie für %s..." #: wb_subversion_list_handler_common.py:932 #, python-format msgid "Locking %(count)d" msgstr "%(count)d Locks erstellt" #: wb_subversion_list_handler_common.py:934 #, python-format msgid "Locking %s..." msgstr "Erstelle Lock für %s..." #: wb_subversion_list_handler_common.py:990 msgid "Unlock" msgstr "Unlock" #: wb_subversion_list_handler_common.py:995 #, python-format msgid "Unlocking %(count)d" msgstr "Unlock für %(count)d Einträge durchgeführt" #: wb_subversion_list_handler_common.py:997 #, python-format msgid "Unlocking %s..." msgstr "Führe Unlock von %s aus..." #: wb_subversion_list_handler.py:69 wb_subversion_tree_handler.py:189 msgid "Update to.." msgstr "Update auf..." #: wb_subversion_list_handler.py:102 #, python-format msgid "Copied %d files to the Clipboard" msgstr "%d Dateien wurden in die Zwischenablage kopiert" #: wb_subversion_list_handler.py:107 #, python-format msgid "Cut %d files to the Clipboard" msgstr "%d Dateien wurden ausgeschnitten und in die Zwischenablage kopiert" #: wb_subversion_list_handler.py:130 msgid "Paste Copy" msgstr "Kopierte Einträge einfügen" #: wb_subversion_list_handler.py:132 msgid "Paste Move" msgstr "Ausgeschnittene Einträge einfügen" #: wb_subversion_list_handler.py:148 msgid "Save As" msgstr "Speichern als" #: wb_subversion_list_handler.py:158 #, python-format msgid "%(title)s: From %(filename)s" msgstr "%(title)s: Von %(filename)s" #: wb_subversion_list_handler.py:161 #, python-format msgid "%(title)s: To %(filename)s" msgstr "%(title)s: Nach %(filename)s" #: wb_subversion_list_handler.py:242 msgid "Delete File" msgstr "Lösche Datei" #: wb_subversion_list_handler.py:323 wb_subversion_list_handler.py:353 #: wb_subversion_list_handler.py:361 #, python-format msgid "Rename %(from)s %(to)s" msgstr "Umbenennen von %(from)s zu %(to)s" #: wb_subversion_list_handler.py:347 msgid "Failed to create tmp file for rename" msgstr "Konnte keine temporäre Datei für das Umbenennen erstellen" #: wb_subversion_list_handler.py:410 msgid "Resolved" msgstr "Gelöst" #: wb_subversion_list_handler.py:424 wb_subversion_list_handler.py:477 #: wb_subversion_report_updates.py:132 wb_subversion_tree_handler.py:828 #: wb_subversion_tree_handler.py:844 #, python-format msgid "Updated %(count)d" msgstr "Update von %(count)d Einträgen" #: wb_subversion_list_handler.py:429 wb_subversion_list_handler.py:482 #: wb_subversion_report_updates.py:131 wb_subversion_tree_handler.py:827 #: wb_subversion_tree_handler.py:843 #, python-format msgid "Update %s..." msgstr "Update für %s..." #: wb_subversion_list_handler.py:451 wb_subversion_list_handler.py:504 #: wb_subversion_report_updates.py:143 wb_subversion_tree_handler.py:876 #, python-format msgid "Updated %(filename)s to revision %(rev)d, no new updates" msgstr "Update von %(filename)s auf Revision %(rev)d, keine Änderungen" #: wb_subversion_list_handler.py:455 wb_subversion_list_handler.py:508 #: wb_subversion_report_updates.py:147 wb_subversion_tree_handler.py:880 #, python-format msgid "Updated %(filename)s to revision %(rev)d, %(count)d new update" msgid_plural "Updated %(filename)s to revision %(rev)d, %(count)d new updates" msgstr[0] "Update von %(filename)s auf Revision %(rev)d, %(count)d Eintrag geändert" msgstr[1] "Update von %(filename)s auf Revision %(rev)d, %(count)d Einträge geändert" #: wb_subversion_list_handler.py:462 wb_subversion_list_handler.py:515 #: wb_subversion_tree_handler.py:889 #, python-format msgid "%d file is in conflict" msgid_plural "%d files are in conflict" msgstr[0] "%d Datei weist einen Konflikt auf" msgstr[1] "%d Dateien weisen einen Konflikt auf" #: wb_subversion_list_handler.py:471 wb_subversion_tree_handler.py:837 msgid "Update to revision" msgstr "Update auf" #: wb_subversion_project_info.py:244 msgid "Hostname" msgstr "Hostname" #: wb_subversion_project_info.py:245 msgid "Valid From" msgstr "Gültig ab" #: wb_subversion_project_info.py:246 msgid "Valid Until" msgstr "Gültig bis" #: wb_subversion_project_info.py:247 msgid "Issuer Name" msgstr "Aussteller" #: wb_subversion_project_info.py:248 msgid "Finger Print" msgstr "Fingerabdruck" #: wb_subversion_properties_dialog.py:92 #: wb_subversion_properties_dialog.py:113 #, python-format msgid "Enter a value for %s" msgstr "Geben Sie einen Wert für %s ein" #: wb_subversion_report_branch_changes.py:25 msgid "Branch changes report" msgstr "Übersicht der Branchänderungen" #: wb_subversion_report_branch_changes.py:52 wb_subversion_report_lock.py:60 #: wb_subversion_report_updates.py:63 msgid "Diff changes against HEAD" msgstr "Diff der lokalen Änderungen" #: wb_subversion_report_lock.py:30 msgid "Repository Lock Report" msgstr "Übersicht der Repository-Locks" #: wb_subversion_report_lock.py:32 msgid "Working Copy Lock Report" msgstr "Übersicht der Arbeitskopie-Locks" #: wb_subversion_report_lock.py:70 wb_toolbars.py:125 msgid "Lock File" msgstr "Lock der Datei" #: wb_subversion_report_lock.py:73 wb_toolbars.py:128 msgid "Unlock File" msgstr "Unlock der Datei" #: wb_subversion_report_revision_changes.py:36 #, python-format msgid "Revision changes - r%(rev1)d vs. r%(rev2)d" msgstr "Änderungen der Revision - r%(rev1)d vs. r%(rev2)d" #: wb_subversion_report_revision_changes.py:44 #, python-format msgid "Diff r%(rev1)d vs. r%(rev1)d..." msgstr "Diff r%(rev1)d vs. r%(rev1)d..." #: wb_subversion_report_revision_changes.py:159 #, python-format msgid "Diff -r%(rev1)d:%(rev2)d %(url)s..." msgstr "Diff -r%(rev1)d:%(rev2)d %(url)s..." #: wb_subversion_report_updates.py:33 msgid "Updates Report" msgstr "Updateübersicht" #: wb_subversion_report_updates.py:47 msgid "Exclude from update" msgstr "Vom Update ausschließen" #: wb_subversion_report_updates.py:48 msgid "Include in update" msgstr "Dem Update hinzufügen" #: wb_subversion_report_updates.py:286 msgid " Update " msgstr " Update " #: wb_subversion_tree_handler.py:208 #, python-format msgid "Creating branch %s" msgstr "Branch %s erzeugen" #: wb_subversion_tree_handler.py:212 #, python-format msgid "Creating tag %s" msgstr "Tag %s erzeugen" #: wb_subversion_tree_handler.py:249 #, python-format msgid "Copied folder %s to the Clipboard" msgstr "Ordner %s wurde in die Zwischenablage kopiert" #: wb_subversion_tree_handler.py:254 #, python-format msgid "Cut folder %s to the Clipboard" msgstr "Ordner %s wurde ausgeschnitten und in die Zwischenablage kopiert" #: wb_subversion_tree_handler.py:262 msgid "Add Folder" msgstr "Verzeichnis hinzufügen" #: wb_subversion_tree_handler.py:275 wb_subversion_tree_handler.py:289 #, python-format msgid "Checkout %s..." msgstr "Checkout für %s..." #: wb_subversion_tree_handler.py:286 msgid "Checkout to revision" msgstr "Checkout auf" #: wb_subversion_tree_handler.py:315 #, python-format msgid "Clean up %s..." msgstr "Cleanup für %s..." #: wb_subversion_tree_handler.py:331 #, python-format msgid "Look for changes to check in %s..." msgstr "Ermittle Änderungen für Checkin von %s..." #: wb_subversion_tree_handler.py:374 msgid "Delete Folder" msgstr "Verzeichnis löschen" #: wb_subversion_tree_handler.py:513 msgid "Make directory" msgstr "Verzeichnis erstellen" #: wb_subversion_tree_handler.py:547 #, python-format msgid "Cannot read template %(filename)s - %(error)s" msgstr "Kann das Template %(filename)s nicht lesen - %(error)s" #: wb_subversion_tree_handler.py:559 #, python-format msgid "Cannot create new file %(filename)s - %(error)s" msgstr "Kann die Datei %(filename)s nicht erzeugen - %(error)s" #: wb_subversion_tree_handler.py:610 #, python-format msgid "Looking for repository locks in %s..." msgstr "Ermittle Locks im Repository für %s..." #: wb_subversion_tree_handler.py:612 #, python-format msgid "Looking for working copy locks in %s..." msgstr "Ermittle Locks der Arbeitskopie für %s..." #: wb_subversion_tree_handler.py:636 msgid "There are no locked files in the repository" msgstr "Im Repository sind keine Locks vorhanden" #: wb_subversion_tree_handler.py:639 msgid "There are no locked files in the working copy" msgstr "In der Arbeitskopie sind keine Locks vorhanden" #: wb_subversion_tree_handler.py:649 #, python-format msgid "Updates %s..." msgstr "Update für %s..." #: wb_subversion_tree_handler.py:668 msgid "All files are up todate" msgstr "Alle Dateien sind auf dem aktuellen Stand" #: wb_subversion_tree_handler.py:684 #, python-format msgid "Branch changes %s..." msgstr "Branchänderungen für %s..." #: wb_subversion_tree_handler.py:774 #, python-format msgid "No files changed yet in branch \"%s\"." msgstr "Im Branch \"%s\" wurden bisher keine Dateien geändert." #: wb_subversion_tree_handler.py:786 msgid "Rename Directory" msgstr "Verzeichnis umbenennen" #: wb_subversion_tree_handler.py:814 msgid "There are no changes to revert" msgstr "Es sind keine Änderungen für einen Revert vorhanden" #: wb_subversion_tree_handler.py:886 msgid "Already up to date" msgstr "Auf dem aktuellen Stand" #: wb_subversion_tree_handler.py:901 #, python-format msgid "Copied %(from)s to %(to)s" msgstr "%(from)s nach %(to)s kopiert" #: wb_subversion_tree_handler.py:913 #, python-format msgid "Moved %(from)s to %(to)s" msgstr "%(from)s nach %(to)s verschoben" #: wb_toolbars.py:82 msgid "Cut Files and Folders" msgstr "Dateien/Verzeichnisse ausschneiden" #: wb_toolbars.py:85 msgid "Copy Files and Folders" msgstr "Dateien/Verzeichnisse kopieren" #: wb_toolbars.py:88 msgid "Paste Files and Folders" msgstr "Dateien/Verzeichnisse einfügen" #: wb_toolbars.py:92 msgid "Start new command shell" msgstr "Neue Shell starten" #: wb_toolbars.py:116 msgid "Add Files and Folders" msgstr "Dateien/Verzeichnisse hinzufügen" #: wb_toolbars.py:119 msgid "Delete selected Files and Folders" msgstr "Löschen der selektierten Dateien/Verzeichnisse" #: wb_toolbars.py:131 msgid "Checkin changes" msgstr "Checkin der Änderungen" #: wb_toolbars.py:134 msgid "Update working copy" msgstr "Update der Arbeitskopie" #: wb_toolbars.py:137 msgid "Use recursive (flat) view" msgstr "Baum/Listen Ansicht" #: wb_toolbars.py:141 msgid "Show only changed files" msgstr "Nur geänderte Dateien zeigen" #: wb_tree_panel.py:244 msgid "Projects:" msgstr "Projekte:" #: wb_tree_panel.py:406 wb_tree_panel.py:471 msgid "Refreshing view..." msgstr "Ansicht aktualisieren..." #: wb_tree_panel.py:997 msgid "&Add Project" msgstr "Projekt hinzufügen (&A)" WorkBench-1.8.2/Source/I18N/pysvn_workbench_hu.po000644 000765 000024 00000162701 11423055754 022077 0ustar00barrystaff000000 000000 # Hungarian translation of pysvn/WorkBench # Copyright (C) 2010 Free Software Foundation, Inc. # This file is distributed under the same license as the pysvn/WorkBench package. # Laszlo Csordas , Copyright (C) 2010. # msgid "" msgstr "" "Project-Id-Version: pysvn_workbench_hu.current\n" "Report-Msgid-Bugs-To: barryscott@tigris.org\n" "POT-Creation-Date: 2009-09-30 19:53+0200\n" "PO-Revision-Date: 2010-07-11 10:15+0100\n" "Last-Translator: csola48 \n" "Language-Team: magyar \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: KBabel 1.10\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Poedit-Language: Hungarian\n" "X-Poedit-Country: HUNGARY\n" "X-Poedit-SourceCharset: utf-8\n" #: wb_app.py:111 #: wb_frame.py:43 msgid "PySVN WorkBench" msgstr "PySVN WorkBench" #: wb_app.py:185 msgid "Work Bench starting" msgstr "WorkBench indítása" #: wb_bookmarks_dialogs.py:58 #: wb_frame.py:158 msgid "Manage Bookmarks" msgstr "Könyvjelző kezelése" #: wb_bookmarks_dialogs.py:73 #: wb_bookmarks_dialogs.py:74 #: wb_bookmarks_dialogs.py:75 #: wb_bookmarks_dialogs.py:76 msgid "Menu" msgstr "Menü" #: wb_bookmarks_dialogs.py:77 msgid "WC Path" msgstr "Munkapéldány útvonala" #: wb_bookmarks_dialogs.py:93 #: wb_preferences_dialog.py:374 msgid " Delete " msgstr "Törlés" #: wb_bookmarks_dialogs.py:96 msgid " Properties " msgstr "Tulajdonságok" #: wb_bookmarks_dialogs.py:99 #: wb_bookmarks_dialogs.py:306 #: wb_dialogs.py:59 #: wb_dialogs.py:187 #: wb_dialogs.py:244 #: wb_dialogs.py:314 #: wb_dialogs.py:388 #: wb_dialogs.py:444 #: wb_dialogs.py:511 #: wb_dialogs.py:610 #: wb_dialogs.py:736 #: wb_preferences_dialog.py:59 #: wb_project_dialogs.py:103 #: wb_subversion_history.py:228 #: wb_subversion_info_dialog.py:35 #: wb_subversion_properties_dialog.py:175 msgid " OK " msgstr " OK " #: wb_bookmarks_dialogs.py:100 #: wb_bookmarks_dialogs.py:307 #: wb_dialogs.py:60 #: wb_dialogs.py:189 #: wb_dialogs.py:246 #: wb_dialogs.py:316 #: wb_dialogs.py:390 #: wb_dialogs.py:446 #: wb_dialogs.py:513 #: wb_dialogs.py:612 #: wb_dialogs.py:739 #: wb_preferences_dialog.py:61 #: wb_project_dialogs.py:105 #: wb_subversion_checkin.py:137 #: wb_subversion_history.py:230 #: wb_subversion_properties_dialog.py:177 #: wb_subversion_report_updates.py:287 msgid " Cancel " msgstr " Töröl" #: wb_bookmarks_dialogs.py:244 msgid "Bookmark Properties" msgstr "Könyvjelző tulajdonságok" #: wb_bookmarks_dialogs.py:286 msgid "WC Path: " msgstr "Munkapéldány útvonala: " #: wb_bookmarks_dialogs.py:290 msgid "Menu1: " msgstr "Menü 1: " #: wb_bookmarks_dialogs.py:294 msgid "Menu2: " msgstr "Menü 2: " #: wb_bookmarks_dialogs.py:298 msgid "Menu3: " msgstr "Menü 3: " #: wb_bookmarks_dialogs.py:302 msgid "Menu Name: " msgstr "Menü neve: " #: wb_dialogs.py:40 msgid "Status" msgstr "Állapot" #: wb_dialogs.py:42 msgid "Filename" msgstr "Fájlnév" #: wb_dialogs.py:56 msgid "Force" msgstr "Kényszerít" #: wb_dialogs.py:134 #: wb_subversion_checkin.py:140 msgid "Insert Last Message" msgstr "Utolsó üzenet beszúrása" #: wb_dialogs.py:164 msgid "Credentials" msgstr "Igazoló iratok" #: wb_dialogs.py:168 msgid "Username:" msgstr "Felhasználó neve:" #: wb_dialogs.py:176 msgid "Password:" msgstr "Jelszó:" #: wb_dialogs.py:182 msgid "Always uses these credentials" msgstr "Már használt ez az igazoló irat" #: wb_dialogs.py:227 #, python-format msgid "Trust server %s" msgstr "Hitelesítő szerver %s" #: wb_dialogs.py:232 msgid "Server Certificate" msgstr "Szerver igazolás" #: wb_dialogs.py:239 msgid "Always trust this server" msgstr "Ez a szerver már hitelesített" #: wb_dialogs.py:290 #: wb_frame.py:114 #: wb_frame.py:157 #: wb_subversion_list_handler.py:73 msgid "Add" msgstr "Hozzáadás" #: wb_dialogs.py:294 #: wb_dialogs.py:366 msgid "From:" msgstr "Innen:" #: wb_dialogs.py:300 msgid "Force Add" msgstr "Kényszerített hozzáadás" #: wb_dialogs.py:306 msgid "Recursive Add" msgstr "Rekurzív hozzáadás" #: wb_dialogs.py:362 #: wb_frame.py:115 #: wb_subversion_list_handler.py:150 #: wb_subversion_list_handler.py:379 #: wb_subversion_list_handler.py:386 #: wb_subversion_tree_handler.py:794 msgid "Rename" msgstr "Átnevezés" #: wb_dialogs.py:372 msgid "To:" msgstr "Eddig:" #: wb_dialogs.py:383 msgid "Force rename" msgstr "Kényszerített átnevezés" #: wb_dialogs.py:436 #: wb_subversion_info_dialog.py:56 #: wb_subversion_tree_handler.py:513 msgid "Name:" msgstr "Név:" #: wb_dialogs.py:437 msgid "New Folder" msgstr "Új dosszié" #: wb_dialogs.py:480 #: wb_dialogs.py:485 msgid "New File" msgstr "Új fájl" #: wb_dialogs.py:489 msgid "New Filename:" msgstr "Új fájlnév:" #: wb_dialogs.py:504 msgid "Template:" msgstr "Minta:" #: wb_dialogs.py:554 msgid "Empty directory" msgstr "Üres könyvtár" #: wb_dialogs.py:556 msgid "Children files only" msgstr "Csak alfájlok" #: wb_dialogs.py:557 msgid "Immediate children" msgstr "Közvetlen alfájlok" #: wb_dialogs.py:558 msgid "Only already checked out descendants" msgstr "Csak már ellenőrzött leszármazottak" #: wb_dialogs.py:559 msgid "All descendants (Full recursion)" msgstr "Összes leszármazott (teljesen rekurzív)" #: wb_dialogs.py:570 msgid "HEAD revision" msgstr "HEAD revízió" #: wb_dialogs.py:578 #: wb_subversion_info_dialog.py:64 #: wb_subversion_info_dialog.py:108 msgid "Revision:" msgstr "Revízió:" #: wb_dialogs.py:590 msgid "Apply on" msgstr "Alkalmazás be" #: wb_dialogs.py:593 msgid "Recursive (all)" msgstr "Rekurzív (összes)" #: wb_dialogs.py:599 msgid "Depth:" msgstr "Mélység:" #: wb_dialogs.py:648 msgid "Please enter a revision number > 0!" msgstr "Írjon egy revízió számot, amely > 0!" #: wb_dialogs.py:653 msgid "Please enter digits only!" msgstr "Csak számjegy lehet!" #: wb_dialogs.py:705 msgid "Copy From:" msgstr "Másol innen:" #: wb_dialogs.py:708 msgid "Copy To:" msgstr "Másol ide:" #: wb_dialogs.py:733 #: wb_subversion_checkin.py:133 msgid "Log message" msgstr "Napló bejegyzés" #: wb_dialogs.py:791 #: wb_frame.py:122 msgid "Create Tag" msgstr "Tag készítése" #: wb_dialogs.py:795 #: wb_frame.py:123 msgid "Create Branch" msgstr "Branch készítése" #: wb_diff_frame.py:49 #: wb_show_diff_frame.py:32 #, python-format msgid "Diff %(title1)s and %(title2)s" msgstr "Összehasonlítás a/z/ %(title1)s és a/z/ %(title2)s között" #: wb_diff_frame.py:59 msgid "Expand folds" msgstr "Dosszié kibontása" #: wb_diff_frame.py:59 msgid "Expand all folds" msgstr "Összes dosszié kibontása" #: wb_diff_frame.py:60 msgid "Collapse folds" msgstr "Dosszié bezárása" #: wb_diff_frame.py:60 msgid "Collapse all folds" msgstr "Összes dosszién bezárása" #: wb_diff_frame.py:62 msgid "Toggle whitespace" msgstr "Látható szóköz bekapcsolása" #: wb_diff_frame.py:62 msgid "Show/hide whitespace" msgstr "Látható szóköz mutatása/rejtése" #: wb_diff_frame.py:64 msgid "Previous difference" msgstr "Korábbi különbség" #: wb_diff_frame.py:64 msgid "Positions the cursor at the previous difference between the files" msgstr "Helyezze a kurzort a fájlok közötti megelőző különbségre" #: wb_diff_frame.py:65 msgid "Next difference" msgstr "Következő különbség" #: wb_diff_frame.py:65 msgid "Positions the cursor at the next difference between the files" msgstr "Helyezze a kurzort a fájlok közötti következő különbségre" #: wb_diff_frame.py:85 msgid "Key: " msgstr "Kulcs: " #: wb_diff_frame.py:86 msgid "Inserted text " msgstr "Beszúrt szöveg " #: wb_diff_frame.py:87 msgid "Deleted text " msgstr "Törölt szöveg" #: wb_diff_frame.py:88 msgid "Changed text" msgstr "Cserélt szöveg" #: wb_diff_frame.py:176 #, python-format msgid "Diff %(change1)d of %(change2)d" msgstr "Összehasonlítás a/z/ %(change1)d és a/z/ %(change2)d között" #: wb_frame.py:62 msgid "&Copy" msgstr "&Másol" #: wb_frame.py:62 msgid "Copy Files" msgstr "Fájl másolása" #: wb_frame.py:63 msgid "&Cut" msgstr "&Vágás" #: wb_frame.py:63 msgid "Cut Files" msgstr "Fájl kivágása" #: wb_frame.py:64 msgid "&Paste" msgstr "&Beillesztés" #: wb_frame.py:64 msgid "Paste Files" msgstr "Fájl beillesztése" #: wb_frame.py:66 msgid "&Clear log" msgstr "Napló kiürítése" #: wb_frame.py:66 msgid "Clear the log window" msgstr "Napló ablak kiürítése" #: wb_frame.py:73 msgid "&Preferences..." msgstr "&Beállítások" #: wb_frame.py:73 #: wb_preferences_dialog.py:24 msgid "Preferences" msgstr "Beállítások" #: wb_frame.py:74 msgid "E&xit" msgstr "&Kilépés" #: wb_frame.py:74 msgid "Exit the application" msgstr "Kilépés egy alkalmazásból" #: wb_frame.py:77 #: wb_subversion_tree_handler.py:174 msgid "&Command Shell" msgstr "&Terminál" #: wb_frame.py:77 #: wb_toolbars.py:91 msgid "Command Shell" msgstr "Terminál" #: wb_frame.py:78 #: wb_subversion_tree_handler.py:175 msgid "&File Browser" msgstr "&Fájlkezelő" #: wb_frame.py:78 #: wb_toolbars.py:95 msgid "File Browser" msgstr "Fájlkezelő" #: wb_frame.py:80 #: wb_subversion_checkin.py:38 #: wb_subversion_checkin.py:335 #: wb_subversion_list_handler.py:42 #: wb_subversion_report_updates.py:216 msgid "Edit" msgstr "Szerkesztés" #: wb_frame.py:81 #: wb_subversion_checkin.py:40 #: wb_subversion_checkin.py:339 #: wb_subversion_list_handler.py:45 #: wb_subversion_report_updates.py:219 msgid "Open" msgstr "Megnyitás" #: wb_frame.py:83 #: wb_subversion_checkin.py:42 #: wb_subversion_checkin.py:343 #: wb_subversion_list_handler.py:48 #: wb_subversion_tree_handler.py:177 msgid "Diff WC vs. BASE..." msgstr "Összehasonlítás: munkapéldány és BASE..." #: wb_frame.py:84 #: wb_subversion_checkin.py:43 #: wb_subversion_checkin.py:344 #: wb_subversion_list_handler.py:49 #: wb_subversion_report_branch_changes.py:30 #: wb_subversion_report_branch_changes.py:158 #: wb_subversion_report_lock.py:38 #: wb_subversion_report_lock.py:194 #: wb_subversion_report_updates.py:38 #: wb_subversion_report_updates.py:222 #: wb_subversion_tree_handler.py:178 msgid "Diff WC vs. HEAD..." msgstr "Összehasonlítás: munkapéldány és HEAD..." #: wb_frame.py:85 #: wb_subversion_checkin.py:44 #: wb_subversion_checkin.py:345 #: wb_subversion_list_handler.py:50 #: wb_subversion_report_branch_changes.py:31 #: wb_subversion_report_branch_changes.py:159 #: wb_subversion_report_lock.py:39 #: wb_subversion_report_lock.py:195 #: wb_subversion_report_updates.py:39 #: wb_subversion_report_updates.py:223 msgid "Diff WC vs. branch origin BASE..." msgstr "Összehasonlítás: munkapéldány kontra branch eredeti BASE..." #: wb_frame.py:86 #: wb_subversion_checkin.py:45 #: wb_subversion_checkin.py:346 #: wb_subversion_list_handler.py:51 #: wb_subversion_report_branch_changes.py:32 #: wb_subversion_report_branch_changes.py:160 #: wb_subversion_report_lock.py:40 #: wb_subversion_report_lock.py:196 #: wb_subversion_report_updates.py:40 #: wb_subversion_report_updates.py:224 msgid "Diff WC vs. branch origin HEAD..." msgstr "Összehasonlítás: munkapéldány kontra branch eredeti HEAD..." #: wb_frame.py:89 #: wb_subversion_list_handler.py:52 msgid "Conflict" msgstr "Konfliktus" #: wb_frame.py:90 #: wb_subversion_list_handler.py:53 msgid "Diff Conflict Old vs. Mine..." msgstr "Összehasonlító konfliktus: régi és saját..." #: wb_frame.py:91 #: wb_subversion_list_handler.py:54 msgid "Diff Conflict Mine vs. New..." msgstr "Összehasonlító konfliktus: saját és új..." #: wb_frame.py:92 #: wb_subversion_list_handler.py:55 msgid "Diff Conflict Old vs. New..." msgstr "Összehasonlító konfliktus: régi és új..." #: wb_frame.py:94 #: wb_subversion_list_handler.py:57 msgid "Resolved Conflict" msgstr "Feloldott konfliktus" #: wb_frame.py:97 #: wb_subversion_checkin.py:48 #: wb_subversion_checkin.py:348 #: wb_subversion_list_handler.py:60 #: wb_subversion_report_branch_changes.py:35 #: wb_subversion_report_branch_changes.py:162 #: wb_subversion_report_lock.py:43 #: wb_subversion_report_lock.py:198 #: wb_subversion_report_revision_changes.py:50 #: wb_subversion_report_revision_changes.py:279 #: wb_subversion_report_updates.py:43 #: wb_subversion_report_updates.py:226 msgid "Annotate..." msgstr "Megjegyzés..." #: wb_frame.py:98 #: wb_subversion_checkin.py:49 #: wb_subversion_checkin.py:349 #: wb_subversion_list_handler.py:61 #: wb_subversion_report_branch_changes.py:36 #: wb_subversion_report_branch_changes.py:163 #: wb_subversion_report_lock.py:44 #: wb_subversion_report_lock.py:199 #: wb_subversion_report_revision_changes.py:51 #: wb_subversion_report_revision_changes.py:280 #: wb_subversion_report_updates.py:44 #: wb_subversion_report_updates.py:227 #: wb_subversion_tree_handler.py:180 msgid "Log history..." msgstr "Napló előzmények..." #: wb_frame.py:99 #: wb_subversion_checkin.py:50 #: wb_subversion_checkin.py:350 #: wb_subversion_list_handler.py:62 #: wb_subversion_report_branch_changes.py:37 #: wb_subversion_report_branch_changes.py:164 #: wb_subversion_report_lock.py:45 #: wb_subversion_report_lock.py:200 #: wb_subversion_report_revision_changes.py:52 #: wb_subversion_report_revision_changes.py:281 #: wb_subversion_tree_handler.py:181 msgid "Information..." msgstr "Információ..." #: wb_frame.py:100 #: wb_subversion_checkin.py:51 #: wb_subversion_checkin.py:351 #: wb_subversion_list_handler.py:63 #: wb_subversion_tree_handler.py:182 msgid "Properties..." msgstr "Tulajdonságok..." #: wb_frame.py:102 #: wb_subversion_list_handler.py:68 #: wb_subversion_tree_handler.py:188 msgid "Update" msgstr "Frissítés" #: wb_frame.py:103 msgid "Update to..." msgstr "Frissítés..." #: wb_frame.py:104 #: wb_subversion_list_handler.py:39 #: wb_subversion_tree_handler.py:185 msgid "Checkout" msgstr "Ellenőrzés" #: wb_frame.py:105 #: wb_subversion_tree_handler.py:186 msgid "Checkout to..." msgstr "Ellenőrzés..." #: wb_frame.py:107 #: wb_subversion_list_handler.py:71 #: wb_subversion_tree_handler.py:191 msgid "Checkin..." msgstr "Ellenőrzés..." #: wb_frame.py:109 #: wb_subversion_list_handler.py:65 #: wb_subversion_report_lock.py:202 msgid "Lock..." msgstr "Zárás..." #: wb_frame.py:110 #: wb_subversion_list_handler.py:66 #: wb_subversion_report_lock.py:203 msgid "Unlock..." msgstr "Nyitás..." #: wb_frame.py:112 #: wb_subversion_tree_handler.py:193 msgid "New File..." msgstr "Új fájl..." #: wb_frame.py:113 #: wb_subversion_tree_handler.py:194 msgid "Make directory..." msgstr "Könyvtár készítése..." #: wb_frame.py:115 #: wb_subversion_list_handler.py:74 #: wb_subversion_tree_handler.py:196 msgid "Rename..." msgstr "Átnevezés..." #: wb_frame.py:117 #: wb_frame.py:167 #: wb_subversion_list_handler.py:76 #: wb_subversion_tree_handler.py:198 msgid "Delete..." msgstr "Törlés..." #: wb_frame.py:117 msgid "Delete" msgstr "Törlés" #: wb_frame.py:118 #: wb_subversion_checkin.py:352 #: wb_subversion_list_handler.py:77 #: wb_subversion_tree_handler.py:199 msgid "Revert..." msgstr "Visszavonás..." #: wb_frame.py:118 #: wb_subversion_checkin.py:93 #: wb_subversion_checkin.py:378 #: wb_subversion_list_handler.py:399 #: wb_subversion_tree_handler.py:819 msgid "Revert" msgstr "Visszavonás" #: wb_frame.py:120 #: wb_subversion_list_handler.py:79 #: wb_subversion_tree_handler.py:201 msgid "Clean up" msgstr "Tisztítás" #: wb_frame.py:120 msgid "Clean up working copy" msgstr "Munkapéldány tisztítása" #: wb_frame.py:122 msgid "Create Tag..." msgstr "Tag készítése..." #: wb_frame.py:123 msgid "Create Branch..." msgstr "Branch készítése..." #: wb_frame.py:126 msgid "Working copy Locks..." msgstr "Munkapéldány zárása..." #: wb_frame.py:126 msgid "Locks held in Working Copy" msgstr "Munkapéldány zárva tartása" #: wb_frame.py:127 msgid "Repository Locks..." msgstr "Tároló zárása..." #: wb_frame.py:127 msgid "Locks held in Repository" msgstr "Tároló zárva tartása" #: wb_frame.py:129 msgid "Changes..." msgstr "Változások..." #: wb_frame.py:129 msgid "Changes available for checkin" msgstr "Változások használata ellenőrzéshez" #: wb_frame.py:130 msgid "Updates..." msgstr "Frissítés..." #: wb_frame.py:130 msgid "Updates available in the Repository" msgstr "Frissítések használata a tárolóban" #: wb_frame.py:131 msgid "Branch changes..." msgstr "Branch cseréje" #: wb_frame.py:131 msgid "Files changed in this branch" msgstr "Fájlok cseréje ebben a branchban" #: wb_frame.py:134 msgid "Show &Controlled files" msgstr "&Ellenőrzött fájlok mutatása" #: wb_frame.py:134 msgid "Show Controlled files" msgstr "Ellenőrzött fájlok mutatása" #: wb_frame.py:135 msgid "Show &Uncontrolled files" msgstr "Ellenőrizetlen fájlok mutatása" #: wb_frame.py:135 msgid "Show Uncontrolled files" msgstr "Ellenőrizetlen fájlok mutatása" #: wb_frame.py:136 msgid "Show &Ignored files" msgstr "Kihagyott fájlok mutatása" #: wb_frame.py:136 msgid "Show ignored files" msgstr "Kihagyott fájlok mutatása" #: wb_frame.py:137 msgid "Show &Only changed files" msgstr "Csak a módosított fájlok mutatása" #: wb_frame.py:137 msgid "Filter out unchanged files" msgstr "Filter kikapcsolása a nem változott fájloknál" #: wb_frame.py:139 msgid "Show &Recursive files" msgstr "Rekurzív fájlok mutatása" #: wb_frame.py:139 msgid "Show recursive files" msgstr "Rekurzív fájlok mutatása" #: wb_frame.py:141 msgid "Use WorkBench Diff" msgstr "WorkBench összehasonlító használata" #: wb_frame.py:142 msgid "Use External GUI Diff" msgstr "Külső GUI összehasonlító használata" #: wb_frame.py:143 msgid "Use External Text Diff" msgstr "Szöveges összehasonlító használata" #: wb_frame.py:144 msgid "Use SVN Diff" msgstr "SVN összehasonlító használata" #: wb_frame.py:146 msgid "&Refresh\tF5" msgstr "Frissítés\tF5" #: wb_frame.py:146 msgid "Refresh display" msgstr "Képernyő frissítése" #: wb_frame.py:147 msgid "&Automatic Refresh" msgstr "&Automatikus frissítés" #: wb_frame.py:147 msgid "Automatic refresh" msgstr "Automatikus frissítés" #: wb_frame.py:157 msgid "Add Bookmark" msgstr "Könyvjelző hozzáadása" #: wb_frame.py:158 msgid "Manage..." msgstr "Intéz..." #: wb_frame.py:164 #: wb_subversion_tree_handler.py:195 msgid "Add..." msgstr "Hozzáad..." #: wb_frame.py:164 msgid "Project Add" msgstr "Projekt hozzáadása" #: wb_frame.py:165 msgid "Settings..." msgstr "Beállítások..." #: wb_frame.py:165 #: wb_project_dialogs.py:303 #: wb_tree_panel.py:574 msgid "Project Settings" msgstr "Projekt beállítások" #: wb_frame.py:167 #: wb_tree_panel.py:575 #: wb_tree_panel.py:616 msgid "Delete Project" msgstr "Projekt törlése" #: wb_frame.py:170 msgid "&About..." msgstr "&Névjegy" #: wb_frame.py:170 msgid "About the application" msgstr "Alkalmazás névjegye" #: wb_frame.py:174 msgid "&File" msgstr "Fájl" #: wb_frame.py:175 msgid "&Edit" msgstr "Szerkesztés" #: wb_frame.py:176 msgid "&View" msgstr "Nézet" #: wb_frame.py:177 #: wb_subversion_checkin.py:60 #: wb_subversion_report_branch_changes.py:40 #: wb_subversion_report_lock.py:48 #: wb_subversion_report_revision_changes.py:55 #: wb_subversion_report_updates.py:51 msgid "&Actions" msgstr "Művelet" #: wb_frame.py:178 msgid "&Reports" msgstr "Beszámoló" #: wb_frame.py:179 msgid "&Bookmarks" msgstr "Könyvjelző" #: wb_frame.py:180 msgid "&Project" msgstr "Projekt" #: wb_frame.py:181 msgid "&Help" msgstr "Súgó" #: wb_frame.py:198 msgid "Work Bench" msgstr "Work Bench" #: wb_frame.py:200 #: wb_frame.py:483 #: wb_subversion_checkin.py:257 #: wb_subversion_list_handler_common.py:704 #: wb_subversion_list_handler_common.py:737 #: wb_subversion_list_handler_common.py:773 #: wb_subversion_list_handler_common.py:821 #: wb_subversion_list_handler_common.py:868 #: wb_subversion_list_handler_common.py:906 #: wb_subversion_list_handler_common.py:951 #: wb_subversion_list_handler_common.py:1017 #: wb_subversion_list_handler.py:467 #: wb_subversion_list_handler.py:520 #: wb_subversion_report_revision_changes.py:143 #: wb_subversion_report_revision_changes.py:180 #: wb_subversion_report_revision_changes.py:219 #: wb_subversion_report_updates.py:159 #: wb_subversion_tree_handler.py:244 #: wb_subversion_tree_handler.py:282 #: wb_subversion_tree_handler.py:296 #: wb_subversion_tree_handler.py:325 #: wb_subversion_tree_handler.py:348 #: wb_subversion_tree_handler.py:416 #: wb_subversion_tree_handler.py:451 #: wb_subversion_tree_handler.py:490 #: wb_subversion_tree_handler.py:629 #: wb_subversion_tree_handler.py:672 #: wb_subversion_tree_handler.py:779 #: wb_subversion_tree_handler.py:894 msgid "Ready" msgstr "Kész" #: wb_frame.py:465 #, python-format msgid "Work Bench version: %s" msgstr "Work Bench verzió: %s" #: wb_frame.py:469 msgid "" "\n" "Copyright Barry Scott (c) 2003-2009. All rights reserved" msgstr "" "\n" "Copyright Barry Scott (C) 2003-2009. Minden jog fenntartva!" #: wb_frame.py:673 #, python-format msgid "Adding bookmark to %s" msgstr "Könyvjelző hozzáadása %s" #: wb_list_panel_common.py:108 #: wb_list_panel_common.py:511 #: wb_subversion_list_handler_common.py:31 #: wb_subversion_list_handler_common.py:110 #: wb_subversion_list_handler_common.py:243 #: wb_subversion_report_branch_changes.py:147 #: wb_subversion_report_lock.py:183 #: wb_subversion_report_revision_changes.py:266 #: wb_subversion_report_updates.py:205 msgid "Name" msgstr "Név" #: wb_list_panel_common.py:511 #: wb_subversion_annotate.py:59 #: wb_subversion_history.py:71 #: wb_subversion_history.py:377 #: wb_subversion_history.py:503 #: wb_subversion_list_handler_common.py:35 #: wb_subversion_list_handler_common.py:114 #: wb_subversion_list_handler_common.py:246 msgid "Author" msgstr "Szerző" #: wb_preferences_dialog.py:32 msgid "PreferencesDialog" msgstr "Beállítások párbeszéd" #: wb_preferences_dialog.py:106 msgid "Editor" msgstr "Szerkesztő" #: wb_preferences_dialog.py:111 msgid "Editor: " msgstr "Szerkesztő" #: wb_preferences_dialog.py:114 msgid "Edit Arguments: " msgstr "Szerkesztő paraméterei:" #: wb_preferences_dialog.py:117 #: wb_preferences_dialog.py:210 #: wb_project_dialogs.py:58 #: wb_project_dialogs.py:87 #: wb_project_dialogs.py:477 #: wb_project_dialogs.py:636 msgid " Browse... " msgstr " Keres..." #: wb_preferences_dialog.py:143 #: wb_preferences_dialog.py:267 msgid "Executable files (*.exe)|*.exe" msgstr "Végrehajtható fájlok (*.exe)|*.exe" #: wb_preferences_dialog.py:145 #: wb_preferences_dialog.py:269 msgid "Applications|*.app|Executable files|*" msgstr "Alkalmazások|*.app|Végrehajtható fájlok|*" #: wb_preferences_dialog.py:147 #: wb_preferences_dialog.py:271 msgid "Executable files|*" msgstr "Végrehajtható fájlok|*" #: wb_preferences_dialog.py:152 #: wb_preferences_dialog.py:276 msgid "Choose an Executable file" msgstr "Végrehajtható fájl választása" #: wb_preferences_dialog.py:182 msgid "You must enter a valid editor executable" msgstr "Érvényes szerkesztő megadása szükséges" #: wb_preferences_dialog.py:183 #: wb_preferences_dialog.py:573 #: wb_preferences_dialog.py:584 #: wb_preferences_dialog.py:631 #: wb_preferences_dialog.py:644 #: wb_preferences_dialog.py:695 #: wb_preferences_dialog.py:761 #: wb_preferences_dialog.py:773 #: wb_preferences_dialog.py:968 #: wb_subversion_checkin.py:314 #: wb_subversion_list_handler.py:224 #: wb_subversion_list_handler.py:464 #: wb_subversion_list_handler.py:517 #: wb_subversion_properties_dialog.py:93 #: wb_subversion_properties_dialog.py:114 #: wb_subversion_tree_handler.py:355 #: wb_subversion_tree_handler.py:637 #: wb_subversion_tree_handler.py:640 #: wb_subversion_tree_handler.py:669 #: wb_subversion_tree_handler.py:815 #: wb_subversion_tree_handler.py:891 msgid "Warning" msgstr "Vigyázat" #: wb_preferences_dialog.py:193 msgid "Diff Tool" msgstr "Összehasonlító eszköz" #: wb_preferences_dialog.py:201 msgid "Work Bench Diff" msgstr "Work Bench összehasonlító" #: wb_preferences_dialog.py:201 msgid "External GUI Diff Command" msgstr "Külső GUI összehasonlító parancs" #: wb_preferences_dialog.py:201 msgid "External Text Diff" msgstr "Külső szöveges összehasonlító" #: wb_preferences_dialog.py:201 msgid "SVN diff" msgstr "SVN összehasonlító" #: wb_preferences_dialog.py:215 msgid "Mode: " msgstr "Módszer:" #: wb_preferences_dialog.py:219 msgid "Diff Tool: " msgstr "Összehasonlító eszköz:" #: wb_preferences_dialog.py:223 msgid "Tool Arguments: " msgstr "Eszköz paraméterei:" #: wb_preferences_dialog.py:227 msgid "Use" msgstr "Alkalmazás" #: wb_preferences_dialog.py:228 msgid "" "%nl for left file name, %nr for right file name,\n" "%tl for left title, %tr for right title" msgstr "" "%nl bal oldali fájlnév, %nr jobb oldali fájl név,\n" "%tl for bal oldali cím, %tr jobb oldali cím" #: wb_preferences_dialog.py:293 msgid "Shell" msgstr "Keret" #: wb_preferences_dialog.py:298 msgid "Terminal Init Command: " msgstr "Terminál indító parancs:" #: wb_preferences_dialog.py:306 msgid "Terminal Program: " msgstr "Terminal program: " #: wb_preferences_dialog.py:323 msgid "File Browser Program: " msgstr "Fájl kereső program: " #: wb_preferences_dialog.py:358 msgid "View" msgstr "Nézet" #: wb_preferences_dialog.py:366 msgid "Exclude filename" msgstr "Kihagyott fájlnév" #: wb_preferences_dialog.py:372 msgid " Add " msgstr " Hozzáad" #: wb_preferences_dialog.py:484 msgid "Columns" msgstr "Oszlopok" #: wb_preferences_dialog.py:492 #: wb_preferences_dialog.py:499 msgid "Column" msgstr "Oszlop" #: wb_preferences_dialog.py:494 #: wb_preferences_dialog.py:501 msgid "Width" msgstr "Szélesség" #: wb_preferences_dialog.py:519 #: wb_preferences_dialog.py:818 msgid " Include --> " msgstr "Tartalmaz --> " #: wb_preferences_dialog.py:521 #: wb_preferences_dialog.py:820 msgid " <-- Exclude " msgstr " <-- Kihagy" #: wb_preferences_dialog.py:524 #: wb_preferences_dialog.py:823 msgid " Move Up " msgstr "Felfelé" #: wb_preferences_dialog.py:526 #: wb_preferences_dialog.py:825 msgid " Move Down " msgstr "Lefelé" #: wb_preferences_dialog.py:571 #: wb_preferences_dialog.py:629 #, python-format msgid "Width for %(name)s must be an number between %(min)d and %(max)d" msgstr "Szélesség értéke %(name)s egy %(min)d és %(max)d közötti szám kell legyen" #: wb_preferences_dialog.py:582 #: wb_preferences_dialog.py:642 #, python-format msgid "Width for %(name)s must be between %(min)d and %(max)d" msgstr "Szélesség értéke %(name)s %(min)d és %(max)d között kell legyen" #: wb_preferences_dialog.py:694 msgid "You must include the Name column" msgstr "Szükséges tartalmaznia az oszlop nevét" #: wb_preferences_dialog.py:705 #: wb_subversion_history.py:183 msgid "Log History" msgstr "Napló előzmények" #: wb_preferences_dialog.py:711 #: wb_subversion_history.py:194 msgid "Show all entries" msgstr "Össze bejegyzés mutatása" #: wb_preferences_dialog.py:711 msgid "Show only" msgstr "Csak mutassa" #: wb_preferences_dialog.py:711 msgid "Show since" msgstr "Mutat onnantól" #: wb_preferences_dialog.py:712 msgid "Default mode: " msgstr "Alaphelyzet:" #: wb_preferences_dialog.py:716 msgid "Default limit: " msgstr "Alapérték:" #: wb_preferences_dialog.py:719 msgid "Default since interval (days): " msgstr "Eltelt időtartam napokban:" #: wb_preferences_dialog.py:722 msgid "Default Include tags: " msgstr "Alapértelmezett tag:" #: wb_preferences_dialog.py:723 #: wb_subversion_history.py:222 msgid "Include tags in log history" msgstr "Tagek az előzmények naplóban" #: wb_preferences_dialog.py:760 msgid "Limit must be greater then 0" msgstr "Nagyobb kell legyen az érték, mint 0" #: wb_preferences_dialog.py:772 msgid "Since days must be greater then 0" msgstr "Eltelt napok értéke nagyobb, mint 0" #: wb_preferences_dialog.py:791 msgid "Toolbar" msgstr "Eszköztár" #: wb_preferences_dialog.py:800 #: wb_preferences_dialog.py:805 msgid "Toolbar Group" msgstr "Eszköztár csoport" #: wb_preferences_dialog.py:829 msgid "Display toolbar: " msgstr "Eszköztár megjelenítése:" #: wb_preferences_dialog.py:830 msgid "Enabled" msgstr "Engedélyezett" #: wb_preferences_dialog.py:832 msgid "Orientation: " msgstr "Irányultság:" #: wb_preferences_dialog.py:833 msgid "Horizontal" msgstr "Vízszintes" #: wb_preferences_dialog.py:833 msgid "Vertical" msgstr "Függőleges" #: wb_preferences_dialog.py:839 msgid "Small" msgstr "Kicsi" #: wb_preferences_dialog.py:839 msgid "Large" msgstr "Nagy" #: wb_preferences_dialog.py:839 msgid "Huge" msgstr "Hatalmas" #: wb_preferences_dialog.py:841 msgid "Icon size: " msgstr "Ikon mérete:" #: wb_preferences_dialog.py:967 msgid "You must include at least one Toolbar group" msgstr "Legalább egy eszköztár csoportot kell tartalmazni" #: wb_preferences_dialog.py:978 msgid "Advanced" msgstr "Haladó" #: wb_preferences_dialog.py:985 msgid "Allow arbitrary paths for tag/branch" msgstr "Önkényes útvonalak megengedése tag/branch-ért" #: wb_preferences.py:117 #, python-format msgid "Wrote preferences to %s" msgstr "Tulajdonságok írása ide %s" #: wb_preferences.py:127 #: wb_preferences.py:130 #, python-format msgid "Reading preferences from %s" msgstr "Tulajdonságok kiolvasása innen %s" #: wb_project_dialogs.py:48 msgid "Project" msgstr "Projekt" #: wb_project_dialogs.py:52 msgid "Project Name:" msgstr "Projekt neve:" #: wb_project_dialogs.py:60 #: wb_project_dialogs.py:475 #: wb_project_dialogs.py:634 #: wb_project_dialogs.py:744 msgid "Working copy Path:" msgstr "Munkapéldány útvonal:" #: wb_project_dialogs.py:67 msgid "Subversion Trunk URL:" msgstr "Subversion törzs URL:" #: wb_project_dialogs.py:73 msgid "Subversion Tags URL:" msgstr "Subversion tagek URL:" #: wb_project_dialogs.py:79 msgid "Subversion Branches URL:" msgstr "Subversion branch URL:" #: wb_project_dialogs.py:85 msgid "New File Template Folder: " msgstr "Új fájl sablon dosszié:" #: wb_project_dialogs.py:93 msgid "Background Colour: " msgstr "Háttérszín:" #: wb_project_dialogs.py:94 msgid "Use custom background colour" msgstr "Saját háttérszín használata" #: wb_project_dialogs.py:95 msgid "Example" msgstr "Például" #: wb_project_dialogs.py:96 msgid " Pick Colour... " msgstr " Szín kijelölése..." #: wb_project_dialogs.py:185 msgid "Enter a project name" msgstr "Proekt név megadása" #: wb_project_dialogs.py:189 #, python-format msgid "Project %s already exist. Choose another name" msgstr "Projket %s már létezik. Válaszon másik nevet." #: wb_project_dialogs.py:195 msgid "Enter a Subversion trunk URL" msgstr "Subversion törzs URL megadása" #: wb_project_dialogs.py:199 #, python-format msgid "%s is not a valid Subversion trunk URL" msgstr "%s nem egy érvényes Subversion törzs URL" #: wb_project_dialogs.py:205 #: wb_project_dialogs.py:211 #, python-format msgid "%s is not a valid Subversion tags URL" msgstr "%s nem egy érvényes Subversion tag URL" #: wb_project_dialogs.py:216 msgid "Enter a Working copy path" msgstr "Munkapéldány útvonal megadása" #: wb_project_dialogs.py:228 #: wb_project_dialogs.py:538 #: wb_project_dialogs.py:714 msgid "Select Working Copy directory" msgstr "Munkapéldány könyvtár megadása" #: wb_project_dialogs.py:247 msgid "Select New File Template directory" msgstr "Új fájl sablon könyvtár kiválasztása" #: wb_project_dialogs.py:347 msgid "Add Project" msgstr "Projekt hozzáadása" #: wb_project_dialogs.py:444 msgid "Working Copy" msgstr "Munkapéldány" #: wb_project_dialogs.py:445 msgid " Use new working copy directory " msgstr " Új munkapéldány könyvtár használata" #: wb_project_dialogs.py:446 msgid " Use existing Working copy directory " msgstr " Meglévő munkapéldány könyvtár használata" #: wb_project_dialogs.py:470 msgid "Select Working Copy" msgstr "Munkapéldány kiválasztása" #: wb_project_dialogs.py:487 #: wb_project_dialogs.py:583 #: wb_project_dialogs.py:646 #: wb_project_dialogs.py:751 msgid "Subversion URL:" msgstr "Subversion URL:" #: wb_project_dialogs.py:512 #, python-format msgid "" "Path %s\n" "Does not exist\n" "Choose an existing subversion working copy directory" msgstr "" "Útvonal %s\n" "Nem létezik\n" "Válaszon egy létező Subversion munkapéldány könyvtárat" #: wb_project_dialogs.py:519 #, python-format msgid "" "Path %s\n" "Is not a directory\n" "Choose an existing subversion working copy directory" msgstr "" "Útvonal %s\n" "ez nem egy könyvtár\n" "Válaszon egy létező Subversion munkapéldány könyvtárat" #: wb_project_dialogs.py:526 #, python-format msgid "" "Path %s\n" "Is not a subversion working copy\n" "Choose an existing subversion working copy directory" msgstr "" "Útvonal %s\n" "ez nem egy létező Subversion munkapéldány\n" "Válaszon egy létező Subversion munkapéldány könyvtárat" #: wb_project_dialogs.py:577 msgid "Select Subversion URL" msgstr "Subversion URL választása" #: wb_project_dialogs.py:603 msgid "Enter a Subversion URL" msgstr "Subversion URL megadása" #: wb_project_dialogs.py:607 #, python-format msgid "%s is not a valid Subversion URL" msgstr "%s nem egy érvényes Subversion URL" #: wb_project_dialogs.py:615 #, python-format msgid "" "%(url)s is not a accessable Subversion URL\n" "%(error)s" msgstr "" "%(url)s nem egy hozzáférhető Subversion URL\n" "%(error)s" #: wb_project_dialogs.py:628 msgid "Create new Working Copy" msgstr "Új munkapéldány készítése" #: wb_project_dialogs.py:664 msgid "Choose a directory that is empty and not in used by subversion" msgstr "Válasszon egy könyvtárat, amely üres és nem használja a Subversion" #: wb_project_dialogs.py:670 #, python-format msgid "" "Path %s\n" "Is not a directory\n" "Choose a directory that is empty and not in use by subversion" msgstr "" "Útvonal %s\n" "ez nem egy könyvtár\n" "Válasszon egy könyvtárat, amely üres és nem használja a Subversion" #: wb_project_dialogs.py:678 #, python-format msgid "" "Path %s\n" "Is a subversion working copy\n" "Choose a directory that is empty and not in use by subversion" msgstr "" "Útvonal %s\n" "ez egy Subversion munkapéldány\n" "Válasszon egy könyvtárat, amely üres és nem használja a Subversion" #: wb_project_dialogs.py:691 #, python-format msgid "" "Path %s\n" "Is not an empty directory\n" "Choose a directory that is empty and not in use by subversion" msgstr "" "Útvonal %s\n" "ez nem egy üres könyvtár\n" "Válasszon egy könyvtárat, amely üres és nem használja a Subversion" #: wb_project_dialogs.py:700 #, python-format msgid "" "Path %(path)s\n" "%(error)s\n" "Choose a directory that is empty and not in use by subversion" msgstr "" "Útvonal %(path)s\n" "%(error)s\n" "Válasszon egy könyvtárat, amely üres és nem használja a Subversion" #: wb_project_dialogs.py:727 msgid "Project Name" msgstr "Projekt neve" #: wb_project_dialogs.py:733 msgid "Project name:" msgstr "Projekt neve" #: wb_project_dialogs.py:768 msgid "" "Project name is blank.\n" "Enter a project name" msgstr "" "Projekt neve nincs kitöltve.\n" "Adja meg a nevet" #: wb_project_dialogs.py:774 #, python-format msgid "" "Project %s already exist.\n" "Choose another name" msgstr "" "Projekt neve %s már létezik.\n" "Válasszon másik nevet" #: wb_shell_macosx_commands.py:58 #: wb_shell_unix_commands.py:51 #: wb_shell_win32_commands.py:53 #, python-format msgid "Open %s" msgstr "Megnyitás %s" #: wb_shell_win32_commands.py:61 #, python-format msgid "" "Unable to shell open %s\n" "Is an application associated with this file type?" msgstr "" "Terminál nem nyitható meg %s\n" "Van egy alkalmazás rendelve ehhez a fájl típushoz?" #: wb_shell_win32_commands.py:64 #, python-format msgid "Unable to shell open %(filename)s - %(error)s" msgstr "Terminál nem nyitható meg %(filename)s - %(error)s" #: wb_shell_win32_commands.py:122 #, python-format msgid "" "Create process failed for command - %(command)s\n" "Reason %(reason)s" msgstr "" "Sikertelen parancs - %(command)s\n" "Indok %(reason)s" #: wb_shell_win32_commands.py:131 #, python-format msgid "Created directory %s" msgstr "Könyvtár létrehozása %s" #: wb_shell_win32_commands.py:134 #, python-format msgid "Create directory %(dir)s - %(error)s" msgstr "Könyvtár létrehozása %(dir)s - %(error)s" #: wb_shell_win32_commands.py:140 #, python-format msgid "%s is not a directory" msgstr "%s nem egy könyvtár" #: wb_subversion_annotate.py:23 #, python-format msgid "Annotation of %s" msgstr "Jegyzet %s" #: wb_subversion_annotate.py:57 msgid "Line" msgstr "Sor" #: wb_subversion_annotate.py:58 #: wb_subversion_history.py:502 msgid "Revision" msgstr "Revízió" #: wb_subversion_annotate.py:60 #: wb_subversion_history.py:504 #: wb_subversion_list_handler_common.py:33 #: wb_subversion_list_handler_common.py:112 msgid "Date" msgstr "Dátum" #: wb_subversion_annotate.py:61 msgid "Text" msgstr "Szöveg" #: wb_subversion_checkin.py:33 #, python-format msgid "Check in for %s" msgstr "Ellenőrzés itt: %s" #: wb_subversion_checkin.py:54 msgid "Select All" msgstr "Összes kiválasztása" #: wb_subversion_checkin.py:54 msgid "Select ALl" msgstr "Összes kiválasztása" #: wb_subversion_checkin.py:56 #: wb_subversion_report_updates.py:47 msgid "Exclude..." msgstr "Kihagy..." #: wb_subversion_checkin.py:56 #: wb_subversion_checkin.py:97 #: wb_subversion_report_updates.py:70 msgid "Exclude from check in" msgstr "Kizárva az ellenőrzésből" #: wb_subversion_checkin.py:57 #: wb_subversion_report_updates.py:48 msgid "Include..." msgstr "Tartalmaz..." #: wb_subversion_checkin.py:57 msgid "Include from check in" msgstr "Beleértve az ellenőrzésbe" #: wb_subversion_checkin.py:72 #: wb_toolbars.py:98 msgid "Edit File" msgstr "Fájl szerkesztése" #: wb_subversion_checkin.py:76 #: wb_toolbars.py:101 msgid "Open File" msgstr "Fájl megnyitása" #: wb_subversion_checkin.py:80 #: wb_toolbars.py:104 msgid "Diff changes against base" msgstr "Változások összehasonlítása a bázisban" #: wb_subversion_checkin.py:83 #: wb_subversion_report_branch_changes.py:55 #: wb_subversion_report_lock.py:63 #: wb_subversion_report_revision_changes.py:70 #: wb_subversion_report_updates.py:66 #: wb_toolbars.py:107 msgid "Show History log" msgstr "Előzmények mutatása" #: wb_subversion_checkin.py:86 #: wb_subversion_report_branch_changes.py:58 #: wb_subversion_report_lock.py:66 #: wb_subversion_report_revision_changes.py:73 #: wb_toolbars.py:110 msgid "File Information" msgstr "Fájl információ" #: wb_subversion_checkin.py:89 #: wb_toolbars.py:113 msgid "File Properties" msgstr "Fájl tulajdonságok" #: wb_subversion_checkin.py:93 #: wb_toolbars.py:122 msgid "Revert selected Files and Folders" msgstr "Fájlok és könyvtárak kiválasztásának visszavonása" #: wb_subversion_checkin.py:100 #: wb_subversion_report_updates.py:73 msgid "Include in check in" msgstr "Beleértve az ellenőrzésbe" #: wb_subversion_checkin.py:136 msgid " Check In " msgstr "r" #: wb_subversion_checkin.py:242 #, python-format msgid "Check in %s..." msgstr "Ellenőrzés %s..." #: wb_subversion_checkin.py:243 #, python-format msgid "Sent %(count)d of %(total)d" msgstr "Küldés %(count)d, ennyiből %(total)d" #: wb_subversion_checkin.py:265 #, python-format msgid "Checkin created revision %d" msgstr "Ellenőrzés készített revízió %d" #: wb_subversion_checkin.py:267 msgid "No changes to checkin " msgstr "Nincsenek cserék az elleőrzéshez" #: wb_subversion_checkin.py:272 msgid "Post commit error" msgstr "Beküldés utáni hiba" #: wb_subversion_checkin.py:313 #: wb_subversion_list_handler.py:223 #: wb_subversion_tree_handler.py:354 msgid "There are no changes to check in" msgstr "Nincsenek cserék az ellenőrzéshez" #: wb_subversion_history.py:73 #: wb_subversion_history.py:377 #: wb_subversion_history.py:467 #: wb_subversion_history.py:534 msgid "Comment" msgstr "Megjegyzés" #: wb_subversion_history.py:75 #: wb_subversion_history.py:377 #: wb_subversion_history.py:522 msgid "Path" msgstr "Útvonal" #: wb_subversion_history.py:200 msgid "Show only:" msgstr "Csak ezt mutassa:" #: wb_subversion_history.py:211 msgid "Show since:" msgstr "Mutat onnantól:" #: wb_subversion_history.py:221 msgid "Include tags: " msgstr "Tagek:" #: wb_subversion_history.py:312 #: wb_subversion_history.py:331 #, python-format msgid "History of %s" msgstr "Előzmények %s" #: wb_subversion_history.py:505 msgid "Label" msgstr "Címke" #: wb_subversion_history.py:506 msgid "Message" msgstr "Üzenet" #: wb_subversion_history.py:521 msgid "Action" msgstr "Művelet" #: wb_subversion_history.py:523 msgid "Copied Revision" msgstr "Másolva" #: wb_subversion_history.py:524 msgid "Copied from" msgstr "Másolva" #: wb_subversion_history.py:536 msgid "Changed Paths" msgstr "Módosított útvonalak" #: wb_subversion_history.py:623 msgid "Diff" msgstr "Összehasonlítás" #: wb_subversion_history.py:625 msgid "Annotate" msgstr "Jegyzet" #: wb_subversion_history.py:627 msgid "Revision Changes" msgstr "Változások reviziója" #: wb_subversion_history.py:724 msgid "Log history View not implemented" msgstr "Napló előzmény nézete nem megvalósítható" #: wb_subversion_history.py:906 #, python-format msgid "Revision changes for r%(rev1)d ignored. Its URL \"%(url1)s\" does not match r%(rev2)d URL \"%(url2)s\"." msgstr "Revízió cseréje r%(rev1)d mellőzve. Ez az URL \"%(url1)s\" nem párja a r%(rev2)d URL-nek \"%(url2)s\"." #: wb_subversion_info_dialog.py:25 msgid "Entry" msgstr "Bejegyzés" #: wb_subversion_info_dialog.py:26 msgid "Path:" msgstr "Útvonal" #: wb_subversion_info_dialog.py:58 #: wb_subversion_info_dialog.py:102 msgid "URL:" msgstr "URL:" #: wb_subversion_info_dialog.py:60 msgid "Repository:" msgstr "Tároló:" #: wb_subversion_info_dialog.py:62 #: wb_subversion_info_dialog.py:106 msgid "Repository UUID:" msgstr "Tároló UUID:" #: wb_subversion_info_dialog.py:66 #: wb_subversion_info_dialog.py:68 #: wb_subversion_info_dialog.py:70 #: wb_subversion_info_dialog.py:72 #: wb_subversion_info_dialog.py:110 #: wb_subversion_info_dialog.py:112 #: wb_subversion_info_dialog.py:114 #: wb_subversion_info_dialog.py:116 msgid "Node kind:" msgstr "Node fajta:" #: wb_subversion_info_dialog.py:66 #: wb_subversion_info_dialog.py:110 msgid "file" msgstr "fájl" #: wb_subversion_info_dialog.py:68 #: wb_subversion_info_dialog.py:112 msgid "directory" msgstr "könyvtár" #: wb_subversion_info_dialog.py:70 #: wb_subversion_info_dialog.py:114 msgid "none" msgstr "egyik sem" #: wb_subversion_info_dialog.py:72 #: wb_subversion_info_dialog.py:116 msgid "unknown" msgstr "ismeretlen" #: wb_subversion_info_dialog.py:75 #: wb_subversion_info_dialog.py:77 #: wb_subversion_info_dialog.py:79 #: wb_subversion_info_dialog.py:81 #: wb_subversion_info_dialog.py:143 #: wb_subversion_info_dialog.py:145 #: wb_subversion_info_dialog.py:147 #: wb_subversion_info_dialog.py:149 #: wb_subversion_info_dialog.py:151 msgid "Schedule:" msgstr "Jegyzék:" #: wb_subversion_info_dialog.py:75 #: wb_subversion_info_dialog.py:143 msgid "normal" msgstr "normál" #: wb_subversion_info_dialog.py:77 #: wb_subversion_info_dialog.py:145 msgid "add" msgstr "hozzáadás" #: wb_subversion_info_dialog.py:79 #: wb_subversion_info_dialog.py:147 msgid "delete" msgstr "törlés" #: wb_subversion_info_dialog.py:81 #: wb_subversion_info_dialog.py:149 msgid "replace" msgstr "csere" #: wb_subversion_info_dialog.py:84 #: wb_subversion_info_dialog.py:153 msgid "Copied From URL:" msgstr "Másolva erről az URL-ről:" #: wb_subversion_info_dialog.py:86 #: wb_subversion_info_dialog.py:155 msgid "Copied From Revision:" msgstr "Másolva ebből a revízióból:" #: wb_subversion_info_dialog.py:88 #: wb_subversion_info_dialog.py:119 msgid "Last Changed Author:" msgstr "Szerző utolsó változása:" #: wb_subversion_info_dialog.py:90 #: wb_subversion_info_dialog.py:121 msgid "Last Changed Revision:" msgstr "Revízió utolsó változása:" #: wb_subversion_info_dialog.py:92 #: wb_subversion_info_dialog.py:123 msgid "Last Changed Date:" msgstr "Dátum utolsó változása:" #: wb_subversion_info_dialog.py:94 #: wb_subversion_info_dialog.py:157 msgid "Text Last Updated:" msgstr "Szöveg utolsó változása:" #: wb_subversion_info_dialog.py:96 #: wb_subversion_info_dialog.py:159 msgid "Properties Last Updated:" msgstr "Tulajonságok utolsó változása:" #: wb_subversion_info_dialog.py:98 #: wb_subversion_info_dialog.py:161 msgid "Checksum:" msgstr "Ellenörző összeg:" #: wb_subversion_info_dialog.py:104 msgid "Repository root URL:" msgstr "Tároló gyökér URL:" #: wb_subversion_info_dialog.py:125 #: wb_subversion_list_handler_common.py:927 msgid "Lock" msgstr "Zárás" #: wb_subversion_info_dialog.py:128 msgid "Lock Owner:" msgstr "Zárás tulajdonosa:" #: wb_subversion_info_dialog.py:129 msgid "Lock Creation Date:" msgstr "Zárás készítés dátuma:" #: wb_subversion_info_dialog.py:131 msgid "Lock Expiration Date:" msgstr "Zárás lejáratának dátuma:" #: wb_subversion_info_dialog.py:132 #: wb_subversion_info_dialog.py:135 msgid "Lock Token:" msgstr "Zárás jelölés:" #: wb_subversion_info_dialog.py:133 msgid "Lock Comment:" msgstr "Zárás megjegyzés:" #: wb_subversion_info_dialog.py:141 msgid "Working copy" msgstr "Munkapéldány" #: wb_subversion_list_handler_common.py:32 #: wb_subversion_list_handler_common.py:111 #: wb_subversion_report_branch_changes.py:147 #: wb_subversion_report_lock.py:183 #: wb_subversion_report_revision_changes.py:266 #: wb_subversion_report_updates.py:205 msgid "State" msgstr "Állapot" #: wb_subversion_list_handler_common.py:34 #: wb_subversion_list_handler_common.py:113 msgid "Rev" msgstr "Rev" #: wb_subversion_list_handler_common.py:37 #: wb_subversion_list_handler_common.py:117 msgid "Mimetype" msgstr "Mimetípus" #: wb_subversion_list_handler_common.py:38 #: wb_subversion_list_handler_common.py:118 msgid "EOL" msgstr "EOL" #: wb_subversion_list_handler_common.py:39 #: wb_subversion_list_handler_common.py:115 msgid "Type" msgstr "Típus" #: wb_subversion_list_handler_common.py:40 #: wb_subversion_list_handler_common.py:119 #: wb_subversion_report_lock.py:183 msgid "Lock Owner" msgstr "Zárás tuljadonosa:" #: wb_subversion_list_handler_common.py:41 #: wb_subversion_list_handler_common.py:120 #: wb_subversion_report_lock.py:183 msgid "Lock Comment" msgstr "Zárás megjegyzés" #: wb_subversion_list_handler_common.py:116 msgid "Size" msgstr "Méret" #: wb_subversion_list_handler_common.py:348 msgid "Use the Checkout command to fetch files" msgstr "Használja az Ellenőrzés parancsot a fájlok elhozásához" #: wb_subversion_list_handler_common.py:350 msgid "Use the Update command to fetch files" msgstr "Használja az Frissít parancsot a fájlok behozásához" #: wb_subversion_list_handler_common.py:682 #: wb_subversion_report_revision_changes.py:121 #, python-format msgid "Annotating %(count)d" msgstr "Jegyzetelés %(count)d" #: wb_subversion_list_handler_common.py:684 #: wb_subversion_report_revision_changes.py:123 #, python-format msgid "Annotate %s..." msgstr "Jegyzet %s..." #: wb_subversion_list_handler_common.py:708 #: wb_subversion_tree_handler.py:385 #, python-format msgid "Diff BASE %s..." msgstr "Összehasonlítás BASE %s..." #: wb_subversion_list_handler_common.py:743 #: wb_subversion_tree_handler.py:421 #, python-format msgid "Diff HEAD %s..." msgstr "Összehasonlítás HEAD %s..." #: wb_subversion_list_handler_common.py:776 #: wb_subversion_list_handler_common.py:824 msgid "Retrieving branch info..." msgstr "Branch információ visszakeresése..." #: wb_subversion_list_handler_common.py:783 #: wb_subversion_list_handler_common.py:830 #: wb_subversion_tree_handler.py:772 #, python-format msgid "\"%s\" is not a branch." msgstr "\"%s\" nem egy branch" #: wb_subversion_list_handler_common.py:783 #: wb_subversion_list_handler_common.py:830 #: wb_subversion_tree_handler.py:772 #: wb_subversion_tree_handler.py:774 msgid "Error" msgstr "Hiba" #: wb_subversion_list_handler_common.py:791 #, python-format msgid "Diff branch origin BASE %s..." msgstr "Összehasonlítás branch eredeti BASE %s..." #: wb_subversion_list_handler_common.py:838 #, python-format msgid "Diff branch origin HEAD %s..." msgstr "Összehasonlítás branch eredeti HEAD %s..." #: wb_subversion_list_handler_common.py:877 #: wb_subversion_tree_handler.py:462 #, python-format msgid "Log history %s..." msgstr "Előzmények napló %s..." #: wb_subversion_list_handler_common.py:932 #, python-format msgid "Locking %(count)d" msgstr "Zárva %(count)d" #: wb_subversion_list_handler_common.py:934 #, python-format msgid "Locking %s..." msgstr "Zárva %s..." #: wb_subversion_list_handler_common.py:990 msgid "Unlock" msgstr "Nyitva" #: wb_subversion_list_handler_common.py:995 #, python-format msgid "Unlocking %(count)d" msgstr "Nyitás %(count)d" #: wb_subversion_list_handler_common.py:997 #, python-format msgid "Unlocking %s..." msgstr "Nyitás %s..." #: wb_subversion_list_handler.py:69 #: wb_subversion_tree_handler.py:189 msgid "Update to.." msgstr "Frissítés..." #: wb_subversion_list_handler.py:102 #, python-format msgid "Copied %d files to the Clipboard" msgstr "%d fájl másolva a vágólapra" #: wb_subversion_list_handler.py:107 #, python-format msgid "Cut %d files to the Clipboard" msgstr "%d fájlok a vágólapra vágva" #: wb_subversion_list_handler.py:130 msgid "Paste Copy" msgstr "Másolás ide" #: wb_subversion_list_handler.py:132 msgid "Paste Move" msgstr "Mozgatás ide" #: wb_subversion_list_handler.py:148 msgid "Save As" msgstr "Mentés másként" #: wb_subversion_list_handler.py:158 #, python-format msgid "%(title)s: From %(filename)s" msgstr "%(title)s: korábban %(filename)s" #: wb_subversion_list_handler.py:161 #, python-format msgid "%(title)s: To %(filename)s" msgstr "%(title)s: lesz %(filename)s" #: wb_subversion_list_handler.py:242 msgid "Delete File" msgstr "Fájl törlése" #: wb_subversion_list_handler.py:323 #: wb_subversion_list_handler.py:353 #: wb_subversion_list_handler.py:361 #, python-format msgid "Rename %(from)s %(to)s" msgstr "Átnevezés %(from)s %(to)s" #: wb_subversion_list_handler.py:347 msgid "Failed to create tmp file for rename" msgstr "Fájl átnevezésénél hiba a tmp fájlban" #: wb_subversion_list_handler.py:410 msgid "Resolved" msgstr "Megoldott" #: wb_subversion_list_handler.py:424 #: wb_subversion_list_handler.py:477 #: wb_subversion_report_updates.py:132 #: wb_subversion_tree_handler.py:828 #: wb_subversion_tree_handler.py:844 #, python-format msgid "Updated %(count)d" msgstr "Frissítve %(count)d" #: wb_subversion_list_handler.py:429 #: wb_subversion_list_handler.py:482 #: wb_subversion_report_updates.py:131 #: wb_subversion_tree_handler.py:827 #: wb_subversion_tree_handler.py:843 #, python-format msgid "Update %s..." msgstr "Frissítve %s..." #: wb_subversion_list_handler.py:451 #: wb_subversion_list_handler.py:504 #: wb_subversion_report_updates.py:143 #: wb_subversion_tree_handler.py:876 #, python-format msgid "Updated %(filename)s to revision %(rev)d, no new updates" msgstr "Frissítve %(filename)s a revízióra %(rev)d, nincs újabb változat" #: wb_subversion_list_handler.py:455 #: wb_subversion_list_handler.py:508 #: wb_subversion_report_updates.py:147 #: wb_subversion_tree_handler.py:880 #, python-format msgid "Updated %(filename)s to revision %(rev)d, %(count)d new update" msgid_plural "Updated %(filename)s to revision %(rev)d, %(count)d new updates" msgstr[0] "Frissítve %(filename)s a revízióra %(rev)d, %(count)d új változat" msgstr[1] "Frissítve %(filename)s a revízióra %(rev)d, %(count)d új változatok" #: wb_subversion_list_handler.py:462 #: wb_subversion_list_handler.py:515 #: wb_subversion_tree_handler.py:889 #, python-format msgid "%d file is in conflict" msgid_plural "%d files are in conflict" msgstr[0] "%d fájlnál konfliktus" msgstr[1] "%d fájloknál konfliktus" #: wb_subversion_list_handler.py:471 #: wb_subversion_tree_handler.py:837 msgid "Update to revision" msgstr "Revízió frissítése" #: wb_subversion_project_info.py:244 msgid "Hostname" msgstr "Hostnév" #: wb_subversion_project_info.py:245 msgid "Valid From" msgstr "Érvényesség kezdete:" #: wb_subversion_project_info.py:246 msgid "Valid Until" msgstr "Érvényesség vége:" #: wb_subversion_project_info.py:247 msgid "Issuer Name" msgstr "Egy érték beírása %s" #: wb_subversion_project_info.py:248 msgid "Finger Print" msgstr "Ujjlenyomat" #: wb_subversion_properties_dialog.py:92 #: wb_subversion_properties_dialog.py:113 #, python-format msgid "Enter a value for %s" msgstr "Egy érték beírása %s" #: wb_subversion_report_branch_changes.py:25 msgid "Branch changes report" msgstr "Branch változások jelentés" #: wb_subversion_report_branch_changes.py:52 #: wb_subversion_report_lock.py:60 #: wb_subversion_report_updates.py:63 msgid "Diff changes against HEAD" msgstr "Változások összehasonlítása a HEAD-ben" #: wb_subversion_report_lock.py:30 msgid "Repository Lock Report" msgstr "Tároló zárási beszámoló" #: wb_subversion_report_lock.py:32 msgid "Working Copy Lock Report" msgstr "Munkapéldány zárás beszámoló" #: wb_subversion_report_lock.py:70 #: wb_toolbars.py:125 msgid "Lock File" msgstr "Fájl zárása" #: wb_subversion_report_lock.py:73 #: wb_toolbars.py:128 msgid "Unlock File" msgstr "Fájl nyitása" #: wb_subversion_report_revision_changes.py:36 #, python-format msgid "Revision changes - r%(rev1)d vs. r%(rev2)d" msgstr "Revizió cseréje - r%(rev1)d és r%(rev2)d között" #: wb_subversion_report_revision_changes.py:44 #, python-format msgid "Diff r%(rev1)d vs. r%(rev1)d..." msgstr "Összehasonlítás r%(rev1)d és r%(rev1)d között..." #: wb_subversion_report_revision_changes.py:159 #, python-format msgid "Diff -r%(rev1)d:%(rev2)d %(url)s..." msgstr "Összehasonlítás -r%(rev1)d:%(rev2)d %(url)s..." #: wb_subversion_report_updates.py:33 msgid "Updates Report" msgstr "Frissítési jelentés" #: wb_subversion_report_updates.py:47 msgid "Exclude from update" msgstr "Kimaradt a frissítésből" #: wb_subversion_report_updates.py:48 msgid "Include in update" msgstr "Tartalmazza a frissítés" #: wb_subversion_report_updates.py:286 msgid " Update " msgstr " Frissítés" #: wb_subversion_tree_handler.py:208 #, python-format msgid "Creating branch %s" msgstr "Branch készítése %s" #: wb_subversion_tree_handler.py:212 #, python-format msgid "Creating tag %s" msgstr "Tag készítése %s" #: wb_subversion_tree_handler.py:249 #, python-format msgid "Copied folder %s to the Clipboard" msgstr "Dosszié %s másolása a vágólapra" #: wb_subversion_tree_handler.py:254 #, python-format msgid "Cut folder %s to the Clipboard" msgstr "Dosszié %s kivágása a vágólapra" #: wb_subversion_tree_handler.py:262 msgid "Add Folder" msgstr "Dosszié hozzáadása" #: wb_subversion_tree_handler.py:275 #: wb_subversion_tree_handler.py:289 #, python-format msgid "Checkout %s..." msgstr "Ellenőrzés %s..." #: wb_subversion_tree_handler.py:286 msgid "Checkout to revision" msgstr "Revízió ellenőrzése" #: wb_subversion_tree_handler.py:315 #, python-format msgid "Clean up %s..." msgstr "Tisztítás %s..." #: wb_subversion_tree_handler.py:331 #, python-format msgid "Look for changes to check in %s..." msgstr "Cserék ellenőrzése itt: %s" #: wb_subversion_tree_handler.py:374 msgid "Delete Folder" msgstr "Dosszié törlése" #: wb_subversion_tree_handler.py:513 msgid "Make directory" msgstr "Könyvtár készítése" #: wb_subversion_tree_handler.py:547 #, python-format msgid "Cannot read template %(filename)s - %(error)s" msgstr "Nem olvasható a sablon %(filename)s - %(error)s" #: wb_subversion_tree_handler.py:559 #, python-format msgid "Cannot create new file %(filename)s - %(error)s" msgstr "Nem készíthető új fájl %(filename)s - %(error)s" #: wb_subversion_tree_handler.py:610 #, python-format msgid "Looking for repository locks in %s..." msgstr "Tároló zárások nézése itt %s..." #: wb_subversion_tree_handler.py:612 #, python-format msgid "Looking for working copy locks in %s..." msgstr "Munkapéldány zárások nézése itt %s..." #: wb_subversion_tree_handler.py:636 msgid "There are no locked files in the repository" msgstr "Nincs zárt fájl a tárolóban" #: wb_subversion_tree_handler.py:639 msgid "There are no locked files in the working copy" msgstr "Nincs zárt fájl a munkapéldányban" #: wb_subversion_tree_handler.py:649 #, python-format msgid "Updates %s..." msgstr "Frissítés %s..." #: wb_subversion_tree_handler.py:668 msgid "All files are up todate" msgstr "Minden fájl naprakész" #: wb_subversion_tree_handler.py:684 #, python-format msgid "Branch changes %s..." msgstr "Branch cserék %s..." #: wb_subversion_tree_handler.py:774 #, python-format msgid "No files changed yet in branch \"%s\"." msgstr "Még nincs cserélt fájl a branchban \"%s\"." #: wb_subversion_tree_handler.py:786 msgid "Rename Directory" msgstr "Könyvtár átnevezése" #: wb_subversion_tree_handler.py:814 msgid "There are no changes to revert" msgstr "Nem történtek cserék" #: wb_subversion_tree_handler.py:886 msgid "Already up to date" msgstr "Már naprakész" #: wb_subversion_tree_handler.py:901 #, python-format msgid "Copied %(from)s to %(to)s" msgstr "Másolva %(from)s innen ide %(to)s" #: wb_subversion_tree_handler.py:913 #, python-format msgid "Moved %(from)s to %(to)s" msgstr "Mozgatva innen %(from)s ide %(to)s" #: wb_toolbars.py:82 msgid "Cut Files and Folders" msgstr "Fájl és dosszié kivágása" #: wb_toolbars.py:85 msgid "Copy Files and Folders" msgstr "Fájl és dosszié másolása" #: wb_toolbars.py:88 msgid "Paste Files and Folders" msgstr "Fájl és dosszié beillesztése" #: wb_toolbars.py:92 msgid "Start new command shell" msgstr "Új terminál indítása" #: wb_toolbars.py:116 msgid "Add Files and Folders" msgstr "Fájl és dosszié hozzáadása" #: wb_toolbars.py:119 msgid "Delete selected Files and Folders" msgstr "Kiválasztott fájlok és dossziék törlése" #: wb_toolbars.py:131 msgid "Checkin changes" msgstr "Cserék ellenőrzése" #: wb_toolbars.py:134 msgid "Update working copy" msgstr "Munkapéldány frissítése" #: wb_toolbars.py:137 msgid "Use recursive (flat) view" msgstr "Rekurzív nézet használata" #: wb_toolbars.py:141 msgid "Show only changed files" msgstr "Csak a változott fájlok mutatása" #: wb_tree_panel.py:244 msgid "Projects:" msgstr "Projekt:" #: wb_tree_panel.py:406 #: wb_tree_panel.py:471 msgid "Refreshing view..." msgstr "Nézet frissítése..." #: wb_tree_panel.py:997 msgid "&Add Project" msgstr "Projekt hozzáadása" WorkBench-1.8.2/Source/toolbar_images/info.png000644 000765 000024 00000003626 10373363203 021565 0ustar00barrystaff000000 000000 PNG  IHDR@@iqbKGDj!V pHYs  tIME ,˔#IDATx[MlG6kcGYlgZ=4rPp($RUKB=VUT*n"@'vcl'vî[^gm-537yo-*\ Մ18Gx(jƭSj6FML>Nrh&3N=b.`$)4~、ekSA_N%7&"+B!%A"OXA9ht52BX4:~)E Rit:+ `}6+lv>.iDC@74߾mۻwe֯vjfM"Xhz}Wg=jz=11qb*W:m}sҸ{g;[^Ԇ`>+x"q`VmjOQ4qֻ\vO$&JM]P =z0+YC=45B`'ɭ`oKe9tw㔶FV8tV>$ȷMM5ַ}ĩYjPc{Ώ?V￁se9|;#@6qXA҉ P(%~u 8{mݧXXw(%0`z\*>f~CƯdMuNψ8/2Q[XݾA ͽxDT%>8jbG@2x͛kc~@SPlbYNu x`n tt7T| ƭS 7Nbem4:]w:H bg66|$lV%@5H >3xjkh $mPѯ#i0*@4JfJ(]WG3W_sJ ͷr>Cz*Yƭ[ƭ|> ؼr<+a+# KFI}x l*\UOJHV9@/=v@{~^ %cqvJb._N=k_8|9%T=%7&5$LcPp$HIm3ٿ DBB! FTHS lnNJo]Ҥ FbRcVxxMZE2@,v%fiKjh^,S q-U Ϟ$.^TǏ:Fp:̜5:UlMDXZR|H*-vZviX}uDAq!9Wz ĉFL!0xQĉ)9 }Lr^wubtuO%Io29yZ4kUWJ@MuMmz&u:k׊LOW_w>vㄤgVt H\Wn<2;ZjLE_ 00/R ^B'IH?|x/fѱk//2Zk%Dba>kH(y{K$!8+diYD_Z/H~u?/L2┴x_Jڜ2j"WeUV_+;!@IENDB`WorkBench-1.8.2/Source/toolbar_images/add.png000644 000765 000024 00000007076 10365523613 021371 0ustar00barrystaff000000 000000 PNG  IHDR@@iqsBIT|d IDATx͛{lG?=/{M6x1 & nY`1H(C9lrp:B ,P(({b/,gp 1g<cf3?0^7grVs c":_2s7) '|rys ŋt:9w===}ᗏƯSiW|zٓgӣT4/f>O?MQQK,ׯGB_A=lԶϔ׬YCkk+.]A󂠀 @SP\KMxOȕ#xÇx@TB~PàiyLAŏ`rGJ{^|>}'{n4np(|Gc.Z[ Hd2QPP@ȇKgJ7Ⱥ9OD z.#G$٬,xDGG/ ;'N ؋28  ok}gHn+W2m4l6$lٲӧiii8,П |k _`{g444}vp:X,V+V~ gB)--%sE_U(- _- ˰Sp}0;L_m۶( ,3<<(HdB$EI ~ܹHBG;?(30o>X^74O1 lذǃ8 $IlE;wǠJP6$t@CWhIA5XPl߾`0djbٰlSSS(;vlF¹&> sah  %00r̩_M+W@ (|><#IAAp'N\u^_׺AUD<(sE!>\V W{r x)++CQ|>[la׮]|tuu"???F(ƍq)?vjgŠpNB05: OW|0BtRfF@Z={())UW_}Qbݺu=zuض^ JaV PP346mLG9a!:.k<6MƢz'õS gσ^'i3x|<-.\;3|۩/qM3?oD444M@~M3xoz|bQU(fWB5uKar/Uفhll("G+F4ThUM/r:u2>9!Ől6ǁGTUEUhz^QUM!"n Ì 0# Q en896g:^{5AeQecD*NJy h`IӠ&l ϬYy!RWWG}Daa! ĜK/ĕ+WFO%Rtuh \;* ZLOme͛7$M*Ҫ*H([!鄕<90I̛7?0gښ磦) U% 0IreOz'cYٸqcL>|_ X^xb2Iv qSY'wA?mYq.Xny0%J$mPB5^_O&r-p%G$o<'ٖ67vHòlf9|kϴ|54"D$|(XUiiz#9Brp! p/*C{ D%;cd(reP':OJAztdDhjFEf|MQ(#Sr+Oqd#QWwKVv|Y 8pS DF`9|td, +J==ʔI>Q 9Q@UUգGW@lK_ ªfP0GRTSnRvZy8u]~C۪$ceWnM pi"?w&K:ujϢEjxVп#ybh7]oo>RͷIuϘ;w*/opk;::UVqt٘_pGB:VxHt,!0{kh)Lz+p2ҹ36 ~(#w6*`)I@6ر+Vѳ1#:N>9؄0'|-[Ķ#@LjC`ڵvihh`޽qm% o<bZ*瑴Q<v{w}7 tlٲv}Cټys\*ɏiGuu5Nm۶s!aip"hb߾}+e +64wz gn=Xz5&_~g} L4jJe@}ss3T͜z4W3Ym9=Īyȓx~:o66mH|e%4[ZZeuT9 ЄפپA`N|_}=4]Op)?fu-H01oLG_SSCmm-E%e rK𓭟\*PS[5o)(4x H5-QE*|st  rlO=͹!`">XXV.Y3Z1KUN;?]f"Y VU=b )XT$nE=d-H}}=˖-/_-xbpP[[ˀmC pYpS]b+ai9TDf2W$@!EBC3˩ W"=PF% ,X ͚5 ɄlЁDLfkXZc*l^ b 䉨۰XL4`Kf2pn仑||bhpLBy><3yCY@ KTWG#e Y(y_4 FqnMm#.+dnA4L_mN VWWǽ'4WbפZ|4 zp`KF? dwduZ"{3"('6UM?;]ƿ$R]Z $K^ ÈEFoNz g{0 5YdF}R_48^^L&w[F"s*2GE eKV H!"j$Qmdw:009P!a?&Ԇ@'IENDB`WorkBench-1.8.2/Source/toolbar_images/file_browser.png000644 000765 000024 00000004406 10366005026 023307 0ustar00barrystaff000000 000000 PNG  IHDR@@iqsBIT|dIDATx]lWkU4vRjBm( J@ xFB(HJ5L%8qX]^#{='c}5ƻݿ3 I0LM%|٨LO5P++Ӟm}!\Ic3dYHϠضGӺjSX:]M4zHen:FTGRpDm*0Lz8;ҳC9Ǚ6 S|;hT.P va:`Z .DhvoV5"P]r <"-o2QJHR7N)^_͒uP]_i[Kغ V ҽ.T9/r|b:dRi Oaꃋ5^3tqJ2dY>x5(Ŷpp p d-{>9 NW;fd1_`X] fGDVW갟_ P0L6,cԐTe》C/_~۫e =#YXSױ-A 5t 6,eEr8:>9ئaXش}7DSYU:,,ɎXaY6]-¢#{VVOv5x-: A*=aw]c-EKqdtߋcM9ٺcF~9"qvi"ޞUjziLSX͆Mc&khY&\.a\}1jRL#cYJ\ڨokuňraeM&844m>dדhe3q`~7xa~I` (;C^`.]gwz^YuXzӨI?G (biBM!k*Cqq"("8eME,C JY/SS A 1J  e(Pdx` Xİ^2ʴwA0iR5K9ҀBVs$f A^&y1E?u-rV2:J)̈Bh:>bD2<{xADt^07AI;d_d_ryA@EAh7%ܚ@C͚05ai&'d_/E Pxj(SQy%oyOq-Z۩A 55$(G[@,gC+C POBOOBDrXڹsg`׮]JL_d)⩧F$B+C4`Ȟ_"``Kb<o>=QTV^͛K B  9' pqjFv3eO:\l6[Ҹ٬b Rq<Qx8 ŖQO{{Z{V$$ׄmS b /<`y =q.MVFFF|._"B<'Jx0 멪x4 %]Cr b8$8D-]H. L~2ɪ*n݊R'N~|֭s555vms!yF7e0_q_(]ibumG]i3Ӡ`Ϟ=3zkk+6l(k+`ݭڰw^7ys8p')~._`(Qa/} =Lyi7FWiѨjFضio/pp [`0c=;y7,64X"aZrs k]_.UwLWfIeX0Jԃ>wAGG)>s3me˖r5MW_}'U,/0ߺ/?+]B z{pw ,Uk7}6 p˺ٸznX^;͖Parrҕ&oxV*Q7ȍ6p͟P駟FDH$\tgl(brrv ¥%U sWk àGUUU駟ӅFj:9h  *Q!pss<ȩS*6Jr̿ݶ6ȿP>9Kԟ#r\:ݙw掝@s}suISSS .Q8ӢlWJ ?t3+}ssG+P۷/D[iTM̬ϿyZ˱CWX"3s|f:_9ß\Nϼ$Őm2uӴ7ѹ/Dq!VZUh4Zв3O7CVoAfdS n~Ν;W2ͮS;xTvjs:/Dt}o}_ pER7sGʥJccciؕmB賒踯FtvvbfV^n /5>|g%Q)߃XGyoQUU.R)FFF`ppuR(ay `bb}LC-5sLLLH$H$iΜ9Cww7s&_ul趏\xxW^i`7jHL5~xx^.^z%a@;خ&N*T>~m\F.cxxNwߥ;  [|$ M6sLMMljb~#]:8si$*~Ξ=ˑ#G4_yd>-粬ezzzSLÜ/I-D-sjIWabKR%QK -QWc?i;IENDB`WorkBench-1.8.2/Source/toolbar_images/onlychanges.xcf000644 000765 000024 00000003324 10701374354 023137 0ustar00barrystaff000000 000000 gimp xcf file@@BB[/ gimp-commentCreated with The GIMPS gimp-commentCreated with The GIMPgimp-image-grid(style intersections) (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) (xspacing 10.000000) (yspacing 10.000000) (spacing-unit inches) (xoffset 0.000000) (yoffset 0.000000) (offset-unit inches) @@Image     @@@@///////// / / + K6/6/6/6/'/'/'/'//'/'/'/////////7/7/7/&/&/&/&//////'/'/'/A/A/A/AAAAA6A6A6A6A'A'A'A'AA'A'A'AA/A/A/AA7AAA=W&A&A&A&A6OAA A A / OA'A'A'A///6666'''''%%=e/ % % 5 e777&&&&'''///6666'''''''///777&&&&'''@@ Background     @@@@WorkBench-1.8.2/Source/toolbar_images/include.png000644 000765 000024 00000006227 10373370234 022257 0ustar00barrystaff000000 000000 PNG  IHDR@@iqbKGDX pHYs  tIME+ $IDATx{lTם?1{^H0) &8mT[5h6 Ey(u7J8&-ҮUyTB(EE*ݰ @ ،!~;xsǵc]|uϹ~3t8" OصkYN{2Ͱu(c2 i.iիW'ꙙD.7 p,@>8===i?k,_ɓ'nrW~~4~q><mmm 7w}%8 >M$^eZZZxgXr3|vw6= :C d9\Ƹ'Jh^v ֮]˜9sZHĪU?t655Q[[KOOEа2̠GҷҔ` 8s b[WO~ aP^^۩vc6X,X,zzzD":餬bgΜMpp9P У?k0lڴm۶i000(H,Hi_p!$AhiS@֮]Kee%pQ >ۍ q 3Mb; {iZ@U!\:\ xxlJ۷EAhhhR$p)*ȇQY)X @Pf3 ~L&o<0%zz ~ Z!K( uuuu"v )g\P_pA{Ӊ IP0;f(dZ8q ( ռ;;+ U:x+O[-wMWU>ix, |ߐke˖GƟ?w3ߑEQw_NwrUfKga,zWkzf^ ^J[aR֭c3k W#!gHGODfI VF*:::ŋꫯNδ0Yfʹhoo駟iK pUUyGIl lذN{{;ٳ' H|jPUロz 0~bxvIYY=of0U3ЗOuu.|>n7۶m㩧1TFJzݛ_UUnEVAEl6֯_'iD.IQjkktR8o>\QaI|pu!r=k֭[q\!`E17n܈n')<r0.J{T\({T)xG8vG L))C/**l'b\+fm.*.1<)8Mq@oq$L؏("y_}#^b*=L6]waX8k^L܂I(t۹B^ی$Q$[@8֨9$I,)KV'|mjPVVƪUһ ?\.Χ_" 3,|V18,"xuM g[%tnK n>{  EhGeSLM2ˋ3Y +"o^N$ ol"+WL 淋uHfⲍ$!<#G!D $/L#ȲL.򖄱ImDd f\ǔ[S!"!QIgc v)d]GH\q )~rK,"IENDB`WorkBench-1.8.2/Source/toolbar_images/diff.png000644 000765 000024 00000000512 10371110103 021514 0ustar00barrystaff000000 000000 PNG  IHDR@@iqbKGD pHYs  tIME $ RtEXtCommentCreated with The GIMPd%nIDATx D]i;wY1@dűxMy0S]?`hKe`;_Q]u$(@ ړէK {#D~qR Z - s*v=nϾ -#Dӡ,Ga/< IENDB`WorkBench-1.8.2/Source/toolbar_images/flatview.png000644 000765 000024 00000000437 10701374354 022454 0ustar00barrystaff000000 000000 PNG  IHDR@@iqbKGD pHYs  tIME5ܬtEXtCommentCreated with The GIMPd%nIDATxADQwdҺ_Z?Ra waδ')-pnI@Х~ݯ$@nnTn Y R )J@:L@h3 GIENDB`WorkBench-1.8.2/Source/toolbar_images/lock.png000644 000765 000024 00000002004 10375074001 021544 0ustar00barrystaff000000 000000 PNG  IHDR szzbKGDKiƃ pHYs  tIME ,}IDATXVMlEfw.qLФH5v%*\h.HETjrUZ$^zρCUV8FH&hҘ&NoqX[jWN8Of}-kW*IxNm[4 8{'zN&쳻1Fkk%%] ܸ:==ΥK\)5[/{?88XA$A DPdA  ~Som**;B]ƼOM:t r\EZR|^EaP '(Ymr*5_E '~CP\n34Ѽ"`XpPKaq `@P9vk{DmqG+x``d9[5slSa@d0Ǭqcj'+kZ?,{rhw85܂Z l&[͹`k^DuIik8X-J hcumi}mo\.ЇmIENDB`WorkBench-1.8.2/Source/toolbar_images/editcut.png000644 000765 000024 00000007220 10365523613 022271 0ustar00barrystaff000000 000000 PNG  IHDR@@iqsBIT|dGIDATxlTWv?3 g U BD A5q %,LQJ""5jŒnŖ*JD%`$f2Ï0~`fƞ?7?,"qmquQWW?<Jf̘F 8<՘3gꫯqF~ |> (QHהP 7o^Z\.&N`t"Gc˖-466\2v F p }F rlt:ZmB۶mzC >hR/_@UUzM%dzn:c!~xb V iZ-:+W* *Ou\r撙 d21sL$a„ fN<33܄ko7@ףiiiaܹbXRCfn -off:}Ɋ%ݳ@mm-NrhZf3Gep(8Tc'g]r%j3g"⪁VR帵Uufo&SL1cƠ 쭵;wRPPٷoO=ӧOg֭߷P`ZZV'T KxAM&l6+Wru˚5kc!*PA:O>huΝvhRBƋ/Hww7:9BMM 555 .\Haa!,[a(((H ~ e3¤VHhѣGVbQ HH$"n={$ȑ#ņ āݻwvܙHre"/_Pbr"= 8vp#^ }4/`0`1 , !BaBPB駟РT2-޽{7oi`` M1d 9 xoaؐK,^{9L&x2L\.N:ťKXvmJsR7P JSM4~M61uT222u@@BpByxia0GغF(MԵ0 JtWalA'P__HPnL0ZʨJkG"~=A x1(@$ LE7k,^z%:fbܓ!DZR"BǷ7쮙@q3 L_u1yLt8h2 ;Vv'ǎ˸qhhhh`ѢE477SXXHyy9f/լe\.Foa;:}Դ#R!tz6~[U Tev;x];%/TIQnn.MMMl߾Eee%& Ngɤ2kw7m]hGȵ.WV7@nVٝhSc)Sa΄ёp?ٳgYx1mmmTVV2zh6mD}}"ݻRRwߥ򵃔q "} T_JƔ@ cRޑ Wl6Ga٨Yt)>}-H$"''Vٱc(rFY}(Y05 eW> ٬vq݉'`Xprrr?>sΥiӦ=ş .3(2d ƣOafAkb/S H_j~W&ԜЀlhtbXZX,~E֢"5O1^G"kL&GY())QQ*58 $0U{0*$;<())IKE0z8}ZH^T$m`|/'&_oAeaMw <*UN80+q]]]x^y<:DCC"H@.t@;lCv:8:rFHDD^C.b|)_-\ aPQ|K^/>ߏ泥?\d^'*l * 8 U.A ߌ[@w5vZFԢҦ`0HggB޽{ S49sa?iNV!-B\!'#aWAHˁUWcC.*R|l1(\r/ŋijjܹsJi. KǧȵrBoF%!:-]Hڐ/(@xY?dh rXĄ\D>wcm?C䌌ݻjlg!hb_!4$זA~8vO~ 3e!1WXBK;Ƌw*3mIENDB`WorkBench-1.8.2/Source/toolbar_images/terminal.png000644 000765 000024 00000005222 10365523613 022443 0ustar00barrystaff000000 000000 PNG  IHDR@@iqgAMA7 IIDATxOl?fvfƮc;RU(`;FAAHHԪB S+(JH=J !n *)!-N86ve3Ӄ6ϳ3㵉+fy}oװ l`HR {hmz,j0 bq7Ɍ;v gk3?ޱcHdU qݵ;ogu|~~o_;~9IG}ft]_8XE&I\|}}}]?yO/=zD"q մ[,]Eu Q[[˦Mr ?qʕA=JmKRo?S[:Qu+u}-"PSSyll9"Lֶ/vB4#>EhF]]455 qu] ByRX)q|_| !"`+pŰ,KQ!T64Möm k lfjj umT*EWWR XJ jt||7xL&CSS?Ʀ&vG;uohTg'2ӈu, qRWx h,dKc }%}|C瞣E#,%ShBY`a055Ecc#aP,6k>twǑeQpt|cBfff0MM|`EG4L&8?Cw$inL 8L^FɲVV_Ē৞+`Ȁ/yhnla1+LݸOǗMh,"CA>oAX(|h]%vK|~&'&p\U166V[bKa9. q4 ˹gD^Yuy%vi !ejjj\V!Bm&ß^}c}?زxeeU~Uk~ hF6erro~%e]0 &^/^*lY,NB(Mb(YmWB-*p7B]eYLOOSSSC<'w]s~M7oh8W'Tto\qmcf`ZV^ , AFT"FD"Q.[ٲm'.,!fZ-V,ZE-:/Y \xT*&^O>ID@]pDHmS*|76~WJX ^sXdhpwH .IHX*#@.^u_ÈH%xQ5/uܡcr^IWeU0_eI@ { 7K. WU:Rm"x{Hv;],L[ou?|/!{챭۷o'A/p_=av>ZQn;pK-dCܳgd24Ir[a|t:=} @5`3Ү?&.,80L寒4ZE>bQள l`z8y2"IENDB`WorkBench-1.8.2/Source/toolbar_images/checkin.xcf000644 000765 000024 00000032222 10373363203 022224 0ustar00barrystaff000000 000000 gimp xcf file@@BB Unnamed#10 AAAAAAAAAAAABBBBBBB&AB&AB&AUnnamed AAXAAAB2BApBApBApB3AB3AB3ABABABABBBBBBBBNBBNBBNABLABLABLABABABAAAAAA$gimp-image-grid(style intersections) (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) (xspacing 10.000000) (yspacing 10.000000) (spacing-unit inches) (xoffset 0.000000) (yoffset 0.000000) (offset-unit inches) m#23]@@ Arrow 2 copy     @@2@@B">||>5>|||>/>|||.> >-> |.> >.|>-{|,>>+||*>>){|(>>'{|{>&=>){{)= {)z{(={({='= {(z{'={'z{&==&z{&=z&{{&zz&={'=zz=*=zzz/=yzyz3=yzz6=zzz:=zz="5/. -. .  - , +  * )##"!  ( (('&%$# '+,,+)(&%#!& #/00100.-,+)'%# )344554320/-+(&$!) (799:99876531.,)'$):<=>>=<;;86420-+((->?ABBA@><;8641-+(ABDEFGFFEECBA><:741" '2DFHIKKJIHFEB@=:74(!FIJLNNOPONNLKHFC@=;7'5JLNPQSSTSSQPOLJGCA=:'#JMPRTVWXXWVTRPMJGC@=&7NPSVXZ\\]\ZXVSPMIFB.&$LPSVY\^`aa``^\ZVTPLIE &8ORVY\_bdefdcb_]ZVROKG!&$LQTX\`cfhjjgec_\XUPMI!&$ORV[_bfiknnlieb^ZWROJ"&;TX\`dhlorsqolhda]XTOK#'),Ebfjnruwurnjfb]YTP8*025Stx{xtojfb^YUP%/9;|>|>|>|>|>|>|<|U|#*|#R|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#Oo#Ok}}wOOPA+KnUi00Files 1      #00#00#%4GFGGGĦHHH۾HQ$IG II JA J1 JH ǀ JI KC K2 KJ KK LA Lҙ- LMメ MKȧ  MK MK  MK  MK  椨 ƭv Ĺ< ¹}  ε篧r ڞt184 42 %4GFGGGĦHHH۾HQ$IG II JA J1 JH Ӝ JI KCǜ K2 KJ KK  LA Lҙ- LM̪ MKԺ  MK MK ǜ MK  MKĜ Ɯ 椨 ƭv Ĺ< ¹}  ε篧r ڞt184 42 %4GFGGGĦHHH۾HQ$IG II JA J1 JH  JI KC K2 KJ KK  LA Lҙ- LM MK  MK MK  MK  MK  椨 ƭv Ĺ< ¹}  ε篧r ڞt184 42 ,668T|>|>|>|>|>|>|>|<|U|#*|#R|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#Oo#Ok}}wOOPA+KnUi@@ Background     3)@@3=@@3M@@Selection Mask 3@@3@@3"6 0/.//.-,+*)('&**))()(('''''&(+04 7;WorkBench-1.8.2/Source/toolbar_images/revert.png000644 000765 000024 00000004160 10366005026 022131 0ustar00barrystaff000000 000000 PNG  IHDR@@iqsBIT|d'IDATxml[Wڎvd%LNN40itLi& Ck~)D6UT D뤺]7hV)4m6k&ub'~|8ݤMH/ľ<<ϹB4#<#<EwX x"ɱ(hsqYX4ݢ5`NEiA`'Mӧ}%AyưƽI۟UMh%ȗq8 yHcPo7Q$mșw都73kǓտ9IYa>G*>O÷Ϭnrl@ 4n&WK?/R#RmAsvPUt\ 6r^C8ʛc]z)pRT`PaI_k7ɣt j 0[Ws֫INf"KTQYhx4 vM8\t=s]-ABtsw0\ g 'n1V Ȧ; uv1qX&BbR x9}bIѓ"I[W$ɦb3fPC#AʗaL=FBcELE$F4'1 uF}!:Vd@`\8LEA(p$~Veˊ2PIdDn.Dw6X5חA?ĹH*BIjƨPƭ0eV猏b |> #gE. E%ANs]n}k'6vS-V3|evz9vOөveu t;yaQf3 b0x2ȱ[Bq[xXS$(W:? uۀn@*Q_Ϗ0MUBXYnsBu׉KslGb|]&8H@!( ƵDvi<xϗ?32ezR یmT[V8PBcgw}2H,u6#I޿: o A3-Ҝϟk P: p~0LAMn_k&»C>qZ;N+s!4tjF[)M.=F:^( /}0?imjV.f֏O֮z4h8ucrM7!o?wO>07?f1f6Nyɢ{򅒖!B.Z~M>==9zFi?HHR%`~q)ƓKj8F^=Єt h$dSNִP1j~+l[,0H'`_f?sv#xF{Ûw2R!` RWUe!~wD]7hznQ;J]^]:򨦞6z m7h}X۲ʙj]cXL G[WL2bi|/_@MM/TÛAybs/FGyGT>G/WIENDB`WorkBench-1.8.2/Source/toolbar_images/property.xcf000644 000765 000024 00000012435 10373363203 022510 0ustar00barrystaff000000 000000 gimp xcf file@@BB$gimp-image-grid(style intersections) (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) (xspacing 10.000000) (yspacing 10.000000) (spacing-unit inches) (xoffset 0.000000) (yoffset 0.000000) (offset-unit inches)  %;P      %gimp-text-layer (text "P") (font "Utopia Bold Italic") (font-size 50.000000) (font-size-unit pixels) (hinting yes) (antialias yes) (language "en-gb") (base-direction ltr) (color (color-rgba 1.000000 1.000000 1.000000 1.000000)) (justify left) (box-mode dynamic) (box-unit pixels) ^%;r%;$ ؾf# TT  p[e    *b2 bV#    Dq= |8w *=%]RO4 3k Yo 3$b> ?\ w Ô\((# XK:rr<  4 ,Ld@@ Background     @@@@-/@@/(/@@/$/@@/  /@@/ /@@/ /@@/  /@ @/ /@"@/ /@$@/  /@&@/ /@(@/@*@ /@*@/ /@,@/ @.@ /@.@/ @0@ @0@/@0@/@2@@2@@2@/@2@/@4@@4@@4@@4@@4@@4@@4@@4@/@2@/@2@@2@@2@/@0@/@0@ @0@ /@.@/ @.@ /@,@/ /@*@/ @*@/@(@/ /@&@/  /@$@/ /@"@/ /@ @/  /@@/ /@@/ /@@/  /@@/$/@@/(/@@/- HS[^^[SH -[____['W__W#^^ ''__  ' '*"*'$' & (_*_'*' , ^.^ . W0W _0_0[2[ _2_  _2_ 2H4HS4S[4[^4^^4^[4[S4SH4H2 _2_  _2_ [2[0_0_ W0W . ^.^ , '*'_*_( & '$'*"*' '  __'' ^^#W__W'[____[- HS[^^[SH -/@@/(/@@/$/@@/  /@@/ /@@/ /@@/  /@ @/ /@"@/ /@$@/  /@&@/ /@(@/@*@ /@*@/ /@,@/ @.@ /@.@/ @0@ @0@/@0@/@2@@2@@2@/@2@/@4@@4@@4@@4@@4@@4@@4@@4@/@2@/@2@@2@@2@/@0@/@0@ @0@ /@.@/ @.@ /@,@/ /@*@/ @*@/@(@/ /@&@/  /@$@/ /@"@/ /@ @/  /@@/ /@@/ /@@/  /@@/$/@@/(/@@/-@@.@̇@'@ ǀ@"@@@ѿ@@ǀ@@ֿ@@ @@"@@$@@&@@(@*ր @,@ @,@ .р @.@0р0@2@2ۀ24@4@4ـ4̀4444444̀4ـ@4@422ۀ@2@00р@.@ .р @,@ @,@ *ր@(@@&@@$@@"@@ @@ֿ@@ǀ@@ѿ@@@"@ ǀ@'@̇@.@@@@Selection Mask @@@@=tơt=-X X'"XX" "4"44$4"&"(*X,X , . . 0 0 X2X22=4=t4t4444444444t4t=4=22X2X 0 0 . . , X,X*("&"4$44"4" "XX"'X X-=tơt=WorkBench-1.8.2/Source/toolbar_images/history.png000644 000765 000024 00000012631 10365523613 022333 0ustar00barrystaff000000 000000 PNG  IHDR@@iqsBIT|dPIDATx՛{pUս?qywH7AV|S uZNK[n[kQ;T{:V^^@E)ZVJ$$B AA O0>&u@Flg!M|>xn ضm|7x5kְzj~</={Ȟ={>@.Tѝojj`ѢE28Ytuu`ٳ9 fԙz'뮣PQQqތO5Ş={hhhH@a Xhʸ TUUx>wuu`Ql< 1f̘3Nx B+a|3'#I$d2ݻ zP'Ouv;H+++q:]f e˨ih<=ÇfsعsNqq1ӦM#L& Äaêt^4# z~"ꁥ̝;wBtAh4j< B0eYf$IBF a\ϟ4552twww^Z[[9r0Pn %KPSSÒ%Kp:XVV+^$  9tvvrĸNJ.;TZx:DSJV-9,حYddQ@5ip(2I$}z{n ӹ`n<AJKKaO]RGww7W~~݄4}xlJVfTK "}(+P$3- Y-j}7p8̛7L&cl ۍ<۱cwq`7~y5LIw dµJiP("b6jRVV"[ZThC& 3o ۇ9@:$t\eZL&yO?4<pO=ևYƝW:pJC( PL&nӃHfP~ӰX 0I"X xEUy?p 7yfctM&&2d2~~L&;C֏dp" TsFY(u)Q nq 3SHb!v &?Oqv2 AUULEvHf΄4[yXblnc %%%0Ja| p0$_} m4S#7{GX,iEfa6t?Lr(,,$FeYQL,BB{8 81~;vd̙C$!f3x< :>4,\|w܍˱]Gz5oNJqq1(fۍjEE^/nUł Dx<N*JJJEd2I4R'W`χܹYfPv;eee\vew'k,2 b n+WS!f`X((( Nz?R%I2Xԩ1F.x( XM" (B[VoJ# |9 Mh X `q%Tg.)$"YIdD],]2CHF Æ>R ti|xg)QTygB4z P7~:MpXK5_!حTyFGp:aDQt z   $l6E1l&:8H`h)^{5nc8l@ha$Qb{)ktX|̏$l6wEY0bT Պ$Ib1ÌY{n#$x<^oS` ZDÂìuܹstww+Sf8'ȍڐ$N@Ed[A)]Vp8L{{;9߼ Z:Ȼ͔حBX,F?PUU!c8r[5׌2K:E 2B@ @ՐI#|)d?6t4MV?䗀pBiTҒ ԡd( f0䪃96dYK/ T`4iԉt|S}; o2 $YbX,36$F4Ѩ7c9s>" fdU0'Hr<Zޓ %Ed5*jH$2<ʎ;fY@x2E8+UTΆ5ε5MDYtG" h''~ ,ty8>.4l)--%H I@ZD2M4}U]r ڵKf`<po/iK)DY&k.|3TWW3cƌ1V|204007MEE]wMÉ4 {hlldmdC~?ؘ9-T}$ bxU mEAE>Νӎ.X gf<ڷonc)KUU"d JxF[[X~=N,lxMIII?8pb1Efd2CaN'EUH+Ýo>)8DD"A2^Ѓ 'wށt+lb0n*(٬q*裏R)JKKZgߏdh*GihVc"BJEU߰d޼ylݺ6°1shMCg~kkksFMCawQYsDFDAI%u_A@1(b@;/w BDI;@o9N%ܺ|yzi&^"1?OIz%GfΜ n9đ-1!ڈ|)H&<'z4X[[O;F{yhGk* nC?]-32 bBSTFO%H ?Z0spJIENDB`WorkBench-1.8.2/Source/toolbar_images/property.png000644 000765 000024 00000004270 10373363203 022512 0ustar00barrystaff000000 000000 PNG  IHDR@@iqbKGDj!V pHYs  tIME .tEIDATx[MluL3ϙNc;P'vVEb5BziC/HpVږʍC%@ rriT 96Vhl:ۓx<{h36Is7_ʡT2m@e{}|c|Z5hZM^O)ie@:$CN N^ gw]i6Pm9ee<p^ 0nf0 Dͩ/Xb XCCC׽\oA {dwHtHtL;oF9!,+} Mj*-_3-BXv.O .\2㕏dUöXm# 89Bj+Z)N~ԲFhK%Y ۇ=ƃI1!sᣥp;sUi z:U 9Bv?7v[|nj܈TZ@y@Ȩ&(*"`egy̦guݥg? dq1z*B95l vEimٵx]-s^]BvƋQbT`FUioS(-v/i*ᱽ*4U Ђ}WZY;ŀ2JƗ5z@Vj8@t*}1*"u+f$6SKƁ#I,m^aM*tPD|3@P| z70 bnOQ2FJWUBR.iU("tu\=$ d<.Я< 8ۿhԎO^O=VWB4?#8?şh#/ݹbY4 v: y^oFhv4Ri4 yuz ; $ ÜtWbG'רז *NV|.9栂A`lNHh^X FEv]F4ċ[ő\<'$&=F'4Kۥi`Ą=c\##,q?b,=/,FEXG݀T|iXSeiQbMأ7e|}74&m<ſT)C0LbB:fʡAsEJEIENDB`WorkBench-1.8.2/Source/toolbar_images/checkin.png000644 000765 000024 00000006405 10373363203 022234 0ustar00barrystaff000000 000000 PNG  IHDR@@iqbKGD|T pHYs  tIME #/2 IDATxkl\ez{ b,P * \" T@[(˗R%>Uڭ*EJXH%Bb gU41رc;z{Ιs<_ 9<<}(xx hR$ _u`eY\xK.@zcݺuk/ѣ1 PX,O?4e9a%EQ֚:^8N]J)FFFBF՜m~wM}c3b}@=$O? ?=='|rMMM\z*W;{;7fgme!PZ|nKK ?xkhh{0 ; s|GPO9`C!`YVUYj|vHh?\ZPoz'BV>xpۻ-4Ag/]gGV8 zJԻ+֚N:B(xLrSKch_19=EYD}r9s4e$DxCRtnd{k+/C D}t&Oˢd81Vx?q {v–uc]E)QAV0:::F^ֽLDyZE__qXCCcccݩzI5t~ڵ]vR)R(_|ExUjq0G'ϙ/ǹ29:q}Wk3 'B}ᇤix&IR5K/qR>{q׎=NAClӣ=:ttd+ZPd/#dNw9 瞺M͡7>8ϳvTk-QϟiF^yb~K9< 5/6qep˦f~}> Kz/ts02hU]ɝ*_OqY%VO?b&,MƖ2rGг"WD%I|M^ PDDC%ŸqeXwm!eǽ|rndՒYqDyѣ|g+~={9|̕RUB9bVN hCi^yj^};gxy &|šYmhC %xƻ-vqAP% Zq'FΰKݻwII۫ne[ZZk>[6ڽ954,^WA6 Qĵ"ɑ#G%_>䓴됊3s??*u1EV˄ɈCft͛7H$j>o1>>˗b``rˉߛ#2CEX h2EA\4ly%?55EOO333LMMqڵPGQ/ 3Zڼ(DFA~h0bD@El㍚6d*ZNOO355333s9rP8~8[nip> SVvoNWx4D^P JL"[  ޠd K" BYl&sa2+b@?V ^-qg-~(ˏ)7kC܋P2QG@)ԂCv |㻻yWFb2rbX-_={;ro 8n{ pq1Ԝʌp:::bbb'Nl$C`V ˲tttDX\L;wcǎ7)+⅀r mh2elqCq˖-;?~"W\aaaB$1_.Y5"B{{;+IO6ĉ7)bm e 5o \ﻺooj?u0w^Yp r0M}ʘ:pR U \5s7ۀO[,ejym]z.)z\ p{1Im[ Nw+nWvP(2Wl2rCP$bxe$[YfW ~OW0jQjJ);鐝O\3bT2ަ e޷̾X*PhۤP>" x`Fx"ަ}OvP"BQ EC} ` H 5ԫ,h7 y?>UtL:AjJ} !=f#Tny LWR`@SF5pRJ 4fd[ߔs@ [7j@UR>SʬX dF~3pi^vNf$Qw5,?9*M4#Sf\^Y ̎H:; {"vY_vLa mze+atAsN'A9MU*CSPp~3X~IgMĶ8m472$P \ڝdU#dō5-};ߎ%Vc}IENDB`WorkBench-1.8.2/Source/toolbar_images/delete.png000644 000765 000024 00000007010 10366005026 022061 0ustar00barrystaff000000 000000 PNG  IHDR@@iqsBIT|d IDATx{lTם?we{3;G8&6) iQD !B]JSD ?ݰn$I$&a#6U4qK. ƅ%q`czgxsc<㷱)_iy=ܫsfD^1AQ`Vv:`4Goݛ+Qt:ҙcHU).NUMZ2u@0AGZiX}ׇ#hFE«hha\!OnL6 ܸ 2+3Q릛k.?ǧNl$E מ b3A#F!B4ֶO%m`\ӰkYFv+-nw )$D>Lq|iʾ  OZ@f%(@˃i8៰Fal1un)$IQ_ͳ}o 8F!Ff&@s@/K _b⸭_6H|s) Ei F&73nq|:^YrLS'HJGju%/!v~Fkb )z(Vp+^Sn[6/c |ԓ?w\oNnf,;与S?KwEdAa8ג[!@Iq[20!H3чot3.P$vCq Q@@ '<@ñZԏc껑;KUO6 f~ꆆl8V;#k>ҒD҃P:;8i?kW/^ZWzN'ۛf-ޱ117镤Mw]>ÞȘA@E''zmy$6<([3q6g.Rۇ;sk;|&BA0++9:c$SJqRdeemdEHv`QI?*CTU]򔁴2=pQ|FJ+aǀ;*6!sXyHZxi@VA@dۘ;B>}yYտp3`d F.;In!< =Q}czw:}7o6 XfT{e+xʕ@I񚴄唗#ťɶ8|XnՓj8 ( s8CSi{7AC1 .B]] H9ĉ ł047HG*'*UCmI(++> ([R[#)#w?10jXv-۷o'???԰1c>nl^^ Yɰ eڻ<44O]\yyc*ʶoK/b1r"SSSX,p+k!-5pXUM9]8`[.((`ٲe,Xlt::V$I455QUUťK،#55RN:ϵI'wvfH$JˇC#AjRVVFQQQBݰ*RH3`+,\5kOss3O?4ǏOp;;6mڔAeuy0h(>N ?6f\x1[lMbܦ\zJg8fK.#!t EsYjnzjjjظq#aZV֭[q8c222vuupX (QϪq5 ؼy3:Ki舋&jRz#+QnDh&36-*@ӧOS^^γ>;J8NǪUF8 sv;gϞ… p߶mc>) tG&``tAVĞ74t%" zi/I*={SOqQV^Mnnd#6ђf 6h̤ڵ,yqdYFQy{߾X|9)\:bk7+ߖG1K 257#ѠFo2PhNO*t^~evEuu5θƲm6[cP촘` B^^XVZ[[ٿ" \\YevgXc9 K60O3pI!rc:lOPYYp|k|&iF/raZ$ ł'77 KH)-l4X-Z!8@FU@,XBl$!7sSțe&嫩0hhh#8z(NWn^o!ċ/țo Q:>N0L^+Wa %<* |tww'Id*p9r~ rP<.]JϥJvz]?!GE ^z: _בroS'@OO%իWcنnO(H{޽n\A458Zjkk9t>Hgg÷}7N__&Z^7")/ $qDu ܼMMM mmmX,JJJN'o4id_/ʆ XH󺛯'~!sLΜ9lb1 gb'`0hDۮȌ_~Z޽{$?N^gSNS67g&#[ZZXz5vNKF؍MGQDQԠTӏb̊>sZ[[㏑e{iɶm۰8v@ѰB+<˦ht":9D"\x'|ݎlFLA!_AfMQfi%9f&P^OGG2.4o(%IENDB`WorkBench-1.8.2/Source/toolbar_images/unlock.png000644 000765 000024 00000002750 10375074001 022117 0ustar00barrystaff000000 000000 PNG  IHDR szzbKGDKiƃ pHYs  tIME V_uIDATX[LTGgo,A@D#o5hc/}hM&Mۤ}1US-&5ҤmL`*^RZXE+M]e9gf`*_:0'3|3MZ(S ny|¸aGIIIK+*ojKccvs@S)VY±ti^$Iڜ,!jc ؅Co-8@S)VWmdEQe jLXmh5޳5#RYYXiPtvMb@@޵k;C,??W[UUzZ3"y^]V 7níW<23s8af5tC{ ,?r`0fpw޺ ދ @hZ(âח/Y}jЃN$n|aNln'>?_@ZZ79RJ(늊/qpunܝ;}磩NFĄM/[l1k?^חeIuz ε;·FFiT־:?E!;HAAUL<A%uX-ɶר"-():۞Wo.HGM&7*XS:Ua 'D@bn}O'y7(;?>>5#w׼f贝LCĽ폕婒G{x{Ãɀ\^ޘI}ړH}}xՏExxwЍEww**Q|[2&))Qq{Q##*HffH*'*luoo`*)B<<5)(e775((c331('a..+'&^* *#&&\& &$&&Z!!  &&X &&U &&S  &&W  &&Y1 &%&?'&#!##+aäv" %  !C  P8/. C- & 3.1 q%7 o: o: o:Qa $ 5:::::::::::7L) #.00.# Unlock rotated     M  #d􋌮ͩV[dS3픙x?PzӔ봊W~`Px{ަ~mؓHz픰}2;KgOHRڥzwE)7eYO3_J':QYEJ':/RUUJ]fz8LzYJ9uNnU?oOA4sPK汁/hOOFzCѳؗƧн„||t{sy~ xntx sYxt 屄[4tsz.1tknΙF--tD2.--ܨb--tt鶍r--ttđ}--tt{ɜO--tqt}th-- tjEekjjD6j j[D#d􋌮ͩV[dS3픙x?PzӔ봊W~`Px{ަ~mؓIz픰}2;KgOHRڥzwE)8eYO3_J':QYEJ':1SUUJ]fz8LzYJ9uMPU?oOA5sPKd~1hOOG_bfjmquy|CzutxxҀg\w{~ЦyOǀcrvۻTHuQjmʬuH渚iDaes˲ZG^C^`̻pF@SDFŸhB5GD{bJ5DC|ĻnQED>A\[Y|ſ{YJ(D%蜿dO8|DD[rUByDDXJvDDKdQ-iDBDq~zNG?rqonmkjigfeXD>(;?>>%> >5%#w׼fs|kAɾRl٧ŸvՀt䘧ⵜاn陰貑JSjsnsߵӍX=dysA]9Urr]]9Uavrxc~ȏMkzqNՏb/'xhsjMЍuqO.''assmOKGC?;730,(kcG1(q{Q##)@geehjj+(XpbUC$%%ffH*'(zRLKK0-JiYJ@(#oo`*'V35595Fb^OB7/#<<5)&R,--B>F\bSE:3/% #75(%M&'KGGWeWH=40,& #(&HNMVcYL@51-*' #"'&B#B]QD82.+(# &&C0He<3/+(%   &%I 5N9,)&"  &"'* 5G&# &@1 &&S,A  &&W62  &&Y1'F(" &%&?'*&&#!## #qC)ɓ+O ܫqnk`%fSAwP"+]M;  o H 7 d 84 E88^88v8888L88b880  Unlock       #d􋌮ͩV䟂?{7zӶk헍~ufg~{8xܺWxܺWzzSRڥRڥ3_J':A\[YYXXVUUTSRRPA5D%|DDyDDvDDiDBDq~zxwvtsrqonmkjigfeXD>(;?>>5#w׼f贝LCĽGʨoʨo陰ksߵsߵA]9US]9USrxckxckzqNՏhqNxhsjMЍajMwassmOKGC?;730,(eR Qq{Q##)@geehjjhffe?'fffH*'(zRLKKL?)ooo`*'V35 5$&<<5)&R,--.*%775(%M&''#331(&H #.+'&B  ##&&C #&%I   $ &"'* # & &&S  &&W  &&Y1 &%&?'&#!##ő,-V/aqZ/:h c" H          s %58 81  Lock rotated     L$ $! $1#d􋌮ͩV䟂?{7zӶxk헍~R|{8x[^Χ`zTɔbRڥFR׷¡f_J'YהYWk_WUJ՟k?):~āSJ9Ŏ9O`UA4AH}ʉ2zܠ2faRҗlהWнʬϻJNۑe ȴtim sDD纽t2.\xt-nˉts-z|tkn-F|tD2.--b݀|tt-rytt-0bYltt 34ZtqttjEekjj[#d􋌮ͩV䟂?{7zӶxk헍~R|{8x[^Χ`zTɔbRڥFR׷¡f_J'MהYWk_WUJk?):~āSJ9tʭ7O`UA5͡oAH}ʉ2^Ĉaʮ4faRx՚ƛniהWwiҬûKNǀci{TuQh۲riD=@|̕^C%%P]ż{}SD6RftkGDATm}SDCGTrTHD>A\[YYXXV(KXujHD%8M^wxJH|DDANWTFyDD94?vDD5iDBDq~zxwvtsrqonmkjigfeXD>(;?>>5#w׼f贝LCĖsG~ٻ{꙰w֫sߵmwøе]9^ۨrl~wnxc9H`رV=TϗkqN,s\d}Ks{mjM@IDNZiZ,!hjҟAOw-M/2:EQacH"#b}o#)@geeh?5(,/3=HVe^:D~بyH*'(zRLK/<"&),04?JZib{pro`*'V35/>' $'*.27BO]knW<5)&R,-39 $(+/3:ER_Y& (%M&"&( %),03>HF3'(&H#!"# *(('&B &&&C  $&%I   # &"'* # &  "# &&S#  &&W!  &&Y1 &%&?'&#!## >oIfچ"m$QLL 9l XE h#. ؂A~ o\x  o n 5 s 3 58W 8p88E8^8v85w2 j Lock     / 0 0 #d􋌮ͩV䟂?{7zӶk헍~~{8xxܺW`zzSbRڥRڥ3f_J_J':A\[YY>A\[YYXXVUUTSRRPA5D%%|DDyDDvDDiDBDq~zxwvtsrqonmkjigfeXD>(;?>>5#w׼f贝LCĽGʨo{꙰陰kɀsߵsߵA]]9USrxcxczkz}qNqNshsxjMjMmamwOw5Ow5!##.eR Q##)@geeh)@geehjjhffe?'H*'(zRLKK(zRLKKL?)o`*'V355'V35 5$&5)&R,--&R,--.*%(%M&''%M&''#(&H&H #'&B&B  #&&C&C #&%I %I   $ &"'* "'* # & &&S  &&W  &&Y1 &%&?'&#!##ő,-V/aqZ/:h c"7YTTUdH          s %58 81 White     f; ;% ;5  Background     ; < <WorkBench-1.8.2/Source/toolbar_images/flatview.xcf000644 000765 000024 00000002356 10701374354 022452 0ustar00barrystaff000000 000000 gimp xcf file@@BBE/ gimp-commentCreated with The GIMPS gimp-commentCreated with The GIMPgimp-image-grid(style intersections) (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) (xspacing 10.000000) (yspacing 10.000000) (spacing-unit inches) (xoffset 0.000000) (yoffset 0.000000) (offset-unit inches) @@Image     @@@@ + + +   *===666 / / /  === 5 5 5 ///6666'''''''///777&&&&'''@@ Background     @@@@WorkBench-1.8.2/Source/toolbar_images/open.png000644 000765 000024 00000006721 10366005026 021570 0ustar00barrystaff000000 000000 PNG  IHDR@@iqsBIT|d IDATxkLT?sF`@2(xPjtj-KxMcbM/f4imb/njIicGH*u`*30̜}qe_,9<>---%<|L%.ϼ@d/a grssY~=Fls?.\IL{@;)--eÆ G Ylv.lyA=@Fn7. A`rrvA5:@gKsyEWoJ&D"244ĭ[p8ܾ}sQTT|/:$Ӱs)رc/"/^wߍUk͛7C$QF8f͚5;v %:;;O9Kh6NP~߾} D"&''ꫯ8y$<mVZN8hgr+  $Ȗ-[8~8H`]x1O?4?8NRjJ<[f O=^ߏgɒ%k4gx>A_LV^Y1Q BB!4 Pm۶qjܻwVN#++8dRT|wl޼p8L8&  j #+I‹H__֭A~~>(   h"1I ~P(@ @kk+۷ott:ef@&&Pxb޽{ijj={000@0T@__^T*z=Vbrr@ |g]Kr ֯_/eC2ό) v޽{RWW(aN'{DY***|2^"V+@,r{< TlٲH P @t dQQQ`)6+MEEر۷SYY`[qZҥKcÆ h4, %.R lܸ.߿_Lm>|6mڄ(qPP(8@hjh48- d 睜TܺuCx@Jf\U&pYf3o8c~(qt#//\US&̩?dxx8Z6neUϔ z9~8wI332Ra#o$72L۶mh4r);Fii)HkQՄ8pbR x<=z7n?4 4/tsbG%KLPXz5Iʂ˚ H*}owtF4XN:$"rlh4\.~?(r=9/3+y" }}}K}}=6mD_qujjjrllfX,M@Auu5VBR)9~χFj5V5)Wq̫T*Ν;;1/̱cI777'{~DAp1b) ,^*//g֭T0$V\ɳ>**Çyp 9 er@ PS^^NUUYYYIgNS1%K+^;A-DQz B_)+)++5IMe5 G_Ȃ[F(twws~J?6TUU)XbMMM  3ha<:@ ((j._-[0s'O3 _֖F"rssٽ{w\A $xr V륶˗'ys o())abb6mD~~~JMD"*(_ .3'HE #///c=Tt/]r(qI=SLGәkdd@ǣ!F'&99 ٦ppZӁ1.7|`0hAHd46@ M$M>(UUUq$?͛)M/bEժTe@\Y !Pn~+ېӊXz5JEd2|pG_ -ZΝ;*mmmeڵL999<XbWݻwHfrLlvB!%noogٲenvڕM~E, SSSLTRzq˩֭[Jl/\K@Zi{*Ӥ™lV455)^pp}F#QEJEkk+vÑ deecJKKZ#"_A2l]v)l:.mۦT*, 555 122B8h4b4T6Cƍ\C0::JNNjZ*@ @qq1P('vt:UUUL&A' MFz*H寕Y)\A( ^Ncbb+Wb62T=UH?%HZdGo߾tRL& PQQ0(`bt]+WzKJv3 ۛO]]SKlp4#שּׁ,֭[f# ZB7Kgs@RI{>YT`/qDiQ"hzXL5 AQ]զ@[fsv;Ns*R@,W^eÆ I Joo/z@ @vv6&c8}4Ν!%gϞŋ %?V2& 96vZZZ<:b6߹~:Z)Ahoo`0}%@8{,ϟ)Dd'MzLrt:y뭷y&n7xCiLLL(Lipi}V8x7y& G o%͐Xz'E8h00 ܻwXYf (qFt T066HmRxx'Ev~+0mkY42|mHD>*{)/N@A^^맢 "qVI3 ^/_Niiiʗoݺ5E+:̎GloL*^M ۑc43hhhF mQhcIENDB`WorkBench-1.8.2/Source/toolbar_images/edit.png000644 000765 000024 00000005656 10365523613 021570 0ustar00barrystaff000000 000000 PNG  IHDR@@iqsBIT|d eIDATxilTް6v0n;5Ib hiHR[QZU Ĕ(,&AT-"|J% TjH(v =eƳسojlf/4g={wp;0(|7֓)F pP|NOp35/V1RGwv`P0?#'; QRyx3 u4XMVo' 3Sys=LK!3}Q&-5 s830D_1&瓝=0!!<:zMMMTWWn|~<>D$I$&'сP9۷og:0Z}*ʚx'rW~.d/ 0=dl6PZZhsہ'>3YQp #)Qn'UUUQUU@nn..[ƌl7)I( Ȥsߧ&#;cyTf0 lٲ{ C/()x",ܸyiꮘ=`G5Rg}5kXn|> y@Zǎp+`ZCD^n7ȲbH)- eY1wu筷x< %4I6K^$I";;Cvv6yyytuu1k,V+;w6 2W|G|=z䒒w؁G({UP¼T 9&mz)1@Nrx{}%0**/qw7qj!4Y~"_ruC3]fY@Y@E.Y\rM(0 Nщdľ}0d Fч*(* =Wlݺ5*.v Fk r"p!Xf^J4F{bJg]xmWGTGys۶mK\奥}tuu~qq1Ŭ]߿<"k j\O>vQRRC !=uIMMvy U9QUTUKT8|]B\KEEE, ~w?H Uw&Mq(//KЭ멯 0æBY_ETY2#pdpKkp<6g7#: 1p TTT<0<`Ϟ=L&ʖ=%sBUDTYB}PA?NǾi۷o_oDpGPD8t'Lee%sKaBLhiid2B.( o#uIO3Pe '~Oz+|9z{{#hL[VVFVVgϞ Tj6i(BFbDQv!  V1 mիWֆngɒ%иz.MݫWA$dEIDL*+%p>H/^o@D_9MxzXĽW fm.@-74?~\/` =f PPP] P@mmm_OOs'=d")) G x< nxF__X,.]Ҋz,0<tvv c$rݳnL!05@R\v /@2~l7,=@/hwL@3+PK(Aގ*uH,χyEQhFf>N(WVVׯy\!fn+ jN+^,2(U ApuuJuڐ $Jf-r%t:*B!Yv{{~ɕn7߬k C^Nϕ T+ 1!OlNLL}V)9 L؊NWy#Rz+B˖LK8Kx<Jg$ uuu,s p,ˆ(~Q 9 Ðe(t*T*==hnn}ڿr3eŶ*ery=UܩW<@$BBqǪg |:F*2(vJLˈR<9RXNXXXO5[$+DQT\F(J!j(oJ^[[[^ٱ*- uovxic۫1,\!ՅnbdxU`aZ*&HdDJ$ؗBp(R[aA Iir "U6-/e*`F3h-i-ixPPX9\!Ϡ(Z4:x^SF0! .P׋]=ZhлȈxllVWf Y#).|~MY!_UX-^X,UiDw{{kIxX\\NNNe*P]]voF4Lt:CD"Qy;b2  FvIENDB`WorkBench-1.8.2/Source/toolbar_images/onlychanges.png000644 000765 000024 00000000471 10701374354 023143 0ustar00barrystaff000000 000000 PNG  IHDR@@iqbKGD pHYs  tIME,+[VtEXtCommentCreated with The GIMPd%nIDATxڽ ap!+prr"m/||3>||>||>)|>|||)|| >(|| |(|| |(|| >'|| |'|| |'|| >&||{&|||&|||&||>%{|{#>{{{"=>{{{>#{>=>{{{{{>{{ {>{=>> =!=>z#==z$== =%{==&={z(==z)==='===z)z=z==)zz=zz*==zz+y=y==+=yy=y==y:=yy=3))((('' '&   &  &  &%   # !  ""##""!#) !#$&&'(('#%(+-!#%()*,,--,-,+*(')+%(*+-/00122110.-,)!$(*-0)+.013456654410.'+.14!$(*-024689::;;:9864%  /158'+.1468:<=>?@@??><:* ! %58; /158:=>@BCDEDDBA?# )>=6+<=??-7,>>944/44Ojj4/4j jO.4j j4.4j j4.4j jO-4j j4-4j j4-4j jO,4jjkjjkj4,4jjklmll5,4jjklmmoopoo7,4jjkmnopqrssU+5llnpqrtuuvu:+5npqstuvxxyzz{zyy;+7qrtvwyz{|}}~}~}}\=<(8svwy{{}}\'9wxz|}>$89Yy{}~@$8twy|~A$:wy|C$:z|~h"$\k#&_I( cK)!gt&*#jw',$nQ.%qR/&u*0'yV2(|,3*Z5+Z6,.7,\9-[\\.  /   / .  .  . -  -  - ,  ,  , ,  +   ! !! !+  "#""+ !!"##$#( !!"#$$%$#' ""#$%%&'&%$""#$%&&'('(('&&$ !"#$%&&''())*)'$!"#$%&'())*+**++*)($"#$&&'((**+,,-,+ $$%&'()**,,--.-..- & &((**,,--..//0/0/..( ()++,-./00101100/) ++,-.0011221$ * ,-//022344343% , !./11334554. #1124456766766/ %2446688988) 0 &56689::9:92 '778::;<;+3 )99;;<<5*;<==>=6+<>??.7,=>>9 @- @,,,@+++@****@)))@&@%"@"""""@"@@$@&@'@@(@@*@,@ -@ @.@ 0@@1@3@4@@5@7@@@@Arrow 2     H@@\@@l<:86 4 2 0.,*(&%)---------------------@@ Arrow copy      @@ @@ UVV 8 6 4 2 0 . , * ( & $ "     ! ## )))))))))))))))))))) 00 Files 1 copy      r#t00#00#%4GFGGGĦHHH۾HQ$IG II JA J1 JH ǀ JI KC K2 KJ KK LA Lҙ- LMメ MKȧ  MK MK  MK  MK  椨 ƭv Ĺ< ¹}  ε篧r ڞt184 42 %4GFGGGĦHHH۾HQ$IG II JA J1 JH Ӝ JI KCǜ K2 KJ KK  LA Lҙ- LM̪ MKԺ  MK MK ǜ MK  MKĜ Ɯ 椨 ƭv Ĺ< ¹}  ε篧r ڞt184 42 %4GFGGGĦHHH۾HQ$IG II JA J1 JH  JI KC K2 KJ KK  LA Lҙ- LM MK  MK MK  MK  MK  椨 ƭv Ĺ< ¹}  ε篧r ڞt184 42 ,668T|>|>|>|>|>|>|>|<|U|#*|#R|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#Oo#Ok}}wOOPA+KnUi00Files 1      2002002%4GFGGGĦHHH۾HQ$IG II JA J1 JH ǀ JI KC K2 KJ KK LA Lҙ- LMメ MKȧ  MK MK  MK  MK  椨 ƭv Ĺ< ¹}  ε篧r ڞt184 42 %4GFGGGĦHHH۾HQ$IG II JA J1 JH Ӝ JI KCǜ K2 KJ KK  LA Lҙ- LM̪ MKԺ  MK MK ǜ MK  MKĜ Ɯ 椨 ƭv Ĺ< ¹}  ε篧r ڞt184 42 %4GFGGGĦHHH۾HQ$IG II JA J1 JH  JI KC K2 KJ KK  LA Lҙ- LM MK  MK MK  MK  MK  椨 ƭv Ĺ< ¹}  ε篧r ڞt184 42 ,668T|>|>|>|>|>|>|>|<|U|#*|#R|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#O|#Oo#Ok}}wOOPA+KnUi@@ Background     B?@@BS@@Bc@@Selection Mask B@@B@@C 00///...----,,,)(%%%%%')*+-/01 3 4 678:WorkBench-1.8.2/Source/toolbar_images/exclude.png000644 000765 000024 00000006467 10373370234 022273 0ustar00barrystaff000000 000000 PNG  IHDR@@iqbKGDX pHYs  tIME!Y'G IDATx{lTיw16㱃y1ٰIYN" hH$V mڔBԕ6D$l"6"$`\X8 6ޙxy,sϙ}G L,08HjADiY?TѨ}sgd[AЉ:I 03Ð~1<_Q8;kܳ_ߺl56S 便P JDEla p??$Ҥ\i-/G`͡ Q~R藏$2!F`xOZ+NgOiH ~ZW:N3ڛ?2﮶3te'uwdzٱy.izAM;^)t^XD~ A.87>ʔLɔL7!v{񾾾}233Kt\0^ s=׼j@L0` )2H0//@ccSdҥgӉ( `ݺul۶ A(..Ni>rHj*&+N `dQxcG;J ־  7yucϞ=8NR%Σ(LLH9kr|>=J]]HAX,WNsr@oT7|1( eee|VesXhQ`12ϜBJ8ohbDnrJ6oL~~Xlq8e0ϊj$ViQ?uѡ 7wJ{{uu7o_b|cSSSX,477s;K!ϱB4].xb Xhg&++ YeIPF*++9|rE'wRZZ#oF:90db8}1zGV+eee%F4,fJИ3g+V&y>p7w:[. AUUtZ|}! )')%v{'8o<6l@AAyxMIGJg4&# Cfl2^/uuuTWWvZ 9ە7n܈餤-[`wuuhT#h%?VVz,((`Ȗl:&Rz jq{c4d.ꫯ 8q۶m瞻V*2˖-[n`'Op88ugϞ6m"iB H_hu`0X~=as>bPADLCuh1G"QUo~ @EEŘm fw]їǃjEQ, `l6`@NN4dYxz9V:U EK H5pyS-,^EQp:Rfu7zW^^ιs(}l. *^/(ňD"zn7L&~01O"0E4cٜ>}wG[ݍC|5|rfD$߻au8 p…6DIM\.JGP:H25;}~Sz8>j]6SmHD @[S kADQGV I'7Z( ;w~-2cJgeR5D *-Rix.Ő8e kp94,Da0rMM HO!wpx._uu5IJ}}Srظv fgDU}^z7|.}pD=/"2t<8⻞ P\,45 6>z8p`,jXa <.\H .uW]c; HOWO'⌜t>7UG3˱#pvI(b%NOrߡ}{CԺ<|zE8 @rCkkkvcKw WQx6@o7մbX())W?rx뭷xϠxXV֬YêG3:|Y7c'Ob6efu`u%^;0Pxk+̈Z Μ9իHΝ;Q/X$;چFgwH$!z86`.FN"122B2j455{g|6E}ꫯqFΝ;Ι3g8s O?4lڴ;wah z$/\o5+V~>44VA|71FcaTUe|||>_Ç?~h4J v1n,9c'1 + jo߾IHR u=Qپm9 i D0tR?zCtv:3" N_\! a9 k֬7}I4 VYa1i8z>CWoL T+3gp˲L*"n'?}K 133dY6~G^{JuCC_3 s2*U/xJD彛oCPFzOHl2~?sssy|2/^?&͢:Q,bݻw4?OuFA0\B u4Œ- unaL冑hI( MUU4MCDQDE>3vٽ{7x+_c]8Cg@)wy\ܶPh::hP_{ iw|D/p8 $I^Q(΋,ˌêU<ׯ__YSҀd"P6M7?whUVPUY(i|>B#"~p8,E br}&''!sϡ( 'N=l,h p+ 1TIA;^$&&&yօ2.R !XaʃK"6o~S-O(iN":K"&Վʢ@r Z O) h%'ڌDNްͮo( {s[jURQ8r&ުaSD0΄YWh3::ʾ}\lEphqIZb*UP'osMx,.:n%4Q`/]('CSQ!z\o b9MT9y$64ƊX,fiJ:Ӯ^|Ibę y2NW^]UDزek`pR)\Fb `0ȳ>RfSQc^y#@BrF*XnǛ0u$:id\5w qܹӔ# rJvQ@3(:ߪ( E,54^l)cWw000Ppr___Sm"TԪ HKmhRt0䫍p&B)JT۳QϘT*8D"D"A LN`MWkP(DwwwC,j`O)lOO{EUUm۶P(ؘc`0Õ-3s@Cf5FC9;pRlM0v;;;=;n( V"$8{ek;dYvm&A!T'?OWշ9]Rr\;G+Pk ؟ԛYǏ|?;V^_ @`Ujo(nv9cǎc^kzjg`DA#޼ys!mu'x&|K